• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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