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/ranges_copy.h>
14 #include <__algorithm/ranges_fill_n.h>
15 #include <__algorithm/ranges_for_each.h>
16 #include <__algorithm/ranges_transform.h>
17 #include <__charconv/to_chars_integral.h>
18 #include <__charconv/to_chars_result.h>
19 #include <__chrono/statically_widen.h>
20 #include <__concepts/same_as.h>
21 #include <__config>
22 #include <__format/buffer.h>
23 #include <__format/concepts.h>
24 #include <__format/escaped_output_table.h>
25 #include <__format/formatter.h>
26 #include <__format/parser_std_format_spec.h>
27 #include <__format/unicode.h>
28 #include <__iterator/back_insert_iterator.h>
29 #include <__iterator/concepts.h>
30 #include <__iterator/readable_traits.h> // iter_value_t
31 #include <__system_error/errc.h>
32 #include <__type_traits/make_unsigned.h>
33 #include <__utility/move.h>
34 #include <__utility/unreachable.h>
35 #include <cstddef>
36 #include <string>
37 #include <string_view>
38
39 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
40 # pragma GCC system_header
41 #endif
42
43 _LIBCPP_BEGIN_NAMESPACE_STD
44
45 #if _LIBCPP_STD_VER >= 20
46
47 namespace __formatter {
48
__hex_to_upper(char __c)49 _LIBCPP_HIDE_FROM_ABI constexpr char __hex_to_upper(char __c) {
50 switch (__c) {
51 case 'a':
52 return 'A';
53 case 'b':
54 return 'B';
55 case 'c':
56 return 'C';
57 case 'd':
58 return 'D';
59 case 'e':
60 return 'E';
61 case 'f':
62 return 'F';
63 }
64 return __c;
65 }
66
67 struct _LIBCPP_TYPE_VIS __padding_size_result {
68 size_t __before_;
69 size_t __after_;
70 };
71
72 _LIBCPP_HIDE_FROM_ABI constexpr __padding_size_result
__padding_size(size_t __size,size_t __width,__format_spec::__alignment __align)73 __padding_size(size_t __size, size_t __width, __format_spec::__alignment __align) {
74 _LIBCPP_ASSERT(__width > __size, "don't call this function when no padding is required");
75 _LIBCPP_ASSERT(
76 __align != __format_spec::__alignment::__zero_padding, "the caller should have handled the zero-padding");
77
78 size_t __fill = __width - __size;
79 switch (__align) {
80 case __format_spec::__alignment::__zero_padding:
81 __libcpp_unreachable();
82
83 case __format_spec::__alignment::__left:
84 return {0, __fill};
85
86 case __format_spec::__alignment::__center: {
87 // The extra padding is divided per [format.string.std]/3
88 // __before = floor(__fill, 2);
89 // __after = ceil(__fill, 2);
90 size_t __before = __fill / 2;
91 size_t __after = __fill - __before;
92 return {__before, __after};
93 }
94 case __format_spec::__alignment::__default:
95 case __format_spec::__alignment::__right:
96 return {__fill, 0};
97 }
98 __libcpp_unreachable();
99 }
100
101 /// Copy wrapper.
102 ///
103 /// This uses a "mass output function" of __format::__output_buffer when possible.
104 template <__fmt_char_type _CharT, __fmt_char_type _OutCharT = _CharT>
105 _LIBCPP_HIDE_FROM_ABI auto __copy(basic_string_view<_CharT> __str, output_iterator<const _OutCharT&> auto __out_it)
106 -> decltype(__out_it) {
107 if constexpr (_VSTD::same_as<decltype(__out_it), _VSTD::back_insert_iterator<__format::__output_buffer<_OutCharT>>>) {
108 __out_it.__get_container()->__copy(__str);
109 return __out_it;
110 } else if constexpr (_VSTD::same_as<decltype(__out_it),
111 typename __format::__retarget_buffer<_OutCharT>::__iterator>) {
112 __out_it.__buffer_->__copy(__str);
113 return __out_it;
114 } else {
115 return std::ranges::copy(__str, _VSTD::move(__out_it)).out;
116 }
117 }
118
119 template <__fmt_char_type _CharT, __fmt_char_type _OutCharT = _CharT>
120 _LIBCPP_HIDE_FROM_ABI auto
121 __copy(const _CharT* __first, const _CharT* __last, output_iterator<const _OutCharT&> auto __out_it)
122 -> decltype(__out_it) {
123 return __formatter::__copy(basic_string_view{__first, __last}, _VSTD::move(__out_it));
124 }
125
126 template <__fmt_char_type _CharT, __fmt_char_type _OutCharT = _CharT>
127 _LIBCPP_HIDE_FROM_ABI auto __copy(const _CharT* __first, size_t __n, output_iterator<const _OutCharT&> auto __out_it)
128 -> decltype(__out_it) {
129 return __formatter::__copy(basic_string_view{__first, __n}, _VSTD::move(__out_it));
130 }
131
132 /// Transform wrapper.
133 ///
134 /// This uses a "mass output function" of __format::__output_buffer when possible.
135 template <__fmt_char_type _CharT, __fmt_char_type _OutCharT = _CharT, class _UnaryOperation>
136 _LIBCPP_HIDE_FROM_ABI auto
137 __transform(const _CharT* __first,
138 const _CharT* __last,
139 output_iterator<const _OutCharT&> auto __out_it,
140 _UnaryOperation __operation) -> decltype(__out_it) {
141 if constexpr (_VSTD::same_as<decltype(__out_it), _VSTD::back_insert_iterator<__format::__output_buffer<_OutCharT>>>) {
142 __out_it.__get_container()->__transform(__first, __last, _VSTD::move(__operation));
143 return __out_it;
144 } else if constexpr (_VSTD::same_as<decltype(__out_it),
145 typename __format::__retarget_buffer<_OutCharT>::__iterator>) {
146 __out_it.__buffer_->__transform(__first, __last, _VSTD::move(__operation));
147 return __out_it;
148 } else {
149 return std::ranges::transform(__first, __last, _VSTD::move(__out_it), __operation).out;
150 }
151 }
152
153 /// Fill wrapper.
154 ///
155 /// This uses a "mass output function" of __format::__output_buffer when possible.
156 template <__fmt_char_type _CharT, output_iterator<const _CharT&> _OutIt>
__fill(_OutIt __out_it,size_t __n,_CharT __value)157 _LIBCPP_HIDE_FROM_ABI _OutIt __fill(_OutIt __out_it, size_t __n, _CharT __value) {
158 if constexpr (_VSTD::same_as<decltype(__out_it), _VSTD::back_insert_iterator<__format::__output_buffer<_CharT>>>) {
159 __out_it.__get_container()->__fill(__n, __value);
160 return __out_it;
161 } else if constexpr (_VSTD::same_as<decltype(__out_it), typename __format::__retarget_buffer<_CharT>::__iterator>) {
162 __out_it.__buffer_->__fill(__n, __value);
163 return __out_it;
164 } else {
165 return std::ranges::fill_n(_VSTD::move(__out_it), __n, __value);
166 }
167 }
168
169 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)170 _LIBCPP_HIDE_FROM_ABI _OutIt __write_using_decimal_separators(_OutIt __out_it, const char* __begin, const char* __first,
171 const char* __last, string&& __grouping, _CharT __sep,
172 __format_spec::__parsed_specifications<_CharT> __specs) {
173 int __size = (__first - __begin) + // [sign][prefix]
174 (__last - __first) + // data
175 (__grouping.size() - 1); // number of separator characters
176
177 __padding_size_result __padding = {0, 0};
178 if (__specs.__alignment_ == __format_spec::__alignment::__zero_padding) {
179 // Write [sign][prefix].
180 __out_it = __formatter::__copy(__begin, __first, _VSTD::move(__out_it));
181
182 if (__specs.__width_ > __size) {
183 // Write zero padding.
184 __padding.__before_ = __specs.__width_ - __size;
185 __out_it = __formatter::__fill(_VSTD::move(__out_it), __specs.__width_ - __size, _CharT('0'));
186 }
187 } else {
188 if (__specs.__width_ > __size) {
189 // Determine padding and write padding.
190 __padding = __formatter::__padding_size(__size, __specs.__width_, __specs.__alignment_);
191
192 __out_it = __formatter::__fill(_VSTD::move(__out_it), __padding.__before_, __specs.__fill_);
193 }
194 // Write [sign][prefix].
195 __out_it = __formatter::__copy(__begin, __first, _VSTD::move(__out_it));
196 }
197
198 auto __r = __grouping.rbegin();
199 auto __e = __grouping.rend() - 1;
200 _LIBCPP_ASSERT(__r != __e, "The slow grouping formatting is used while "
201 "there will be no separators written.");
202 // The output is divided in small groups of numbers to write:
203 // - A group before the first separator.
204 // - A separator and a group, repeated for the number of separators.
205 // - A group after the last separator.
206 // This loop achieves that process by testing the termination condition
207 // midway in the loop.
208 //
209 // TODO FMT This loop evaluates the loop invariant `__parser.__type !=
210 // _Flags::_Type::__hexadecimal_upper_case` for every iteration. (This test
211 // happens in the __write call.) Benchmark whether making two loops and
212 // hoisting the invariant is worth the effort.
213 while (true) {
214 if (__specs.__std_.__type_ == __format_spec::__type::__hexadecimal_upper_case) {
215 __last = __first + *__r;
216 __out_it = __formatter::__transform(__first, __last, _VSTD::move(__out_it), __hex_to_upper);
217 __first = __last;
218 } else {
219 __out_it = __formatter::__copy(__first, *__r, _VSTD::move(__out_it));
220 __first += *__r;
221 }
222
223 if (__r == __e)
224 break;
225
226 ++__r;
227 *__out_it++ = __sep;
228 }
229
230 return __formatter::__fill(_VSTD::move(__out_it), __padding.__after_, __specs.__fill_);
231 }
232
233 /// Writes the input to the output with the required padding.
234 ///
235 /// Since the output column width is specified the function can be used for
236 /// ASCII and Unicode output.
237 ///
238 /// \pre \a __size <= \a __width. Using this function when this pre-condition
239 /// doesn't hold incurs an unwanted overhead.
240 ///
241 /// \param __str The string to write.
242 /// \param __out_it The output iterator to write to.
243 /// \param __specs The parsed formatting specifications.
244 /// \param __size The (estimated) output column width. When the elements
245 /// to be written are ASCII the following condition holds
246 /// \a __size == \a __last - \a __first.
247 ///
248 /// \returns An iterator pointing beyond the last element written.
249 ///
250 /// \note The type of the elements in range [\a __first, \a __last) can differ
251 /// from the type of \a __specs. Integer output uses \c std::to_chars for its
252 /// conversion, which means the [\a __first, \a __last) always contains elements
253 /// of the type \c char.
254 template <class _CharT, class _ParserCharT>
255 _LIBCPP_HIDE_FROM_ABI auto
256 __write(basic_string_view<_CharT> __str,
257 output_iterator<const _CharT&> auto __out_it,
258 __format_spec::__parsed_specifications<_ParserCharT> __specs,
259 ptrdiff_t __size) -> decltype(__out_it) {
260 if (__size >= __specs.__width_)
261 return __formatter::__copy(__str, _VSTD::move(__out_it));
262
263 __padding_size_result __padding = __formatter::__padding_size(__size, __specs.__width_, __specs.__std_.__alignment_);
264 __out_it = __formatter::__fill(_VSTD::move(__out_it), __padding.__before_, __specs.__fill_);
265 __out_it = __formatter::__copy(__str, _VSTD::move(__out_it));
266 return __formatter::__fill(_VSTD::move(__out_it), __padding.__after_, __specs.__fill_);
267 }
268
269 template <contiguous_iterator _Iterator, class _ParserCharT>
270 _LIBCPP_HIDE_FROM_ABI auto
271 __write(_Iterator __first,
272 _Iterator __last,
273 output_iterator<const iter_value_t<_Iterator>&> auto __out_it,
274 __format_spec::__parsed_specifications<_ParserCharT> __specs,
275 ptrdiff_t __size) -> decltype(__out_it) {
276 _LIBCPP_ASSERT(__first <= __last, "Not a valid range");
277 return __formatter::__write(basic_string_view{__first, __last}, _VSTD::move(__out_it), __specs, __size);
278 }
279
280 /// \overload
281 ///
282 /// Calls the function above where \a __size = \a __last - \a __first.
283 template <contiguous_iterator _Iterator, class _ParserCharT>
284 _LIBCPP_HIDE_FROM_ABI auto
285 __write(_Iterator __first,
286 _Iterator __last,
287 output_iterator<const iter_value_t<_Iterator>&> auto __out_it,
288 __format_spec::__parsed_specifications<_ParserCharT> __specs) -> decltype(__out_it) {
289 _LIBCPP_ASSERT(__first <= __last, "Not a valid range");
290 return __formatter::__write(__first, __last, _VSTD::move(__out_it), __specs, __last - __first);
291 }
292
293 template <class _CharT, class _ParserCharT, class _UnaryOperation>
294 _LIBCPP_HIDE_FROM_ABI auto __write_transformed(const _CharT* __first, const _CharT* __last,
295 output_iterator<const _CharT&> auto __out_it,
296 __format_spec::__parsed_specifications<_ParserCharT> __specs,
297 _UnaryOperation __op) -> decltype(__out_it) {
298 _LIBCPP_ASSERT(__first <= __last, "Not a valid range");
299
300 ptrdiff_t __size = __last - __first;
301 if (__size >= __specs.__width_)
302 return __formatter::__transform(__first, __last, _VSTD::move(__out_it), __op);
303
304 __padding_size_result __padding = __formatter::__padding_size(__size, __specs.__width_, __specs.__alignment_);
305 __out_it = __formatter::__fill(_VSTD::move(__out_it), __padding.__before_, __specs.__fill_);
306 __out_it = __formatter::__transform(__first, __last, _VSTD::move(__out_it), __op);
307 return __formatter::__fill(_VSTD::move(__out_it), __padding.__after_, __specs.__fill_);
308 }
309
310 /// Writes additional zero's for the precision before the exponent.
311 /// This is used when the precision requested in the format string is larger
312 /// than the maximum precision of the floating-point type. These precision
313 /// digits are always 0.
314 ///
315 /// \param __exponent The location of the exponent character.
316 /// \param __num_trailing_zeros The number of 0's to write before the exponent
317 /// character.
318 template <class _CharT, class _ParserCharT>
319 _LIBCPP_HIDE_FROM_ABI auto __write_using_trailing_zeros(
320 const _CharT* __first,
321 const _CharT* __last,
322 output_iterator<const _CharT&> auto __out_it,
323 __format_spec::__parsed_specifications<_ParserCharT> __specs,
324 size_t __size,
325 const _CharT* __exponent,
326 size_t __num_trailing_zeros) -> decltype(__out_it) {
327 _LIBCPP_ASSERT(__first <= __last, "Not a valid range");
328 _LIBCPP_ASSERT(__num_trailing_zeros > 0, "The overload not writing trailing zeros should have been used");
329
330 __padding_size_result __padding =
331 __formatter::__padding_size(__size + __num_trailing_zeros, __specs.__width_, __specs.__alignment_);
332 __out_it = __formatter::__fill(_VSTD::move(__out_it), __padding.__before_, __specs.__fill_);
333 __out_it = __formatter::__copy(__first, __exponent, _VSTD::move(__out_it));
334 __out_it = __formatter::__fill(_VSTD::move(__out_it), __num_trailing_zeros, _CharT('0'));
335 __out_it = __formatter::__copy(__exponent, __last, _VSTD::move(__out_it));
336 return __formatter::__fill(_VSTD::move(__out_it), __padding.__after_, __specs.__fill_);
337 }
338
339 /// Writes a string using format's width estimation algorithm.
340 ///
341 /// \pre !__specs.__has_precision()
342 ///
343 /// \note When \c _LIBCPP_HAS_NO_UNICODE is defined the function assumes the
344 /// input is ASCII.
345 template <class _CharT>
346 _LIBCPP_HIDE_FROM_ABI auto __write_string_no_precision(
347 basic_string_view<_CharT> __str,
348 output_iterator<const _CharT&> auto __out_it,
349 __format_spec::__parsed_specifications<_CharT> __specs) -> decltype(__out_it) {
350 _LIBCPP_ASSERT(!__specs.__has_precision(), "use __write_string");
351
352 // No padding -> copy the string
353 if (!__specs.__has_width())
354 return __formatter::__copy(__str, _VSTD::move(__out_it));
355
356 // Note when the estimated width is larger than size there's no padding. So
357 // there's no reason to get the real size when the estimate is larger than or
358 // equal to the minimum field width.
359 size_t __size =
360 __format_spec::__estimate_column_width(__str, __specs.__width_, __format_spec::__column_width_rounding::__up)
361 .__width_;
362 return __formatter::__write(__str, _VSTD::move(__out_it), __specs, __size);
363 }
364
365 template <class _CharT>
__truncate(basic_string_view<_CharT> & __str,int __precision)366 _LIBCPP_HIDE_FROM_ABI int __truncate(basic_string_view<_CharT>& __str, int __precision) {
367 __format_spec::__column_width_result __result =
368 __format_spec::__estimate_column_width(__str, __precision, __format_spec::__column_width_rounding::__down);
369 __str = basic_string_view<_CharT>{__str.begin(), __result.__last_};
370 return __result.__width_;
371 }
372
373 /// Writes a string using format's width estimation algorithm.
374 ///
375 /// \note When \c _LIBCPP_HAS_NO_UNICODE is defined the function assumes the
376 /// input is ASCII.
377 template <class _CharT>
378 _LIBCPP_HIDE_FROM_ABI auto __write_string(
379 basic_string_view<_CharT> __str,
380 output_iterator<const _CharT&> auto __out_it,
381 __format_spec::__parsed_specifications<_CharT> __specs) -> decltype(__out_it) {
382 if (!__specs.__has_precision())
383 return __formatter::__write_string_no_precision(__str, _VSTD::move(__out_it), __specs);
384
385 int __size = __formatter::__truncate(__str, __specs.__precision_);
386
387 return __formatter::__write(__str.begin(), __str.end(), _VSTD::move(__out_it), __specs, __size);
388 }
389
390 # if _LIBCPP_STD_VER >= 23
391
392 struct __nul_terminator {};
393
394 template <class _CharT>
395 _LIBCPP_HIDE_FROM_ABI bool operator==(const _CharT* __cstr, __nul_terminator) {
396 return *__cstr == _CharT('\0');
397 }
398
399 template <class _CharT>
400 _LIBCPP_HIDE_FROM_ABI void
__write_escaped_code_unit(basic_string<_CharT> & __str,char32_t __value,const _CharT * __prefix)401 __write_escaped_code_unit(basic_string<_CharT>& __str, char32_t __value, const _CharT* __prefix) {
402 back_insert_iterator __out_it{__str};
403 std::ranges::copy(__prefix, __nul_terminator{}, __out_it);
404
405 char __buffer[8];
406 to_chars_result __r = std::to_chars(std::begin(__buffer), std::end(__buffer), __value, 16);
407 _LIBCPP_ASSERT(__r.ec == errc(0), "Internal buffer too small");
408 std::ranges::copy(std::begin(__buffer), __r.ptr, __out_it);
409
410 __str += _CharT('}');
411 }
412
413 // [format.string.escaped]/2.2.1.2
414 // ...
415 // then the sequence \u{hex-digit-sequence} is appended to E, where
416 // hex-digit-sequence is the shortest hexadecimal representation of C using
417 // lower-case hexadecimal digits.
418 template <class _CharT>
__write_well_formed_escaped_code_unit(basic_string<_CharT> & __str,char32_t __value)419 _LIBCPP_HIDE_FROM_ABI void __write_well_formed_escaped_code_unit(basic_string<_CharT>& __str, char32_t __value) {
420 __formatter::__write_escaped_code_unit(__str, __value, _LIBCPP_STATICALLY_WIDEN(_CharT, "\\u{"));
421 }
422
423 // [format.string.escaped]/2.2.3
424 // Otherwise (X is a sequence of ill-formed code units), each code unit U is
425 // appended to E in order as the sequence \x{hex-digit-sequence}, where
426 // hex-digit-sequence is the shortest hexadecimal representation of U using
427 // lower-case hexadecimal digits.
428 template <class _CharT>
__write_escape_ill_formed_code_unit(basic_string<_CharT> & __str,char32_t __value)429 _LIBCPP_HIDE_FROM_ABI void __write_escape_ill_formed_code_unit(basic_string<_CharT>& __str, char32_t __value) {
430 __formatter::__write_escaped_code_unit(__str, __value, _LIBCPP_STATICALLY_WIDEN(_CharT, "\\x{"));
431 }
432
433 template <class _CharT>
__is_escaped_sequence_written(basic_string<_CharT> & __str,char32_t __value)434 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool __is_escaped_sequence_written(basic_string<_CharT>& __str, char32_t __value) {
435 # ifdef _LIBCPP_HAS_NO_UNICODE
436 // For ASCII assume everything above 127 is printable.
437 if (__value > 127)
438 return false;
439 # endif
440
441 if (!__escaped_output_table::__needs_escape(__value))
442 return false;
443
444 __formatter::__write_well_formed_escaped_code_unit(__str, __value);
445 return true;
446 }
447
448 template <class _CharT>
__to_char32(_CharT __value)449 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr char32_t __to_char32(_CharT __value) {
450 return static_cast<make_unsigned_t<_CharT>>(__value);
451 }
452
453 enum class _LIBCPP_ENUM_VIS __escape_quotation_mark { __apostrophe, __double_quote };
454
455 // [format.string.escaped]/2
456 template <class _CharT>
457 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool
__is_escaped_sequence_written(basic_string<_CharT> & __str,char32_t __value,__escape_quotation_mark __mark)458 __is_escaped_sequence_written(basic_string<_CharT>& __str, char32_t __value, __escape_quotation_mark __mark) {
459 // 2.2.1.1 - Mapped character in [tab:format.escape.sequences]
460 switch (__value) {
461 case _CharT('\t'):
462 __str += _LIBCPP_STATICALLY_WIDEN(_CharT, "\\t");
463 return true;
464 case _CharT('\n'):
465 __str += _LIBCPP_STATICALLY_WIDEN(_CharT, "\\n");
466 return true;
467 case _CharT('\r'):
468 __str += _LIBCPP_STATICALLY_WIDEN(_CharT, "\\r");
469 return true;
470 case _CharT('\''):
471 if (__mark == __escape_quotation_mark::__apostrophe)
472 __str += _LIBCPP_STATICALLY_WIDEN(_CharT, R"(\')");
473 else
474 __str += __value;
475 return true;
476 case _CharT('"'):
477 if (__mark == __escape_quotation_mark::__double_quote)
478 __str += _LIBCPP_STATICALLY_WIDEN(_CharT, R"(\")");
479 else
480 __str += __value;
481 return true;
482 case _CharT('\\'):
483 __str += _LIBCPP_STATICALLY_WIDEN(_CharT, R"(\\)");
484 return true;
485
486 // 2.2.1.2 - Space
487 case _CharT(' '):
488 __str += __value;
489 return true;
490 }
491
492 // 2.2.2
493 // Otherwise, if X is a shift sequence, the effect on E and further
494 // decoding of S is unspecified.
495 // For now shift sequences are ignored and treated as Unicode. Other parts
496 // of the format library do the same. It's unknown how ostream treats them.
497 // TODO FMT determine what to do with shift sequences.
498
499 // 2.2.1.2.1 and 2.2.1.2.2 - Escape
500 return __formatter::__is_escaped_sequence_written(__str, __formatter::__to_char32(__value));
501 }
502
503 template <class _CharT>
504 _LIBCPP_HIDE_FROM_ABI void
__escape(basic_string<_CharT> & __str,basic_string_view<_CharT> __values,__escape_quotation_mark __mark)505 __escape(basic_string<_CharT>& __str, basic_string_view<_CharT> __values, __escape_quotation_mark __mark) {
506 __unicode::__code_point_view<_CharT> __view{__values.begin(), __values.end()};
507
508 while (!__view.__at_end()) {
509 auto __first = __view.__position();
510 typename __unicode::__consume_result __result = __view.__consume();
511 if (__result.__status == __unicode::__consume_result::__ok) {
512 if (!__formatter::__is_escaped_sequence_written(__str, __result.__code_point, __mark))
513 // 2.2.1.3 - Add the character
514 ranges::copy(__first, __view.__position(), std::back_insert_iterator(__str));
515 } else {
516 // 2.2.3 sequence of ill-formed code units
517 ranges::for_each(__first, __view.__position(), [&](_CharT __value) {
518 __formatter::__write_escape_ill_formed_code_unit(__str, __formatter::__to_char32(__value));
519 });
520 }
521 }
522 }
523
524 template <class _CharT>
525 _LIBCPP_HIDE_FROM_ABI auto
526 __format_escaped_char(_CharT __value,
527 output_iterator<const _CharT&> auto __out_it,
528 __format_spec::__parsed_specifications<_CharT> __specs) -> decltype(__out_it) {
529 basic_string<_CharT> __str;
530 __str += _CharT('\'');
531 __formatter::__escape(__str, basic_string_view{std::addressof(__value), 1}, __escape_quotation_mark::__apostrophe);
532 __str += _CharT('\'');
533 return __formatter::__write(__str.data(), __str.data() + __str.size(), _VSTD::move(__out_it), __specs, __str.size());
534 }
535
536 template <class _CharT>
537 _LIBCPP_HIDE_FROM_ABI auto
538 __format_escaped_string(basic_string_view<_CharT> __values,
539 output_iterator<const _CharT&> auto __out_it,
540 __format_spec::__parsed_specifications<_CharT> __specs) -> decltype(__out_it) {
541 basic_string<_CharT> __str;
542 __str += _CharT('"');
543 __formatter::__escape(__str, __values, __escape_quotation_mark::__double_quote);
544 __str += _CharT('"');
545 return __formatter::__write_string(basic_string_view{__str}, _VSTD::move(__out_it), __specs);
546 }
547
548 # endif // _LIBCPP_STD_VER >= 23
549
550 } // namespace __formatter
551
552 #endif //_LIBCPP_STD_VER >= 20
553
554 _LIBCPP_END_NAMESPACE_STD
555
556 #endif // _LIBCPP___FORMAT_FORMATTER_OUTPUT_H
557