1 // -*- C++ -*-
2 //===----------------------------------------------------------------------===//
3 //
4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 // See https://llvm.org/LICENSE.txt for license information.
6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //
8 //===----------------------------------------------------------------------===//
9
10 #ifndef _LIBCPP___FORMAT_FORMATTER_OUTPUT_H
11 #define _LIBCPP___FORMAT_FORMATTER_OUTPUT_H
12
13 #include <__algorithm/copy.h>
14 #include <__algorithm/copy_n.h>
15 #include <__algorithm/fill_n.h>
16 #include <__algorithm/transform.h>
17 #include <__config>
18 #include <__format/formatter.h>
19 #include <__format/parser_std_format_spec.h>
20 #include <__format/unicode.h>
21 #include <__utility/move.h>
22 #include <__utility/unreachable.h>
23 #include <cstddef>
24 #include <string>
25 #include <string_view>
26
27 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
28 # pragma GCC system_header
29 #endif
30
31 _LIBCPP_BEGIN_NAMESPACE_STD
32
33 #if _LIBCPP_STD_VER > 17
34
35 namespace __formatter {
36
__hex_to_upper(char __c)37 _LIBCPP_HIDE_FROM_ABI constexpr char __hex_to_upper(char __c) {
38 switch (__c) {
39 case 'a':
40 return 'A';
41 case 'b':
42 return 'B';
43 case 'c':
44 return 'C';
45 case 'd':
46 return 'D';
47 case 'e':
48 return 'E';
49 case 'f':
50 return 'F';
51 }
52 return __c;
53 }
54
55 struct _LIBCPP_TYPE_VIS __padding_size_result {
56 size_t __before_;
57 size_t __after_;
58 };
59
60 _LIBCPP_HIDE_FROM_ABI constexpr __padding_size_result
__padding_size(size_t __size,size_t __width,__format_spec::__alignment __align)61 __padding_size(size_t __size, size_t __width, __format_spec::__alignment __align) {
62 _LIBCPP_ASSERT(__width > __size, "don't call this function when no padding is required");
63 _LIBCPP_ASSERT(
64 __align != __format_spec::__alignment::__zero_padding, "the caller should have handled the zero-padding");
65
66 size_t __fill = __width - __size;
67 switch (__align) {
68 case __format_spec::__alignment::__zero_padding:
69 __libcpp_unreachable();
70
71 case __format_spec::__alignment::__left:
72 return {0, __fill};
73
74 case __format_spec::__alignment::__center: {
75 // The extra padding is divided per [format.string.std]/3
76 // __before = floor(__fill, 2);
77 // __after = ceil(__fill, 2);
78 size_t __before = __fill / 2;
79 size_t __after = __fill - __before;
80 return {__before, __after};
81 }
82 case __format_spec::__alignment::__default:
83 case __format_spec::__alignment::__right:
84 return {__fill, 0};
85 }
86 __libcpp_unreachable();
87 }
88
89 template <class _OutIt, class _CharT>
__write_using_decimal_separators(_OutIt __out_it,const char * __begin,const char * __first,const char * __last,string && __grouping,_CharT __sep,__format_spec::__parsed_specifications<_CharT> __specs)90 _LIBCPP_HIDE_FROM_ABI _OutIt __write_using_decimal_separators(_OutIt __out_it, const char* __begin, const char* __first,
91 const char* __last, string&& __grouping, _CharT __sep,
92 __format_spec::__parsed_specifications<_CharT> __specs) {
93 int __size = (__first - __begin) + // [sign][prefix]
94 (__last - __first) + // data
95 (__grouping.size() - 1); // number of separator characters
96
97 __padding_size_result __padding = {0, 0};
98 if (__specs.__alignment_ == __format_spec::__alignment::__zero_padding) {
99 // Write [sign][prefix].
100 __out_it = _VSTD::copy(__begin, __first, _VSTD::move(__out_it));
101
102 if (__specs.__width_ > __size) {
103 // Write zero padding.
104 __padding.__before_ = __specs.__width_ - __size;
105 __out_it = _VSTD::fill_n(_VSTD::move(__out_it), __specs.__width_ - __size, _CharT('0'));
106 }
107 } else {
108 if (__specs.__width_ > __size) {
109 // Determine padding and write padding.
110 __padding = __padding_size(__size, __specs.__width_, __specs.__alignment_);
111
112 __out_it = _VSTD::fill_n(_VSTD::move(__out_it), __padding.__before_, __specs.__fill_);
113 }
114 // Write [sign][prefix].
115 __out_it = _VSTD::copy(__begin, __first, _VSTD::move(__out_it));
116 }
117
118 auto __r = __grouping.rbegin();
119 auto __e = __grouping.rend() - 1;
120 _LIBCPP_ASSERT(__r != __e, "The slow grouping formatting is used while "
121 "there will be no separators written.");
122 // The output is divided in small groups of numbers to write:
123 // - A group before the first separator.
124 // - A separator and a group, repeated for the number of separators.
125 // - A group after the last separator.
126 // This loop achieves that process by testing the termination condition
127 // midway in the loop.
128 //
129 // TODO FMT This loop evaluates the loop invariant `__parser.__type !=
130 // _Flags::_Type::__hexadecimal_upper_case` for every iteration. (This test
131 // happens in the __write call.) Benchmark whether making two loops and
132 // hoisting the invariant is worth the effort.
133 while (true) {
134 if (__specs.__std_.__type_ == __format_spec::__type::__hexadecimal_upper_case) {
135 __last = __first + *__r;
136 __out_it = _VSTD::transform(__first, __last, _VSTD::move(__out_it), __hex_to_upper);
137 __first = __last;
138 } else {
139 __out_it = _VSTD::copy_n(__first, *__r, _VSTD::move(__out_it));
140 __first += *__r;
141 }
142
143 if (__r == __e)
144 break;
145
146 ++__r;
147 *__out_it++ = __sep;
148 }
149
150 return _VSTD::fill_n(_VSTD::move(__out_it), __padding.__after_, __specs.__fill_);
151 }
152
153 /// Writes the input to the output with the required padding.
154 ///
155 /// Since the output column width is specified the function can be used for
156 /// ASCII and Unicode output.
157 ///
158 /// \pre [\a __first, \a __last) is a valid range.
159 /// \pre \a __size <= \a __width. Using this function when this pre-condition
160 /// doesn't hold incurs an unwanted overhead.
161 ///
162 /// \param __first Pointer to the first element to write.
163 /// \param __last Pointer beyond the last element to write.
164 /// \param __out_it The output iterator to write to.
165 /// \param __specs The parsed formatting specifications.
166 /// \param __size The (estimated) output column width. When the elements
167 /// to be written are ASCII the following condition holds
168 /// \a __size == \a __last - \a __first.
169 ///
170 /// \returns An iterator pointing beyond the last element written.
171 ///
172 /// \note The type of the elements in range [\a __first, \a __last) can differ
173 /// from the type of \a __specs. Integer output uses \c std::to_chars for its
174 /// conversion, which means the [\a __first, \a __last) always contains elements
175 /// of the type \c char.
176 template <class _CharT, class _ParserCharT>
177 _LIBCPP_HIDE_FROM_ABI auto __write(
178 const _CharT* __first,
179 const _CharT* __last,
180 output_iterator<const _CharT&> auto __out_it,
181 __format_spec::__parsed_specifications<_ParserCharT> __specs,
182 ptrdiff_t __size) -> decltype(__out_it) {
183 _LIBCPP_ASSERT(__first <= __last, "Not a valid range");
184
185 if (__size >= __specs.__width_)
186 return _VSTD::copy(__first, __last, _VSTD::move(__out_it));
187
188 __padding_size_result __padding = __formatter::__padding_size(__size, __specs.__width_, __specs.__std_.__alignment_);
189 __out_it = _VSTD::fill_n(_VSTD::move(__out_it), __padding.__before_, __specs.__fill_);
190 __out_it = _VSTD::copy(__first, __last, _VSTD::move(__out_it));
191 return _VSTD::fill_n(_VSTD::move(__out_it), __padding.__after_, __specs.__fill_);
192 }
193
194 /// \overload
195 ///
196 /// Calls the function above where \a __size = \a __last - \a __first.
197 template <class _CharT, class _ParserCharT>
198 _LIBCPP_HIDE_FROM_ABI auto __write(const _CharT* __first, const _CharT* __last,
199 output_iterator<const _CharT&> auto __out_it,
200 __format_spec::__parsed_specifications<_ParserCharT> __specs) -> decltype(__out_it) {
201 return __write(__first, __last, _VSTD::move(__out_it), __specs, __last - __first);
202 }
203
204 template <class _CharT, class _ParserCharT, class _UnaryOperation>
205 _LIBCPP_HIDE_FROM_ABI auto __write_transformed(const _CharT* __first, const _CharT* __last,
206 output_iterator<const _CharT&> auto __out_it,
207 __format_spec::__parsed_specifications<_ParserCharT> __specs,
208 _UnaryOperation __op) -> decltype(__out_it) {
209 _LIBCPP_ASSERT(__first <= __last, "Not a valid range");
210
211 ptrdiff_t __size = __last - __first;
212 if (__size >= __specs.__width_)
213 return _VSTD::transform(__first, __last, _VSTD::move(__out_it), __op);
214
215 __padding_size_result __padding = __padding_size(__size, __specs.__width_, __specs.__alignment_);
216 __out_it = _VSTD::fill_n(_VSTD::move(__out_it), __padding.__before_, __specs.__fill_);
217 __out_it = _VSTD::transform(__first, __last, _VSTD::move(__out_it), __op);
218 return _VSTD::fill_n(_VSTD::move(__out_it), __padding.__after_, __specs.__fill_);
219 }
220
221 /// Writes additional zero's for the precision before the exponent.
222 /// This is used when the precision requested in the format string is larger
223 /// than the maximum precision of the floating-point type. These precision
224 /// digits are always 0.
225 ///
226 /// \param __exponent The location of the exponent character.
227 /// \param __num_trailing_zeros The number of 0's to write before the exponent
228 /// character.
229 template <class _CharT, class _ParserCharT>
230 _LIBCPP_HIDE_FROM_ABI auto __write_using_trailing_zeros(
231 const _CharT* __first,
232 const _CharT* __last,
233 output_iterator<const _CharT&> auto __out_it,
234 __format_spec::__parsed_specifications<_ParserCharT> __specs,
235 size_t __size,
236 const _CharT* __exponent,
237 size_t __num_trailing_zeros) -> decltype(__out_it) {
238 _LIBCPP_ASSERT(__first <= __last, "Not a valid range");
239 _LIBCPP_ASSERT(__num_trailing_zeros > 0, "The overload not writing trailing zeros should have been used");
240
241 __padding_size_result __padding =
242 __padding_size(__size + __num_trailing_zeros, __specs.__width_, __specs.__alignment_);
243 __out_it = _VSTD::fill_n(_VSTD::move(__out_it), __padding.__before_, __specs.__fill_);
244 __out_it = _VSTD::copy(__first, __exponent, _VSTD::move(__out_it));
245 __out_it = _VSTD::fill_n(_VSTD::move(__out_it), __num_trailing_zeros, _CharT('0'));
246 __out_it = _VSTD::copy(__exponent, __last, _VSTD::move(__out_it));
247 return _VSTD::fill_n(_VSTD::move(__out_it), __padding.__after_, __specs.__fill_);
248 }
249
250 /// Writes a string using format's width estimation algorithm.
251 ///
252 /// \pre !__specs.__has_precision()
253 ///
254 /// \note When \c _LIBCPP_HAS_NO_UNICODE is defined the function assumes the
255 /// input is ASCII.
256 template <class _CharT>
257 _LIBCPP_HIDE_FROM_ABI auto __write_string_no_precision(
258 basic_string_view<_CharT> __str,
259 output_iterator<const _CharT&> auto __out_it,
260 __format_spec::__parsed_specifications<_CharT> __specs) -> decltype(__out_it) {
261 _LIBCPP_ASSERT(!__specs.__has_precision(), "use __write_string");
262
263 // No padding -> copy the string
264 if (!__specs.__has_width())
265 return _VSTD::copy(__str.begin(), __str.end(), _VSTD::move(__out_it));
266
267 // Note when the estimated width is larger than size there's no padding. So
268 // there's no reason to get the real size when the estimate is larger than or
269 // equal to the minimum field width.
270 size_t __size =
271 __format_spec::__estimate_column_width(__str, __specs.__width_, __format_spec::__column_width_rounding::__up)
272 .__width_;
273
274 return __formatter::__write(__str.begin(), __str.end(), _VSTD::move(__out_it), __specs, __size);
275 }
276
277 template <class _CharT>
__truncate(basic_string_view<_CharT> & __str,int __precision)278 _LIBCPP_HIDE_FROM_ABI int __truncate(basic_string_view<_CharT>& __str, int __precision) {
279 __format_spec::__column_width_result<_CharT> __result =
280 __format_spec::__estimate_column_width(__str, __precision, __format_spec::__column_width_rounding::__down);
281 __str = basic_string_view<_CharT>{__str.begin(), __result.__last_};
282 return __result.__width_;
283 }
284
285 /// Writes a string using format's width estimation algorithm.
286 ///
287 /// \note When \c _LIBCPP_HAS_NO_UNICODE is defined the function assumes the
288 /// input is ASCII.
289 template <class _CharT>
290 _LIBCPP_HIDE_FROM_ABI auto __write_string(
291 basic_string_view<_CharT> __str,
292 output_iterator<const _CharT&> auto __out_it,
293 __format_spec::__parsed_specifications<_CharT> __specs) -> decltype(__out_it) {
294 if (!__specs.__has_precision())
295 return __formatter::__write_string_no_precision(__str, _VSTD::move(__out_it), __specs);
296
297 int __size = __formatter::__truncate(__str, __specs.__precision_);
298
299 return __write(__str.begin(), __str.end(), _VSTD::move(__out_it), __specs, __size);
300 }
301
302 } // namespace __formatter
303
304 #endif //_LIBCPP_STD_VER > 17
305
306 _LIBCPP_END_NAMESPACE_STD
307
308 #endif // _LIBCPP___FORMAT_FORMATTER_OUTPUT_H
309