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