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
42namespace __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
62struct _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.
99template <__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
114template <__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
121template <__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.
130template <__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.
151template <__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
164template <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.
249template <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
264template <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.
278template <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
288template <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.
313template <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.
340template <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
360template <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.
372template <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
387struct __nul_terminator {};
388
389template <class _CharT>
390_LIBCPP_HIDE_FROM_ABI bool operator==(const _CharT* __cstr, __nul_terminator) {
391  return *__cstr == _CharT('\0');
392}
393
394template <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.
413template <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.
423template <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
428template <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
443template <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
448enum class _LIBCPP_ENUM_VIS __escape_quotation_mark { __apostrophe, __double_quote };
449
450// [format.string.escaped]/2
451template <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
498template <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
538template <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
550template <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