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