| // <format> Formatting -*- C++ -*- | |
| // Copyright The GNU Toolchain Authors. | |
| // | |
| // This file is part of the GNU ISO C++ Library. This library is free | |
| // software; you can redistribute it and/or modify it under the | |
| // terms of the GNU General Public License as published by the | |
| // Free Software Foundation; either version 3, or (at your option) | |
| // any later version. | |
| // This library is distributed in the hope that it will be useful, | |
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| // GNU General Public License for more details. | |
| // Under Section 7 of GPL version 3, you are granted additional | |
| // permissions described in the GCC Runtime Library Exception, version | |
| // 3.1, as published by the Free Software Foundation. | |
| // You should have received a copy of the GNU General Public License and | |
| // a copy of the GCC Runtime Library Exception along with this program; | |
| // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see | |
| // <http://www.gnu.org/licenses/>. | |
| /** @file include/format | |
| * This is a Standard C++ Library header. | |
| */ | |
| #ifndef _GLIBCXX_FORMAT | |
| #define _GLIBCXX_FORMAT 1 | |
| #ifdef _GLIBCXX_SYSHDR | |
| #pragma GCC system_header | |
| #endif | |
| #include <bits/requires_hosted.h> // for std::string | |
| #define __glibcxx_want_format | |
| #define __glibcxx_want_format_ranges | |
| #define __glibcxx_want_format_uchar | |
| #define __glibcxx_want_constexpr_exceptions | |
| #define __glibcxx_want_constexpr_format | |
| #include <bits/version.h> | |
| #ifdef __cpp_lib_format // C++ >= 20 && HOSTED | |
| #include <array> | |
| #include <charconv> | |
| #include <concepts> | |
| #include <limits> | |
| #include <locale> | |
| #include <optional> | |
| #include <span> | |
| #include <string_view> | |
| #include <string> | |
| #include <bits/monostate.h> | |
| #include <bits/formatfwd.h> | |
| #include <bits/ranges_base.h> // input_range, range_reference_t | |
| #include <bits/ranges_util.h> // subrange | |
| #include <bits/ranges_algobase.h> // ranges::copy | |
| #include <bits/stl_iterator.h> // counted_iterator | |
| #include <bits/stl_pair.h> // __is_pair | |
| #include <bits/unicode.h> // __is_scalar_value, _Utf_view, etc. | |
| #include <bits/utility.h> // tuple_size_v | |
| #include <ext/numeric_traits.h> // __int_traits | |
| #ifdef __glibcxx_constexpr_format // C++ >= 26 && HOSTED && CXX11 strings | |
| # define _GLIBCXX_CONSTEXPR_FORMAT constexpr | |
| #else | |
| # define _GLIBCXX_CONSTEXPR_FORMAT | |
| #endif | |
| #pragma GCC diagnostic push | |
| #pragma GCC diagnostic ignored "-Wpedantic" // __int128 | |
| #pragma GCC diagnostic ignored "-Wc++23-extensions" // bf16 | |
| namespace std _GLIBCXX_VISIBILITY(default) | |
| { | |
| _GLIBCXX_BEGIN_NAMESPACE_VERSION | |
| // [format.fmt.string], class template basic_format_string | |
| template<typename _CharT, typename... _Args> struct basic_format_string; | |
| /// @cond undocumented | |
| namespace __format | |
| { | |
| // STATICALLY-WIDEN, see C++20 [time.general] | |
| // It doesn't matter for format strings (which can only be char or wchar_t) | |
| // but this returns the narrow string for anything that isn't wchar_t. This | |
| // is done because const char* can be inserted into any ostream type, and | |
| // will be widened at runtime if necessary. | |
| template<typename _CharT> | |
| consteval auto | |
| _Widen(const char* __narrow, const wchar_t* __wide) | |
| { | |
| if constexpr (is_same_v<_CharT, wchar_t>) | |
| return __wide; | |
| else | |
| return __narrow; | |
| } | |
| #define _GLIBCXX_WIDEN_(C, S) ::std::__format::_Widen<C>(S, L##S) | |
| #define _GLIBCXX_WIDEN(S) _GLIBCXX_WIDEN_(_CharT, S) | |
| // Size for stack located buffer | |
| template<typename _CharT> | |
| constexpr size_t __stackbuf_size = 32 * sizeof(void*) / sizeof(_CharT); | |
| // Type-erased character sinks. | |
| template<typename _CharT> class _Sink; | |
| template<typename _CharT> class _Fixedbuf_sink; | |
| template<typename _Out, typename _CharT> class _Padding_sink; | |
| template<typename _Out, typename _CharT> class _Escaping_sink; | |
| // Output iterator that writes to a type-erase character sink. | |
| template<typename _CharT> | |
| class _Sink_iter; | |
| // Output iterator that ignores the characters | |
| template<typename _CharT> | |
| class _Drop_iter; | |
| // An unspecified output iterator type used in the `formattable` concept. | |
| template<typename _CharT> | |
| struct _Iter_for | |
| { using type = _Drop_iter<_CharT>; }; | |
| template<typename _CharT> | |
| using __format_context = basic_format_context<_Sink_iter<_CharT>, _CharT>; | |
| template<typename _CharT> | |
| struct _Dynamic_format_string | |
| { | |
| [[__gnu__::__always_inline__]] | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| _Dynamic_format_string(basic_string_view<_CharT> __s) noexcept | |
| : _M_str(__s) { } | |
| _Dynamic_format_string(const _Dynamic_format_string&) = delete; | |
| void operator=(const _Dynamic_format_string&) = delete; | |
| private: | |
| basic_string_view<_CharT> _M_str; | |
| template<typename, typename...> friend struct std::basic_format_string; | |
| }; | |
| } // namespace __format | |
| /// @endcond | |
| using format_context = __format::__format_context<char>; | |
| #ifdef _GLIBCXX_USE_WCHAR_T | |
| using wformat_context = __format::__format_context<wchar_t>; | |
| #endif | |
| // [format.args], class template basic_format_args | |
| template<typename _Context> class basic_format_args; | |
| using format_args = basic_format_args<format_context>; | |
| #ifdef _GLIBCXX_USE_WCHAR_T | |
| using wformat_args = basic_format_args<wformat_context>; | |
| #endif | |
| // [format.arguments], arguments | |
| // [format.arg], class template basic_format_arg | |
| template<typename _Context> | |
| class basic_format_arg; | |
| /** A compile-time checked format string for the specified argument types. | |
| * | |
| * @since C++23 but available as an extension in C++20. | |
| */ | |
| template<typename _CharT, typename... _Args> | |
| struct basic_format_string | |
| { | |
| template<typename _Tp> | |
| requires convertible_to<const _Tp&, basic_string_view<_CharT>> | |
| consteval | |
| basic_format_string(const _Tp& __s) noexcept; | |
| [[__gnu__::__always_inline__]] | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| basic_format_string(__format::_Dynamic_format_string<_CharT> __s) noexcept | |
| : _M_str(__s._M_str) | |
| { } | |
| [[__gnu__::__always_inline__]] | |
| constexpr basic_string_view<_CharT> | |
| get() const noexcept | |
| { return _M_str; } | |
| private: | |
| basic_string_view<_CharT> _M_str; | |
| }; | |
| template<typename... _Args> | |
| using format_string = basic_format_string<char, type_identity_t<_Args>...>; | |
| #ifdef _GLIBCXX_USE_WCHAR_T | |
| template<typename... _Args> | |
| using wformat_string | |
| = basic_format_string<wchar_t, type_identity_t<_Args>...>; | |
| #endif | |
| #if __cpp_lib_format >= 202603L // >= C++26 | |
| [[__gnu__::__always_inline__]] | |
| inline _GLIBCXX_CONSTEXPR_FORMAT __format::_Dynamic_format_string<char> | |
| dynamic_format(string_view __fmt) noexcept | |
| { return __fmt; } | |
| #ifdef _GLIBCXX_USE_WCHAR_T | |
| [[__gnu__::__always_inline__]] | |
| inline _GLIBCXX_CONSTEXPR_FORMAT __format::_Dynamic_format_string<wchar_t> | |
| dynamic_format(wstring_view __fmt) noexcept | |
| { return __fmt; } | |
| #endif | |
| #endif // C++26 | |
| // [format.formatter], formatter | |
| /// The primary template of std::formatter is disabled. | |
| template<typename _Tp, typename _CharT> | |
| struct formatter | |
| { | |
| formatter() = delete; // No std::formatter specialization for this type. | |
| formatter(const formatter&) = delete; | |
| formatter& operator=(const formatter&) = delete; | |
| }; | |
| // [format.error], class format_error | |
| class format_error : public runtime_error | |
| { | |
| public: | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| explicit format_error(const string& __what) | |
| : runtime_error(__what) { } | |
| _GLIBCXX_CONSTEXPR_FORMAT explicit | |
| format_error(const char* __what) | |
| : runtime_error(__what) { } | |
| }; | |
| /// @cond undocumented | |
| [[noreturn]] | |
| inline _GLIBCXX_CONSTEXPR_FORMAT void | |
| __throw_format_error(const char* __what) | |
| { _GLIBCXX_THROW_OR_ABORT(format_error(__what)); } | |
| namespace __format | |
| { | |
| // XXX use named functions for each constexpr error? | |
| [[noreturn]] | |
| inline _GLIBCXX_CONSTEXPR_FORMAT void | |
| __unmatched_left_brace_in_format_string() | |
| { __throw_format_error("format error: unmatched '{' in format string"); } | |
| [[noreturn]] | |
| inline _GLIBCXX_CONSTEXPR_FORMAT void | |
| __unmatched_right_brace_in_format_string() | |
| { __throw_format_error("format error: unmatched '}' in format string"); } | |
| [[noreturn]] | |
| inline _GLIBCXX_CONSTEXPR_FORMAT void | |
| __conflicting_indexing_in_format_string() | |
| { __throw_format_error("format error: conflicting indexing style in format string"); } | |
| [[noreturn]] | |
| inline _GLIBCXX_CONSTEXPR_FORMAT void | |
| __invalid_arg_id_in_format_string() | |
| { __throw_format_error("format error: invalid arg-id in format string"); } | |
| [[noreturn]] | |
| inline _GLIBCXX_CONSTEXPR_FORMAT void | |
| __failed_to_parse_format_spec() | |
| { __throw_format_error("format error: failed to parse format-spec"); } | |
| template<typename _CharT> class _Scanner; | |
| enum class _Arg_t : unsigned char { | |
| _Arg_none, _Arg_bool, _Arg_c, _Arg_i, _Arg_u, _Arg_ll, _Arg_ull, | |
| _Arg_flt, _Arg_dbl, _Arg_ldbl, _Arg_str, _Arg_sv, _Arg_ptr, _Arg_handle, | |
| _Arg_i128, _Arg_u128, _Arg_float128, | |
| _Arg_bf16, _Arg_f16, _Arg_f32, _Arg_f64, | |
| _Arg_max_, | |
| #ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT | |
| _Arg_ibm128 = _Arg_ldbl, | |
| _Arg_ieee128 = _Arg_float128, | |
| #endif | |
| }; | |
| using enum _Arg_t; | |
| template<typename _CharT, typename _Tp> | |
| consteval _Arg_t | |
| __to_arg_t_enum() noexcept; | |
| } // namespace __format | |
| /// @endcond | |
| // [format.parse.ctx], class template basic_format_parse_context | |
| template<typename _CharT> class basic_format_parse_context; | |
| using format_parse_context = basic_format_parse_context<char>; | |
| #ifdef _GLIBCXX_USE_WCHAR_T | |
| using wformat_parse_context = basic_format_parse_context<wchar_t>; | |
| #endif | |
| template<typename _CharT> | |
| // _GLIBCXX_RESOLVE_LIB_DEFECTS | |
| // 3975. Specializations of basic_format_context should not be permitted | |
| class _GLIBCXX_NO_SPECIALIZATIONS basic_format_parse_context | |
| { | |
| public: | |
| using char_type = _CharT; | |
| using const_iterator = typename basic_string_view<_CharT>::const_iterator; | |
| using iterator = const_iterator; | |
| constexpr explicit | |
| basic_format_parse_context(basic_string_view<_CharT> __fmt) noexcept | |
| : _M_begin(__fmt.begin()), _M_end(__fmt.end()) | |
| { } | |
| basic_format_parse_context(const basic_format_parse_context&) = delete; | |
| void operator=(const basic_format_parse_context&) = delete; | |
| constexpr const_iterator begin() const noexcept { return _M_begin; } | |
| constexpr const_iterator end() const noexcept { return _M_end; } | |
| constexpr void | |
| advance_to(const_iterator __it) noexcept | |
| { _M_begin = __it; } | |
| constexpr size_t | |
| next_arg_id() | |
| { | |
| if (_M_indexing == _Manual) | |
| __format::__conflicting_indexing_in_format_string(); | |
| _M_indexing = _Auto; | |
| // _GLIBCXX_RESOLVE_LIB_DEFECTS | |
| // 3825. Missing compile-time argument id check in next_arg_id | |
| if (std::is_constant_evaluated()) | |
| if (_M_next_arg_id == _M_num_args) | |
| __format::__invalid_arg_id_in_format_string(); | |
| return _M_next_arg_id++; | |
| } | |
| constexpr void | |
| check_arg_id(size_t __id) | |
| { | |
| if (_M_indexing == _Auto) | |
| __format::__conflicting_indexing_in_format_string(); | |
| _M_indexing = _Manual; | |
| if (std::is_constant_evaluated()) | |
| if (__id >= _M_num_args) | |
| __format::__invalid_arg_id_in_format_string(); | |
| } | |
| #if __cpp_lib_format >= 202305L // >= C++26 | |
| template<typename... _Ts> | |
| constexpr void | |
| check_dynamic_spec(size_t __id) noexcept | |
| { | |
| static_assert(__valid_types_for_check_dynamic_spec<_Ts...>(), | |
| "template arguments for check_dynamic_spec<Ts...>(id) " | |
| "must be unique and must be one of the allowed types"); | |
| if constexpr (sizeof...(_Ts)) | |
| if consteval { | |
| const __format::_Arg_t __t[] = { | |
| __format::__to_arg_t_enum<_CharT, _Ts>()... | |
| }; | |
| __check_dynamic_spec(__id, __t); | |
| } | |
| } | |
| constexpr void | |
| check_dynamic_spec_integral(size_t __id) noexcept | |
| { | |
| if consteval { | |
| using enum __format::_Arg_t; | |
| const __format::_Arg_t __t[] = { _Arg_i, _Arg_u, _Arg_ll, _Arg_ull }; | |
| __check_dynamic_spec(__id, __t); | |
| } | |
| } | |
| constexpr void | |
| check_dynamic_spec_string(size_t __id) noexcept | |
| { | |
| if consteval { | |
| using enum __format::_Arg_t; | |
| const __format::_Arg_t __t[] = { _Arg_str, _Arg_sv }; | |
| __check_dynamic_spec(__id, __t); | |
| } | |
| } | |
| private: | |
| // True if _Tp occurs exactly once in _Ts. | |
| template<typename _Tp, typename... _Ts> | |
| static constexpr bool __once = (is_same_v<_Tp, _Ts> + ...) == 1; | |
| template<typename... _Ts> | |
| static consteval bool | |
| __valid_types_for_check_dynamic_spec() | |
| { | |
| // _GLIBCXX_RESOLVE_LIB_DEFECTS | |
| // 4142. check_dynamic_spec should require at least one type | |
| if constexpr (sizeof...(_Ts) == 0) | |
| return false; | |
| else | |
| { | |
| // The types in Ts... are unique. Each type in Ts... is one of | |
| // bool, char_type, int, unsigned int, long long int, | |
| // unsigned long long int, float, double, long double, | |
| // const char_type*, basic_string_view<char_type>, or const void*. | |
| unsigned __sum | |
| = __once<bool, _Ts...> | |
| + __once<char_type, _Ts...> | |
| + __once<int, _Ts...> | |
| + __once<unsigned int, _Ts...> | |
| + __once<long long int, _Ts...> | |
| + __once<unsigned long long int, _Ts...> | |
| + __once<float, _Ts...> | |
| + __once<double, _Ts...> | |
| + __once<long double, _Ts...> | |
| + __once<const char_type*, _Ts...> | |
| + __once<basic_string_view<char_type>, _Ts...> | |
| + __once<const void*, _Ts...>; | |
| return __sum == sizeof...(_Ts); | |
| } | |
| } | |
| // Common implementation of check_dynamic_spec{,_string,_integral} | |
| consteval void | |
| __check_dynamic_spec(size_t __id, | |
| span<const __format::_Arg_t> __types) noexcept | |
| { | |
| if (__id >= _M_num_args) | |
| __format::__invalid_arg_id_in_format_string(); | |
| // This static_cast is safe because all parse contexts created by the | |
| // library are _Scan_parse_context (or a type derived from that). | |
| // User code can only create basic_format_parse_context objects with | |
| // _M_num_args == 0 and those will be rejected by the condition above. | |
| if (auto* __args = static_cast<_Scan_parse_context*>(this)->_M_types) | |
| { | |
| for (auto __t : __types) | |
| if (__args[__id] == __t) | |
| return; | |
| __invalid_dynamic_spec("arg(id) type does not match"); | |
| } | |
| // else this is a formatting scanner, do not do any type checks. | |
| } | |
| // This must not be constexpr. | |
| static void __invalid_dynamic_spec(const char*); | |
| #endif | |
| // This constructor should only be used by the implementation. | |
| constexpr explicit | |
| basic_format_parse_context(basic_string_view<_CharT> __fmt, | |
| size_t __num_args) noexcept | |
| : _M_begin(__fmt.begin()), _M_end(__fmt.end()), _M_num_args(__num_args) | |
| { } | |
| private: | |
| iterator _M_begin; | |
| iterator _M_end; | |
| enum _Indexing { _Unknown, _Manual, _Auto }; | |
| _Indexing _M_indexing = _Unknown; | |
| size_t _M_next_arg_id = 0; | |
| size_t _M_num_args = 0; | |
| // Derived parse context used by Scanner when checking format strings. | |
| struct _Scan_parse_context; | |
| friend __format::_Scanner<_CharT>; | |
| }; | |
| template<typename _CharT> | |
| struct basic_format_parse_context<_CharT>::_Scan_parse_context | |
| : basic_format_parse_context<_CharT> | |
| { | |
| using basic_format_parse_context<_CharT>::basic_format_parse_context; | |
| const __format::_Arg_t* _M_types = nullptr; | |
| }; | |
| /// @cond undocumented | |
| template<typename _Tp, template<typename...> class _Class> | |
| constexpr bool __is_specialization_of = false; | |
| template<template<typename...> class _Class, typename... _Args> | |
| constexpr bool __is_specialization_of<_Class<_Args...>, _Class> = true; | |
| namespace __format | |
| { | |
| // pre: first != last | |
| template<typename _CharT> | |
| constexpr pair<unsigned short, const _CharT*> | |
| __parse_integer(const _CharT* __first, const _CharT* __last) | |
| { | |
| if (__first == __last) | |
| __builtin_unreachable(); | |
| if constexpr (is_same_v<_CharT, char>) | |
| { | |
| const auto __start = __first; | |
| unsigned short __val = 0; | |
| // N.B. std::from_chars is not constexpr in C++20. | |
| if (__detail::__from_chars_alnum<true>(__first, __last, __val, 10) | |
| && __first != __start) [[likely]] | |
| return {__val, __first}; | |
| } | |
| else | |
| { | |
| constexpr int __n = 32; | |
| char __buf[__n]{}; | |
| for (int __i = 0; __i < __n && (__first + __i) != __last; ++__i) | |
| __buf[__i] = __first[__i]; | |
| auto [__v, __ptr] = __format::__parse_integer(__buf, __buf + __n); | |
| if (__ptr) [[likely]] | |
| return {__v, __first + (__ptr - __buf)}; | |
| } | |
| return {0, nullptr}; | |
| } | |
| template<typename _CharT> | |
| constexpr pair<unsigned short, const _CharT*> | |
| __parse_arg_id(const _CharT* __first, const _CharT* __last) | |
| { | |
| if (__first == __last) | |
| __builtin_unreachable(); | |
| if (*__first == '0') | |
| return {0, __first + 1}; // No leading zeros allowed, so '0...' == 0 | |
| if ('1' <= *__first && *__first <= '9') | |
| { | |
| const unsigned short __id = *__first - '0'; | |
| const auto __next = __first + 1; | |
| // Optimize for most likely case of single digit arg-id. | |
| if (__next == __last || !('0' <= *__next && *__next <= '9')) | |
| return {__id, __next}; | |
| else | |
| return __format::__parse_integer(__first, __last); | |
| } | |
| return {0, nullptr}; | |
| } | |
| enum class _Pres_type : unsigned char { | |
| _Pres_none = 0, // Default type (not valid for integer presentation types). | |
| _Pres_s = 1, // For strings, bool, ranges | |
| // Presentation types for integral types (including bool and charT). | |
| _Pres_c = 2, _Pres_x, _Pres_X, _Pres_d, _Pres_o, _Pres_b, _Pres_B, | |
| // Presentation types for floating-point types | |
| _Pres_g = 1, _Pres_G, _Pres_a, _Pres_A, _Pres_e, _Pres_E, _Pres_f, _Pres_F, | |
| _Pres_p, _Pres_P, | |
| _Pres_max = 0xf, | |
| }; | |
| using enum _Pres_type; | |
| enum class _Sign : unsigned char { | |
| _Sign_default, | |
| _Sign_plus, | |
| _Sign_minus, // XXX does this need to be distinct from _Sign_default? | |
| _Sign_space, | |
| }; | |
| using enum _Sign; | |
| enum _WidthPrec : unsigned char { | |
| _WP_none, // No width/prec specified. | |
| _WP_value, // Fixed width/prec specified. | |
| _WP_from_arg // Use a formatting argument for width/prec. | |
| }; | |
| using enum _WidthPrec; | |
| template<typename _Context> | |
| _GLIBCXX_CONSTEXPR_FORMAT size_t | |
| __int_from_arg(const basic_format_arg<_Context>& __arg); | |
| constexpr bool __is_digit(char __c) | |
| { return std::__detail::__from_chars_alnum_to_val(__c) < 10; } | |
| constexpr bool __is_xdigit(char __c) | |
| { return std::__detail::__from_chars_alnum_to_val(__c) < 16; } | |
| // Used to make _Spec a non-C++98 POD, so the tail-padding is used. | |
| // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#pod | |
| struct _SpecBase | |
| { }; | |
| template<typename _CharT> | |
| struct _Spec : _SpecBase | |
| { | |
| unsigned short _M_width; | |
| unsigned short _M_prec; | |
| char32_t _M_fill = ' '; | |
| _Align _M_align : 2; | |
| _Sign _M_sign : 2; | |
| unsigned _M_alt : 1; | |
| unsigned _M_localized : 1; | |
| unsigned _M_zero_fill : 1; | |
| _WidthPrec _M_width_kind : 2; | |
| _WidthPrec _M_prec_kind : 2; | |
| unsigned _M_debug : 1; | |
| _Pres_type _M_type : 4; | |
| unsigned _M_reserved : 8; | |
| // This class has 8 bits of tail padding, that can be used by | |
| // derived classes. | |
| using iterator = typename basic_string_view<_CharT>::iterator; | |
| static constexpr _Align | |
| _S_align(_CharT __c) noexcept | |
| { | |
| switch (__c) | |
| { | |
| case '<': return _Align_left; | |
| case '>': return _Align_right; | |
| case '^': return _Align_centre; | |
| default: return _Align_default; | |
| } | |
| } | |
| // pre: __first != __last | |
| constexpr iterator | |
| _M_parse_fill_and_align(iterator __first, iterator __last) noexcept | |
| { return _M_parse_fill_and_align(__first, __last, "{"); } | |
| // pre: __first != __last | |
| constexpr iterator | |
| _M_parse_fill_and_align(iterator __first, iterator __last, string_view __not_fill) noexcept | |
| { | |
| for (char __c : __not_fill) | |
| if (*__first == static_cast<_CharT>(__c)) | |
| return __first; | |
| using namespace __unicode; | |
| if constexpr (__literal_encoding_is_unicode<_CharT>()) | |
| { | |
| // Accept any UCS scalar value as fill character. | |
| _Utf32_view<ranges::subrange<iterator>> __uv({__first, __last}); | |
| if (!__uv.empty()) | |
| { | |
| auto __beg = __uv.begin(); | |
| char32_t __c = *__beg++; | |
| if (__is_scalar_value(__c)) | |
| if (auto __next = __beg.base(); __next != __last) | |
| if (_Align __align = _S_align(*__next); __align != _Align_default) | |
| { | |
| _M_fill = __c; | |
| _M_align = __align; | |
| return ++__next; | |
| } | |
| } | |
| } | |
| else if (__last - __first >= 2) | |
| if (_Align __align = _S_align(__first[1]); __align != _Align_default) | |
| { | |
| _M_fill = *__first; | |
| _M_align = __align; | |
| return __first + 2; | |
| } | |
| if (_Align __align = _S_align(__first[0]); __align != _Align_default) | |
| { | |
| _M_fill = ' '; | |
| _M_align = __align; | |
| return __first + 1; | |
| } | |
| return __first; | |
| } | |
| static constexpr _Sign | |
| _S_sign(_CharT __c) noexcept | |
| { | |
| switch (__c) | |
| { | |
| case '+': return _Sign_plus; | |
| case '-': return _Sign_minus; | |
| case ' ': return _Sign_space; | |
| default: return _Sign_default; | |
| } | |
| } | |
| // pre: __first != __last | |
| constexpr iterator | |
| _M_parse_sign(iterator __first, iterator) noexcept | |
| { | |
| if (_Sign __sign = _S_sign(*__first); __sign != _Sign_default) | |
| { | |
| _M_sign = __sign; | |
| return __first + 1; | |
| } | |
| return __first; | |
| } | |
| // pre: *__first is valid | |
| constexpr iterator | |
| _M_parse_alternate_form(iterator __first, iterator) noexcept | |
| { | |
| if (*__first == '#') | |
| { | |
| _M_alt = true; | |
| ++__first; | |
| } | |
| return __first; | |
| } | |
| // pre: __first != __last | |
| constexpr iterator | |
| _M_parse_zero_fill(iterator __first, iterator /* __last */) noexcept | |
| { | |
| if (*__first == '0') | |
| { | |
| _M_zero_fill = true; | |
| ++__first; | |
| } | |
| return __first; | |
| } | |
| // pre: __first != __last | |
| static constexpr iterator | |
| _S_parse_width_or_precision(iterator __first, iterator __last, | |
| unsigned short& __val, bool& __arg_id, | |
| basic_format_parse_context<_CharT>& __pc) | |
| { | |
| if (__format::__is_digit(*__first)) | |
| { | |
| auto [__v, __ptr] = __format::__parse_integer(__first, __last); | |
| if (!__ptr) | |
| __throw_format_error("format error: invalid width or precision " | |
| "in format-spec"); | |
| __first = __ptr; | |
| __val = __v; | |
| } | |
| else if (*__first == '{') | |
| { | |
| __arg_id = true; | |
| ++__first; | |
| if (__first == __last) | |
| __format::__unmatched_left_brace_in_format_string(); | |
| if (*__first == '}') | |
| __val = __pc.next_arg_id(); | |
| else | |
| { | |
| auto [__v, __ptr] = __format::__parse_arg_id(__first, __last); | |
| if (__ptr == nullptr || __ptr == __last || *__ptr != '}') | |
| __format::__invalid_arg_id_in_format_string(); | |
| __first = __ptr; | |
| __pc.check_arg_id(__v); | |
| __val = __v; | |
| } | |
| #if __cpp_lib_format >= 202305L | |
| __pc.check_dynamic_spec_integral(__val); | |
| #endif | |
| ++__first; // past the '}' | |
| } | |
| return __first; | |
| } | |
| // pre: __first != __last | |
| constexpr iterator | |
| _M_parse_width(iterator __first, iterator __last, | |
| basic_format_parse_context<_CharT>& __pc) | |
| { | |
| bool __arg_id = false; | |
| if (*__first == '0') | |
| __throw_format_error("format error: width must be non-zero in " | |
| "format string"); | |
| auto __next = _S_parse_width_or_precision(__first, __last, _M_width, | |
| __arg_id, __pc); | |
| if (__next != __first) | |
| _M_width_kind = __arg_id ? _WP_from_arg : _WP_value; | |
| return __next; | |
| } | |
| // pre: __first != __last | |
| constexpr iterator | |
| _M_parse_precision(iterator __first, iterator __last, | |
| basic_format_parse_context<_CharT>& __pc) | |
| { | |
| if (__first[0] != '.') | |
| return __first; | |
| iterator __next = ++__first; | |
| bool __arg_id = false; | |
| if (__next != __last) | |
| __next = _S_parse_width_or_precision(__first, __last, _M_prec, | |
| __arg_id, __pc); | |
| if (__next == __first) | |
| __throw_format_error("format error: missing precision after '.' in " | |
| "format string"); | |
| _M_prec_kind = __arg_id ? _WP_from_arg : _WP_value; | |
| return __next; | |
| } | |
| // pre: __first != __last | |
| constexpr iterator | |
| _M_parse_locale(iterator __first, iterator /* __last */) noexcept | |
| { | |
| if (*__first == 'L') | |
| { | |
| _M_localized = true; | |
| ++__first; | |
| } | |
| return __first; | |
| } | |
| template<typename _Context> | |
| _GLIBCXX_CONSTEXPR_FORMAT size_t | |
| _M_get_width(_Context& __ctx) const | |
| { | |
| size_t __width = 0; | |
| if (_M_width_kind == _WP_value) | |
| __width = _M_width; | |
| else if (_M_width_kind == _WP_from_arg) | |
| __width = __format::__int_from_arg(__ctx.arg(_M_width)); | |
| return __width; | |
| } | |
| template<typename _Context> | |
| _GLIBCXX_CONSTEXPR_FORMAT size_t | |
| _M_get_precision(_Context& __ctx) const | |
| { | |
| size_t __prec = -1; | |
| if (_M_prec_kind == _WP_value) | |
| __prec = _M_prec; | |
| else if (_M_prec_kind == _WP_from_arg) | |
| __prec = __format::__int_from_arg(__ctx.arg(_M_prec)); | |
| return __prec; | |
| } | |
| }; | |
| template<typename _Int> | |
| inline _GLIBCXX_CONSTEXPR_FORMAT char* | |
| __put_sign(_Int __i, _Sign __sign, char* __dest) noexcept | |
| { | |
| if (__i < 0) | |
| *__dest = '-'; | |
| else if (__sign == _Sign_plus) | |
| *__dest = '+'; | |
| else if (__sign == _Sign_space) | |
| *__dest = ' '; | |
| else | |
| ++__dest; | |
| return __dest; | |
| } | |
| // Write STR to OUT (and do so efficiently if OUT is a _Sink_iter). | |
| template<typename _Out, typename _CharT> | |
| requires output_iterator<_Out, const _CharT&> | |
| inline _GLIBCXX_CONSTEXPR_FORMAT _Out | |
| __write(_Out __out, basic_string_view<_CharT> __str) | |
| { | |
| if constexpr (is_same_v<_Out, _Sink_iter<_CharT>>) | |
| { | |
| if (__str.size()) | |
| __out = __str; | |
| } | |
| else | |
| for (_CharT __c : __str) | |
| *__out++ = __c; | |
| return __out; | |
| } | |
| // Write STR to OUT with NFILL copies of FILL_CHAR specified by ALIGN. | |
| // pre: __align != _Align_default | |
| template<typename _Out, typename _CharT> | |
| _GLIBCXX_CONSTEXPR_FORMAT _Out | |
| __write_padded(_Out __out, basic_string_view<_CharT> __str, | |
| _Align __align, size_t __nfill, char32_t __fill_char) | |
| { | |
| const size_t __buflen = 0x20; | |
| _CharT __padding_chars[__buflen]; | |
| __padding_chars[0] = _CharT(); | |
| basic_string_view<_CharT> __padding{__padding_chars, __buflen}; | |
| auto __pad = [&__padding] (size_t __n, _Out& __o) { | |
| if (__n == 0) | |
| return; | |
| while (__n > __padding.size()) | |
| { | |
| __o = __format::__write(std::move(__o), __padding); | |
| __n -= __padding.size(); | |
| } | |
| if (__n != 0) | |
| __o = __format::__write(std::move(__o), __padding.substr(0, __n)); | |
| }; | |
| size_t __l, __r, __max; | |
| if (__align == _Align_centre) | |
| { | |
| __l = __nfill / 2; | |
| __r = __l + (__nfill & 1); | |
| __max = __r; | |
| } | |
| else if (__align == _Align_right) | |
| { | |
| __l = __nfill; | |
| __r = 0; | |
| __max = __l; | |
| } | |
| else | |
| { | |
| __l = 0; | |
| __r = __nfill; | |
| __max = __r; | |
| } | |
| using namespace __unicode; | |
| if constexpr (__literal_encoding_is_unicode<_CharT>()) | |
| if (!__is_single_code_unit<_CharT>(__fill_char)) [[unlikely]] | |
| { | |
| // Encode fill char as multiple code units of type _CharT. | |
| const char32_t __arr[1]{ __fill_char }; | |
| _Utf_view<_CharT, span<const char32_t, 1>> __v(__arr); | |
| basic_string<_CharT> __padstr(__v.begin(), __v.end()); | |
| __padding = __padstr; | |
| while (__l-- > 0) | |
| __out = __format::__write(std::move(__out), __padding); | |
| __out = __format::__write(std::move(__out), __str); | |
| while (__r-- > 0) | |
| __out = __format::__write(std::move(__out), __padding); | |
| return __out; | |
| } | |
| if (__max < __buflen) | |
| __padding.remove_suffix(__buflen - __max); | |
| else | |
| __max = __buflen; | |
| char_traits<_CharT>::assign(__padding_chars, __max, __fill_char); | |
| __pad(__l, __out); | |
| __out = __format::__write(std::move(__out), __str); | |
| __pad(__r, __out); | |
| return __out; | |
| } | |
| // Write STR to OUT, with alignment and padding as determined by SPEC. | |
| // pre: __spec._M_align != _Align_default || __align != _Align_default | |
| template<typename _CharT, typename _Out> | |
| _GLIBCXX_CONSTEXPR_FORMAT _Out | |
| __write_padded_as_spec(basic_string_view<type_identity_t<_CharT>> __str, | |
| size_t __estimated_width, | |
| basic_format_context<_Out, _CharT>& __fc, | |
| const _Spec<_CharT>& __spec, | |
| _Align __align = _Align_left) | |
| { | |
| size_t __width = __spec._M_get_width(__fc); | |
| if (__width <= __estimated_width) | |
| return __format::__write(__fc.out(), __str); | |
| const size_t __nfill = __width - __estimated_width; | |
| if (__spec._M_align != _Align_default) | |
| __align = __spec._M_align; | |
| return __format::__write_padded(__fc.out(), __str, __align, __nfill, | |
| __spec._M_fill); | |
| } | |
| template<typename _CharT> | |
| _GLIBCXX_CONSTEXPR_FORMAT size_t | |
| __truncate(basic_string_view<_CharT>& __s, size_t __prec) | |
| { | |
| if constexpr (__unicode::__literal_encoding_is_unicode<_CharT>()) | |
| { | |
| if (__prec != (size_t)-1) | |
| return __unicode::__truncate(__s, __prec); | |
| else | |
| return __unicode::__field_width(__s); | |
| } | |
| else | |
| { | |
| __s = __s.substr(0, __prec); | |
| return __s.size(); | |
| } | |
| } | |
| enum class _Term_char : unsigned char { | |
| _Term_none, | |
| _Term_quote, | |
| _Term_apos, | |
| }; | |
| using enum _Term_char; | |
| template<typename _CharT> | |
| struct _Escapes | |
| { | |
| using _Str_view = basic_string_view<_CharT>; | |
| static consteval | |
| _Str_view _S_all() | |
| { return _GLIBCXX_WIDEN("\t\\t\n\\n\r\\r\\\\\\\"\\\"'\\'\\u\\x"); } | |
| static consteval | |
| _Str_view _S_tab() | |
| { return _S_all().substr(0, 3); } | |
| static consteval | |
| _Str_view _S_newline() | |
| { return _S_all().substr(3, 3); } | |
| static consteval | |
| _Str_view _S_return() | |
| { return _S_all().substr(6, 3); } | |
| static consteval | |
| _Str_view _S_bslash() | |
| { return _S_all().substr(9, 3); } | |
| static consteval | |
| _Str_view _S_quote() | |
| { return _S_all().substr(12, 3); } | |
| static consteval | |
| _Str_view _S_apos() | |
| { return _S_all().substr(15, 3); } | |
| static consteval | |
| _Str_view _S_u() | |
| { return _S_all().substr(18, 2); } | |
| static consteval | |
| _Str_view _S_x() | |
| { return _S_all().substr(20, 2); } | |
| static constexpr | |
| _Str_view _S_term(_Term_char __term) | |
| { | |
| switch (__term) | |
| { | |
| case _Term_none: | |
| return _Str_view(); | |
| case _Term_quote: | |
| return _S_quote().substr(0, 1); | |
| case _Term_apos: | |
| return _S_apos().substr(0, 1); | |
| } | |
| __builtin_unreachable(); | |
| } | |
| }; | |
| template<typename _CharT> | |
| struct _Separators | |
| { | |
| using _Str_view = basic_string_view<_CharT>; | |
| static consteval | |
| _Str_view _S_all() | |
| { return _GLIBCXX_WIDEN("[]{}(), : "); } | |
| static consteval | |
| _Str_view _S_squares() | |
| { return _S_all().substr(0, 2); } | |
| static consteval | |
| _Str_view _S_braces() | |
| { return _S_all().substr(2, 2); } | |
| static consteval | |
| _Str_view _S_parens() | |
| { return _S_all().substr(4, 2); } | |
| static consteval | |
| _Str_view _S_comma() | |
| { return _S_all().substr(6, 2); } | |
| static consteval | |
| _Str_view _S_colon() | |
| { return _S_all().substr(8, 2); } | |
| }; | |
| template<typename _CharT> | |
| constexpr bool __should_escape_ascii(_CharT __c, _Term_char __term) | |
| { | |
| using _Esc = _Escapes<_CharT>; | |
| switch (__c) | |
| { | |
| case _Esc::_S_tab()[0]: | |
| case _Esc::_S_newline()[0]: | |
| case _Esc::_S_return()[0]: | |
| case _Esc::_S_bslash()[0]: | |
| return true; | |
| case _Esc::_S_quote()[0]: | |
| return __term == _Term_quote; | |
| case _Esc::_S_apos()[0]: | |
| return __term == _Term_apos; | |
| default: | |
| return (__c >= 0 && __c < 0x20) || __c == 0x7f; | |
| }; | |
| } | |
| // @pre __c <= 0x10FFFF | |
| constexpr bool __should_escape_unicode(char32_t __c, bool __prev_esc) | |
| { | |
| if (__unicode::__should_escape_category(__c)) | |
| return __c != U' '; | |
| if (!__prev_esc) | |
| return false; | |
| return __unicode::__grapheme_cluster_break_property(__c) | |
| == __unicode::_Gcb_property::_Gcb_Extend; | |
| } | |
| using uint_least32_t = __UINT_LEAST32_TYPE__; | |
| template<typename _Out, typename _CharT> | |
| _GLIBCXX_CONSTEXPR_FORMAT _Out | |
| __write_escape_seq(_Out __out, uint_least32_t __val, | |
| basic_string_view<_CharT> __prefix) | |
| { | |
| constexpr size_t __max = 8; | |
| char __buf[__max]; | |
| const string_view __narrow( | |
| __buf, | |
| std::__to_chars_i<uint_least32_t>(__buf, __buf + __max, __val, 16).ptr); | |
| __out = __format::__write(__out, __prefix); | |
| *__out = _Separators<_CharT>::_S_braces()[0]; | |
| ++__out; | |
| if constexpr (is_same_v<char, _CharT>) | |
| __out = __format::__write(__out, __narrow); | |
| #ifdef _GLIBCXX_USE_WCHAR_T | |
| else | |
| { | |
| wchar_t __wbuf[__max]; | |
| const size_t __n = __narrow.size(); | |
| std::__to_wstring_numeric(__narrow.data(), __n, __wbuf); | |
| __out = __format::__write(__out, wstring_view(__wbuf, __n)); | |
| } | |
| #endif | |
| *__out = _Separators<_CharT>::_S_braces()[1]; | |
| return ++__out; | |
| } | |
| template<typename _Out, typename _CharT> | |
| _GLIBCXX_CONSTEXPR_FORMAT _Out | |
| __write_escape_seqs(_Out __out, basic_string_view<_CharT> __units) | |
| { | |
| using _UChar = make_unsigned_t<_CharT>; | |
| for (_CharT __c : __units) | |
| __out = __format::__write_escape_seq( | |
| __out, static_cast<_UChar>(__c), _Escapes<_CharT>::_S_x()); | |
| return __out; | |
| } | |
| template<typename _Out, typename _CharT> | |
| _GLIBCXX_CONSTEXPR_FORMAT _Out | |
| __write_escaped_char(_Out __out, _CharT __c) | |
| { | |
| using _UChar = make_unsigned_t<_CharT>; | |
| using _Esc = _Escapes<_CharT>; | |
| switch (__c) | |
| { | |
| case _Esc::_S_tab()[0]: | |
| return __format::__write(__out, _Esc::_S_tab().substr(1, 2)); | |
| case _Esc::_S_newline()[0]: | |
| return __format::__write(__out, _Esc::_S_newline().substr(1, 2)); | |
| case _Esc::_S_return()[0]: | |
| return __format::__write(__out, _Esc::_S_return().substr(1, 2)); | |
| case _Esc::_S_bslash()[0]: | |
| return __format::__write(__out, _Esc::_S_bslash().substr(1, 2)); | |
| case _Esc::_S_quote()[0]: | |
| return __format::__write(__out, _Esc::_S_quote().substr(1, 2)); | |
| case _Esc::_S_apos()[0]: | |
| return __format::__write(__out, _Esc::_S_apos().substr(1, 2)); | |
| default: | |
| return __format::__write_escape_seq( | |
| __out, static_cast<_UChar>(__c), _Esc::_S_u()); | |
| } | |
| } | |
| template<typename _CharT, typename _Out> | |
| _GLIBCXX_CONSTEXPR_FORMAT _Out | |
| __write_escaped_ascii(_Out __out, | |
| basic_string_view<_CharT> __str, | |
| _Term_char __term) | |
| { | |
| using _Str_view = basic_string_view<_CharT>; | |
| if consteval { | |
| // As set of the escaped characters depends on the encoding, for | |
| // compile time allow only printable ASCII and standard escapes. | |
| constexpr _Str_view __supported(_GLIBCXX_WIDEN( | |
| "ABCDEFGHIJKLMNOPQRSTUWXYZ" | |
| "abdeefghijklmnopqrstuwzyz" | |
| " !#$%&'()*+-./:;<=>?[]^_{|}~" | |
| "0123456789" "\t\n\r\\\"\'\0" | |
| ), 95); | |
| if (__str.find_first_not_of(__supported) != _Str_view::npos) | |
| #if __has_builtin(__builtin_constexpr_diag) | |
| __builtin_constexpr_diag (2, "", | |
| "for non-Unicode literal encodings, only" | |
| " printable ASCII characters and standard" | |
| " escape sequencess can be escaped in constant" | |
| " expressions"); | |
| #else | |
| __asm__(""); | |
| #endif | |
| } | |
| auto __first = __str.begin(); | |
| auto const __last = __str.end(); | |
| while (__first != __last) | |
| { | |
| auto __print = __first; | |
| // assume anything outside ASCII is printable | |
| while (__print != __last | |
| && !__format::__should_escape_ascii(*__print, __term)) | |
| ++__print; | |
| if (__print != __first) | |
| __out = __format::__write(__out, _Str_view(__first, __print)); | |
| if (__print == __last) | |
| return __out; | |
| __first = __print; | |
| __out = __format::__write_escaped_char(__out, *__first); | |
| ++__first; | |
| } | |
| return __out; | |
| } | |
| template<typename _CharT, typename _Out> | |
| _GLIBCXX_CONSTEXPR_FORMAT _Out | |
| __write_escaped_unicode_part(_Out __out, basic_string_view<_CharT>& __str, | |
| bool& __prev_esc, _Term_char __term) | |
| { | |
| using _Str_view = basic_string_view<_CharT>; | |
| using _Esc = _Escapes<_CharT>; | |
| static constexpr char32_t __replace = U'\uFFFD'; | |
| static constexpr _Str_view __replace_rep = [] | |
| { | |
| // N.B. "\uFFFD" is ill-formed if encoding is not unicode. | |
| if constexpr (is_same_v<char, _CharT>) | |
| return "\xEF\xBF\xBD"; | |
| else | |
| return L"\xFFFD"; | |
| }(); | |
| __unicode::_Utf_view<char32_t, _Str_view> __v(std::move(__str)); | |
| __str = {}; | |
| auto __first = __v.begin(); | |
| auto const __last = __v.end(); | |
| while (__first != __last) | |
| { | |
| bool __esc_ascii = false; | |
| bool __esc_unicode = false; | |
| bool __esc_replace = false; | |
| auto __should_escape = [&](auto const& __it) | |
| { | |
| if (*__it <= 0x7f) | |
| return __esc_ascii | |
| = __format::__should_escape_ascii(*__it.base(), __term); | |
| if (__format::__should_escape_unicode(*__it, __prev_esc)) | |
| return __esc_unicode = true; | |
| if (*__it == __replace) | |
| { | |
| _Str_view __units(__it.base(), __it._M_units()); | |
| return __esc_replace = (__units != __replace_rep); | |
| } | |
| return false; | |
| }; | |
| auto __print = __first; | |
| while (__print != __last && !__should_escape(__print)) | |
| { | |
| __prev_esc = false; | |
| ++__print; | |
| } | |
| if (__print != __first) | |
| __out = __format::__write(__out, _Str_view(__first.base(), __print.base())); | |
| if (__print == __last) | |
| return __out; | |
| __first = __print; | |
| if (__esc_ascii) | |
| __out = __format::__write_escaped_char(__out, *__first.base()); | |
| else if (__esc_unicode) | |
| __out = __format::__write_escape_seq(__out, *__first, _Esc::_S_u()); | |
| // __esc_replace | |
| else if (_Str_view __units(__first.base(), __first._M_units()); | |
| __units.end() != __last.base()) | |
| __out = __format::__write_escape_seqs(__out, __units); | |
| else | |
| { | |
| __str = __units; | |
| return __out; | |
| } | |
| __prev_esc = true; | |
| ++__first; | |
| } | |
| return __out; | |
| } | |
| template<typename _CharT, typename _Out> | |
| _GLIBCXX_CONSTEXPR_FORMAT _Out | |
| __write_escaped_unicode(_Out __out, basic_string_view<_CharT> __str, | |
| _Term_char __term) | |
| { | |
| bool __prev_escape = true; | |
| __out = __format::__write_escaped_unicode_part(__out, __str, | |
| __prev_escape, __term); | |
| __out = __format::__write_escape_seqs(__out, __str); | |
| return __out; | |
| } | |
| template<typename _CharT, typename _Out> | |
| _GLIBCXX_CONSTEXPR_FORMAT _Out | |
| __write_escaped(_Out __out, basic_string_view<_CharT> __str, _Term_char __term) | |
| { | |
| __out = __format::__write(__out, _Escapes<_CharT>::_S_term(__term)); | |
| if constexpr (__unicode::__literal_encoding_is_unicode<_CharT>()) | |
| __out = __format::__write_escaped_unicode(__out, __str, __term); | |
| else if constexpr (is_same_v<char, _CharT> | |
| && __unicode::__literal_encoding_is_extended_ascii()) | |
| __out = __format::__write_escaped_ascii(__out, __str, __term); | |
| else | |
| // TODO Handle non-ascii extended encoding | |
| __out = __format::__write_escaped_ascii(__out, __str, __term); | |
| return __format::__write(__out, _Escapes<_CharT>::_S_term(__term)); | |
| } | |
| // A lightweight optional<locale>. | |
| struct _Optional_locale | |
| { | |
| [[__gnu__::__always_inline__]] | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| _Optional_locale() : _M_dummy(), _M_hasval(false) { } | |
| _Optional_locale(const locale& __loc) noexcept | |
| : _M_loc(__loc), _M_hasval(true) | |
| { } | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| _Optional_locale(const _Optional_locale& __l) noexcept | |
| : _M_dummy(), _M_hasval(__l._M_hasval) | |
| { | |
| if (_M_hasval) | |
| std::construct_at(&_M_loc, __l._M_loc); | |
| } | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| _Optional_locale& | |
| operator=(const _Optional_locale& __l) noexcept | |
| { | |
| if (_M_hasval) | |
| { | |
| if (__l._M_hasval) | |
| _M_loc = __l._M_loc; | |
| else | |
| { | |
| _M_loc.~locale(); | |
| _M_hasval = false; | |
| } | |
| } | |
| else if (__l._M_hasval) | |
| { | |
| std::construct_at(&_M_loc, __l._M_loc); | |
| _M_hasval = true; | |
| } | |
| return *this; | |
| } | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| ~_Optional_locale() { if (_M_hasval) _M_loc.~locale(); } | |
| _Optional_locale& | |
| operator=(locale&& __loc) noexcept | |
| { | |
| if (_M_hasval) | |
| _M_loc = std::move(__loc); | |
| else | |
| { | |
| std::construct_at(&_M_loc, std::move(__loc)); | |
| _M_hasval = true; | |
| } | |
| return *this; | |
| } | |
| const locale& | |
| value() noexcept | |
| { | |
| if (!_M_hasval) | |
| { | |
| std::construct_at(&_M_loc); | |
| _M_hasval = true; | |
| } | |
| return _M_loc; | |
| } | |
| _GLIBCXX_CONSTEXPR_FORMAT bool | |
| has_value() const noexcept { return _M_hasval; } | |
| union { | |
| char _M_dummy = '\0'; | |
| std::locale _M_loc; | |
| }; | |
| bool _M_hasval = false; | |
| }; | |
| template<__char _CharT> | |
| struct __formatter_str | |
| { | |
| __formatter_str() = default; | |
| constexpr | |
| __formatter_str(_Spec<_CharT> __spec) noexcept | |
| : _M_spec(__spec) | |
| { } | |
| constexpr typename basic_format_parse_context<_CharT>::iterator | |
| parse(basic_format_parse_context<_CharT>& __pc) | |
| { | |
| auto __first = __pc.begin(); | |
| const auto __last = __pc.end(); | |
| _Spec<_CharT> __spec{}; | |
| auto __finalize = [this, &__spec] { | |
| _M_spec = __spec; | |
| }; | |
| auto __finished = [&] { | |
| if (__first == __last || *__first == '}') | |
| { | |
| __finalize(); | |
| return true; | |
| } | |
| return false; | |
| }; | |
| if (__finished()) | |
| return __first; | |
| __first = __spec._M_parse_fill_and_align(__first, __last); | |
| if (__finished()) | |
| return __first; | |
| __first = __spec._M_parse_width(__first, __last, __pc); | |
| if (__finished()) | |
| return __first; | |
| __first = __spec._M_parse_precision(__first, __last, __pc); | |
| if (__finished()) | |
| return __first; | |
| if (*__first == 's') | |
| { | |
| __spec._M_type = _Pres_s; | |
| ++__first; | |
| } | |
| #if __glibcxx_format_ranges // C++ >= 23 && HOSTED | |
| else if (*__first == '?') | |
| { | |
| __spec._M_debug = true; | |
| ++__first; | |
| } | |
| #endif | |
| if (__finished()) | |
| return __first; | |
| __format::__failed_to_parse_format_spec(); | |
| } | |
| template<typename _Out> | |
| _GLIBCXX_CONSTEXPR_FORMAT _Out | |
| format(basic_string_view<_CharT> __s, | |
| basic_format_context<_Out, _CharT>& __fc) const | |
| { | |
| if (_M_spec._M_debug) | |
| return _M_format_escaped(__s, __fc); | |
| if (_M_spec._M_width_kind == _WP_none | |
| && _M_spec._M_prec_kind == _WP_none) | |
| return __format::__write(__fc.out(), __s); | |
| const size_t __maxwidth = _M_spec._M_get_precision(__fc); | |
| const size_t __width = __format::__truncate(__s, __maxwidth); | |
| return __format::__write_padded_as_spec(__s, __width, __fc, _M_spec); | |
| } | |
| template<typename _Out> | |
| _GLIBCXX_CONSTEXPR_FORMAT _Out | |
| _M_format_escaped(basic_string_view<_CharT> __s, | |
| basic_format_context<_Out, _CharT>& __fc) const | |
| { | |
| const size_t __padwidth = _M_spec._M_get_width(__fc); | |
| if (__padwidth == 0 && _M_spec._M_prec_kind == _WP_none) | |
| return __format::__write_escaped(__fc.out(), __s, _Term_quote); | |
| const size_t __maxwidth = _M_spec._M_get_precision(__fc); | |
| const size_t __width = __truncate(__s, __maxwidth); | |
| // N.B. Escaping only increases width | |
| if (__padwidth <= __width && _M_spec._M_prec_kind == _WP_none) | |
| return __format::__write_escaped(__fc.out(), __s, _Term_quote); | |
| // N.B. [tab:format.type.string] defines '?' as | |
| // Copies the escaped string ([format.string.escaped]) to the output, | |
| // so precision seem to appy to escaped string. | |
| _Padding_sink<_Out, _CharT> __sink(__fc.out(), __padwidth, __maxwidth); | |
| __format::__write_escaped(__sink.out(), __s, _Term_quote); | |
| return __sink._M_finish(_M_spec._M_align, _M_spec._M_fill); | |
| } | |
| #if __glibcxx_format_ranges // C++ >= 23 && HOSTED | |
| template<ranges::input_range _Rg, typename _Out> | |
| requires same_as<remove_cvref_t<ranges::range_reference_t<_Rg>>, _CharT> | |
| _GLIBCXX_CONSTEXPR_FORMAT _Out | |
| _M_format_range(_Rg&& __rg, basic_format_context<_Out, _CharT>& __fc) const | |
| { | |
| using _Range = remove_reference_t<_Rg>; | |
| using _String_view = basic_string_view<_CharT>; | |
| if constexpr (ranges::contiguous_range<_Rg>) | |
| { | |
| _String_view __str(ranges::data(__rg), | |
| size_t(ranges::distance(__rg))); | |
| return format(__str, __fc); | |
| } | |
| else if constexpr (!is_const_v<_Range> | |
| && __simply_formattable_range<_Range, _CharT>) | |
| return _M_format_range<const _Range&>(__rg, __fc); | |
| else if constexpr (!is_lvalue_reference_v<_Rg>) | |
| return _M_format_range<_Range&>(__rg, __fc); | |
| else | |
| { | |
| auto __handle_debug = [this, &__rg]<typename _NOut>(_NOut __nout) | |
| { | |
| if (!_M_spec._M_debug) | |
| return ranges::copy(__rg, std::move(__nout)).out; | |
| _Escaping_sink<_NOut, _CharT> | |
| __sink(std::move(__nout), _Term_quote); | |
| ranges::copy(__rg, __sink.out()); | |
| return __sink._M_finish(); | |
| }; | |
| const size_t __padwidth = _M_spec._M_get_width(__fc); | |
| if (__padwidth == 0 && _M_spec._M_prec_kind == _WP_none) | |
| return __handle_debug(__fc.out()); | |
| _Padding_sink<_Out, _CharT> | |
| __sink(__fc.out(), __padwidth, _M_spec._M_get_precision(__fc)); | |
| __handle_debug(__sink.out()); | |
| return __sink._M_finish(_M_spec._M_align, _M_spec._M_fill); | |
| } | |
| } | |
| constexpr void | |
| set_debug_format() noexcept | |
| { _M_spec._M_debug = true; } | |
| #endif | |
| private: | |
| _Spec<_CharT> _M_spec{}; | |
| }; | |
| // A partial implementation of std::toupper that is constexpr-enabled, | |
| // sufficient for formatting purposes. | |
| [[__gnu__::__always_inline__]] | |
| constexpr char | |
| __toupper_numeric(char __c) | |
| { | |
| switch (__c) | |
| { | |
| case 'a': return 'A'; | |
| case 'b': return 'B'; | |
| case 'c': return 'C'; | |
| case 'd': return 'D'; | |
| case 'e': return 'E'; | |
| case 'f': return 'F'; | |
| case 'i': return 'I'; | |
| case 'n': return 'N'; | |
| case 'p': return 'P'; | |
| case 'x': return 'X'; | |
| default: return __c; | |
| } | |
| } | |
| template<__char _CharT> | |
| struct __formatter_int | |
| { | |
| // If no presentation type is specified, meaning of "none" depends | |
| // whether we are formatting an integer or a char or a bool. | |
| static constexpr _Pres_type _AsInteger = _Pres_d; | |
| static constexpr _Pres_type _AsBool = _Pres_s; | |
| static constexpr _Pres_type _AsChar = _Pres_c; | |
| __formatter_int() = default; | |
| constexpr | |
| __formatter_int(_Spec<_CharT> __spec) noexcept | |
| : _M_spec(__spec) | |
| { | |
| if (_M_spec._M_type == _Pres_none) | |
| _M_spec._M_type = _Pres_d; | |
| } | |
| constexpr typename basic_format_parse_context<_CharT>::iterator | |
| _M_do_parse(basic_format_parse_context<_CharT>& __pc, _Pres_type __type) | |
| { | |
| _Spec<_CharT> __spec{}; | |
| __spec._M_type = __type; | |
| const auto __last = __pc.end(); | |
| auto __first = __pc.begin(); | |
| auto __finalize = [this, &__spec] { | |
| _M_spec = __spec; | |
| }; | |
| auto __finished = [&] { | |
| if (__first == __last || *__first == '}') | |
| { | |
| __finalize(); | |
| return true; | |
| } | |
| return false; | |
| }; | |
| if (__finished()) | |
| return __first; | |
| __first = __spec._M_parse_fill_and_align(__first, __last); | |
| if (__finished()) | |
| return __first; | |
| __first = __spec._M_parse_sign(__first, __last); | |
| if (__finished()) | |
| return __first; | |
| __first = __spec._M_parse_alternate_form(__first, __last); | |
| if (__finished()) | |
| return __first; | |
| __first = __spec._M_parse_zero_fill(__first, __last); | |
| if (__finished()) | |
| return __first; | |
| __first = __spec._M_parse_width(__first, __last, __pc); | |
| if (__finished()) | |
| return __first; | |
| __first = __spec._M_parse_locale(__first, __last); | |
| if (__finished()) | |
| return __first; | |
| switch (*__first) | |
| { | |
| case 'b': | |
| __spec._M_type = _Pres_b; | |
| ++__first; | |
| break; | |
| case 'B': | |
| __spec._M_type = _Pres_B; | |
| ++__first; | |
| break; | |
| case 'c': | |
| // _GLIBCXX_RESOLVE_LIB_DEFECTS | |
| // 3586. format should not print bool with 'c' | |
| if (__type != _AsBool) | |
| { | |
| __spec._M_type = _Pres_c; | |
| ++__first; | |
| } | |
| break; | |
| case 'd': | |
| __spec._M_type = _Pres_d; | |
| ++__first; | |
| break; | |
| case 'o': | |
| __spec._M_type = _Pres_o; | |
| ++__first; | |
| break; | |
| case 'x': | |
| __spec._M_type = _Pres_x; | |
| ++__first; | |
| break; | |
| case 'X': | |
| __spec._M_type = _Pres_X; | |
| ++__first; | |
| break; | |
| case 's': | |
| if (__type == _AsBool) | |
| { | |
| __spec._M_type = _Pres_s; // same meaning as "none" for bool | |
| ++__first; | |
| } | |
| break; | |
| #if __glibcxx_format_ranges // C++ >= 23 && HOSTED | |
| case '?': | |
| if (__type == _AsChar) | |
| { | |
| __spec._M_debug = true; | |
| ++__first; | |
| } | |
| #endif | |
| break; | |
| } | |
| if (__finished()) | |
| return __first; | |
| __format::__failed_to_parse_format_spec(); | |
| } | |
| template<typename _Tp> | |
| constexpr typename basic_format_parse_context<_CharT>::iterator | |
| _M_parse(basic_format_parse_context<_CharT>& __pc) | |
| { | |
| if constexpr (is_same_v<_Tp, bool>) | |
| { | |
| auto __end = _M_do_parse(__pc, _AsBool); | |
| if (_M_spec._M_type == _Pres_s) | |
| if (_M_spec._M_sign != _Sign_default || _M_spec._M_alt | |
| || _M_spec._M_zero_fill) | |
| __throw_format_error("format error: format-spec contains " | |
| "invalid formatting options for " | |
| "'bool'"); | |
| return __end; | |
| } | |
| else if constexpr (__char<_Tp>) | |
| { | |
| auto __end = _M_do_parse(__pc, _AsChar); | |
| if (_M_spec._M_type == _Pres_c) | |
| if (_M_spec._M_sign != _Sign_default || _M_spec._M_alt | |
| || _M_spec._M_zero_fill | |
| /* XXX should be invalid? || _M_spec._M_localized */) | |
| __throw_format_error("format error: format-spec contains " | |
| "invalid formatting options for " | |
| "'charT'"); | |
| return __end; | |
| } | |
| else | |
| return _M_do_parse(__pc, _AsInteger); | |
| } | |
| template<typename _Int, typename _Out> | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| typename basic_format_context<_Out, _CharT>::iterator | |
| format(_Int __i, basic_format_context<_Out, _CharT>& __fc) const | |
| { | |
| if (_M_spec._M_type == _Pres_c) | |
| return _M_format_character(_S_to_character(__i), __fc); | |
| constexpr size_t __buf_size = sizeof(_Int) * __CHAR_BIT__ + 3; | |
| char __buf[__buf_size]; | |
| to_chars_result __res{}; | |
| string_view __base_prefix; | |
| make_unsigned_t<_Int> __u; | |
| if (__i < 0) | |
| __u = -static_cast<make_unsigned_t<_Int>>(__i); | |
| else | |
| __u = __i; | |
| char* __start = __buf + 3; | |
| char* const __end = __buf + sizeof(__buf); | |
| char* const __start_digits = __start; | |
| switch (_M_spec._M_type) | |
| { | |
| case _Pres_b: | |
| case _Pres_B: | |
| __base_prefix = _M_spec._M_type == _Pres_b ? "0b" : "0B"; | |
| __res = to_chars(__start, __end, __u, 2); | |
| break; | |
| #if 0 | |
| case _Pres_c: | |
| return _M_format_character(_S_to_character(__i), __fc); | |
| #endif | |
| default: // Fallback for _Pres_type values introduces in later versions. | |
| case _Pres_none: | |
| // Should not reach here with _Pres_none for bool or charT, so: | |
| [[fallthrough]]; | |
| case _Pres_d: | |
| __res = to_chars(__start, __end, __u, 10); | |
| break; | |
| case _Pres_o: | |
| if (__i != 0) | |
| __base_prefix = "0"; | |
| __res = to_chars(__start, __end, __u, 8); | |
| break; | |
| case _Pres_x: | |
| case _Pres_X: | |
| __base_prefix = _M_spec._M_type == _Pres_x ? "0x" : "0X"; | |
| __res = to_chars(__start, __end, __u, 16); | |
| if (_M_spec._M_type == _Pres_X) | |
| for (auto __p = __start; __p != __res.ptr; ++__p) | |
| *__p = __format::__toupper_numeric(*__p); | |
| break; | |
| } | |
| if (_M_spec._M_alt && __base_prefix.size()) | |
| { | |
| __start -= __base_prefix.size(); | |
| ranges::copy(__base_prefix, __start); | |
| } | |
| __start = __format::__put_sign(__i, _M_spec._M_sign, __start - 1); | |
| string_view __narrow_str(__start, __res.ptr - __start); | |
| size_t __prefix_len = __start_digits - __start; | |
| if constexpr (is_same_v<char, _CharT>) | |
| return _M_format_int(__narrow_str, __prefix_len, __fc); | |
| #ifdef _GLIBCXX_USE_WCHAR_T | |
| else | |
| { | |
| _CharT __wbuf[__buf_size]; | |
| size_t __n = __narrow_str.size(); | |
| // _GLIBCXX_RESOLVE_LIB_DEFECTS | |
| // 4522. Clarify that `std::format` transcodes for `std::wformat_strings` | |
| std::__to_wstring_numeric(__narrow_str.data(), __n, __wbuf); | |
| return _M_format_int(basic_string_view<_CharT>(__wbuf, __n), | |
| __prefix_len, __fc); | |
| } | |
| #endif | |
| } | |
| template<typename _Out> | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| typename basic_format_context<_Out, _CharT>::iterator | |
| format(bool __i, basic_format_context<_Out, _CharT>& __fc) const | |
| { | |
| if (_M_spec._M_type == _Pres_c) | |
| return _M_format_character(static_cast<unsigned char>(__i), __fc); | |
| if (_M_spec._M_type != _Pres_s) | |
| return format(static_cast<unsigned char>(__i), __fc); | |
| basic_string<_CharT> __s; | |
| size_t __est_width; | |
| if (_M_spec._M_localized) [[unlikely]] | |
| { | |
| auto& __np = std::use_facet<numpunct<_CharT>>(__fc.locale()); | |
| __s = __i ? __np.truename() : __np.falsename(); | |
| __est_width = __s.size(); // TODO Unicode-aware estimate | |
| } | |
| else | |
| { | |
| if constexpr (is_same_v<char, _CharT>) | |
| __s = __i ? "true" : "false"; | |
| else | |
| __s = __i ? L"true" : L"false"; | |
| __est_width = __s.size(); | |
| } | |
| return __format::__write_padded_as_spec(__s, __est_width, __fc, | |
| _M_spec); | |
| } | |
| template<typename _Out> | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| typename basic_format_context<_Out, _CharT>::iterator | |
| _M_format_character(_CharT __c, | |
| basic_format_context<_Out, _CharT>& __fc) const | |
| { | |
| basic_string_view<_CharT> __in(&__c, 1u); | |
| size_t __width = 1u; | |
| // N.B. single byte cannot encode character of width greater than 1 | |
| if constexpr (sizeof(_CharT) > 1u && | |
| __unicode::__literal_encoding_is_unicode<_CharT>()) | |
| __width = __unicode::__field_width(__c); | |
| if (!_M_spec._M_debug) | |
| return __format::__write_padded_as_spec(__in, __width, | |
| __fc, _M_spec); | |
| __width += 2; | |
| if (_M_spec._M_get_width(__fc) <= __width) | |
| return __format::__write_escaped(__fc.out(), __in, _Term_apos); | |
| _CharT __buf[12]; | |
| _Fixedbuf_sink<_CharT> __sink(__buf); | |
| __format::__write_escaped(__sink.out(), __in, _Term_apos); | |
| __in = __sink.view(); | |
| if (__in[1] == _Escapes<_CharT>::_S_bslash()[0]) // escape sequence | |
| __width = __in.size(); | |
| return __format::__write_padded_as_spec(__in, __width, | |
| __fc, _M_spec); | |
| } | |
| template<typename _Int> | |
| static _GLIBCXX_CONSTEXPR_FORMAT _CharT | |
| _S_to_character(_Int __i) | |
| { | |
| using _Traits = __gnu_cxx::__int_traits<_CharT>; | |
| if constexpr (is_signed_v<_Int> == is_signed_v<_CharT>) | |
| { | |
| if (_Traits::__min <= __i && __i <= _Traits::__max) | |
| return static_cast<_CharT>(__i); | |
| } | |
| else if constexpr (is_signed_v<_Int>) | |
| { | |
| if (__i >= 0 && make_unsigned_t<_Int>(__i) <= _Traits::__max) | |
| return static_cast<_CharT>(__i); | |
| } | |
| else if (__i <= make_unsigned_t<_CharT>(_Traits::__max)) | |
| return static_cast<_CharT>(__i); | |
| __throw_format_error("format error: integer not representable as " | |
| "character"); | |
| } | |
| template<typename _Out> | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| typename basic_format_context<_Out, _CharT>::iterator | |
| _M_format_int(basic_string_view<_CharT> __str, size_t __prefix_len, | |
| basic_format_context<_Out, _CharT>& __fc) const | |
| { | |
| size_t __width = _M_spec._M_get_width(__fc); | |
| if (_M_spec._M_localized) | |
| { | |
| const auto& __l = __fc.locale(); | |
| if (__l.name() != "C") | |
| { | |
| auto& __np = use_facet<numpunct<_CharT>>(__l); | |
| string __grp = __np.grouping(); | |
| if (!__grp.empty()) | |
| { | |
| size_t __n = __str.size() - __prefix_len; | |
| auto __p = (_CharT*)__builtin_alloca(2 * __n | |
| * sizeof(_CharT) | |
| + __prefix_len); | |
| auto __s = __str.data(); | |
| char_traits<_CharT>::copy(__p, __s, __prefix_len); | |
| __s += __prefix_len; | |
| auto __end = std::__add_grouping(__p + __prefix_len, | |
| __np.thousands_sep(), | |
| __grp.data(), | |
| __grp.size(), | |
| __s, __s + __n); | |
| __str = {__p, size_t(__end - __p)}; | |
| } | |
| } | |
| } | |
| if (__width <= __str.size()) | |
| return __format::__write(__fc.out(), __str); | |
| char32_t __fill_char = _M_spec._M_fill; | |
| _Align __align = _M_spec._M_align; | |
| size_t __nfill = __width - __str.size(); | |
| auto __out = __fc.out(); | |
| if (__align == _Align_default) | |
| { | |
| __align = _Align_right; | |
| if (_M_spec._M_zero_fill) | |
| { | |
| __fill_char = _CharT('0'); | |
| // Write sign and base prefix before zero filling. | |
| if (__prefix_len != 0) | |
| { | |
| __out = __format::__write(std::move(__out), | |
| __str.substr(0, __prefix_len)); | |
| __str.remove_prefix(__prefix_len); | |
| } | |
| } | |
| else | |
| __fill_char = _CharT(' '); | |
| } | |
| return __format::__write_padded(std::move(__out), __str, | |
| __align, __nfill, __fill_char); | |
| } | |
| _Spec<_CharT> _M_spec{}; | |
| }; | |
| #ifdef __BFLT16_DIG__ | |
| using __bflt16_t = decltype(0.0bf16); | |
| #endif | |
| // Decide how 128-bit floating-point types should be formatted (or not). | |
| // When supported, the typedef __format::__flt128_t is the type that format | |
| // arguments should be converted to before passing them to __formatter_fp. | |
| // Define the macro _GLIBCXX_FORMAT_F128 to say they're supported. | |
| // The __float128, _Float128 will be formatted by converting them to: | |
| // __ieee128 (same as __float128) when _GLIBCXX_FORMAT_F128=1, | |
| // long double when _GLIBCXX_FORMAT_F128=2, | |
| // _Float128 when _GLIBCXX_FORMAT_F128=3. | |
| #undef _GLIBCXX_FORMAT_F128 | |
| #ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT | |
| // Format 128-bit floating-point types using __ieee128. | |
| using __flt128_t = __ieee128; | |
| # define _GLIBCXX_FORMAT_F128 1 | |
| #ifdef __LONG_DOUBLE_IEEE128__ | |
| // These overloads exist in the library, but are not declared. | |
| // Make them available as std::__format::to_chars. | |
| to_chars_result | |
| to_chars(char*, char*, __ibm128) noexcept | |
| __asm("_ZSt8to_charsPcS_e"); | |
| to_chars_result | |
| to_chars(char*, char*, __ibm128, chars_format) noexcept | |
| __asm("_ZSt8to_charsPcS_eSt12chars_format"); | |
| to_chars_result | |
| to_chars(char*, char*, __ibm128, chars_format, int) noexcept | |
| __asm("_ZSt8to_charsPcS_eSt12chars_formati"); | |
| #elif __cplusplus == 202002L | |
| to_chars_result | |
| to_chars(char*, char*, __ieee128) noexcept | |
| __asm("_ZSt8to_charsPcS_u9__ieee128"); | |
| to_chars_result | |
| to_chars(char*, char*, __ieee128, chars_format) noexcept | |
| __asm("_ZSt8to_charsPcS_u9__ieee128St12chars_format"); | |
| to_chars_result | |
| to_chars(char*, char*, __ieee128, chars_format, int) noexcept | |
| __asm("_ZSt8to_charsPcS_u9__ieee128St12chars_formati"); | |
| #endif | |
| #elif defined _GLIBCXX_LDOUBLE_IS_IEEE_BINARY128 | |
| // Format 128-bit floating-point types using long double. | |
| using __flt128_t = long double; | |
| # define _GLIBCXX_FORMAT_F128 2 | |
| #elif __FLT128_DIG__ && defined(_GLIBCXX_HAVE_FLOAT128_MATH) | |
| // Format 128-bit floating-point types using _Float128. | |
| using __flt128_t = _Float128; | |
| # define _GLIBCXX_FORMAT_F128 3 | |
| # if __cplusplus == 202002L | |
| // These overloads exist in the library, but are not declared for C++20. | |
| // Make them available as std::__format::to_chars. | |
| to_chars_result | |
| to_chars(char*, char*, _Float128) noexcept | |
| # if _GLIBCXX_INLINE_VERSION | |
| __asm("_ZNSt3__88to_charsEPcS0_DF128_"); | |
| # else | |
| __asm("_ZSt8to_charsPcS_DF128_"); | |
| # endif | |
| to_chars_result | |
| to_chars(char*, char*, _Float128, chars_format) noexcept | |
| # if _GLIBCXX_INLINE_VERSION | |
| __asm("_ZNSt3__88to_charsEPcS0_DF128_NS_12chars_formatE"); | |
| # else | |
| __asm("_ZSt8to_charsPcS_DF128_St12chars_format"); | |
| # endif | |
| to_chars_result | |
| to_chars(char*, char*, _Float128, chars_format, int) noexcept | |
| # if _GLIBCXX_INLINE_VERSION | |
| __asm("_ZNSt3__88to_charsEPcS0_DF128_NS_12chars_formatEi"); | |
| # else | |
| __asm("_ZSt8to_charsPcS_DF128_St12chars_formati"); | |
| # endif | |
| # endif | |
| #endif | |
| using std::to_chars; | |
| // We can format a floating-point type iff it is usable with to_chars. | |
| template<typename _Tp> | |
| concept __formattable_float | |
| = is_same_v<remove_cv_t<_Tp>, _Tp> && requires (_Tp __t, char* __p) | |
| { __format::to_chars(__p, __p, __t, chars_format::scientific, 6); }; | |
| template<__char _CharT> | |
| struct __formatter_fp | |
| { | |
| constexpr typename basic_format_parse_context<_CharT>::iterator | |
| parse(basic_format_parse_context<_CharT>& __pc) | |
| { | |
| _Spec<_CharT> __spec{}; | |
| const auto __last = __pc.end(); | |
| auto __first = __pc.begin(); | |
| auto __finalize = [this, &__spec] { | |
| _M_spec = __spec; | |
| }; | |
| auto __finished = [&] { | |
| if (__first == __last || *__first == '}') | |
| { | |
| __finalize(); | |
| return true; | |
| } | |
| return false; | |
| }; | |
| if (__finished()) | |
| return __first; | |
| __first = __spec._M_parse_fill_and_align(__first, __last); | |
| if (__finished()) | |
| return __first; | |
| __first = __spec._M_parse_sign(__first, __last); | |
| if (__finished()) | |
| return __first; | |
| __first = __spec._M_parse_alternate_form(__first, __last); | |
| if (__finished()) | |
| return __first; | |
| __first = __spec._M_parse_zero_fill(__first, __last); | |
| if (__finished()) | |
| return __first; | |
| if (__first[0] != '.') | |
| { | |
| __first = __spec._M_parse_width(__first, __last, __pc); | |
| if (__finished()) | |
| return __first; | |
| } | |
| __first = __spec._M_parse_precision(__first, __last, __pc); | |
| if (__finished()) | |
| return __first; | |
| __first = __spec._M_parse_locale(__first, __last); | |
| if (__finished()) | |
| return __first; | |
| switch (*__first) | |
| { | |
| case 'a': | |
| __spec._M_type = _Pres_a; | |
| ++__first; | |
| break; | |
| case 'A': | |
| __spec._M_type = _Pres_A; | |
| ++__first; | |
| break; | |
| case 'e': | |
| __spec._M_type = _Pres_e; | |
| ++__first; | |
| break; | |
| case 'E': | |
| __spec._M_type = _Pres_E; | |
| ++__first; | |
| break; | |
| case 'f': | |
| __spec._M_type = _Pres_f; | |
| ++__first; | |
| break; | |
| case 'F': | |
| __spec._M_type = _Pres_F; | |
| ++__first; | |
| break; | |
| case 'g': | |
| __spec._M_type = _Pres_g; | |
| ++__first; | |
| break; | |
| case 'G': | |
| __spec._M_type = _Pres_G; | |
| ++__first; | |
| break; | |
| } | |
| if (__finished()) | |
| return __first; | |
| __format::__failed_to_parse_format_spec(); | |
| } | |
| template<typename _Fp, typename _Out> | |
| typename basic_format_context<_Out, _CharT>::iterator | |
| format(_Fp __v, basic_format_context<_Out, _CharT>& __fc) const | |
| { | |
| std::string __dynbuf; | |
| char __buf[128]; | |
| to_chars_result __res{}; | |
| size_t __prec = 6; | |
| bool __use_prec = _M_spec._M_prec_kind != _WP_none; | |
| if (__use_prec) | |
| __prec = _M_spec._M_get_precision(__fc); | |
| chars_format __fmt{}; | |
| bool __upper = false; | |
| bool __trailing_zeros = false; | |
| char __expc = 'e'; | |
| size_t __offset = 1; // reserve space for sign | |
| switch (_M_spec._M_type) | |
| { | |
| case _Pres_P: | |
| if (__builtin_isfinite(__v)) | |
| __offset += 2; // reserve space for prefix | |
| [[fallthrough]]; | |
| case _Pres_A: | |
| __upper = true; | |
| __expc = 'P'; | |
| __fmt = chars_format::hex; | |
| break; | |
| case _Pres_p: | |
| if (__builtin_isfinite(__v)) | |
| __offset += 2; // reserve space for prefix | |
| [[fallthrough]]; | |
| case _Pres_a: | |
| __expc = 'p'; | |
| __fmt = chars_format::hex; | |
| break; | |
| case _Pres_E: | |
| __upper = true; | |
| __expc = 'E'; | |
| [[fallthrough]]; | |
| case _Pres_e: | |
| __use_prec = true; | |
| __fmt = chars_format::scientific; | |
| break; | |
| case _Pres_F: | |
| __upper = true; | |
| [[fallthrough]]; | |
| case _Pres_f: | |
| __use_prec = true; | |
| __fmt = chars_format::fixed; | |
| break; | |
| case _Pres_G: | |
| __upper = true; | |
| __expc = 'E'; | |
| [[fallthrough]]; | |
| case _Pres_g: | |
| __trailing_zeros = true; | |
| __use_prec = true; | |
| __fmt = chars_format::general; | |
| break; | |
| default: // Fallback for _Pres_type values introduces in later versions. | |
| case _Pres_none: | |
| if (__use_prec) | |
| __fmt = chars_format::general; | |
| break; | |
| } | |
| char* __start = __buf + __offset; | |
| char* __end = __buf + sizeof(__buf); | |
| // Write value into buffer using std::to_chars. | |
| auto __to_chars = [&](char* __b, char* __e) { | |
| if (__use_prec) | |
| return __format::to_chars(__b, __e, __v, __fmt, __prec); | |
| else if (__fmt != chars_format{}) | |
| return __format::to_chars(__b, __e, __v, __fmt); | |
| else | |
| return __format::to_chars(__b, __e, __v); | |
| }; | |
| // First try using stack buffer. | |
| __res = __to_chars(__start, __end); | |
| if (__builtin_expect(__res.ec == errc::value_too_large, 0)) | |
| { | |
| // If the buffer is too small it's probably because of a large | |
| // precision, or a very large value in fixed format. | |
| size_t __guess = 7 + __offset + __prec; | |
| if (__fmt == chars_format::fixed) // +ddd.prec | |
| { | |
| if constexpr (is_same_v<_Fp, float> || is_same_v<_Fp, double> | |
| || is_same_v<_Fp, long double>) | |
| { | |
| // The number of digits to the left of the decimal point | |
| // is floor(log10(max(abs(__v),1)))+1 | |
| int __exp{}; | |
| if constexpr (is_same_v<_Fp, float>) | |
| __builtin_frexpf(__v, &__exp); | |
| else if constexpr (is_same_v<_Fp, double>) | |
| __builtin_frexp(__v, &__exp); | |
| else if constexpr (is_same_v<_Fp, long double>) | |
| __builtin_frexpl(__v, &__exp); | |
| if (__exp > 0) | |
| __guess += 1U + __exp * 4004U / 13301U; // log10(2) approx. | |
| } | |
| else | |
| __guess += numeric_limits<_Fp>::max_exponent10; | |
| } | |
| if (__guess <= sizeof(__buf)) [[unlikely]] | |
| __guess = sizeof(__buf) * 2; | |
| __dynbuf.reserve(__guess); | |
| do | |
| { | |
| // Mangling of this lambda, and thus resize_and_overwrite | |
| // instantiated with it, was fixed in ABI 18 (G++ 13). Since | |
| // <format> was new in G++ 13, and is experimental, that | |
| // isn't a problem. | |
| auto __overwrite = [&__to_chars, &__res, __offset] (char* __p, size_t __n) | |
| { | |
| __res = __to_chars(__p + __offset, __p + __n - __offset); | |
| return __res.ec == errc{} ? __res.ptr - __p : 0; | |
| }; | |
| __dynbuf.__resize_and_overwrite(__dynbuf.capacity() * 2, | |
| __overwrite); | |
| __start = __dynbuf.data() + __offset; // reserve space for sign and prefix | |
| __end = __dynbuf.data() + __dynbuf.size(); | |
| } | |
| while (__builtin_expect(__res.ec == errc::value_too_large, 0)); | |
| } | |
| if (__offset == 3) | |
| { | |
| __start -= 2; | |
| if (__builtin_signbit(__v)) | |
| ranges::copy(string_view("-0x"), __start); | |
| else | |
| ranges::copy(string_view("0x"), __start); | |
| } | |
| // Use uppercase for 'A', 'P', 'E', and 'G' formats. | |
| if (__upper) | |
| { | |
| for (char* __p = __start; __p != __res.ptr; ++__p) | |
| *__p = __format::__toupper_numeric(*__p); | |
| } | |
| // Add sign for non-negative values. | |
| if (!__builtin_signbit(__v)) | |
| { | |
| if (_M_spec._M_sign == _Sign_plus) | |
| *--__start = '+'; | |
| else if (_M_spec._M_sign == _Sign_space) | |
| *--__start = ' '; | |
| else | |
| --__offset; | |
| } | |
| string_view __narrow_str(__start, __res.ptr - __start); | |
| // Use alternate form. Ensure decimal point is always present, | |
| // and add trailing zeros (up to precision) for g and G forms. | |
| if (_M_spec._M_alt && __builtin_isfinite(__v)) | |
| { | |
| string_view __s = __narrow_str; | |
| size_t __sigfigs; // Number of significant figures. | |
| size_t __z = 0; // Number of trailing zeros to add. | |
| size_t __p; // Position of the exponent character (if any). | |
| size_t __d = __s.find('.'); // Position of decimal point. | |
| if (__d != __s.npos) // Found decimal point. | |
| { | |
| __p = __s.find(__expc, __d + 1); | |
| if (__p == __s.npos) | |
| __p = __s.size(); | |
| // If presentation type is g or G we might need to add zeros. | |
| if (__trailing_zeros) | |
| { | |
| // Find number of digits after first significant figure. | |
| if (__s[__offset] != '0') | |
| // A string like "D.D" or "-D.DDD" | |
| __sigfigs = __p - __offset - 1; | |
| else | |
| // A string like "0.D" or "-0.0DD". | |
| // Safe to assume there is a non-zero digit, because | |
| // otherwise there would be no decimal point. | |
| __sigfigs = __p - __s.find_first_not_of('0', __d + 1); | |
| } | |
| } | |
| else // No decimal point, we need to insert one. | |
| { | |
| __p = __s.find(__expc); // Find the exponent, if present. | |
| if (__p == __s.npos) | |
| __p = __s.size(); | |
| __d = __p; // Position where '.' should be inserted. | |
| __sigfigs = __d - __offset; | |
| } | |
| if (__trailing_zeros && __prec != 0) | |
| { | |
| // For g and G presentation types std::to_chars produces | |
| // no more than prec significant figures. Insert this many | |
| // zeros so the result has exactly prec significant figures. | |
| __z = __prec - __sigfigs; | |
| } | |
| if (size_t __extras = int(__d == __p) + __z) // How many to add. | |
| { | |
| if (__dynbuf.empty() && __extras <= size_t(__end - __res.ptr)) | |
| { | |
| // The stack buffer is large enough for the result. | |
| // Move exponent to make space for extra chars. | |
| __builtin_memmove(__start + __p + __extras, | |
| __start + __p, | |
| __s.size() - __p); | |
| if (__d == __p) | |
| __start[__p++] = '.'; | |
| __builtin_memset(__start + __p, '0', __z); | |
| __narrow_str = {__s.data(), __s.size() + __extras}; | |
| } | |
| else // Need to switch to the dynamic buffer. | |
| { | |
| __dynbuf.reserve(__s.size() + __extras); | |
| if (__dynbuf.empty()) | |
| { | |
| __dynbuf = __s.substr(0, __p); | |
| if (__d == __p) | |
| __dynbuf += '.'; | |
| if (__z) | |
| __dynbuf.append(__z, '0'); | |
| __dynbuf.append(__s.substr(__p)); | |
| } | |
| else | |
| { | |
| __dynbuf.insert(__p, __extras, '0'); | |
| if (__d == __p) | |
| __dynbuf[__p] = '.'; | |
| } | |
| __narrow_str = __dynbuf; | |
| } | |
| } | |
| } | |
| basic_string<_CharT> __wstr; | |
| basic_string_view<_CharT> __str; | |
| if constexpr (is_same_v<_CharT, char>) | |
| __str = __narrow_str; | |
| #ifdef _GLIBCXX_USE_WCHAR_T | |
| else | |
| { | |
| // _GLIBCXX_RESOLVE_LIB_DEFECTS | |
| // 4522. Clarify that `std::format` transcodes for `std::wformat_strings` | |
| __wstr = std::__to_wstring_numeric(__narrow_str); | |
| __str = __wstr; | |
| } | |
| #endif | |
| if (_M_spec._M_localized && __builtin_isfinite(__v)) | |
| { | |
| auto __s = _M_localize(__str, __expc, __offset, __fc.locale()); | |
| if (!__s.empty()) | |
| __str = __wstr = std::move(__s); | |
| } | |
| size_t __width = _M_spec._M_get_width(__fc); | |
| if (__width <= __str.size()) | |
| return __format::__write(__fc.out(), __str); | |
| char32_t __fill_char = _M_spec._M_fill; | |
| _Align __align = _M_spec._M_align; | |
| size_t __nfill = __width - __str.size(); | |
| auto __out = __fc.out(); | |
| if (__align == _Align_default) | |
| { | |
| __align = _Align_right; | |
| if (_M_spec._M_zero_fill && __builtin_isfinite(__v)) | |
| { | |
| __fill_char = _CharT('0'); | |
| if (__offset > 0) | |
| { | |
| __out = __format::__write(__out, __str.substr(0, __offset)); | |
| __str.remove_prefix(__offset); | |
| } | |
| } | |
| else | |
| __fill_char = _CharT(' '); | |
| } | |
| return __format::__write_padded(std::move(__out), __str, | |
| __align, __nfill, __fill_char); | |
| } | |
| // Locale-specific format. | |
| basic_string<_CharT> | |
| _M_localize(basic_string_view<_CharT> __str, char __expc, | |
| int __offset, const locale& __loc) const | |
| { | |
| basic_string<_CharT> __lstr; | |
| if (__loc == locale::classic()) | |
| return __lstr; // Nothing to do. | |
| const auto& __np = use_facet<numpunct<_CharT>>(__loc); | |
| const _CharT __point = __np.decimal_point(); | |
| const string __grp = __np.grouping(); | |
| _CharT __dot, __exp; | |
| if constexpr (is_same_v<_CharT, char>) | |
| { | |
| __dot = '.'; | |
| __exp = __expc; | |
| } | |
| else | |
| { | |
| __dot = L'.'; | |
| switch (__expc) | |
| { | |
| case 'e': | |
| __exp = L'e'; | |
| break; | |
| case 'E': | |
| __exp = L'E'; | |
| break; | |
| case 'p': | |
| __exp = L'p'; | |
| break; | |
| case 'P': | |
| __exp = L'P'; | |
| break; | |
| default: | |
| __builtin_unreachable(); | |
| } | |
| } | |
| if (__grp.empty() && __point == __dot) | |
| return __lstr; // Locale uses '.' and no grouping. | |
| size_t __d = __str.find(__dot); // Index of radix character (if any). | |
| size_t __e = min(__d, __str.find(__exp)); // First of radix or exponent | |
| if (__e == __str.npos) | |
| __e = __str.size(); | |
| const size_t __r = __str.size() - __e; // Length of remainder. | |
| auto __overwrite = [&](_CharT* __p, size_t) { | |
| // Copy any +/- sign and "0x" prefix | |
| ranges::copy_n(__str.data(), __offset, __p); | |
| // Apply grouping to the digits before the radix or exponent. | |
| auto __end = std::__add_grouping(__p + __offset, __np.thousands_sep(), | |
| __grp.data(), __grp.size(), | |
| __str.data() + __offset, | |
| __str.data() + __e); | |
| if (__r) // If there's a fractional part or exponent | |
| { | |
| if (__d != __str.npos) | |
| { | |
| *__end = __point; // Add the locale's radix character. | |
| ++__end; | |
| ++__e; | |
| } | |
| const size_t __rlen = __str.size() - __e; | |
| // Append fractional digits and/or exponent: | |
| char_traits<_CharT>::copy(__end, __str.data() + __e, __rlen); | |
| __end += __rlen; | |
| } | |
| return (__end - __p); | |
| }; | |
| __lstr.__resize_and_overwrite(__e * 2 + __r, __overwrite); | |
| return __lstr; | |
| } | |
| _Spec<_CharT> _M_spec{}; | |
| }; | |
| template<__format::__char _CharT> | |
| struct __formatter_ptr | |
| { | |
| constexpr | |
| __formatter_ptr() noexcept | |
| : _M_spec() | |
| { | |
| _M_spec._M_type = _Pres_x; | |
| _M_spec._M_alt = true; | |
| } | |
| constexpr | |
| __formatter_ptr(_Spec<_CharT> __spec) noexcept | |
| : _M_spec(__spec) | |
| { _M_set_default(); } | |
| constexpr typename basic_format_parse_context<_CharT>::iterator | |
| parse(basic_format_parse_context<_CharT>& __pc) | |
| { | |
| __format::_Spec<_CharT> __spec{}; | |
| const auto __last = __pc.end(); | |
| auto __first = __pc.begin(); | |
| auto __finalize = [this, &__spec] { | |
| _M_spec = __spec; | |
| _M_set_default(); | |
| }; | |
| auto __finished = [&] { | |
| if (__first == __last || *__first == '}') | |
| { | |
| __finalize(); | |
| return true; | |
| } | |
| return false; | |
| }; | |
| if (__finished()) | |
| return __first; | |
| __first = __spec._M_parse_fill_and_align(__first, __last); | |
| if (__finished()) | |
| return __first; | |
| // _GLIBCXX_RESOLVE_LIB_DEFECTS | |
| // P2510R3 Formatting pointers | |
| #if __glibcxx_format >= 202304L | |
| __first = __spec._M_parse_zero_fill(__first, __last); | |
| if (__finished()) | |
| return __first; | |
| #endif | |
| __first = __spec._M_parse_width(__first, __last, __pc); | |
| if (__finished()) | |
| return __first; | |
| if (*__first == 'p') | |
| { | |
| __spec._M_type = _Pres_x; | |
| __spec._M_alt = true; | |
| ++__first; | |
| } | |
| #if __glibcxx_format >= 202304L | |
| else if (*__first == 'P') | |
| { | |
| __spec._M_type = _Pres_X; | |
| __spec._M_alt = true; | |
| ++__first; | |
| } | |
| #endif | |
| if (__finished()) | |
| return __first; | |
| __format::__failed_to_parse_format_spec(); | |
| } | |
| template<typename _Out> | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| typename basic_format_context<_Out, _CharT>::iterator | |
| format(const void* __v, basic_format_context<_Out, _CharT>& __fc) const | |
| { | |
| auto __u = __v | |
| ? reinterpret_cast<__UINTPTR_TYPE__>(__v) | |
| : static_cast<__UINTPTR_TYPE__>(0); | |
| return __formatter_int<_CharT>(_M_spec).format(__u, __fc); | |
| } | |
| private: | |
| [[__gnu__::__always_inline__]] | |
| constexpr void | |
| _M_set_default() | |
| { | |
| if (_M_spec._M_type == _Pres_none) | |
| { | |
| _M_spec._M_type = _Pres_x; | |
| _M_spec._M_alt = true; | |
| } | |
| } | |
| __format::_Spec<_CharT> _M_spec; | |
| }; | |
| } // namespace __format | |
| /// @endcond | |
| /// Format a character. | |
| template<__format::__char _CharT> | |
| struct formatter<_CharT, _CharT> | |
| { | |
| formatter() = default; | |
| constexpr typename basic_format_parse_context<_CharT>::iterator | |
| parse(basic_format_parse_context<_CharT>& __pc) | |
| { | |
| return _M_f.template _M_parse<_CharT>(__pc); | |
| } | |
| template<typename _Out> | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| typename basic_format_context<_Out, _CharT>::iterator | |
| format(_CharT __u, basic_format_context<_Out, _CharT>& __fc) const | |
| { | |
| if (_M_f._M_spec._M_type == __format::_Pres_c) | |
| return _M_f._M_format_character(__u, __fc); | |
| else | |
| return _M_f.format(static_cast<make_unsigned_t<_CharT>>(__u), __fc); | |
| } | |
| #if __glibcxx_format_ranges // C++ >= 23 && HOSTED | |
| constexpr void | |
| set_debug_format() noexcept | |
| { _M_f._M_spec._M_debug = true; } | |
| #endif | |
| private: | |
| __format::__formatter_int<_CharT> _M_f; | |
| }; | |
| #if __glibcxx_print >= 202403L | |
| template<__format::__char _CharT> | |
| constexpr bool enable_nonlocking_formatter_optimization<_CharT> = true; | |
| #endif | |
| #ifdef _GLIBCXX_USE_WCHAR_T | |
| /// Format a char value for wide character output. | |
| template<> | |
| struct formatter<char, wchar_t> | |
| { | |
| formatter() = default; | |
| constexpr typename basic_format_parse_context<wchar_t>::iterator | |
| parse(basic_format_parse_context<wchar_t>& __pc) | |
| { | |
| return _M_f._M_parse<char>(__pc); | |
| } | |
| template<typename _Out> | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| typename basic_format_context<_Out, wchar_t>::iterator | |
| format(char __u, basic_format_context<_Out, wchar_t>& __fc) const | |
| { | |
| if (_M_f._M_spec._M_type == __format::_Pres_c) | |
| return _M_f._M_format_character(__u, __fc); | |
| else | |
| return _M_f.format(static_cast<unsigned char>(__u), __fc); | |
| } | |
| #if __glibcxx_format_ranges // C++ >= 23 && HOSTED | |
| constexpr void | |
| set_debug_format() noexcept | |
| { _M_f._M_spec._M_debug = true; } | |
| #endif | |
| private: | |
| __format::__formatter_int<wchar_t> _M_f; | |
| }; | |
| #endif // USE_WCHAR_T | |
| /** Format a string. | |
| * @{ | |
| */ | |
| template<__format::__char _CharT> | |
| struct formatter<_CharT*, _CharT> | |
| { | |
| formatter() = default; | |
| [[__gnu__::__always_inline__]] | |
| constexpr typename basic_format_parse_context<_CharT>::iterator | |
| parse(basic_format_parse_context<_CharT>& __pc) | |
| { return _M_f.parse(__pc); } | |
| template<typename _Out> | |
| [[__gnu__::__nonnull__]] | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| typename basic_format_context<_Out, _CharT>::iterator | |
| format(_CharT* __u, basic_format_context<_Out, _CharT>& __fc) const | |
| { return _M_f.format(__u, __fc); } | |
| #if __glibcxx_format_ranges // C++ >= 23 && HOSTED | |
| constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); } | |
| #endif | |
| private: | |
| __format::__formatter_str<_CharT> _M_f; | |
| }; | |
| #if __glibcxx_print >= 202403L | |
| template<__format::__char _CharT> | |
| constexpr bool enable_nonlocking_formatter_optimization<_CharT*> = true; | |
| #endif | |
| template<__format::__char _CharT> | |
| struct formatter<const _CharT*, _CharT> | |
| { | |
| formatter() = default; | |
| [[__gnu__::__always_inline__]] | |
| constexpr typename basic_format_parse_context<_CharT>::iterator | |
| parse(basic_format_parse_context<_CharT>& __pc) | |
| { return _M_f.parse(__pc); } | |
| template<typename _Out> | |
| [[__gnu__::__nonnull__]] | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| typename basic_format_context<_Out, _CharT>::iterator | |
| format(const _CharT* __u, | |
| basic_format_context<_Out, _CharT>& __fc) const | |
| { return _M_f.format(__u, __fc); } | |
| #if __glibcxx_format_ranges // C++ >= 23 && HOSTED | |
| constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); } | |
| #endif | |
| private: | |
| __format::__formatter_str<_CharT> _M_f; | |
| }; | |
| #if __glibcxx_print >= 202403L | |
| template<__format::__char _CharT> | |
| constexpr bool | |
| enable_nonlocking_formatter_optimization<const _CharT*> = true; | |
| #endif | |
| template<__format::__char _CharT, size_t _Nm> | |
| struct formatter<_CharT[_Nm], _CharT> | |
| { | |
| formatter() = default; | |
| [[__gnu__::__always_inline__]] | |
| constexpr typename basic_format_parse_context<_CharT>::iterator | |
| parse(basic_format_parse_context<_CharT>& __pc) | |
| { return _M_f.parse(__pc); } | |
| template<typename _Out> | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| typename basic_format_context<_Out, _CharT>::iterator | |
| format(const _CharT (&__u)[_Nm], | |
| basic_format_context<_Out, _CharT>& __fc) const | |
| { return _M_f.format({__u, _Nm}, __fc); } | |
| #if __glibcxx_format_ranges // C++ >= 23 && HOSTED | |
| constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); } | |
| #endif | |
| private: | |
| __format::__formatter_str<_CharT> _M_f; | |
| }; | |
| #if __glibcxx_print >= 202403L | |
| template<__format::__char _CharT, size_t _Nm> | |
| constexpr bool enable_nonlocking_formatter_optimization<_CharT[_Nm]> = true; | |
| #endif | |
| template<typename _Traits, typename _Alloc> | |
| struct formatter<basic_string<char, _Traits, _Alloc>, char> | |
| { | |
| formatter() = default; | |
| [[__gnu__::__always_inline__]] | |
| constexpr typename basic_format_parse_context<char>::iterator | |
| parse(basic_format_parse_context<char>& __pc) | |
| { return _M_f.parse(__pc); } | |
| template<typename _Out> | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| typename basic_format_context<_Out, char>::iterator | |
| format(const basic_string<char, _Traits, _Alloc>& __u, | |
| basic_format_context<_Out, char>& __fc) const | |
| { return _M_f.format(__u, __fc); } | |
| #if __glibcxx_format_ranges // C++ >= 23 && HOSTED | |
| constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); } | |
| #endif | |
| private: | |
| __format::__formatter_str<char> _M_f; | |
| }; | |
| #if __glibcxx_print >= 202403L | |
| template<typename _Tr, typename _Alloc> | |
| constexpr bool | |
| enable_nonlocking_formatter_optimization<basic_string<char, _Tr, _Alloc>> | |
| = true; | |
| #endif | |
| #ifdef _GLIBCXX_USE_WCHAR_T | |
| template<typename _Traits, typename _Alloc> | |
| struct formatter<basic_string<wchar_t, _Traits, _Alloc>, wchar_t> | |
| { | |
| formatter() = default; | |
| [[__gnu__::__always_inline__]] | |
| constexpr typename basic_format_parse_context<wchar_t>::iterator | |
| parse(basic_format_parse_context<wchar_t>& __pc) | |
| { return _M_f.parse(__pc); } | |
| template<typename _Out> | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| typename basic_format_context<_Out, wchar_t>::iterator | |
| format(const basic_string<wchar_t, _Traits, _Alloc>& __u, | |
| basic_format_context<_Out, wchar_t>& __fc) const | |
| { return _M_f.format(__u, __fc); } | |
| #if __glibcxx_format_ranges // C++ >= 23 && HOSTED | |
| constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); } | |
| #endif | |
| private: | |
| __format::__formatter_str<wchar_t> _M_f; | |
| }; | |
| #if __glibcxx_print >= 202403L | |
| template<typename _Tr, typename _Alloc> | |
| constexpr bool | |
| enable_nonlocking_formatter_optimization<basic_string<wchar_t, _Tr, _Alloc>> | |
| = true; | |
| #endif | |
| #endif // USE_WCHAR_T | |
| template<typename _Traits> | |
| struct formatter<basic_string_view<char, _Traits>, char> | |
| { | |
| formatter() = default; | |
| [[__gnu__::__always_inline__]] | |
| constexpr typename basic_format_parse_context<char>::iterator | |
| parse(basic_format_parse_context<char>& __pc) | |
| { return _M_f.parse(__pc); } | |
| template<typename _Out> | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| typename basic_format_context<_Out, char>::iterator | |
| format(basic_string_view<char, _Traits> __u, | |
| basic_format_context<_Out, char>& __fc) const | |
| { return _M_f.format(__u, __fc); } | |
| #if __glibcxx_format_ranges // C++ >= 23 && HOSTED | |
| constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); } | |
| #endif | |
| private: | |
| __format::__formatter_str<char> _M_f; | |
| }; | |
| #if __glibcxx_print >= 202403L | |
| template<typename _Tr> | |
| constexpr bool | |
| enable_nonlocking_formatter_optimization<basic_string_view<char, _Tr>> | |
| = true; | |
| #endif | |
| #ifdef _GLIBCXX_USE_WCHAR_T | |
| template<typename _Traits> | |
| struct formatter<basic_string_view<wchar_t, _Traits>, wchar_t> | |
| { | |
| formatter() = default; | |
| [[__gnu__::__always_inline__]] | |
| constexpr typename basic_format_parse_context<wchar_t>::iterator | |
| parse(basic_format_parse_context<wchar_t>& __pc) | |
| { return _M_f.parse(__pc); } | |
| template<typename _Out> | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| typename basic_format_context<_Out, wchar_t>::iterator | |
| format(basic_string_view<wchar_t, _Traits> __u, | |
| basic_format_context<_Out, wchar_t>& __fc) const | |
| { return _M_f.format(__u, __fc); } | |
| #if __glibcxx_format_ranges // C++ >= 23 && HOSTED | |
| constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); } | |
| #endif | |
| private: | |
| __format::__formatter_str<wchar_t> _M_f; | |
| }; | |
| #if __glibcxx_print >= 202403L | |
| template<typename _Tr> | |
| constexpr bool | |
| enable_nonlocking_formatter_optimization<basic_string_view<wchar_t, _Tr>> | |
| = true; | |
| #endif | |
| #endif // USE_WCHAR_T | |
| /// @} | |
| /// @cond undocumented | |
| namespace __format | |
| { | |
| // each cv-unqualified arithmetic type ArithmeticT other than | |
| // char, wchar_t, char8_t, char16_t, or char32_t | |
| template<typename _Tp> | |
| constexpr bool __is_formattable_integer = __is_integer<_Tp>::__value; | |
| #if defined __SIZEOF_INT128__ | |
| template<> inline constexpr bool __is_formattable_integer<__int128> = true; | |
| template<> inline constexpr bool __is_formattable_integer<unsigned __int128> | |
| = true; | |
| #endif | |
| template<> inline constexpr bool __is_formattable_integer<char> = false; | |
| template<> inline constexpr bool __is_formattable_integer<wchar_t> = false; | |
| #ifdef _GLIBCXX_USE_CHAR8_T | |
| template<> inline constexpr bool __is_formattable_integer<char8_t> = false; | |
| #endif | |
| template<> inline constexpr bool __is_formattable_integer<char16_t> = false; | |
| template<> inline constexpr bool __is_formattable_integer<char32_t> = false; | |
| template<typename _Tp> | |
| concept __formattable_integer = __is_formattable_integer<_Tp>; | |
| } | |
| /// @endcond | |
| /// Format an integer. | |
| template<__format::__formattable_integer _Tp, __format::__char _CharT> | |
| struct formatter<_Tp, _CharT> | |
| { | |
| formatter() = default; | |
| [[__gnu__::__always_inline__]] | |
| constexpr typename basic_format_parse_context<_CharT>::iterator | |
| parse(basic_format_parse_context<_CharT>& __pc) | |
| { | |
| return _M_f.template _M_parse<_Tp>(__pc); | |
| } | |
| template<typename _Out> | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| typename basic_format_context<_Out, _CharT>::iterator | |
| format(_Tp __u, basic_format_context<_Out, _CharT>& __fc) const | |
| { return _M_f.format(__u, __fc); } | |
| private: | |
| __format::__formatter_int<_CharT> _M_f; | |
| }; | |
| #if __glibcxx_print >= 202403L | |
| template<__format::__formattable_integer _Tp> | |
| constexpr bool | |
| enable_nonlocking_formatter_optimization<_Tp> = true; | |
| #endif | |
| #if defined __glibcxx_to_chars | |
| /// Format a floating-point value. | |
| template<__format::__formattable_float _Tp, __format::__char _CharT> | |
| struct formatter<_Tp, _CharT> | |
| { | |
| formatter() = default; | |
| [[__gnu__::__always_inline__]] | |
| constexpr typename basic_format_parse_context<_CharT>::iterator | |
| parse(basic_format_parse_context<_CharT>& __pc) | |
| { return _M_f.parse(__pc); } | |
| template<typename _Out> | |
| typename basic_format_context<_Out, _CharT>::iterator | |
| format(_Tp __u, basic_format_context<_Out, _CharT>& __fc) const | |
| { return _M_f.format(__u, __fc); } | |
| private: | |
| __format::__formatter_fp<_CharT> _M_f; | |
| }; | |
| #if __glibcxx_print >= 202403L | |
| template<__format::__formattable_float _Tp> | |
| constexpr bool | |
| enable_nonlocking_formatter_optimization<_Tp> = true; | |
| #endif | |
| #if __LDBL_MANT_DIG__ == __DBL_MANT_DIG__ | |
| // Reuse __formatter_fp<C>::format<double, Out> for long double. | |
| template<__format::__char _CharT> | |
| struct formatter<long double, _CharT> | |
| { | |
| formatter() = default; | |
| [[__gnu__::__always_inline__]] | |
| constexpr typename basic_format_parse_context<_CharT>::iterator | |
| parse(basic_format_parse_context<_CharT>& __pc) | |
| { return _M_f.parse(__pc); } | |
| template<typename _Out> | |
| typename basic_format_context<_Out, _CharT>::iterator | |
| format(long double __u, basic_format_context<_Out, _CharT>& __fc) const | |
| { return _M_f.format((double)__u, __fc); } | |
| private: | |
| __format::__formatter_fp<_CharT> _M_f; | |
| }; | |
| #endif | |
| #if defined(__FLT32_DIG__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) | |
| // Reuse __formatter_fp<C>::format<float, Out> for _Float32. | |
| template<__format::__char _CharT> | |
| struct formatter<_Float32, _CharT> | |
| { | |
| formatter() = default; | |
| [[__gnu__::__always_inline__]] | |
| constexpr typename basic_format_parse_context<_CharT>::iterator | |
| parse(basic_format_parse_context<_CharT>& __pc) | |
| { return _M_f.parse(__pc); } | |
| template<typename _Out> | |
| typename basic_format_context<_Out, _CharT>::iterator | |
| format(_Float32 __u, basic_format_context<_Out, _CharT>& __fc) const | |
| { return _M_f.format((float)__u, __fc); } | |
| private: | |
| __format::__formatter_fp<_CharT> _M_f; | |
| }; | |
| #endif | |
| #if defined(__FLT64_DIG__) && defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64) | |
| // Reuse __formatter_fp<C>::format<double, Out> for _Float64. | |
| template<__format::__char _CharT> | |
| struct formatter<_Float64, _CharT> | |
| { | |
| formatter() = default; | |
| [[__gnu__::__always_inline__]] | |
| constexpr typename basic_format_parse_context<_CharT>::iterator | |
| parse(basic_format_parse_context<_CharT>& __pc) | |
| { return _M_f.parse(__pc); } | |
| template<typename _Out> | |
| typename basic_format_context<_Out, _CharT>::iterator | |
| format(_Float64 __u, basic_format_context<_Out, _CharT>& __fc) const | |
| { return _M_f.format((double)__u, __fc); } | |
| private: | |
| __format::__formatter_fp<_CharT> _M_f; | |
| }; | |
| #endif | |
| #if defined(__FLT128_DIG__) && _GLIBCXX_FORMAT_F128 | |
| // Use __formatter_fp<C>::format<__format::__flt128_t, Out> for _Float128. | |
| template<__format::__char _CharT> | |
| struct formatter<_Float128, _CharT> | |
| { | |
| formatter() = default; | |
| [[__gnu__::__always_inline__]] | |
| constexpr typename basic_format_parse_context<_CharT>::iterator | |
| parse(basic_format_parse_context<_CharT>& __pc) | |
| { return _M_f.parse(__pc); } | |
| template<typename _Out> | |
| typename basic_format_context<_Out, _CharT>::iterator | |
| format(_Float128 __u, basic_format_context<_Out, _CharT>& __fc) const | |
| { return _M_f.format((__format::__flt128_t)__u, __fc); } | |
| private: | |
| __format::__formatter_fp<_CharT> _M_f; | |
| }; | |
| #endif | |
| #if defined(__SIZEOF_FLOAT128__) && _GLIBCXX_FORMAT_F128 == 2 | |
| // Use __formatter_fp<C>::format<__format::__flt128_t, Out> for __float128, | |
| // when long double is not 128bit IEEE type. | |
| template<__format::__char _CharT> | |
| struct formatter<__float128, _CharT> | |
| { | |
| formatter() = default; | |
| [[__gnu__::__always_inline__]] | |
| constexpr typename basic_format_parse_context<_CharT>::iterator | |
| parse(basic_format_parse_context<_CharT>& __pc) | |
| { return _M_f.parse(__pc); } | |
| template<typename _Out> | |
| typename basic_format_context<_Out, _CharT>::iterator | |
| format(__float128 __u, basic_format_context<_Out, _CharT>& __fc) const | |
| { return _M_f.format((__format::__flt128_t)__u, __fc); } | |
| private: | |
| __format::__formatter_fp<_CharT> _M_f; | |
| }; | |
| #endif | |
| #endif // __cpp_lib_to_chars | |
| /** Format a pointer. | |
| * @{ | |
| */ | |
| template<__format::__char _CharT> | |
| struct formatter<const void*, _CharT> | |
| { | |
| formatter() = default; | |
| constexpr typename basic_format_parse_context<_CharT>::iterator | |
| parse(basic_format_parse_context<_CharT>& __pc) | |
| { return _M_f.parse(__pc); } | |
| template<typename _Out> | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| typename basic_format_context<_Out, _CharT>::iterator | |
| format(const void* __v, basic_format_context<_Out, _CharT>& __fc) const | |
| { return _M_f.format(__v, __fc); } | |
| private: | |
| __format::__formatter_ptr<_CharT> _M_f; | |
| }; | |
| #if __glibcxx_print >= 202403L | |
| template<> | |
| inline constexpr bool | |
| enable_nonlocking_formatter_optimization<const void*> = true; | |
| #endif | |
| template<__format::__char _CharT> | |
| struct formatter<void*, _CharT> | |
| { | |
| formatter() = default; | |
| [[__gnu__::__always_inline__]] | |
| constexpr typename basic_format_parse_context<_CharT>::iterator | |
| parse(basic_format_parse_context<_CharT>& __pc) | |
| { return _M_f.parse(__pc); } | |
| template<typename _Out> | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| typename basic_format_context<_Out, _CharT>::iterator | |
| format(void* __v, basic_format_context<_Out, _CharT>& __fc) const | |
| { return _M_f.format(__v, __fc); } | |
| private: | |
| __format::__formatter_ptr<_CharT> _M_f; | |
| }; | |
| #if __glibcxx_print >= 202403l | |
| template<> | |
| inline constexpr bool | |
| enable_nonlocking_formatter_optimization<void*> = true; | |
| #endif | |
| template<__format::__char _CharT> | |
| struct formatter<nullptr_t, _CharT> | |
| { | |
| formatter() = default; | |
| [[__gnu__::__always_inline__]] | |
| constexpr typename basic_format_parse_context<_CharT>::iterator | |
| parse(basic_format_parse_context<_CharT>& __pc) | |
| { return _M_f.parse(__pc); } | |
| template<typename _Out> | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| typename basic_format_context<_Out, _CharT>::iterator | |
| format(nullptr_t, basic_format_context<_Out, _CharT>& __fc) const | |
| { return _M_f.format(nullptr, __fc); } | |
| private: | |
| __format::__formatter_ptr<_CharT> _M_f; | |
| }; | |
| /// @} | |
| #if __glibcxx_print >= 202403L | |
| template<> | |
| inline constexpr bool | |
| enable_nonlocking_formatter_optimization<nullptr_t> = true; | |
| #endif | |
| #if defined _GLIBCXX_USE_WCHAR_T && __glibcxx_format_ranges | |
| // _GLIBCXX_RESOLVE_LIB_DEFECTS | |
| // 3944. Formatters converting sequences of char to sequences of wchar_t | |
| struct __formatter_disabled | |
| { | |
| __formatter_disabled() = delete; // Cannot format char sequence to wchar_t | |
| __formatter_disabled(const __formatter_disabled&) = delete; | |
| __formatter_disabled& operator=(const __formatter_disabled&) = delete; | |
| }; | |
| template<> | |
| struct formatter<char*, wchar_t> | |
| : private __formatter_disabled { }; | |
| template<> | |
| struct formatter<const char*, wchar_t> | |
| : private __formatter_disabled { }; | |
| template<size_t _Nm> | |
| struct formatter<char[_Nm], wchar_t> | |
| : private __formatter_disabled { }; | |
| template<class _Traits, class _Allocator> | |
| struct formatter<basic_string<char, _Traits, _Allocator>, wchar_t> | |
| : private __formatter_disabled { }; | |
| template<class _Traits> | |
| struct formatter<basic_string_view<char, _Traits>, wchar_t> | |
| : private __formatter_disabled { }; | |
| #endif | |
| /// An iterator after the last character written, and the number of | |
| /// characters that would have been written. | |
| template<typename _Out> | |
| struct format_to_n_result | |
| { | |
| _Out out; | |
| iter_difference_t<_Out> size; | |
| }; | |
| _GLIBCXX_BEGIN_NAMESPACE_CONTAINER | |
| template<typename, typename> class vector; | |
| _GLIBCXX_END_NAMESPACE_CONTAINER | |
| /// @cond undocumented | |
| namespace __format | |
| { | |
| template<typename _CharT> | |
| class _Drop_iter | |
| { | |
| public: | |
| using iterator_category = output_iterator_tag; | |
| using value_type = void; | |
| using difference_type = ptrdiff_t; | |
| using pointer = void; | |
| using reference = void; | |
| _Drop_iter() = default; | |
| _Drop_iter(const _Drop_iter&) = default; | |
| _Drop_iter& operator=(const _Drop_iter&) = default; | |
| [[__gnu__::__always_inline__]] | |
| constexpr _Drop_iter& | |
| operator=(_CharT __c) | |
| { return *this; } | |
| [[__gnu__::__always_inline__]] | |
| constexpr _Drop_iter& | |
| operator=(basic_string_view<_CharT> __s) | |
| { return *this; } | |
| [[__gnu__::__always_inline__]] | |
| constexpr _Drop_iter& | |
| operator*() { return *this; } | |
| [[__gnu__::__always_inline__]] | |
| constexpr _Drop_iter& | |
| operator++() { return *this; } | |
| [[__gnu__::__always_inline__]] | |
| constexpr _Drop_iter | |
| operator++(int) { return *this; } | |
| }; | |
| template<typename _CharT> | |
| class _Sink_iter | |
| { | |
| _Sink<_CharT>* _M_sink = nullptr; | |
| public: | |
| using iterator_category = output_iterator_tag; | |
| using value_type = void; | |
| using difference_type = ptrdiff_t; | |
| using pointer = void; | |
| using reference = void; | |
| _Sink_iter() = default; | |
| _Sink_iter(const _Sink_iter&) = default; | |
| _Sink_iter& operator=(const _Sink_iter&) = default; | |
| [[__gnu__::__always_inline__]] | |
| explicit constexpr | |
| _Sink_iter(_Sink<_CharT>& __sink) : _M_sink(std::addressof(__sink)) { } | |
| [[__gnu__::__always_inline__]] | |
| constexpr _Sink_iter& | |
| operator=(_CharT __c) | |
| { | |
| _M_sink->_M_write(__c); | |
| return *this; | |
| } | |
| [[__gnu__::__always_inline__]] | |
| constexpr _Sink_iter& | |
| operator=(basic_string_view<_CharT> __s) | |
| { | |
| _M_sink->_M_write(__s); | |
| return *this; | |
| } | |
| [[__gnu__::__always_inline__]] | |
| constexpr _Sink_iter& | |
| operator*() { return *this; } | |
| [[__gnu__::__always_inline__]] | |
| constexpr _Sink_iter& | |
| operator++() { return *this; } | |
| [[__gnu__::__always_inline__]] | |
| constexpr _Sink_iter | |
| operator++(int) { return *this; } | |
| _GLIBCXX_CONSTEXPR_FORMAT auto | |
| _M_reserve(size_t __n) const | |
| { return _M_sink->_M_reserve(__n); } | |
| _GLIBCXX_CONSTEXPR_FORMAT bool | |
| _M_discarding() const | |
| { return _M_sink->_M_discarding(); } | |
| }; | |
| // Abstract base class for type-erased character sinks. | |
| // All formatting and output is done via this type's iterator, | |
| // to reduce the number of different template instantiations. | |
| template<typename _CharT> | |
| class _Sink | |
| { | |
| friend class _Sink_iter<_CharT>; | |
| span<_CharT> _M_span; | |
| typename span<_CharT>::iterator _M_next; | |
| // Called when the span is full, to make more space available. | |
| // Precondition: _M_next != _M_span.begin() | |
| // Postcondition: _M_next != _M_span.end() | |
| // TODO: remove the precondition? could make overflow handle it. | |
| virtual void _M_overflow() = 0; | |
| protected: | |
| // Precondition: __span.size() != 0 | |
| [[__gnu__::__always_inline__]] | |
| explicit constexpr | |
| _Sink(span<_CharT> __span) noexcept | |
| : _M_span(__span), _M_next(__span.begin()) | |
| { } | |
| // The portion of the span that has been written to. | |
| [[__gnu__::__always_inline__]] | |
| _GLIBCXX_CONSTEXPR_FORMAT span<_CharT> | |
| _M_used() const noexcept | |
| { return _M_span.first(_M_next - _M_span.begin()); } | |
| // The portion of the span that has not been written to. | |
| [[__gnu__::__always_inline__]] | |
| constexpr span<_CharT> | |
| _M_unused() const noexcept | |
| { return _M_span.subspan(_M_next - _M_span.begin()); } | |
| // Use the start of the span as the next write position. | |
| [[__gnu__::__always_inline__]] | |
| constexpr void | |
| _M_rewind() noexcept | |
| { _M_next = _M_span.begin(); } | |
| // Replace the current output range. | |
| _GLIBCXX_CONSTEXPR_FORMAT void | |
| _M_reset(span<_CharT> __s, size_t __pos = 0) noexcept | |
| { | |
| _M_span = __s; | |
| _M_next = __s.begin() + __pos; | |
| } | |
| // Called by the iterator for *it++ = c | |
| constexpr void | |
| _M_write(_CharT __c) | |
| { | |
| *_M_next++ = __c; | |
| if (_M_next - _M_span.begin() == std::ssize(_M_span)) [[unlikely]] | |
| _M_overflow(); | |
| } | |
| constexpr void | |
| _M_write(basic_string_view<_CharT> __s) | |
| { | |
| span __to = _M_unused(); | |
| while (__to.size() <= __s.size()) | |
| { | |
| __s.copy(__to.data(), __to.size()); | |
| _M_next += __to.size(); | |
| __s.remove_prefix(__to.size()); | |
| _M_overflow(); | |
| __to = _M_unused(); | |
| } | |
| if (__s.size()) | |
| { | |
| __s.copy(__to.data(), __s.size()); | |
| _M_next += __s.size(); | |
| } | |
| } | |
| // A successful _Reservation can be used to directly write | |
| // up to N characters to the sink to avoid unwanted buffering. | |
| struct _Reservation | |
| { | |
| // True if the reservation was successful, false otherwise. | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| explicit operator bool() const noexcept { return _M_sink; } | |
| // A pointer to write directly to the sink. | |
| _GLIBCXX_CONSTEXPR_FORMAT _CharT* | |
| get() const noexcept { return _M_sink->_M_next.operator->(); } | |
| // Add n to the _M_next iterator for the sink. | |
| _GLIBCXX_CONSTEXPR_FORMAT void | |
| _M_bump(size_t __n) { _M_sink->_M_bump(__n); } | |
| _Sink* _M_sink; | |
| }; | |
| // Attempt to reserve space to write n characters to the sink. | |
| // If anything is written to the reservation then there must be a call | |
| // to _M_bump(N2) before any call to another member function of *this, | |
| // where N2 is the number of characters written. | |
| _GLIBCXX_CONSTEXPR_FORMAT virtual _Reservation | |
| _M_reserve(size_t __n) | |
| { | |
| if (__n <= _M_unused().size()) | |
| return { this }; | |
| if (__n <= _M_span.size()) // Cannot meet the request. | |
| { | |
| _M_overflow(); // Make more space available. | |
| if (__n <= _M_unused().size()) | |
| return { this }; | |
| } | |
| return { nullptr }; | |
| } | |
| // Update the next output position after writing directly to the sink. | |
| // pre: no calls to _M_write or _M_overflow since _M_reserve. | |
| _GLIBCXX_CONSTEXPR_FORMAT virtual void | |
| _M_bump(size_t __n) | |
| { _M_next += __n; } | |
| // Returns true if the _Sink is discarding incoming characters. | |
| _GLIBCXX_CONSTEXPR_FORMAT virtual bool | |
| _M_discarding() const | |
| { return false; } | |
| public: | |
| _Sink(const _Sink&) = delete; | |
| _Sink& operator=(const _Sink&) = delete; | |
| [[__gnu__::__always_inline__]] | |
| constexpr _Sink_iter<_CharT> | |
| out() noexcept | |
| { return _Sink_iter<_CharT>(*this); } | |
| }; | |
| template<typename _CharT> | |
| class _Fixedbuf_sink final : public _Sink<_CharT> | |
| { | |
| _GLIBCXX_CONSTEXPR_FORMAT void | |
| _M_overflow() override | |
| { | |
| __glibcxx_assert(false); | |
| this->_M_rewind(); | |
| } | |
| public: | |
| [[__gnu__::__always_inline__]] | |
| constexpr explicit | |
| _Fixedbuf_sink(span<_CharT> __buf) | |
| : _Sink<_CharT>(__buf) | |
| { } | |
| constexpr basic_string_view<_CharT> | |
| view() const | |
| { | |
| auto __s = this->_M_used(); | |
| return basic_string_view<_CharT>(__s.data(), __s.size()); | |
| } | |
| }; | |
| // A sink with an internal buffer. This is used to implement concrete sinks. | |
| template<typename _CharT> | |
| class _Buf_sink : public _Sink<_CharT> | |
| { | |
| protected: | |
| _CharT _M_buf[__stackbuf_size<_CharT>]; | |
| [[__gnu__::__always_inline__]] | |
| constexpr | |
| _Buf_sink() noexcept | |
| : _Sink<_CharT>(_M_buf) | |
| { } | |
| }; | |
| using _GLIBCXX_STD_C::vector; | |
| // A sink that fills a sequence (e.g. std::string, std::vector, std::deque). | |
| // Writes to a buffer then appends that to the sequence when it fills up. | |
| template<typename _Seq> | |
| class _Seq_sink : public _Buf_sink<typename _Seq::value_type> | |
| { | |
| using _CharT = typename _Seq::value_type; | |
| _Seq _M_seq; | |
| protected: | |
| // Transfer buffer contents to the sequence, so buffer can be refilled. | |
| _GLIBCXX_CONSTEXPR_FORMAT void | |
| _M_overflow() override | |
| { | |
| auto __s = this->_M_used(); | |
| if (__s.empty()) [[unlikely]] | |
| return; // Nothing in the buffer to transfer to _M_seq. | |
| // If _M_reserve was called then _M_bump must have been called too. | |
| _GLIBCXX_DEBUG_ASSERT(__s.data() != _M_seq.data()); | |
| if constexpr (__is_specialization_of<_Seq, basic_string>) | |
| _M_seq.append(__s.data(), __s.size()); | |
| else | |
| _M_seq.insert(_M_seq.end(), __s.begin(), __s.end()); | |
| // Make the whole of _M_buf available for the next write: | |
| this->_M_rewind(); | |
| } | |
| _GLIBCXX_CONSTEXPR_FORMAT typename _Sink<_CharT>::_Reservation | |
| _M_reserve(size_t __n) override | |
| { | |
| // We might already have n characters available in this->_M_unused(), | |
| // but the whole point of this function is to be an optimization for | |
| // the std::format("{}", x) case. We want to avoid writing to _M_buf | |
| // and then copying that into a basic_string if possible, so this | |
| // function prefers to create space directly in _M_seq rather than | |
| // using _M_buf. | |
| if constexpr (__is_specialization_of<_Seq, basic_string> | |
| || __is_specialization_of<_Seq, vector>) | |
| { | |
| // Flush the buffer to _M_seq first (should not be needed). | |
| if (this->_M_used().size()) [[unlikely]] | |
| _Seq_sink::_M_overflow(); | |
| // Expand _M_seq to make __n new characters available: | |
| const auto __sz = _M_seq.size(); | |
| if constexpr (is_same_v<string, _Seq> || is_same_v<wstring, _Seq>) | |
| _M_seq.__resize_and_overwrite(__sz + __n, | |
| [](auto, auto __n2) { | |
| return __n2; | |
| }); | |
| else | |
| _M_seq.resize(__sz + __n); | |
| // Set _M_used() to be a span over the original part of _M_seq | |
| // and _M_unused() to be the extra capacity we just created: | |
| this->_M_reset(_M_seq, __sz); | |
| return { this }; | |
| } | |
| else // Try to use the base class' buffer. | |
| return _Sink<_CharT>::_M_reserve(__n); | |
| } | |
| _GLIBCXX_CONSTEXPR_FORMAT void | |
| _M_bump(size_t __n) override | |
| { | |
| if constexpr (__is_specialization_of<_Seq, basic_string> | |
| || __is_specialization_of<_Seq, vector>) | |
| { | |
| auto __s = this->_M_used(); | |
| _GLIBCXX_DEBUG_ASSERT(__s.data() == _M_seq.data()); | |
| // Truncate the sequence to the part that was actually written to: | |
| _M_seq.resize(__s.size() + __n); | |
| // Switch back to using buffer: | |
| this->_M_reset(this->_M_buf); | |
| } | |
| } | |
| _GLIBCXX_CONSTEXPR_FORMAT void | |
| _M_trim(span<const _CharT> __s) | |
| requires __is_specialization_of<_Seq, basic_string> | |
| { | |
| _GLIBCXX_DEBUG_ASSERT(__s.data() == this->_M_buf | |
| || __s.data() == _M_seq.data()); | |
| if (__s.data() == _M_seq.data()) | |
| _M_seq.resize(__s.size()); | |
| else | |
| this->_M_reset(this->_M_buf, __s.size()); | |
| } | |
| public: | |
| // TODO: for SSO string, use SSO buffer as initial span, then switch | |
| // to _M_buf if it overflows? Or even do that for all unused capacity? | |
| [[__gnu__::__always_inline__]] | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| _Seq_sink() noexcept(is_nothrow_default_constructible_v<_Seq>) | |
| { } | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| _Seq_sink(_Seq&& __s) noexcept(is_nothrow_move_constructible_v<_Seq>) | |
| : _M_seq(std::move(__s)) | |
| { } | |
| using _Sink<_CharT>::out; | |
| _GLIBCXX_CONSTEXPR_FORMAT _Seq | |
| get() && | |
| { | |
| if (this->_M_used().size() != 0) | |
| _Seq_sink::_M_overflow(); | |
| return std::move(_M_seq); | |
| } | |
| // A writable span that views everything written to the sink. | |
| // Will be either a view over _M_seq or the used part of _M_buf. | |
| _GLIBCXX_CONSTEXPR_FORMAT span<_CharT> | |
| _M_span() | |
| { | |
| auto __s = this->_M_used(); | |
| if (_M_seq.size()) | |
| { | |
| if (__s.size() != 0) | |
| _Seq_sink::_M_overflow(); | |
| return _M_seq; | |
| } | |
| return __s; | |
| } | |
| _GLIBCXX_CONSTEXPR_FORMAT basic_string_view<_CharT> | |
| view() | |
| { | |
| auto __span = _M_span(); | |
| return basic_string_view<_CharT>(__span.data(), __span.size()); | |
| } | |
| }; | |
| template<typename _CharT, typename _Alloc = allocator<_CharT>> | |
| using _Str_sink | |
| = _Seq_sink<basic_string<_CharT, char_traits<_CharT>, _Alloc>>; | |
| // template<typename _CharT, typename _Alloc = allocator<_CharT>> | |
| // using _Vec_sink = _Seq_sink<vector<_CharTthis-> sink that writes to an output iterator. | |
| // Writes to a fixed-size buffer and then flushes to the output iterator | |
| // when the buffer fills up. | |
| template<typename _CharT, typename _OutIter> | |
| class _Iter_sink : public _Buf_sink<_CharT> | |
| { | |
| _OutIter _M_out; | |
| iter_difference_t<_OutIter> _M_max; | |
| protected: | |
| size_t _M_count = 0; | |
| _GLIBCXX_CONSTEXPR_FORMAT void | |
| _M_overflow() override | |
| { | |
| auto __s = this->_M_used(); | |
| if (_M_max < 0) // No maximum. | |
| _M_out = ranges::copy(__s, std::move(_M_out)).out; | |
| else if (_M_count < static_cast<size_t>(_M_max)) | |
| { | |
| auto __max = _M_max - _M_count; | |
| span<_CharT> __first; | |
| if (__max < __s.size()) | |
| __first = __s.first(static_cast<size_t>(__max)); | |
| else | |
| __first = __s; | |
| _M_out = ranges::copy(__first, std::move(_M_out)).out; | |
| } | |
| this->_M_rewind(); | |
| _M_count += __s.size(); | |
| } | |
| _GLIBCXX_CONSTEXPR_FORMAT bool | |
| _M_discarding() const override | |
| { | |
| // format_to_n return total number of characters, that would be written, | |
| // see C++20 [format.functions] p20 | |
| return false; | |
| } | |
| public: | |
| [[__gnu__::__always_inline__]] | |
| _GLIBCXX_CONSTEXPR_FORMAT explicit | |
| _Iter_sink(_OutIter __out, iter_difference_t<_OutIter> __max = -1) | |
| : _M_out(std::move(__out)), _M_max(__max) | |
| { } | |
| using _Sink<_CharT>::out; | |
| _GLIBCXX_CONSTEXPR_FORMAT format_to_n_result<_OutIter> | |
| _M_finish() && | |
| { | |
| if (this->_M_used().size() != 0) | |
| _Iter_sink::_M_overflow(); | |
| iter_difference_t<_OutIter> __count(_M_count); | |
| return { std::move(_M_out), __count }; | |
| } | |
| }; | |
| // Used for contiguous iterators. | |
| // No buffer is used, characters are written straight to the iterator. | |
| // We do not know the size of the output range, so the span size just grows | |
| // as needed. The end of the span might be an invalid pointer outside the | |
| // valid range, but we never actually call _M_span.end(). This class does | |
| // not introduce any invalid pointer arithmetic or overflows that would not | |
| // have happened anyway. | |
| template<typename _CharT> | |
| class _Ptr_sink : public _Sink<_CharT> | |
| { | |
| static constexpr size_t _S_no_limit = size_t(-1); | |
| size_t _M_max; | |
| protected: | |
| size_t _M_count = 0; | |
| private: | |
| _CharT _M_buf[64]; // Write here after outputting _M_max characters. | |
| protected: | |
| _GLIBCXX_CONSTEXPR_FORMAT void | |
| _M_overflow() override | |
| { | |
| if (this->_M_unused().size() != 0) | |
| return; // No need to switch to internal buffer yet. | |
| auto __s = this->_M_used(); | |
| if (_M_max != _S_no_limit) | |
| { | |
| _M_count += __s.size(); | |
| // Span was already sized for the maximum character count, | |
| // if it overflows then any further output must go to the | |
| // internal buffer, to be discarded. | |
| this->_M_reset(this->_M_buf); | |
| } | |
| else | |
| { | |
| // No maximum character count. Just extend the span to allow | |
| // writing more characters to it. | |
| _M_rebuf(__s.data(), __s.size() + 1024, __s.size()); | |
| } | |
| } | |
| _GLIBCXX_CONSTEXPR_FORMAT bool | |
| _M_discarding() const override | |
| { | |
| // format_to_n return total number of characters, that would be written, | |
| // see C++20 [format.functions] p20 | |
| return false; | |
| } | |
| _GLIBCXX_CONSTEXPR_FORMAT typename _Sink<_CharT>::_Reservation | |
| _M_reserve(size_t __n) final | |
| { | |
| auto __avail = this->_M_unused(); | |
| if (__n > __avail.size()) | |
| { | |
| if (_M_max != _S_no_limit) | |
| return {}; // cannot grow | |
| auto __s = this->_M_used(); | |
| _M_rebuf(__s.data(), __s.size() + __n, __s.size()); | |
| } | |
| return { this }; | |
| } | |
| private: | |
| template<typename _IterDifference> | |
| static _GLIBCXX_CONSTEXPR_FORMAT size_t | |
| _S_trim_max(_IterDifference __max) | |
| { | |
| if (__max < 0) | |
| return _S_no_limit; | |
| if constexpr (!is_integral_v<_IterDifference> || sizeof(__max) > sizeof(size_t)) | |
| // __int128 or __detail::__max_diff_type | |
| if (_IterDifference((size_t)-1) < __max) | |
| return _S_no_limit; | |
| return size_t(__max); | |
| } | |
| [[__gnu__::__always_inline__]] | |
| _GLIBCXX_CONSTEXPR_FORMAT void | |
| _M_rebuf(_CharT* __ptr, size_t __total, size_t __inuse = 0) | |
| { | |
| std::span<_CharT> __span(__ptr, __total); | |
| this->_M_reset(__span, __inuse); | |
| } | |
| public: | |
| _GLIBCXX_CONSTEXPR_FORMAT explicit | |
| _Ptr_sink(_CharT* __ptr, size_t __n = _S_no_limit) noexcept | |
| : _Sink<_CharT>(_M_buf), _M_max(__n) | |
| { | |
| if (__n == 0) | |
| return; // Only write to the internal buffer. | |
| else if (__n != _S_no_limit) | |
| _M_rebuf(__ptr, __n); | |
| #if __has_builtin(__builtin_dynamic_object_size) | |
| else if (size_t __bytes = __builtin_dynamic_object_size(__ptr, 2)) | |
| _M_rebuf(__ptr, __bytes / sizeof(_CharT)); | |
| #endif | |
| else | |
| { | |
| // Avoid forming a pointer to a different memory page. | |
| const auto __off = reinterpret_cast<__UINTPTR_TYPE__>(__ptr) % 1024; | |
| __n = (1024 - __off) / sizeof(_CharT); | |
| if (__n > 0) [[likely]] | |
| _M_rebuf(__ptr, __n); | |
| else // Misaligned/packed buffer of wchar_t? | |
| _M_rebuf(__ptr, 1); | |
| } | |
| } | |
| template<contiguous_iterator _OutIter> | |
| _GLIBCXX_CONSTEXPR_FORMAT explicit | |
| _Ptr_sink(_OutIter __out, iter_difference_t<_OutIter> __n = -1) | |
| : _Ptr_sink(std::to_address(__out), _S_trim_max(__n)) | |
| { } | |
| template<contiguous_iterator _OutIter> | |
| _GLIBCXX_CONSTEXPR_FORMAT format_to_n_result<_OutIter> | |
| _M_finish(_OutIter __first) const | |
| { | |
| auto __s = this->_M_used(); | |
| if (__s.data() == _M_buf) | |
| { | |
| // Switched to internal buffer, so must have written _M_max. | |
| iter_difference_t<_OutIter> __m(_M_max); | |
| iter_difference_t<_OutIter> __count(_M_count + __s.size()); | |
| return { __first + __m, __count }; | |
| } | |
| else // Not using internal buffer yet | |
| { | |
| iter_difference_t<_OutIter> __count(__s.size()); | |
| return { __first + __count, __count }; | |
| } | |
| } | |
| }; | |
| template<typename _CharT, typename _OutIter> | |
| concept __contiguous_char_iter | |
| = contiguous_iterator<_OutIter> | |
| && same_as<iter_value_t<_OutIter>, _CharT>; | |
| // A sink for handling the padded outputs (_M_padwidth) or truncated | |
| // (_M_maxwidth). The handling is done by writting to buffer (_Str_strink) | |
| // until sufficient number of characters is written. After that if sequence | |
| // is longer than _M_padwidth it's written to _M_out, and further writes are | |
| // either: | |
| // * buffered and forwarded to _M_out, if below _M_maxwidth, | |
| // * ignored otherwise | |
| // If field width of written sequence is no greater than _M_padwidth, the | |
| // sequence is written during _M_finish call. | |
| template<typename _Out, typename _CharT> | |
| class _Padding_sink : public _Str_sink<_CharT> | |
| { | |
| size_t _M_padwidth; | |
| size_t _M_maxwidth; | |
| _Out _M_out; | |
| size_t _M_printwidth; | |
| [[__gnu__::__always_inline__]] | |
| _GLIBCXX_CONSTEXPR_FORMAT bool | |
| _M_ignoring() const | |
| { return _M_printwidth >= _M_maxwidth; } | |
| [[__gnu__::__always_inline__]] | |
| _GLIBCXX_CONSTEXPR_FORMAT bool | |
| _M_buffering() const | |
| { | |
| if (_M_printwidth < _M_padwidth) | |
| return true; | |
| if (_M_maxwidth != (size_t)-1) | |
| return _M_printwidth < _M_maxwidth; | |
| return false; | |
| } | |
| _GLIBCXX_CONSTEXPR_FORMAT void | |
| _M_sync_discarding() | |
| { | |
| if constexpr (is_same_v<_Out, _Sink_iter<_CharT>>) | |
| if (_M_out._M_discarding()) | |
| _M_maxwidth = _M_printwidth; | |
| } | |
| _GLIBCXX_CONSTEXPR_FORMAT void | |
| _M_flush() | |
| { | |
| span<_CharT> __new = this->_M_used(); | |
| basic_string_view<_CharT> __str(__new.data(), __new.size()); | |
| _M_out = __format::__write(std::move(_M_out), __str); | |
| _M_sync_discarding(); | |
| this->_M_rewind(); | |
| } | |
| _GLIBCXX_CONSTEXPR_FORMAT bool | |
| _M_force_update() | |
| { | |
| auto __str = this->view(); | |
| // Compute actual field width, possibly truncated. | |
| _M_printwidth = __format::__truncate(__str, _M_maxwidth); | |
| if (_M_ignoring()) | |
| this->_M_trim(__str); | |
| if (_M_buffering()) | |
| return true; | |
| // We have more characters than padidng, no padding is needed, | |
| // write direclty to _M_out. | |
| if (_M_printwidth >= _M_padwidth) | |
| { | |
| _M_out = __format::__write(std::move(_M_out), __str); | |
| _M_sync_discarding(); | |
| } | |
| // We reached _M_maxwidth that is smaller than _M_padwidth. | |
| // Store the prefix sequence in _M_seq, and free _M_buf. | |
| else | |
| _Str_sink<_CharT>::_M_overflow(); | |
| // Use internal buffer for writes to _M_out. | |
| this->_M_reset(this->_M_buf); | |
| return false; | |
| } | |
| _GLIBCXX_CONSTEXPR_FORMAT bool | |
| _M_update(size_t __new) | |
| { | |
| _M_printwidth += __new; | |
| // Compute estimated width, to see if is not reduced. | |
| if (_M_printwidth >= _M_padwidth || _M_printwidth >= _M_maxwidth) | |
| return _M_force_update(); | |
| return true; | |
| } | |
| _GLIBCXX_CONSTEXPR_FORMAT void | |
| _M_overflow() override | |
| { | |
| // Ignore characters in buffer, and override it. | |
| if (_M_ignoring()) | |
| this->_M_rewind(); | |
| // Write buffer to _M_out, and override it. | |
| else if (!_M_buffering()) | |
| _M_flush(); | |
| // Update written count, and if input still should be buffered, | |
| // flush the to _M_seq. | |
| else if (_M_update(this->_M_used().size())) | |
| _Str_sink<_CharT>::_M_overflow(); | |
| } | |
| _GLIBCXX_CONSTEXPR_FORMAT bool | |
| _M_discarding() const override | |
| { return _M_ignoring(); } | |
| _GLIBCXX_CONSTEXPR_FORMAT typename _Sink<_CharT>::_Reservation | |
| _M_reserve(size_t __n) override | |
| { | |
| // Ignore characters in buffer, if any. | |
| if (_M_ignoring()) | |
| this->_M_rewind(); | |
| else if constexpr (is_same_v<_Out, _Sink_iter<_CharT>>) | |
| if (!_M_buffering()) | |
| { | |
| // Write pending characters if any | |
| if (!this->_M_used().empty()) | |
| _M_flush(); | |
| // Try to reserve from _M_out sink. | |
| if (auto __reserved = _M_out._M_reserve(__n)) | |
| return __reserved; | |
| } | |
| return _Sink<_CharT>::_M_reserve(__n); | |
| } | |
| _GLIBCXX_CONSTEXPR_FORMAT void | |
| _M_bump(size_t __n) override | |
| { | |
| // Ignore the written characters. | |
| if (_M_ignoring()) | |
| return; | |
| // If reservation was made directy sink associated _M_out, | |
| // _M_bump will be called on that sink. | |
| _Sink<_CharT>::_M_bump(__n); | |
| if (_M_buffering()) | |
| _M_update(__n); | |
| } | |
| public: | |
| [[__gnu__::__always_inline__]] | |
| _GLIBCXX_CONSTEXPR_FORMAT explicit | |
| _Padding_sink(_Out __out, size_t __padwidth, size_t __maxwidth) | |
| : _M_padwidth(__padwidth), _M_maxwidth(__maxwidth), | |
| _M_out(std::move(__out)), _M_printwidth(0) | |
| { _M_sync_discarding(); } | |
| [[__gnu__::__always_inline__]] | |
| _GLIBCXX_CONSTEXPR_FORMAT explicit | |
| _Padding_sink(_Out __out, size_t __padwidth) | |
| : _Padding_sink(std::move(__out), __padwidth, (size_t)-1) | |
| { } | |
| _GLIBCXX_CONSTEXPR_FORMAT _Out | |
| _M_finish(_Align __align, char32_t __fill_char) | |
| { | |
| // Handle any characters in the buffer. | |
| if (auto __rem = this->_M_used().size()) | |
| { | |
| if (_M_ignoring()) | |
| this->_M_rewind(); | |
| else if (!_M_buffering()) | |
| _M_flush(); | |
| else | |
| _M_update(__rem); | |
| } | |
| if (!_M_buffering() || !_M_force_update()) | |
| // Characters were already written to _M_out. | |
| if (_M_printwidth >= _M_padwidth) | |
| return std::move(_M_out); | |
| const auto __str = this->view(); | |
| if (_M_printwidth >= _M_padwidth) | |
| return __format::__write(std::move(_M_out), __str); | |
| const size_t __nfill = _M_padwidth - _M_printwidth; | |
| return __format::__write_padded(std::move(_M_out), __str, | |
| __align, __nfill, __fill_char); | |
| } | |
| }; | |
| template<typename _Out, typename _CharT> | |
| class _Escaping_sink : public _Buf_sink<_CharT> | |
| { | |
| using _Esc = _Escapes<_CharT>; | |
| _Out _M_out; | |
| _Term_char _M_term : 2; | |
| unsigned _M_prev_escape : 1; | |
| unsigned _M_out_discards : 1; | |
| _GLIBCXX_CONSTEXPR_FORMAT void | |
| _M_sync_discarding() | |
| { | |
| if constexpr (is_same_v<_Out, _Sink_iter<_CharT>>) | |
| _M_out_discards = _M_out._M_discarding(); | |
| } | |
| _GLIBCXX_CONSTEXPR_FORMAT void | |
| _M_write() | |
| { | |
| span<_CharT> __bytes = this->_M_used(); | |
| basic_string_view<_CharT> __str(__bytes.data(), __bytes.size()); | |
| size_t __rem = 0; | |
| if constexpr (__unicode::__literal_encoding_is_unicode<_CharT>()) | |
| { | |
| bool __prev_escape = _M_prev_escape; | |
| _M_out = __format::__write_escaped_unicode_part( | |
| std::move(_M_out), __str, __prev_escape, _M_term); | |
| _M_prev_escape = __prev_escape; | |
| __rem = __str.size(); | |
| if (__rem > 0 && __str.data() != this->_M_buf) [[unlikely]] | |
| ranges::move(__str, this->_M_buf); | |
| } | |
| else | |
| _M_out = __format::__write_escaped_ascii( | |
| std::move(_M_out), __str, _M_term); | |
| this->_M_reset(this->_M_buf, __rem); | |
| _M_sync_discarding(); | |
| } | |
| _GLIBCXX_CONSTEXPR_FORMAT void | |
| _M_overflow() override | |
| { | |
| if (_M_out_discards) | |
| this->_M_rewind(); | |
| else | |
| _M_write(); | |
| } | |
| _GLIBCXX_CONSTEXPR_FORMAT bool | |
| _M_discarding() const override | |
| { return _M_out_discards; } | |
| public: | |
| [[__gnu__::__always_inline__]] | |
| _GLIBCXX_CONSTEXPR_FORMAT explicit | |
| _Escaping_sink(_Out __out, _Term_char __term) | |
| : _M_out(std::move(__out)), _M_term(__term), | |
| _M_prev_escape(true), _M_out_discards(false) | |
| { | |
| _M_out = __format::__write(std::move(_M_out), _Esc::_S_term(_M_term)); | |
| _M_sync_discarding(); | |
| } | |
| _GLIBCXX_CONSTEXPR_FORMAT _Out | |
| _M_finish() | |
| { | |
| if (_M_out_discards) | |
| return std::move(_M_out); | |
| if (!this->_M_used().empty()) | |
| { | |
| _M_write(); | |
| if constexpr (__unicode::__literal_encoding_is_unicode<_CharT>()) | |
| if (auto __rem = this->_M_used(); !__rem.empty()) | |
| { | |
| basic_string_view<_CharT> __str(__rem.data(), __rem.size()); | |
| _M_out = __format::__write_escape_seqs(std::move(_M_out), __str); | |
| } | |
| } | |
| return __format::__write(std::move(_M_out), _Esc::_S_term(_M_term)); | |
| } | |
| }; | |
| template<typename _Context> | |
| struct _Arg_value | |
| { | |
| using _CharT = typename _Context::char_type; | |
| class handle | |
| { | |
| using _CharT = typename _Context::char_type; | |
| using _Func = void(*)(basic_format_parse_context<_CharT>&, | |
| _Context&, const void*); | |
| // Format as const if possible, to reduce instantiations. | |
| template<typename _Tp> | |
| using __maybe_const_t | |
| = __conditional_t<__formattable_with<const _Tp, _Context>, | |
| const _Tp, _Tp>; | |
| template<typename _Tq> | |
| static _GLIBCXX_CONSTEXPR_FORMAT void | |
| _S_format(basic_format_parse_context<_CharT>& __parse_ctx, | |
| _Context& __format_ctx, const void* __ptr) | |
| { | |
| using _Td = remove_const_t<_Tq>; | |
| typename _Context::template formatter_type<_Td> __f; | |
| __parse_ctx.advance_to(__f.parse(__parse_ctx)); | |
| _Tq& __val = *const_cast<_Tq*>(static_cast<const _Td*>(__ptr)); | |
| __format_ctx.advance_to(__f.format(__val, __format_ctx)); | |
| } | |
| template<typename _Tp> | |
| requires (!is_same_v<remove_cv_t<_Tp>, handle>) | |
| explicit _GLIBCXX_CONSTEXPR_FORMAT | |
| handle(_Tp& __val) noexcept | |
| : _M_ptr(__builtin_addressof(__val)) | |
| , _M_func(&_S_format<__maybe_const_t<_Tp>>) | |
| { } | |
| friend class basic_format_arg<_Context>; | |
| public: | |
| handle(const handle&) = default; | |
| handle& operator=(const handle&) = default; | |
| [[__gnu__::__always_inline__]] | |
| void _GLIBCXX_CONSTEXPR_FORMAT | |
| format(basic_format_parse_context<_CharT>& __pc, _Context& __fc) const | |
| { _M_func(__pc, __fc, this->_M_ptr); } | |
| private: | |
| const void* _M_ptr; | |
| _Func _M_func; | |
| }; | |
| union | |
| { | |
| monostate _M_none; | |
| bool _M_bool; | |
| _CharT _M_c; | |
| int _M_i; | |
| unsigned _M_u; | |
| long long _M_ll; | |
| unsigned long long _M_ull; | |
| float _M_flt; | |
| double _M_dbl; | |
| #ifndef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT // No long double if it's ambiguous. | |
| long double _M_ldbl; | |
| #else | |
| __ibm128 _M_ibm128; | |
| __ieee128 _M_ieee128; | |
| #endif | |
| #ifdef __SIZEOF_FLOAT128__ | |
| __float128 _M_float128; | |
| #endif | |
| const _CharT* _M_str; | |
| basic_string_view<_CharT> _M_sv; | |
| const void* _M_ptr; | |
| handle _M_handle; | |
| #ifdef __SIZEOF_INT128__ | |
| __int128 _M_i128; | |
| unsigned __int128 _M_u128; | |
| #endif | |
| #ifdef __BFLT16_DIG__ | |
| __bflt16_t _M_bf16; | |
| #endif | |
| #ifdef __FLT16_DIG__ | |
| _Float16 _M_f16; | |
| #endif | |
| #ifdef __FLT32_DIG__ | |
| _Float32 _M_f32; | |
| #endif | |
| #ifdef __FLT64_DIG__ | |
| _Float64 _M_f64; | |
| #endif | |
| }; | |
| [[__gnu__::__always_inline__]] | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| _Arg_value() : _M_none() { } | |
| #if 0 | |
| template<typename _Tp> | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| _Arg_value(in_place_type_t<_Tp>, _Tp __val) | |
| { _S_get<_Tp>() = __val; } | |
| #endif | |
| // Returns reference to the _Arg_value member with the type _Tp. | |
| // Value of second argument (if provided), is assigned to that member. | |
| template<typename _Tp, typename _Self, typename... _Value> | |
| [[__gnu__::__always_inline__]] | |
| static _GLIBCXX_CONSTEXPR_FORMAT auto& | |
| _S_access(_Self& __u, _Value... __value) noexcept | |
| { | |
| static_assert(sizeof...(_Value) <= 1); | |
| if constexpr (is_same_v<_Tp, bool>) | |
| return (__u._M_bool = ... = __value); | |
| else if constexpr (is_same_v<_Tp, _CharT>) | |
| return (__u._M_c = ... = __value); | |
| else if constexpr (is_same_v<_Tp, int>) | |
| return (__u._M_i = ... = __value); | |
| else if constexpr (is_same_v<_Tp, unsigned>) | |
| return (__u._M_u = ... = __value); | |
| else if constexpr (is_same_v<_Tp, long long>) | |
| return (__u._M_ll = ... = __value); | |
| else if constexpr (is_same_v<_Tp, unsigned long long>) | |
| return (__u._M_ull = ... = __value); | |
| else if constexpr (is_same_v<_Tp, float>) | |
| return (__u._M_flt = ... = __value); | |
| else if constexpr (is_same_v<_Tp, double>) | |
| return (__u._M_dbl = ... = __value); | |
| #ifndef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT | |
| else if constexpr (is_same_v<_Tp, long double>) | |
| return (__u._M_ldbl = ... = __value); | |
| #else | |
| else if constexpr (is_same_v<_Tp, __ibm128>) | |
| return (__u._M_ibm128 = ... = __value); | |
| else if constexpr (is_same_v<_Tp, __ieee128>) | |
| return (__u._M_ieee128 = ... = __value); | |
| #endif | |
| #ifdef __SIZEOF_FLOAT128__ | |
| else if constexpr (is_same_v<_Tp, __float128>) | |
| return (__u._M_float128 = ... = __value); | |
| #endif | |
| else if constexpr (is_same_v<_Tp, const _CharT*>) | |
| return (__u._M_str = ... = __value); | |
| else if constexpr (is_same_v<_Tp, basic_string_view<_CharT>>) | |
| return (__u._M_sv = ... = __value); | |
| else if constexpr (is_same_v<_Tp, const void*>) | |
| return (__u._M_ptr = ... = __value); | |
| #ifdef __SIZEOF_INT128__ | |
| else if constexpr (is_same_v<_Tp, __int128>) | |
| return (__u._M_i128 = ... = __value); | |
| else if constexpr (is_same_v<_Tp, unsigned __int128>) | |
| return (__u._M_u128 = ... = __value); | |
| #endif | |
| #ifdef __BFLT16_DIG__ | |
| else if constexpr (is_same_v<_Tp, __bflt16_t>) | |
| return (__u._M_bf16 = ... = __value); | |
| #endif | |
| #ifdef __FLT16_DIG__ | |
| else if constexpr (is_same_v<_Tp, _Float16>) | |
| return (__u._M_f16 = ... = __value); | |
| #endif | |
| #ifdef __FLT32_DIG__ | |
| else if constexpr (is_same_v<_Tp, _Float32>) | |
| return (__u._M_f32 = ... = __value); | |
| #endif | |
| #ifdef __FLT64_DIG__ | |
| else if constexpr (is_same_v<_Tp, _Float64>) | |
| return (__u._M_f64 = ... = __value); | |
| #endif | |
| else if constexpr (is_same_v<_Tp, handle>) | |
| return __u._M_handle; | |
| // Otherwise, ill-formed. | |
| __builtin_unreachable(); | |
| } | |
| template<typename _Tp> | |
| [[__gnu__::__always_inline__]] | |
| _GLIBCXX_CONSTEXPR_FORMAT auto& | |
| _M_get() noexcept | |
| { return _S_access<_Tp>(*this); } | |
| template<typename _Tp> | |
| [[__gnu__::__always_inline__]] | |
| _GLIBCXX_CONSTEXPR_FORMAT const auto& | |
| _M_get() const noexcept | |
| { return _S_access<_Tp>(*this); } | |
| template<typename _Tp> | |
| [[__gnu__::__always_inline__]] | |
| _GLIBCXX_CONSTEXPR_FORMAT void | |
| _M_set(_Tp __v) noexcept | |
| { | |
| // Explicitly construct types without trivial default constructor. | |
| if constexpr (is_same_v<_Tp, basic_string_view<_CharT>>) | |
| std::construct_at(&_M_sv, __v); | |
| else if constexpr (is_same_v<_Tp, handle>) | |
| std::construct_at(&_M_handle, __v); | |
| else | |
| // Builtin types are trivially default constructible, and assignment | |
| // changes active member per N5032 [class.union.general] p5. | |
| _S_access<_Tp>(*this, __v); | |
| } | |
| }; | |
| // [format.arg.store], class template format-arg-store | |
| template<typename _Context, typename... _Args> | |
| class _Arg_store; | |
| template<typename _Visitor, typename _Ctx> | |
| _GLIBCXX_CONSTEXPR_FORMAT decltype(auto) | |
| __visit_format_arg(_Visitor&&, basic_format_arg<_Ctx>); | |
| template<typename _Ch, typename _Tp> | |
| consteval _Arg_t | |
| __to_arg_t_enum() noexcept; | |
| } // namespace __format | |
| /// @endcond | |
| template<typename _Context> | |
| class basic_format_arg | |
| { | |
| using _CharT = typename _Context::char_type; | |
| public: | |
| using handle = __format::_Arg_value<_Context>::handle; | |
| [[__gnu__::__always_inline__]] | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| basic_format_arg() noexcept : _M_type(__format::_Arg_none) { } | |
| [[nodiscard,__gnu__::__always_inline__]] | |
| explicit _GLIBCXX_CONSTEXPR_FORMAT operator bool() const noexcept | |
| { return _M_type != __format::_Arg_none; } | |
| #if __cpp_lib_format >= 202306L // >= C++26 | |
| template<typename _Visitor> | |
| _GLIBCXX_CONSTEXPR_FORMAT decltype(auto) | |
| visit(this basic_format_arg __arg, _Visitor&& __vis) | |
| { return __arg._M_visit_user(std::forward<_Visitor>(__vis)); } | |
| template<typename _Res, typename _Visitor> | |
| _GLIBCXX_CONSTEXPR_FORMAT _Res | |
| visit(this basic_format_arg __arg, _Visitor&& __vis) | |
| { return __arg._M_visit_user(std::forward<_Visitor>(__vis)); } | |
| #endif | |
| private: | |
| template<typename _Ctx> | |
| friend class basic_format_args; | |
| template<typename _Ctx, typename... _Args> | |
| friend class __format::_Arg_store; | |
| static_assert(is_trivially_copyable_v<__format::_Arg_value<_Context>>); | |
| __format::_Arg_value<_Context> _M_val; | |
| __format::_Arg_t _M_type; | |
| // Transform incoming argument type to the type stored in _Arg_value. | |
| // e.g. short -> int, std::string -> std::string_view, | |
| // char[3] -> const char*. | |
| template<typename _Tp> | |
| static consteval auto | |
| _S_to_arg_type() | |
| { | |
| using _Td = remove_const_t<_Tp>; | |
| if constexpr (is_same_v<_Td, bool>) | |
| return type_identity<bool>(); | |
| else if constexpr (is_same_v<_Td, _CharT>) | |
| return type_identity<_CharT>(); | |
| else if constexpr (is_same_v<_Td, char> && is_same_v<_CharT, wchar_t>) | |
| return type_identity<_CharT>(); | |
| #ifdef __SIZEOF_INT128__ // Check before signed/unsigned integer | |
| else if constexpr (is_same_v<_Td, __int128>) | |
| return type_identity<__int128>(); | |
| else if constexpr (is_same_v<_Td, unsigned __int128>) | |
| return type_identity<unsigned __int128>(); | |
| #endif | |
| else if constexpr (__is_signed_integer<_Td>::value) | |
| { | |
| if constexpr (sizeof(_Td) <= sizeof(int)) | |
| return type_identity<int>(); | |
| else if constexpr (sizeof(_Td) <= sizeof(long long)) | |
| return type_identity<long long>(); | |
| } | |
| else if constexpr (__is_unsigned_integer<_Td>::value) | |
| { | |
| if constexpr (sizeof(_Td) <= sizeof(unsigned)) | |
| return type_identity<unsigned>(); | |
| else if constexpr (sizeof(_Td) <= sizeof(unsigned long long)) | |
| return type_identity<unsigned long long>(); | |
| } | |
| else if constexpr (is_same_v<_Td, float>) | |
| return type_identity<float>(); | |
| else if constexpr (is_same_v<_Td, double>) | |
| return type_identity<double>(); | |
| #ifndef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT | |
| else if constexpr (is_same_v<_Td, long double>) | |
| return type_identity<long double>(); | |
| #else | |
| else if constexpr (is_same_v<_Td, __ibm128>) | |
| return type_identity<__ibm128>(); | |
| else if constexpr (is_same_v<_Td, __ieee128>) | |
| return type_identity<__ieee128>(); | |
| #endif | |
| #if defined(__SIZEOF_FLOAT128__) && _GLIBCXX_FORMAT_F128 | |
| else if constexpr (is_same_v<_Td, __float128>) | |
| return type_identity<__float128>(); | |
| #endif | |
| #if defined(__STDCPP_BFLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) | |
| else if constexpr (is_same_v<_Td, __format::__bflt16_t>) | |
| return type_identity<__format::__bflt16_t>(); | |
| #endif | |
| #if defined(__STDCPP_FLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) | |
| else if constexpr (is_same_v<_Td, _Float16>) | |
| return type_identity<_Float16>(); | |
| #endif | |
| #if defined(__FLT32_DIG__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) | |
| else if constexpr (is_same_v<_Td, _Float32>) | |
| return type_identity<_Float32>(); | |
| #endif | |
| #if defined(__FLT64_DIG__) && defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64) | |
| else if constexpr (is_same_v<_Td, _Float64>) | |
| return type_identity<_Float64>(); | |
| #endif | |
| else if constexpr (__is_specialization_of<_Td, basic_string_view> | |
| || __is_specialization_of<_Td, basic_string>) | |
| { | |
| if constexpr (is_same_v<typename _Td::value_type, _CharT>) | |
| return type_identity<basic_string_view<_CharT>>(); | |
| else | |
| return type_identity<handle>(); | |
| } | |
| else if constexpr (is_same_v<decay_t<_Td>, const _CharT*>) | |
| return type_identity<const _CharT*>(); | |
| else if constexpr (is_same_v<decay_t<_Td>, _CharT*>) | |
| return type_identity<const _CharT*>(); | |
| else if constexpr (is_void_v<remove_pointer_t<_Td>>) | |
| return type_identity<const void*>(); | |
| else if constexpr (is_same_v<_Td, nullptr_t>) | |
| return type_identity<const void*>(); | |
| else | |
| return type_identity<handle>(); | |
| } | |
| // Transform a formattable type to the appropriate storage type. | |
| template<typename _Tp> | |
| using _Normalize = typename decltype(_S_to_arg_type<_Tp>())::type; | |
| // Get the _Arg_t value corresponding to a normalized type. | |
| template<typename _Tp> | |
| static consteval __format::_Arg_t | |
| _S_to_enum() | |
| { | |
| using namespace __format; | |
| if constexpr (is_same_v<_Tp, bool>) | |
| return _Arg_bool; | |
| else if constexpr (is_same_v<_Tp, _CharT>) | |
| return _Arg_c; | |
| else if constexpr (is_same_v<_Tp, int>) | |
| return _Arg_i; | |
| else if constexpr (is_same_v<_Tp, unsigned>) | |
| return _Arg_u; | |
| else if constexpr (is_same_v<_Tp, long long>) | |
| return _Arg_ll; | |
| else if constexpr (is_same_v<_Tp, unsigned long long>) | |
| return _Arg_ull; | |
| else if constexpr (is_same_v<_Tp, float>) | |
| return _Arg_flt; | |
| else if constexpr (is_same_v<_Tp, double>) | |
| return _Arg_dbl; | |
| #ifndef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT | |
| else if constexpr (is_same_v<_Tp, long double>) | |
| return _Arg_ldbl; | |
| #else | |
| // Don't use _Arg_ldbl for this target, it's ambiguous. | |
| else if constexpr (is_same_v<_Tp, __ibm128>) | |
| return _Arg_ibm128; | |
| else if constexpr (is_same_v<_Tp, __ieee128>) | |
| return _Arg_ieee128; | |
| #endif | |
| #if defined(__SIZEOF_FLOAT128__) && _GLIBCXX_FORMAT_F128 | |
| else if constexpr (is_same_v<_Tp, __float128>) | |
| return _Arg_float128; | |
| #endif | |
| #if defined(__STDCPP_BFLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) | |
| else if constexpr (is_same_v<_Tp, __format::__bflt16_t>) | |
| return _Arg_bf16; | |
| #endif | |
| #if defined(__STDCPP_FLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) | |
| else if constexpr (is_same_v<_Tp, _Float16>) | |
| return _Arg_f16; | |
| #endif | |
| #if defined(__FLT32_DIG__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) | |
| else if constexpr (is_same_v<_Tp, _Float32>) | |
| return _Arg_f32; | |
| #endif | |
| #if defined(__FLT64_DIG__) && defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64) | |
| else if constexpr (is_same_v<_Tp, _Float64>) | |
| return _Arg_f64; | |
| #endif | |
| else if constexpr (is_same_v<_Tp, const _CharT*>) | |
| return _Arg_str; | |
| else if constexpr (is_same_v<_Tp, basic_string_view<_CharT>>) | |
| return _Arg_sv; | |
| else if constexpr (is_same_v<_Tp, const void*>) | |
| return _Arg_ptr; | |
| #ifdef __SIZEOF_INT128__ | |
| else if constexpr (is_same_v<_Tp, __int128>) | |
| return _Arg_i128; | |
| else if constexpr (is_same_v<_Tp, unsigned __int128>) | |
| return _Arg_u128; | |
| #endif | |
| else if constexpr (is_same_v<_Tp, handle>) | |
| return _Arg_handle; | |
| } | |
| template<typename _Tp> | |
| _GLIBCXX_CONSTEXPR_FORMAT void | |
| _M_set(_Tp __v) noexcept | |
| { | |
| _M_type = _S_to_enum<_Tp>(); | |
| _M_val._M_set(__v); | |
| } | |
| template<typename _Tp> | |
| requires __format::__formattable_with<_Tp, _Context> | |
| _GLIBCXX_CONSTEXPR_FORMAT explicit | |
| basic_format_arg(_Tp& __v) noexcept | |
| { | |
| using _Td = _Normalize<_Tp>; | |
| if constexpr (is_same_v<_Td, basic_string_view<_CharT>>) | |
| _M_set(_Td{__v.data(), __v.size()}); | |
| else if constexpr (is_same_v<remove_const_t<_Tp>, char> | |
| && is_same_v<_CharT, wchar_t>) | |
| _M_set(static_cast<_Td>(static_cast<unsigned char>(__v))); | |
| else | |
| _M_set(static_cast<_Td>(__v)); | |
| } | |
| template<typename _Ctx, typename... _Argz> | |
| friend _GLIBCXX_CONSTEXPR_FORMAT auto | |
| make_format_args(_Argz&...) noexcept; | |
| template<typename _Visitor, typename _Ctx> | |
| friend _GLIBCXX_CONSTEXPR_FORMAT decltype(auto) | |
| visit_format_arg(_Visitor&& __vis, basic_format_arg<_Ctx>); | |
| template<typename _Visitor, typename _Ctx> | |
| friend _GLIBCXX_CONSTEXPR_FORMAT decltype(auto) | |
| __format::__visit_format_arg(_Visitor&&, basic_format_arg<_Ctx>); | |
| template<typename _Ch, typename _Tp> | |
| friend consteval __format::_Arg_t | |
| __format::__to_arg_t_enum() noexcept; | |
| [[__gnu__::__noinline__]] | |
| handle | |
| _M_handle_unrecognized() const; | |
| template<typename _Visitor> | |
| _GLIBCXX_CONSTEXPR_FORMAT decltype(auto) | |
| _M_visit(_Visitor&& __vis) | |
| { | |
| switch (_M_type) | |
| { | |
| using enum __format::_Arg_t; | |
| case _Arg_none: | |
| return std::forward<_Visitor>(__vis)(_M_val._M_none); | |
| case _Arg_bool: | |
| return std::forward<_Visitor>(__vis)(_M_val._M_bool); | |
| case _Arg_c: | |
| return std::forward<_Visitor>(__vis)(_M_val._M_c); | |
| case _Arg_i: | |
| return std::forward<_Visitor>(__vis)(_M_val._M_i); | |
| case _Arg_u: | |
| return std::forward<_Visitor>(__vis)(_M_val._M_u); | |
| case _Arg_ll: | |
| return std::forward<_Visitor>(__vis)(_M_val._M_ll); | |
| case _Arg_ull: | |
| return std::forward<_Visitor>(__vis)(_M_val._M_ull); | |
| #if __glibcxx_to_chars // FIXME: need to be able to format these types! | |
| case _Arg_flt: | |
| return std::forward<_Visitor>(__vis)(_M_val._M_flt); | |
| case _Arg_dbl: | |
| return std::forward<_Visitor>(__vis)(_M_val._M_dbl); | |
| #ifndef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT | |
| case _Arg_ldbl: | |
| return std::forward<_Visitor>(__vis)(_M_val._M_ldbl); | |
| #if defined(__SIZEOF_FLOAT128__) && _GLIBCXX_FORMAT_F128 | |
| case _Arg_float128: | |
| return std::forward<_Visitor>(__vis)(_M_val._M_float128); | |
| #endif | |
| #else | |
| case _Arg_ibm128: | |
| return std::forward<_Visitor>(__vis)(_M_val._M_ibm128); | |
| case _Arg_ieee128: | |
| return std::forward<_Visitor>(__vis)(_M_val._M_ieee128); | |
| #endif | |
| #if defined(__STDCPP_BFLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) | |
| case _Arg_bf16: | |
| return std::forward<_Visitor>(__vis)(_M_val._M_bf16); | |
| #endif | |
| #if defined(__STDCPP_FLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) | |
| case _Arg_f16: | |
| return std::forward<_Visitor>(__vis)(_M_val._M_f16); | |
| #endif | |
| #if defined(__FLT32_DIG__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) | |
| case _Arg_f32: | |
| return std::forward<_Visitor>(__vis)(_M_val._M_f32); | |
| #endif | |
| #if defined(__FLT64_DIG__) && defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64) | |
| case _Arg_f64: | |
| return std::forward<_Visitor>(__vis)(_M_val._M_f64); | |
| #endif | |
| #endif // __glibcxx_to_chars | |
| case _Arg_str: | |
| return std::forward<_Visitor>(__vis)(_M_val._M_str); | |
| case _Arg_sv: | |
| return std::forward<_Visitor>(__vis)(_M_val._M_sv); | |
| case _Arg_ptr: | |
| return std::forward<_Visitor>(__vis)(_M_val._M_ptr); | |
| case _Arg_handle: | |
| return std::forward<_Visitor>(__vis)(_M_val._M_handle); | |
| #ifdef __SIZEOF_INT128__ | |
| case _Arg_i128: | |
| return std::forward<_Visitor>(__vis)(_M_val._M_i128); | |
| case _Arg_u128: | |
| return std::forward<_Visitor>(__vis)(_M_val._M_u128); | |
| #endif | |
| default: | |
| // Call exported definition of _M_handle_unrecognized from | |
| // libstdc++.so, that should recognize new _Arg_t values and | |
| // return basic_format_arg, containing a handle to that value. | |
| handle __h = _M_handle_unrecognized(); | |
| return std::forward<_Visitor>(__vis)(__h); | |
| } | |
| } | |
| template<typename _Visitor> | |
| _GLIBCXX_CONSTEXPR_FORMAT decltype(auto) | |
| _M_visit_user(_Visitor&& __vis) | |
| { | |
| return _M_visit([&__vis]<typename _Tp>(_Tp& __val) -> decltype(auto) | |
| { | |
| constexpr bool __user_facing = __is_one_of<_Tp, | |
| monostate, bool, _CharT, | |
| int, unsigned int, long long int, unsigned long long int, | |
| float, double, long double, | |
| const _CharT*, basic_string_view<_CharT>, | |
| const void*, handle>::value; | |
| if constexpr (__user_facing) | |
| return std::forward<_Visitor>(__vis)(__val); | |
| else | |
| { | |
| handle __h(__val); | |
| return std::forward<_Visitor>(__vis)(__h); | |
| } | |
| }); | |
| } | |
| }; | |
| template<typename _Visitor, typename _Context> | |
| _GLIBCXX26_DEPRECATED_SUGGEST("std::basic_format_arg::visit") | |
| inline _GLIBCXX_CONSTEXPR_FORMAT decltype(auto) | |
| visit_format_arg(_Visitor&& __vis, basic_format_arg<_Context> __arg) | |
| { return __arg._M_visit_user(std::forward<_Visitor>(__vis)); } | |
| /// @cond undocumented | |
| namespace __format | |
| { | |
| template<typename _Visitor, typename _Ctx> | |
| inline _GLIBCXX_CONSTEXPR_FORMAT decltype(auto) | |
| __visit_format_arg(_Visitor&& __vis, basic_format_arg<_Ctx> __arg) | |
| { return __arg._M_visit(std::forward<_Visitor>(__vis)); } | |
| struct _WidthPrecVisitor | |
| { | |
| template<typename _Tp> | |
| _GLIBCXX_CONSTEXPR_FORMAT size_t | |
| operator()(_Tp& __arg) const | |
| { | |
| if constexpr (is_same_v<_Tp, monostate>) | |
| __format::__invalid_arg_id_in_format_string(); | |
| // _GLIBCXX_RESOLVE_LIB_DEFECTS | |
| // 3720. Restrict the valid types of arg-id for width and precision | |
| // 3721. Allow an arg-id with a value of zero for width | |
| else if constexpr (sizeof(_Tp) <= sizeof(long long)) | |
| { | |
| // _GLIBCXX_RESOLVE_LIB_DEFECTS | |
| // 3720. Restrict the valid types of arg-id for width and precision | |
| if constexpr (__is_unsigned_integer<_Tp>::value) | |
| return __arg; | |
| else if constexpr (__is_signed_integer<_Tp>::value) | |
| if (__arg >= 0) | |
| return __arg; | |
| } | |
| __throw_format_error("format error: argument used for width or " | |
| "precision must be a non-negative integer"); | |
| } | |
| }; | |
| #pragma GCC diagnostic push | |
| #pragma GCC diagnostic ignored "-Wdeprecated-declarations" | |
| template<typename _Context> | |
| inline _GLIBCXX_CONSTEXPR_FORMAT size_t | |
| __int_from_arg(const basic_format_arg<_Context>& __arg) | |
| { return __format::__visit_format_arg(_WidthPrecVisitor(), __arg); } | |
| // Pack _Arg_t enum values into a single 60-bit integer. | |
| template<int _Bits, size_t _Nm> | |
| constexpr auto | |
| __pack_arg_types(const array<_Arg_t, _Nm>& __types) | |
| { | |
| __UINT64_TYPE__ __packed_types = 0; | |
| for (auto __i = __types.rbegin(); __i != __types.rend(); ++__i) | |
| __packed_types = (__packed_types << _Bits) | (unsigned)*__i; | |
| return __packed_types; | |
| } | |
| } // namespace __format | |
| /// @endcond | |
| template<typename _Context> | |
| class basic_format_args | |
| { | |
| static constexpr int _S_packed_type_bits = 5; // _Arg_t values [0,20] | |
| static constexpr int _S_packed_type_mask = 0b11111; | |
| static constexpr int _S_max_packed_args = 12; | |
| static_assert( (unsigned)__format::_Arg_max_ <= (1u << _S_packed_type_bits) ); | |
| template<typename... _Args> | |
| using _Store = __format::_Arg_store<_Context, _Args...>; | |
| template<typename _Ctx, typename... _Args> | |
| friend class __format::_Arg_store; | |
| using uint64_t = __UINT64_TYPE__; | |
| using _Format_arg = basic_format_arg<_Context>; | |
| using _Format_arg_val = __format::_Arg_value<_Context>; | |
| // If args are packed then the number of args is in _M_packed_size and | |
| // the packed types are in _M_unpacked_size, accessed via _M_type(i). | |
| // If args are not packed then the number of args is in _M_unpacked_size | |
| // and _M_packed_size is zero. | |
| uint64_t _M_packed_size : 4; | |
| uint64_t _M_unpacked_size : 60; | |
| union { | |
| const _Format_arg_val* _M_values; // Active when _M_packed_size != 0 | |
| const _Format_arg* _M_args; // Active when _M_packed_size == 0 | |
| }; | |
| _GLIBCXX_CONSTEXPR_FORMAT size_t | |
| _M_size() const noexcept | |
| { return _M_packed_size ? _M_packed_size : _M_unpacked_size; } | |
| _GLIBCXX_CONSTEXPR_FORMAT typename __format::_Arg_t | |
| _M_type(size_t __i) const noexcept | |
| { | |
| uint64_t __t = _M_unpacked_size >> (__i * _S_packed_type_bits); | |
| return static_cast<__format::_Arg_t>(__t & _S_packed_type_mask); | |
| } | |
| template<typename _Ctx, typename... _Args> | |
| friend _GLIBCXX_CONSTEXPR_FORMAT auto | |
| make_format_args(_Args&...) noexcept; | |
| // An array of _Arg_t enums corresponding to _Args... | |
| template<typename... _Args> | |
| static consteval array<__format::_Arg_t, sizeof...(_Args)> | |
| _S_types_to_pack() | |
| { return {_Format_arg::template _S_to_enum<_Args>()...}; } | |
| public: | |
| template<typename... _Args> | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| basic_format_args(const _Store<_Args...>& __store) noexcept; | |
| [[nodiscard,__gnu__::__always_inline__]] | |
| _GLIBCXX_CONSTEXPR_FORMAT basic_format_arg<_Context> | |
| get(size_t __i) const noexcept | |
| { | |
| basic_format_arg<_Context> __arg; | |
| if (__i < _M_packed_size) | |
| { | |
| __arg._M_type = _M_type(__i); | |
| __arg._M_val = _M_values[__i]; | |
| } | |
| else if (_M_packed_size == 0 && __i < _M_unpacked_size) | |
| __arg = _M_args[__i]; | |
| return __arg; | |
| } | |
| }; | |
| // _GLIBCXX_RESOLVE_LIB_DEFECTS | |
| // 3810. CTAD for std::basic_format_args | |
| template<typename _Context, typename... _Args> | |
| basic_format_args(__format::_Arg_store<_Context, _Args...>) | |
| -> basic_format_args<_Context>; | |
| template<typename _Context, typename... _Args> | |
| _GLIBCXX_CONSTEXPR_FORMAT auto | |
| make_format_args(_Args&... __fmt_args) noexcept; | |
| // An array of type-erased formatting arguments. | |
| template<typename _Context, typename... _Args> | |
| class __format::_Arg_store | |
| { | |
| friend std::basic_format_args<_Context>; | |
| template<typename _Ctx, typename... _Argz> | |
| friend _GLIBCXX_CONSTEXPR_FORMAT auto std:: | |
| #if _GLIBCXX_INLINE_VERSION | |
| __8:: // Needed for PR c++/59256 | |
| #endif | |
| make_format_args(_Argz&...) noexcept; | |
| // For a sufficiently small number of arguments we only store values. | |
| // basic_format_args can get the types from the _Args pack. | |
| static constexpr bool _S_values_only | |
| = sizeof...(_Args) <= basic_format_args<_Context>::_S_max_packed_args; | |
| using _Element_t | |
| = __conditional_t<_S_values_only, | |
| __format::_Arg_value<_Context>, | |
| basic_format_arg<_Context>>; | |
| _Element_t _M_args[sizeof...(_Args)]; | |
| template<typename _Tp> | |
| static _GLIBCXX_CONSTEXPR_FORMAT _Element_t | |
| _S_make_elt(_Tp& __v) | |
| { | |
| using _Tq = remove_const_t<_Tp>; | |
| using _CharT = typename _Context::char_type; | |
| static_assert(is_default_constructible_v<formatter<_Tq, _CharT>>, | |
| "std::formatter must be specialized for the type " | |
| "of each format arg"); | |
| using __format::__formattable_with; | |
| if constexpr (is_const_v<_Tp>) | |
| if constexpr (!__formattable_with<_Tp, _Context>) | |
| if constexpr (__formattable_with<_Tq, _Context>) | |
| static_assert(__formattable_with<_Tp, _Context>, | |
| "format arg must be non-const because its " | |
| "std::formatter specialization has a " | |
| "non-const reference parameter"); | |
| basic_format_arg<_Context> __arg(__v); | |
| if constexpr (_S_values_only) | |
| return __arg._M_val; | |
| else | |
| return __arg; | |
| } | |
| template<typename... _Tp> | |
| requires (sizeof...(_Tp) == sizeof...(_Args)) | |
| [[__gnu__::__always_inline__]] | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| _Arg_store(_Tp&... __a) noexcept | |
| : _M_args{_S_make_elt(__a)...} | |
| { } | |
| }; | |
| template<typename _Context> | |
| class __format::_Arg_store<_Context> | |
| { }; | |
| template<typename _Context> | |
| template<typename... _Args> | |
| inline _GLIBCXX_CONSTEXPR_FORMAT | |
| basic_format_args<_Context>:: | |
| basic_format_args(const _Store<_Args...>& __store) noexcept | |
| { | |
| if constexpr (sizeof...(_Args) == 0) | |
| { | |
| _M_packed_size = 0; | |
| _M_unpacked_size = 0; | |
| _M_args = nullptr; | |
| } | |
| else if constexpr (sizeof...(_Args) <= _S_max_packed_args) | |
| { | |
| // The number of packed arguments: | |
| _M_packed_size = sizeof...(_Args); | |
| // The packed type enums: | |
| _M_unpacked_size | |
| = __format::__pack_arg_types<_S_packed_type_bits>(_S_types_to_pack<_Args...>()); | |
| // The _Arg_value objects. | |
| _M_values = __store._M_args; | |
| } | |
| else | |
| { | |
| // No packed arguments: | |
| _M_packed_size = 0; | |
| // The number of unpacked arguments: | |
| _M_unpacked_size = sizeof...(_Args); | |
| // The basic_format_arg objects: | |
| _M_args = __store._M_args; | |
| } | |
| } | |
| /// Capture formatting arguments for use by `std::vformat`. | |
| template<typename _Context = format_context, typename... _Args> | |
| [[nodiscard,__gnu__::__always_inline__]] | |
| inline _GLIBCXX_CONSTEXPR_FORMAT auto | |
| make_format_args(_Args&... __fmt_args) noexcept | |
| { | |
| using _Fmt_arg = basic_format_arg<_Context>; | |
| using _Store = __format::_Arg_store<_Context, typename _Fmt_arg::template | |
| _Normalize<_Args>...>; | |
| return _Store(__fmt_args...); | |
| } | |
| #ifdef _GLIBCXX_USE_WCHAR_T | |
| /// Capture formatting arguments for use by `std::vformat` (for wide output). | |
| template<typename... _Args> | |
| [[nodiscard,__gnu__::__always_inline__]] | |
| inline _GLIBCXX_CONSTEXPR_FORMAT auto | |
| make_wformat_args(_Args&... __args) noexcept | |
| { return std::make_format_args<wformat_context>(__args...); } | |
| #endif | |
| /// @cond undocumented | |
| namespace __format | |
| { | |
| template<typename _Out, typename _CharT, typename _Context> | |
| _GLIBCXX_CONSTEXPR_FORMAT _Out | |
| __do_vformat_to(_Out, basic_string_view<_CharT>, | |
| const basic_format_args<_Context>&, | |
| const locale* = nullptr); | |
| template<typename _CharT> struct __formatter_chrono; | |
| } // namespace __format | |
| /// @endcond | |
| /** Context for std::format and similar functions. | |
| * | |
| * A formatting context contains an output iterator and locale to use | |
| * for the formatting operations. Most programs will never need to use | |
| * this class template explicitly. For typical uses of `std::format` the | |
| * library will use the specializations `std::format_context` (for `char`) | |
| * and `std::wformat_context` (for `wchar_t`). | |
| * | |
| * You are not allowed to define partial or explicit specializations of | |
| * this class template. | |
| * | |
| * @since C++20 | |
| */ | |
| template<typename _Out, typename _CharT> | |
| // _GLIBCXX_RESOLVE_LIB_DEFECTS | |
| // 3975. Specializations of basic_format_context should not be permitted | |
| class _GLIBCXX_NO_SPECIALIZATIONS basic_format_context | |
| { | |
| static_assert( output_iterator<_Out, const _CharT&> ); | |
| basic_format_args<basic_format_context> _M_args; | |
| _Out _M_out; | |
| __format::_Optional_locale _M_loc; | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| basic_format_context(basic_format_args<basic_format_context> __args, | |
| _Out __out) | |
| : _M_args(__args), _M_out(std::move(__out)) | |
| { } | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| basic_format_context(basic_format_args<basic_format_context> __args, | |
| _Out __out, const std::locale& __loc) | |
| : _M_args(__args), _M_out(std::move(__out)), _M_loc(__loc) | |
| { } | |
| // _GLIBCXX_RESOLVE_LIB_DEFECTS | |
| // 4061. Should std::basic_format_context be | |
| // default-constructible/copyable/movable? | |
| basic_format_context(const basic_format_context&) = delete; | |
| basic_format_context& operator=(const basic_format_context&) = delete; | |
| template<typename _Out2, typename _CharT2, typename _Context2> | |
| friend _GLIBCXX_CONSTEXPR_FORMAT _Out2 | |
| __format::__do_vformat_to(_Out2, basic_string_view<_CharT2>, | |
| const basic_format_args<_Context2>&, | |
| const locale*); | |
| friend __format::__formatter_chrono<_CharT>; | |
| public: | |
| ~basic_format_context() = default; | |
| using iterator = _Out; | |
| using char_type = _CharT; | |
| template<typename _Tp> | |
| using formatter_type = formatter<_Tp, _CharT>; | |
| [[nodiscard]] | |
| _GLIBCXX_CONSTEXPR_FORMAT basic_format_arg<basic_format_context> | |
| arg(size_t __id) const noexcept | |
| { return _M_args.get(__id); } | |
| [[nodiscard]] | |
| std::locale locale() { return _M_loc.value(); } | |
| [[nodiscard]] | |
| _GLIBCXX_CONSTEXPR_FORMAT iterator | |
| out() { return std::move(_M_out); } | |
| _GLIBCXX_CONSTEXPR_FORMAT void | |
| advance_to(iterator __it) { _M_out = std::move(__it); } | |
| }; | |
| #if _GLIBCXX_EXTERN_TEMPLATE | |
| // The defintion _M_handle_unrecognized is placed in format-inst.cc | |
| // source file, to ensure that it will not be inlined by compiler. | |
| extern template basic_format_arg<format_context>::handle | |
| basic_format_arg<format_context>::_M_handle_unrecognized() const; | |
| # ifdef _GLIBCXX_USE_WCHAR_T | |
| extern template basic_format_arg<wformat_context>::handle | |
| basic_format_arg<wformat_context>::_M_handle_unrecognized() const; | |
| # endif | |
| #else | |
| template<typename _Context> | |
| typename basic_format_arg<_Context>::handle | |
| basic_format_arg<_Context>::_M_handle_unrecognized() const | |
| { | |
| // If _M_type corresponds to a new value of _Arg_t introduced after | |
| // GCC 16, this function should return a handle that refers to the | |
| // union member of _M_val corresponding to that _Arg_t value. | |
| __throw_format_error("format error: unrecognized argument type"); | |
| } | |
| #endif | |
| /// @cond undocumented | |
| namespace __format | |
| { | |
| // Abstract base class defining an interface for scanning format strings. | |
| // Scan the characters in a format string, dividing it up into strings of | |
| // ordinary characters, escape sequences, and replacement fields. | |
| // Call virtual functions for derived classes to parse format-specifiers | |
| // or write formatted output. | |
| template<typename _CharT> | |
| struct _Scanner | |
| { | |
| using iterator = typename basic_format_parse_context<_CharT>::iterator; | |
| typename basic_format_parse_context<_CharT>::_Scan_parse_context _M_pc; | |
| constexpr explicit | |
| _Scanner(basic_string_view<_CharT> __str, size_t __nargs = (size_t)-1) | |
| : _M_pc(__str, __nargs) | |
| { } | |
| constexpr iterator begin() const noexcept { return _M_pc.begin(); } | |
| constexpr iterator end() const noexcept { return _M_pc.end(); } | |
| constexpr void | |
| _M_scan() | |
| { | |
| basic_string_view<_CharT> __fmt = _M_fmt_str(); | |
| if (__fmt.size() == 2 && __fmt[0] == '{' && __fmt[1] == '}') | |
| { | |
| _M_pc.advance_to(begin() + 1); | |
| _M_format_arg(_M_pc.next_arg_id()); | |
| return; | |
| } | |
| size_t __lbr = __fmt.find('{'); | |
| size_t __rbr = __fmt.find('}'); | |
| while (__fmt.size()) | |
| { | |
| auto __cmp = __lbr <=> __rbr; | |
| if (__cmp == 0) | |
| { | |
| _M_on_chars(end()); | |
| _M_pc.advance_to(end()); | |
| return; | |
| } | |
| else if (__cmp < 0) | |
| { | |
| if (__lbr + 1 == __fmt.size() | |
| || (__rbr == __fmt.npos && __fmt[__lbr + 1] != '{')) | |
| __format::__unmatched_left_brace_in_format_string(); | |
| const bool __is_escape = __fmt[__lbr + 1] == '{'; | |
| iterator __last = begin() + __lbr + int(__is_escape); | |
| _M_on_chars(__last); | |
| _M_pc.advance_to(__last + 1); | |
| __fmt = _M_fmt_str(); | |
| if (__is_escape) | |
| { | |
| if (__rbr != __fmt.npos) | |
| __rbr -= __lbr + 2; | |
| __lbr = __fmt.find('{'); | |
| } | |
| else | |
| { | |
| _M_on_replacement_field(); | |
| __fmt = _M_fmt_str(); | |
| __lbr = __fmt.find('{'); | |
| __rbr = __fmt.find('}'); | |
| } | |
| } | |
| else | |
| { | |
| if (++__rbr == __fmt.size() || __fmt[__rbr] != '}') | |
| __format::__unmatched_right_brace_in_format_string(); | |
| iterator __last = begin() + __rbr; | |
| _M_on_chars(__last); | |
| _M_pc.advance_to(__last + 1); | |
| __fmt = _M_fmt_str(); | |
| if (__lbr != __fmt.npos) | |
| __lbr -= __rbr + 1; | |
| __rbr = __fmt.find('}'); | |
| } | |
| } | |
| } | |
| constexpr basic_string_view<_CharT> | |
| _M_fmt_str() const noexcept | |
| { return {begin(), end()}; } | |
| constexpr virtual void _M_on_chars(iterator) { } | |
| constexpr void _M_on_replacement_field() | |
| { | |
| auto __next = begin(); | |
| size_t __id; | |
| if (*__next == '}') | |
| __id = _M_pc.next_arg_id(); | |
| else if (*__next == ':') | |
| { | |
| __id = _M_pc.next_arg_id(); | |
| _M_pc.advance_to(++__next); | |
| } | |
| else | |
| { | |
| auto [__i, __ptr] = __format::__parse_arg_id(begin(), end()); | |
| if (!__ptr || !(*__ptr == '}' || *__ptr == ':')) | |
| __format::__invalid_arg_id_in_format_string(); | |
| _M_pc.check_arg_id(__id = __i); | |
| if (*__ptr == ':') | |
| { | |
| _M_pc.advance_to(++__ptr); | |
| } | |
| else | |
| _M_pc.advance_to(__ptr); | |
| } | |
| _M_format_arg(__id); | |
| if (begin() == end() || *begin() != '}') | |
| __format::__unmatched_left_brace_in_format_string(); | |
| _M_pc.advance_to(begin() + 1); // Move past '}' | |
| } | |
| constexpr virtual void _M_format_arg(size_t __id) = 0; | |
| }; | |
| // Process a format string and format the arguments in the context. | |
| template<typename _Out, typename _CharT> | |
| class _Formatting_scanner : public _Scanner<_CharT> | |
| { | |
| public: | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| _Formatting_scanner(basic_format_context<_Out, _CharT>& __fc, | |
| basic_string_view<_CharT> __str) | |
| : _Scanner<_CharT>(__str), _M_fc(__fc) | |
| { } | |
| private: | |
| basic_format_context<_Out, _CharT>& _M_fc; | |
| using iterator = typename _Scanner<_CharT>::iterator; | |
| constexpr void | |
| _M_on_chars(iterator __last) override | |
| { | |
| basic_string_view<_CharT> __str(this->begin(), __last); | |
| _M_fc.advance_to(__format::__write(_M_fc.out(), __str)); | |
| } | |
| constexpr void | |
| _M_format_arg(size_t __id) override | |
| { | |
| using _Context = basic_format_context<_Out, _CharT>; | |
| using handle = typename basic_format_arg<_Context>::handle; | |
| __format::__visit_format_arg([this](auto& __arg) { | |
| using _Type = remove_reference_t<decltype(__arg)>; | |
| using _Formatter = typename _Context::template formatter_type<_Type>; | |
| if constexpr (is_same_v<_Type, monostate>) | |
| __format::__invalid_arg_id_in_format_string(); | |
| else if constexpr (is_same_v<_Type, handle>) | |
| __arg.format(this->_M_pc, this->_M_fc); | |
| else if constexpr (is_default_constructible_v<_Formatter>) | |
| { | |
| _Formatter __f; | |
| this->_M_pc.advance_to(__f.parse(this->_M_pc)); | |
| this->_M_fc.advance_to(__f.format(__arg, this->_M_fc)); | |
| } | |
| else | |
| static_assert(__format::__formattable_with<_Type, _Context>); | |
| }, _M_fc.arg(__id)); | |
| } | |
| }; | |
| template<typename _CharT, typename _Tp> | |
| consteval _Arg_t | |
| __to_arg_t_enum() noexcept | |
| { | |
| using _Context = __format::__format_context<_CharT>; | |
| using _Fmt_arg = basic_format_arg<_Context>; | |
| using _NormalizedTp = typename _Fmt_arg::template _Normalize<_Tp>; | |
| return _Fmt_arg::template _S_to_enum<_NormalizedTp>(); | |
| } | |
| // Validate a format string for Args. | |
| template<typename _CharT, typename... _Args> | |
| class _Checking_scanner : public _Scanner<_CharT> | |
| { | |
| static_assert( | |
| (is_default_constructible_v<formatter<_Args, _CharT>> && ...), | |
| "std::formatter must be specialized for each type being formatted"); | |
| public: | |
| consteval | |
| _Checking_scanner(basic_string_view<_CharT> __str) | |
| : _Scanner<_CharT>(__str, sizeof...(_Args)) | |
| { | |
| #if __cpp_lib_format >= 202305L | |
| this->_M_pc._M_types = _M_types.data(); | |
| #endif | |
| } | |
| private: | |
| constexpr void | |
| _M_format_arg(size_t __id) override | |
| { | |
| if constexpr (sizeof...(_Args) != 0) | |
| { | |
| if (__id < sizeof...(_Args)) | |
| { | |
| _M_parse_format_spec<_Args...>(__id); | |
| return; | |
| } | |
| } | |
| __builtin_unreachable(); | |
| } | |
| template<typename _Tp, typename... _OtherArgs> | |
| constexpr void | |
| _M_parse_format_spec(size_t __id) | |
| { | |
| if (__id == 0) | |
| { | |
| formatter<_Tp, _CharT> __f; | |
| this->_M_pc.advance_to(__f.parse(this->_M_pc)); | |
| } | |
| else if constexpr (sizeof...(_OtherArgs) != 0) | |
| _M_parse_format_spec<_OtherArgs...>(__id - 1); | |
| else | |
| __builtin_unreachable(); | |
| } | |
| #if __cpp_lib_format >= 202305L | |
| array<_Arg_t, sizeof...(_Args)> | |
| _M_types{ { __format::__to_arg_t_enum<_CharT, _Args>()... } }; | |
| #endif | |
| }; | |
| template<typename _CharT, unsigned = __unicode::__literal_encoding_is_unicode<_CharT>()> | |
| _GLIBCXX_CONSTEXPR_FORMAT _Sink_iter<_CharT> | |
| __do_vformat_to(_Sink_iter<_CharT> __out, basic_string_view<_CharT> __fmt, | |
| __format_context<_CharT>& __ctx) | |
| { | |
| if constexpr (is_same_v<_CharT, char>) | |
| // Fast path for "{}" format strings and simple format arg types. | |
| if (__fmt.size() == 2 && __fmt[0] == '{' && __fmt[1] == '}') | |
| { | |
| bool __done = false; | |
| __format::__visit_format_arg([&](auto& __arg) { | |
| using _Tp = remove_cvref_t<decltype(__arg)>; | |
| if constexpr (is_same_v<_Tp, bool>) | |
| { | |
| size_t __len = 4 + !__arg; | |
| const char* __chars[] = { "false", "true" }; | |
| if (auto __res = __out._M_reserve(__len)) | |
| { | |
| ranges::copy_n(__chars[__arg], __len, __res.get()); | |
| __res._M_bump(__len); | |
| __done = true; | |
| } | |
| } | |
| else if constexpr (is_same_v<_Tp, char>) | |
| { | |
| if (auto __res = __out._M_reserve(1)) | |
| { | |
| *__res.get() = __arg; | |
| __res._M_bump(1); | |
| __done = true; | |
| } | |
| } | |
| else if constexpr (is_integral_v<_Tp>) | |
| { | |
| make_unsigned_t<_Tp> __uval; | |
| const bool __neg = __arg < 0; | |
| if (__neg) | |
| __uval = make_unsigned_t<_Tp>(~__arg) + 1u; | |
| else | |
| __uval = __arg; | |
| const auto __n = __detail::__to_chars_len(__uval); | |
| if (auto __res = __out._M_reserve(__n + __neg)) | |
| { | |
| auto __ptr = __res.get(); | |
| *__ptr = '-'; | |
| __detail::__to_chars_10_impl(__ptr + (int)__neg, __n, | |
| __uval); | |
| __res._M_bump(__n + __neg); | |
| __done = true; | |
| } | |
| } | |
| else if constexpr (is_convertible_v<_Tp, string_view>) | |
| { | |
| string_view __sv = __arg; | |
| if (auto __res = __out._M_reserve(__sv.size())) | |
| { | |
| ranges::copy(__sv, __res.get()); | |
| __res._M_bump(__sv.size()); | |
| __done = true; | |
| } | |
| } | |
| }, __ctx.arg(0)); | |
| if (__done) | |
| return __out; | |
| } | |
| _Formatting_scanner<_Sink_iter<_CharT>, _CharT> __scanner(__ctx, __fmt); | |
| __scanner._M_scan(); | |
| return __out; | |
| } | |
| // The behavior of the formatters (interpretation of fill character) depends | |
| // on the literal encoding. As explicit instantiation of __do_vformat_to | |
| // instantiates formatters for types stored in basic_format_arg, we can | |
| // support only single encoding, in this case unicode. This should cover | |
| // most common use cases. | |
| #if __cplusplus <= 202002L && _GLIBCXX_EXTERN_TEMPLATE | |
| extern template _Sink_iter<char> | |
| __do_vformat_to<char, 1>(_Sink_iter<char>, string_view, | |
| format_context&); | |
| # ifdef _GLIBCXX_USE_WCHAR_T | |
| extern template _Sink_iter<wchar_t> | |
| __do_vformat_to<wchar_t, 1>(_Sink_iter<wchar_t>, wstring_view, | |
| wformat_context&); | |
| # endif | |
| #endif | |
| template<typename _Out, typename _CharT, typename _Context> | |
| inline _GLIBCXX_CONSTEXPR_FORMAT _Out | |
| __do_vformat_to(_Out __out, basic_string_view<_CharT> __fmt, | |
| const basic_format_args<_Context>& __args, | |
| const locale* __loc) | |
| { | |
| if constexpr (is_same_v<_Out, _Sink_iter<_CharT>>) | |
| { | |
| auto __ctx = __loc == nullptr | |
| ? _Context(__args, __out) | |
| : _Context(__args, __out, *__loc); | |
| return __format::__do_vformat_to(__out, __fmt, __ctx); | |
| } | |
| else if constexpr (__contiguous_char_iter<_CharT, _Out>) | |
| { | |
| _Ptr_sink<_CharT> __sink(__out); | |
| __format::__do_vformat_to(__sink.out(), __fmt, __args, __loc); | |
| return std::move(__sink)._M_finish(__out).out; | |
| } | |
| else | |
| { | |
| _Iter_sink<_CharT, _Out> __sink(std::move(__out)); | |
| __format::__do_vformat_to(__sink.out(), __fmt, __args, __loc); | |
| return std::move(__sink)._M_finish().out; | |
| } | |
| } | |
| template<typename _Out, typename _CharT> | |
| inline _GLIBCXX_CONSTEXPR_FORMAT format_to_n_result<_Out> | |
| __do_vformat_to_n(_Out __out, iter_difference_t<_Out> __n, | |
| basic_string_view<_CharT> __fmt, | |
| const type_identity_t< | |
| basic_format_args<__format_context<_CharT>>>& __args, | |
| const locale* __loc = nullptr) | |
| { | |
| if constexpr (__contiguous_char_iter<_CharT, _Out>) | |
| { | |
| _Ptr_sink<_CharT> __sink(__out, __n); | |
| __format::__do_vformat_to(__sink.out(), __fmt, __args, __loc); | |
| return std::move(__sink)._M_finish(__out); | |
| } | |
| else | |
| { | |
| _Iter_sink<_CharT, _Out> __sink(std::move(__out), __n); | |
| __format::__do_vformat_to(__sink.out(), __fmt, __args, __loc); | |
| return std::move(__sink)._M_finish(); | |
| } | |
| } | |
| #pragma GCC diagnostic pop | |
| } // namespace __format | |
| /// @endcond | |
| template<typename _CharT, typename... _Args> | |
| template<typename _Tp> | |
| requires convertible_to<const _Tp&, basic_string_view<_CharT>> | |
| consteval | |
| basic_format_string<_CharT, _Args...>:: | |
| basic_format_string(const _Tp& __s) noexcept | |
| : _M_str(__s) | |
| { | |
| __format::_Checking_scanner<_CharT, remove_cvref_t<_Args>...> | |
| __scanner(_M_str); | |
| __scanner._M_scan(); | |
| } | |
| // [format.functions], formatting functions | |
| template<typename _Out> requires output_iterator<_Out, const char&> | |
| [[__gnu__::__always_inline__]] | |
| inline _GLIBCXX_CONSTEXPR_FORMAT _Out | |
| vformat_to(_Out __out, string_view __fmt, format_args __args) | |
| { return __format::__do_vformat_to(std::move(__out), __fmt, __args); } | |
| #ifdef _GLIBCXX_USE_WCHAR_T | |
| template<typename _Out> requires output_iterator<_Out, const wchar_t&> | |
| [[__gnu__::__always_inline__]] | |
| inline _GLIBCXX_CONSTEXPR_FORMAT _Out | |
| vformat_to(_Out __out, wstring_view __fmt, wformat_args __args) | |
| { return __format::__do_vformat_to(std::move(__out), __fmt, __args); } | |
| #endif | |
| template<typename _Out> requires output_iterator<_Out, const char&> | |
| [[__gnu__::__always_inline__]] | |
| inline _Out | |
| vformat_to(_Out __out, const locale& __loc, string_view __fmt, | |
| format_args __args) | |
| { | |
| return __format::__do_vformat_to(std::move(__out), __fmt, __args, &__loc); | |
| } | |
| #ifdef _GLIBCXX_USE_WCHAR_T | |
| template<typename _Out> requires output_iterator<_Out, const wchar_t&> | |
| [[__gnu__::__always_inline__]] | |
| inline _Out | |
| vformat_to(_Out __out, const locale& __loc, wstring_view __fmt, | |
| wformat_args __args) | |
| { | |
| return __format::__do_vformat_to(std::move(__out), __fmt, __args, &__loc); | |
| } | |
| #endif | |
| [[nodiscard]] | |
| inline _GLIBCXX_CONSTEXPR_FORMAT string | |
| vformat(string_view __fmt, format_args __args) | |
| { | |
| __format::_Str_sink<char> __buf; | |
| std::vformat_to(__buf.out(), __fmt, __args); | |
| return std::move(__buf).get(); | |
| } | |
| #ifdef _GLIBCXX_USE_WCHAR_T | |
| [[nodiscard]] | |
| inline _GLIBCXX_CONSTEXPR_FORMAT wstring | |
| vformat(wstring_view __fmt, wformat_args __args) | |
| { | |
| __format::_Str_sink<wchar_t> __buf; | |
| std::vformat_to(__buf.out(), __fmt, __args); | |
| return std::move(__buf).get(); | |
| } | |
| #endif | |
| [[nodiscard]] | |
| inline string | |
| vformat(const locale& __loc, string_view __fmt, format_args __args) | |
| { | |
| __format::_Str_sink<char> __buf; | |
| std::vformat_to(__buf.out(), __loc, __fmt, __args); | |
| return std::move(__buf).get(); | |
| } | |
| #ifdef _GLIBCXX_USE_WCHAR_T | |
| [[nodiscard]] | |
| inline wstring | |
| vformat(const locale& __loc, wstring_view __fmt, wformat_args __args) | |
| { | |
| __format::_Str_sink<wchar_t> __buf; | |
| std::vformat_to(__buf.out(), __loc, __fmt, __args); | |
| return std::move(__buf).get(); | |
| } | |
| #endif | |
| template<typename... _Args> | |
| [[nodiscard]] | |
| inline _GLIBCXX_CONSTEXPR_FORMAT string | |
| format(format_string<_Args...> __fmt, _Args&&... __args) | |
| { return std::vformat(__fmt.get(), std::make_format_args(__args...)); } | |
| #ifdef _GLIBCXX_USE_WCHAR_T | |
| template<typename... _Args> | |
| [[nodiscard]] | |
| inline _GLIBCXX_CONSTEXPR_FORMAT wstring | |
| format(wformat_string<_Args...> __fmt, _Args&&... __args) | |
| { return std::vformat(__fmt.get(), std::make_wformat_args(__args...)); } | |
| #endif | |
| template<typename... _Args> | |
| [[nodiscard]] | |
| inline string | |
| format(const locale& __loc, format_string<_Args...> __fmt, | |
| _Args&&... __args) | |
| { | |
| return std::vformat(__loc, __fmt.get(), | |
| std::make_format_args(__args...)); | |
| } | |
| #ifdef _GLIBCXX_USE_WCHAR_T | |
| template<typename... _Args> | |
| [[nodiscard]] | |
| inline wstring | |
| format(const locale& __loc, wformat_string<_Args...> __fmt, | |
| _Args&&... __args) | |
| { | |
| return std::vformat(__loc, __fmt.get(), | |
| std::make_wformat_args(__args...)); | |
| } | |
| #endif | |
| template<typename _Out, typename... _Args> | |
| requires output_iterator<_Out, const char&> | |
| inline _GLIBCXX_CONSTEXPR_FORMAT _Out | |
| format_to(_Out __out, format_string<_Args...> __fmt, _Args&&... __args) | |
| { | |
| return std::vformat_to(std::move(__out), __fmt.get(), | |
| std::make_format_args(__args...)); | |
| } | |
| #ifdef _GLIBCXX_USE_WCHAR_T | |
| template<typename _Out, typename... _Args> | |
| requires output_iterator<_Out, const wchar_t&> | |
| inline _GLIBCXX_CONSTEXPR_FORMAT _Out | |
| format_to(_Out __out, wformat_string<_Args...> __fmt, _Args&&... __args) | |
| { | |
| return std::vformat_to(std::move(__out), __fmt.get(), | |
| std::make_wformat_args(__args...)); | |
| } | |
| #endif | |
| template<typename _Out, typename... _Args> | |
| requires output_iterator<_Out, const char&> | |
| inline _Out | |
| format_to(_Out __out, const locale& __loc, format_string<_Args...> __fmt, | |
| _Args&&... __args) | |
| { | |
| return std::vformat_to(std::move(__out), __loc, __fmt.get(), | |
| std::make_format_args(__args...)); | |
| } | |
| #ifdef _GLIBCXX_USE_WCHAR_T | |
| template<typename _Out, typename... _Args> | |
| requires output_iterator<_Out, const wchar_t&> | |
| inline _Out | |
| format_to(_Out __out, const locale& __loc, wformat_string<_Args...> __fmt, | |
| _Args&&... __args) | |
| { | |
| return std::vformat_to(std::move(__out), __loc, __fmt.get(), | |
| std::make_wformat_args(__args...)); | |
| } | |
| #endif | |
| template<typename _Out, typename... _Args> | |
| requires output_iterator<_Out, const char&> | |
| inline _GLIBCXX_CONSTEXPR_FORMAT format_to_n_result<_Out> | |
| format_to_n(_Out __out, iter_difference_t<_Out> __n, | |
| format_string<_Args...> __fmt, _Args&&... __args) | |
| { | |
| return __format::__do_vformat_to_n( | |
| std::move(__out), __n, __fmt.get(), | |
| std::make_format_args(__args...)); | |
| } | |
| #ifdef _GLIBCXX_USE_WCHAR_T | |
| template<typename _Out, typename... _Args> | |
| requires output_iterator<_Out, const wchar_t&> | |
| inline _GLIBCXX_CONSTEXPR_FORMAT format_to_n_result<_Out> | |
| format_to_n(_Out __out, iter_difference_t<_Out> __n, | |
| wformat_string<_Args...> __fmt, _Args&&... __args) | |
| { | |
| return __format::__do_vformat_to_n( | |
| std::move(__out), __n, __fmt.get(), | |
| std::make_wformat_args(__args...)); | |
| } | |
| #endif | |
| template<typename _Out, typename... _Args> | |
| requires output_iterator<_Out, const char&> | |
| inline format_to_n_result<_Out> | |
| format_to_n(_Out __out, iter_difference_t<_Out> __n, const locale& __loc, | |
| format_string<_Args...> __fmt, _Args&&... __args) | |
| { | |
| return __format::__do_vformat_to_n( | |
| std::move(__out), __n, __fmt.get(), | |
| std::make_format_args(__args...), &__loc); | |
| } | |
| #ifdef _GLIBCXX_USE_WCHAR_T | |
| template<typename _Out, typename... _Args> | |
| requires output_iterator<_Out, const wchar_t&> | |
| inline format_to_n_result<_Out> | |
| format_to_n(_Out __out, iter_difference_t<_Out> __n, const locale& __loc, | |
| wformat_string<_Args...> __fmt, _Args&&... __args) | |
| { | |
| return __format::__do_vformat_to_n( | |
| std::move(__out), __n, __fmt.get(), | |
| std::make_wformat_args(__args...), &__loc); | |
| } | |
| #endif | |
| /// @cond undocumented | |
| namespace __format | |
| { | |
| #if 1 | |
| template<typename _CharT> | |
| class _Counting_sink final : public _Ptr_sink<_CharT> | |
| { | |
| public: | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| _Counting_sink() : _Ptr_sink<_CharT>(nullptr, 0) { } | |
| [[__gnu__::__always_inline__]] | |
| _GLIBCXX_CONSTEXPR_FORMAT size_t | |
| count() const | |
| { return this->_M_count + this->_M_used().size(); } | |
| }; | |
| #else | |
| template<typename _CharT> | |
| class _Counting_sink : public _Buf_sink<_CharT> | |
| { | |
| size_t _M_count = 0; | |
| void | |
| _M_overflow() override | |
| { | |
| if (!std::is_constant_evaluated()) | |
| _M_count += this->_M_used().size(); | |
| this->_M_rewind(); | |
| } | |
| public: | |
| _Counting_sink() = default; | |
| [[__gnu__::__always_inline__]] | |
| size_t | |
| count() noexcept | |
| { | |
| _Counting_sink::_M_overflow(); | |
| return _M_count; | |
| } | |
| }; | |
| #endif | |
| } // namespace __format | |
| /// @endcond | |
| template<typename... _Args> | |
| [[nodiscard]] | |
| inline _GLIBCXX_CONSTEXPR_FORMAT size_t | |
| formatted_size(format_string<_Args...> __fmt, _Args&&... __args) | |
| { | |
| __format::_Counting_sink<char> __buf; | |
| std::vformat_to(__buf.out(), __fmt.get(), | |
| std::make_format_args(__args...)); | |
| return __buf.count(); | |
| } | |
| #ifdef _GLIBCXX_USE_WCHAR_T | |
| template<typename... _Args> | |
| [[nodiscard]] | |
| inline _GLIBCXX_CONSTEXPR_FORMAT size_t | |
| formatted_size(wformat_string<_Args...> __fmt, _Args&&... __args) | |
| { | |
| __format::_Counting_sink<wchar_t> __buf; | |
| std::vformat_to(__buf.out(), __fmt.get(), | |
| std::make_wformat_args(__args...)); | |
| return __buf.count(); | |
| } | |
| #endif | |
| template<typename... _Args> | |
| [[nodiscard]] | |
| inline size_t | |
| formatted_size(const locale& __loc, format_string<_Args...> __fmt, | |
| _Args&&... __args) | |
| { | |
| __format::_Counting_sink<char> __buf; | |
| std::vformat_to(__buf.out(), __loc, __fmt.get(), | |
| std::make_format_args(__args...)); | |
| return __buf.count(); | |
| } | |
| #ifdef _GLIBCXX_USE_WCHAR_T | |
| template<typename... _Args> | |
| [[nodiscard]] | |
| inline size_t | |
| formatted_size(const locale& __loc, wformat_string<_Args...> __fmt, | |
| _Args&&... __args) | |
| { | |
| __format::_Counting_sink<wchar_t> __buf; | |
| std::vformat_to(__buf.out(), __loc, __fmt.get(), | |
| std::make_wformat_args(__args...)); | |
| return __buf.count(); | |
| } | |
| #endif | |
| #if __glibcxx_format_ranges // C++ >= 23 && HOSTED | |
| /// @cond undocumented | |
| template<typename _Tp> | |
| consteval range_format | |
| __fmt_kind() | |
| { | |
| using _Ref = ranges::range_reference_t<_Tp>; | |
| if constexpr (is_same_v<remove_cvref_t<_Ref>, _Tp>) | |
| return range_format::disabled; | |
| else if constexpr (requires { typename _Tp::key_type; }) | |
| { | |
| if constexpr (requires { typename _Tp::mapped_type; }) | |
| { | |
| using _Up = remove_cvref_t<_Ref>; | |
| if constexpr (__is_pair<_Up>) | |
| return range_format::map; | |
| else if constexpr (__is_specialization_of<_Up, tuple>) | |
| if constexpr (tuple_size_v<_Up> == 2) | |
| return range_format::map; | |
| } | |
| return range_format::set; | |
| } | |
| else | |
| return range_format::sequence; | |
| } | |
| /// @endcond | |
| /// A constant determining how a range should be formatted. | |
| template<ranges::input_range _Rg> requires same_as<_Rg, remove_cvref_t<_Rg>> | |
| constexpr range_format format_kind<_Rg> = __fmt_kind<_Rg>(); | |
| /// @cond undocumented | |
| namespace __format | |
| { | |
| template<typename _CharT, typename _Out, typename _Callback> | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| typename basic_format_context<_Out, _CharT>::iterator | |
| __format_padded(basic_format_context<_Out, _CharT>& __fc, | |
| const _Spec<_CharT>& __spec, | |
| _Callback&& __call) | |
| { | |
| if constexpr (is_same_v<_Out, _Drop_iter<_CharT>>) | |
| return __fc.out(); | |
| else | |
| { | |
| // This is required to implement formatting with padding, | |
| // as we need to format to temporary buffer, using the same iterator. | |
| static_assert(is_same_v<_Out, _Sink_iter<_CharT>>); | |
| const size_t __padwidth = __spec._M_get_width(__fc); | |
| if (__padwidth == 0) | |
| return __call(__fc); | |
| struct _Restore_out | |
| { | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| _Restore_out(basic_format_context<_Sink_iter<_CharT>, _CharT>& __fc) | |
| : _M_ctx(std::addressof(__fc)), _M_out(__fc.out()) | |
| { } | |
| _GLIBCXX_CONSTEXPR_FORMAT void | |
| _M_disarm() | |
| { _M_ctx = nullptr; } | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| ~_Restore_out() | |
| { | |
| if (_M_ctx) | |
| _M_ctx->advance_to(_M_out); | |
| } | |
| private: | |
| basic_format_context<_Sink_iter<_CharT>, _CharT>* _M_ctx; | |
| _Sink_iter<_CharT> _M_out; | |
| }; | |
| _Restore_out __restore(__fc); | |
| _Padding_sink<_Sink_iter<_CharT>, _CharT> __sink(__fc.out(), __padwidth); | |
| __fc.advance_to(__sink.out()); | |
| __call(__fc); | |
| __fc.advance_to(__sink._M_finish(__spec._M_align, __spec._M_fill)); | |
| __restore._M_disarm(); | |
| return __fc.out(); | |
| } | |
| } | |
| template<size_t _Pos, typename _Tp, typename _CharT> | |
| struct __indexed_formatter_storage | |
| { | |
| constexpr void | |
| _M_parse() | |
| { | |
| // _GLIBCXX_RESOLVE_LIB_DEFECTS | |
| // 3892. Incorrect formatting of nested ranges and tuples | |
| basic_format_parse_context<_CharT> __pc({}); | |
| if (_M_formatter.parse(__pc) != __pc.end()) | |
| __format::__failed_to_parse_format_spec(); | |
| } | |
| template<typename _Out> | |
| _GLIBCXX_CONSTEXPR_FORMAT void | |
| _M_format(__maybe_const<_Tp, _CharT>& __elem, | |
| basic_format_context<_Out, _CharT>& __fc, | |
| basic_string_view<_CharT> __sep) const | |
| { | |
| if constexpr (_Pos != 0) | |
| __fc.advance_to(__format::__write(__fc.out(), __sep)); | |
| __fc.advance_to(_M_formatter.format(__elem, __fc)); | |
| } | |
| [[__gnu__::__always_inline__]] | |
| constexpr void | |
| set_debug_format() | |
| { | |
| if constexpr (__has_debug_format<formatter<_Tp, _CharT>>) | |
| _M_formatter.set_debug_format(); | |
| } | |
| private: | |
| formatter<_Tp, _CharT> _M_formatter; | |
| }; | |
| template<typename _CharT, typename... _Tps> | |
| class __tuple_formatter | |
| { | |
| using _String_view = basic_string_view<_CharT>; | |
| using _Seps = __format::_Separators<_CharT>; | |
| public: | |
| constexpr void | |
| set_separator(basic_string_view<_CharT> __sep) noexcept | |
| { _M_sep = __sep; } | |
| constexpr void | |
| set_brackets(basic_string_view<_CharT> __open, | |
| basic_string_view<_CharT> __close) noexcept | |
| { | |
| _M_open = __open; | |
| _M_close = __close; | |
| } | |
| // We deviate from standard, that declares this as template accepting | |
| // unconstrained ParseContext type, which seems unimplementable. | |
| constexpr typename basic_format_parse_context<_CharT>::iterator | |
| parse(basic_format_parse_context<_CharT>& __pc) | |
| { | |
| auto __first = __pc.begin(); | |
| const auto __last = __pc.end(); | |
| __format::_Spec<_CharT> __spec{}; | |
| auto __finished = [&] | |
| { | |
| if (__first != __last && *__first != '}') | |
| return false; | |
| _M_spec = __spec; | |
| _M_felems._M_parse(); | |
| _M_felems.set_debug_format(); | |
| return true; | |
| }; | |
| if (__finished()) | |
| return __first; | |
| __first = __spec._M_parse_fill_and_align(__first, __last, "{:"); | |
| if (__finished()) | |
| return __first; | |
| __first = __spec._M_parse_width(__first, __last, __pc); | |
| if (__finished()) | |
| return __first; | |
| if (*__first == 'n') | |
| { | |
| ++__first; | |
| _M_open = _M_close = _String_view(); | |
| } | |
| else if (*__first == 'm') | |
| { | |
| ++__first; | |
| if constexpr (sizeof...(_Tps) == 2) | |
| { | |
| _M_sep = _Seps::_S_colon(); | |
| _M_open = _M_close = _String_view(); | |
| } | |
| else | |
| __throw_format_error("format error: 'm' specifier requires range" | |
| " of pair or tuple of two elements"); | |
| } | |
| if (__finished()) | |
| return __first; | |
| __format::__failed_to_parse_format_spec(); | |
| } | |
| protected: | |
| template<typename _Tuple, typename _Out, size_t... _Ids> | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| typename basic_format_context<_Out, _CharT>::iterator | |
| _M_format(_Tuple& __tuple, index_sequence<_Ids...>, | |
| basic_format_context<_Out, _CharT>& __fc) const | |
| { return _M_format_elems(std::get<_Ids>(__tuple)..., __fc); } | |
| template<typename _Out> | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| typename basic_format_context<_Out, _CharT>::iterator | |
| _M_format_elems(__maybe_const<_Tps, _CharT>&... __elems, | |
| basic_format_context<_Out, _CharT>& __fc) const | |
| { | |
| return __format::__format_padded( | |
| __fc, _M_spec, | |
| [this, &__elems...](basic_format_context<_Out, _CharT>& __nfc) | |
| { | |
| __nfc.advance_to(__format::__write(__nfc.out(), _M_open)); | |
| _M_felems._M_format(__elems..., __nfc, _M_sep); | |
| return __format::__write(__nfc.out(), _M_close); | |
| }); | |
| } | |
| private: | |
| template<size_t... _Ids> | |
| struct __formatters_storage | |
| : __indexed_formatter_storage<_Ids, _Tps, _CharT>... | |
| { | |
| template<size_t _Id, typename _Up> | |
| using _Base = __indexed_formatter_storage<_Id, _Up, _CharT>; | |
| constexpr void | |
| _M_parse() | |
| { | |
| (_Base<_Ids, _Tps>::_M_parse(), ...); | |
| } | |
| template<typename _Out> | |
| _GLIBCXX_CONSTEXPR_FORMAT void | |
| _M_format(__maybe_const<_Tps, _CharT>&... __elems, | |
| basic_format_context<_Out, _CharT>& __fc, | |
| _String_view __sep) const | |
| { | |
| (_Base<_Ids, _Tps>::_M_format(__elems, __fc, __sep), ...); | |
| } | |
| constexpr void | |
| set_debug_format() | |
| { | |
| (_Base<_Ids, _Tps>::set_debug_format(), ...); | |
| } | |
| }; | |
| template<size_t... _Ids> | |
| static _GLIBCXX_CONSTEXPR_FORMAT auto | |
| _S_create_storage(index_sequence<_Ids...>) | |
| -> __formatters_storage<_Ids...>; | |
| using _Formatters | |
| = decltype(_S_create_storage(index_sequence_for<_Tps...>())); | |
| _Spec<_CharT> _M_spec{}; | |
| _String_view _M_open = _Seps::_S_parens().substr(0, 1); | |
| _String_view _M_close = _Seps::_S_parens().substr(1, 1); | |
| _String_view _M_sep = _Seps::_S_comma(); | |
| _Formatters _M_felems; | |
| }; | |
| template<typename _Tp> | |
| concept __is_map_formattable | |
| = __is_pair<_Tp> || (__is_tuple_v<_Tp> && tuple_size_v<_Tp> == 2); | |
| } // namespace __format | |
| /// @endcond | |
| // [format.tuple] Tuple formatter | |
| template<__format::__char _CharT, formattable<_CharT> _Fp, | |
| formattable<_CharT> _Sp> | |
| struct formatter<pair<_Fp, _Sp>, _CharT> | |
| : __format::__tuple_formatter<_CharT, remove_cvref_t<_Fp>, | |
| remove_cvref_t<_Sp>> | |
| { | |
| private: | |
| using __maybe_const_pair | |
| = __conditional_t<formattable<const _Fp, _CharT> | |
| && formattable<const _Sp, _CharT>, | |
| const pair<_Fp, _Sp>, pair<_Fp, _Sp>>; | |
| public: | |
| // We deviate from standard, that declares this as template accepting | |
| // unconstrained FormatContext type, which seems unimplementable. | |
| template<typename _Out> | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| typename basic_format_context<_Out, _CharT>::iterator | |
| format(__maybe_const_pair& __p, | |
| basic_format_context<_Out, _CharT>& __fc) const | |
| { return this->_M_format_elems(__p.first, __p.second, __fc); } | |
| }; | |
| #if __glibcxx_print >= 202406L | |
| // _GLIBCXX_RESOLVE_LIB_DEFECTS | |
| // 4399. enable_nonlocking_formatter_optimization for pair and tuple needs remove_cvref_t | |
| template<typename _Fp, typename _Sp> | |
| constexpr bool enable_nonlocking_formatter_optimization<pair<_Fp, _Sp>> | |
| = enable_nonlocking_formatter_optimization<remove_cvref_t<_Fp>> | |
| && enable_nonlocking_formatter_optimization<remove_cvref_t<_Sp>>; | |
| #endif | |
| template<__format::__char _CharT, formattable<_CharT>... _Tps> | |
| struct formatter<tuple<_Tps...>, _CharT> | |
| : __format::__tuple_formatter<_CharT, remove_cvref_t<_Tps>...> | |
| { | |
| private: | |
| using __maybe_const_tuple | |
| = __conditional_t<(formattable<const _Tps, _CharT> && ...), | |
| const tuple<_Tps...>, tuple<_Tps...>>; | |
| public: | |
| // We deviate from standard, that declares this as template accepting | |
| // unconstrained FormatContext type, which seems unimplementable. | |
| template<typename _Out> | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| typename basic_format_context<_Out, _CharT>::iterator | |
| format(__maybe_const_tuple& __t, | |
| basic_format_context<_Out, _CharT>& __fc) const | |
| { return this->_M_format(__t, index_sequence_for<_Tps...>(), __fc); } | |
| }; | |
| #if __glibcxx_print >= 202406L | |
| // _GLIBCXX_RESOLVE_LIB_DEFECTS | |
| // 4399. enable_nonlocking_formatter_optimization for pair and tuple needs remove_cvref_t | |
| template<typename... _Tps> | |
| constexpr bool enable_nonlocking_formatter_optimization<tuple<_Tps...>> | |
| = (enable_nonlocking_formatter_optimization<remove_cvref_t<_Tps>> && ...); | |
| #endif | |
| // [format.range.formatter], class template range_formatter | |
| template<typename _Tp, __format::__char _CharT> | |
| requires same_as<remove_cvref_t<_Tp>, _Tp> && formattable<_Tp, _CharT> | |
| class range_formatter | |
| { | |
| using _String_view = basic_string_view<_CharT>; | |
| using _Seps = __format::_Separators<_CharT>; | |
| public: | |
| constexpr void | |
| set_separator(basic_string_view<_CharT> __sep) noexcept | |
| { _M_sep = __sep; } | |
| constexpr void | |
| set_brackets(basic_string_view<_CharT> __open, | |
| basic_string_view<_CharT> __close) noexcept | |
| { | |
| _M_open = __open; | |
| _M_close = __close; | |
| } | |
| constexpr formatter<_Tp, _CharT>& | |
| underlying() noexcept | |
| { return _M_fval; } | |
| constexpr const formatter<_Tp, _CharT>& | |
| underlying() const noexcept | |
| { return _M_fval; } | |
| // We deviate from standard, that declares this as template accepting | |
| // unconstrained ParseContext type, which seems unimplementable. | |
| constexpr typename basic_format_parse_context<_CharT>::iterator | |
| parse(basic_format_parse_context<_CharT>& __pc) | |
| { | |
| auto __first = __pc.begin(); | |
| const auto __last = __pc.end(); | |
| __format::_Spec<_CharT> __spec{}; | |
| bool __no_brace = false; | |
| auto __finished = [&] | |
| { return __first == __last || *__first == '}'; }; | |
| auto __finalize = [&] | |
| { | |
| _M_spec = __spec; | |
| return __first; | |
| }; | |
| auto __parse_val = [&](_String_view __nfs = _String_view()) | |
| { | |
| // _GLIBCXX_RESOLVE_LIB_DEFECTS | |
| // 3892. Incorrect formatting of nested ranges and tuples | |
| basic_format_parse_context<_CharT> __npc(__nfs); | |
| if (_M_fval.parse(__npc) != __npc.end()) | |
| __format::__failed_to_parse_format_spec(); | |
| if constexpr (__format::__has_debug_format<formatter<_Tp, _CharT>>) | |
| _M_fval.set_debug_format(); | |
| return __finalize(); | |
| }; | |
| if (__finished()) | |
| return __parse_val(); | |
| __first = __spec._M_parse_fill_and_align(__first, __last, "{:"); | |
| if (__finished()) | |
| return __parse_val(); | |
| __first = __spec._M_parse_width(__first, __last, __pc); | |
| if (__finished()) | |
| return __parse_val(); | |
| if (*__first == '?') | |
| { | |
| ++__first; | |
| __spec._M_debug = true; | |
| if (__finished() || *__first != 's') | |
| __throw_format_error("format error: '?' is allowed only in" | |
| " combination with 's'"); | |
| } | |
| if (*__first == 's') | |
| { | |
| ++__first; | |
| if constexpr (same_as<_Tp, _CharT>) | |
| { | |
| __spec._M_type = __format::_Pres_s; | |
| if (__finished()) | |
| return __finalize(); | |
| __throw_format_error("format error: element format specifier" | |
| " cannot be provided when 's' specifier is used"); | |
| } | |
| else | |
| __throw_format_error("format error: 's' specifier requires" | |
| " range of character types"); | |
| } | |
| if (__finished()) | |
| return __parse_val(); | |
| if (*__first == 'n') | |
| { | |
| ++__first; | |
| _M_open = _M_close = _String_view(); | |
| __no_brace = true; | |
| } | |
| if (__finished()) | |
| return __parse_val(); | |
| if (*__first == 'm') | |
| { | |
| _String_view __m(__first, 1); | |
| ++__first; | |
| if constexpr (__format::__is_map_formattable<_Tp>) | |
| { | |
| _M_sep = _Seps::_S_comma(); | |
| if (!__no_brace) | |
| { | |
| _M_open = _Seps::_S_braces().substr(0, 1); | |
| _M_close = _Seps::_S_braces().substr(1, 1); | |
| } | |
| if (__finished()) | |
| return __parse_val(__m); | |
| __throw_format_error("format error: element format specifier" | |
| " cannot be provided when 'm' specifier is used"); | |
| } | |
| else | |
| __throw_format_error("format error: 'm' specifier requires" | |
| " range of pairs or tuples of two elements"); | |
| } | |
| if (__finished()) | |
| return __parse_val(); | |
| if (*__first == ':') | |
| { | |
| __pc.advance_to(++__first); | |
| __first = _M_fval.parse(__pc); | |
| } | |
| if (__finished()) | |
| return __finalize(); | |
| __format::__failed_to_parse_format_spec(); | |
| } | |
| // We deviate from standard, that declares this as template accepting | |
| // unconstrained FormatContext type, which seems unimplementable. | |
| template<ranges::input_range _Rg, typename _Out> | |
| requires formattable<ranges::range_reference_t<_Rg>, _CharT> && | |
| same_as<remove_cvref_t<ranges::range_reference_t<_Rg>>, _Tp> | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| typename basic_format_context<_Out, _CharT>::iterator | |
| format(_Rg&& __rg, basic_format_context<_Out, _CharT>& __fc) const | |
| { | |
| using _Range = remove_reference_t<_Rg>; | |
| if constexpr (ranges::contiguous_range<_Rg>) | |
| { | |
| const span<__format::__maybe_const<_Tp, _CharT>> | |
| __spn(ranges::data(__rg), size_t(ranges::distance(__rg))); | |
| return _M_format(__spn, __fc); | |
| } | |
| else if constexpr (__format::__simply_formattable_range<_Range, _CharT>) | |
| return _M_format<const _Range>(__rg, __fc); | |
| else | |
| return _M_format(__rg, __fc); | |
| } | |
| private: | |
| template<ranges::input_range _Rg, typename _Out> | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| typename basic_format_context<_Out, _CharT>::iterator | |
| _M_format(_Rg& __rg, basic_format_context<_Out, _CharT>& __fc) const | |
| { | |
| if constexpr (same_as<_Tp, _CharT>) | |
| if (_M_spec._M_type == __format::_Pres_s) | |
| { | |
| __format::__formatter_str __fstr(_M_spec); | |
| return __fstr._M_format_range(__rg, __fc); | |
| } | |
| return __format::__format_padded( | |
| __fc, _M_spec, | |
| [this, &__rg](basic_format_context<_Out, _CharT>& __nfc) | |
| { return _M_format_elems(__rg, __nfc); }); | |
| } | |
| template<ranges::input_range _Rg, typename _Out> | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| typename basic_format_context<_Out, _CharT>::iterator | |
| _M_format_elems(_Rg& __rg, | |
| basic_format_context<_Out, _CharT>& __fc) const | |
| { | |
| auto __out = __format::__write(__fc.out(), _M_open); | |
| auto __first = ranges::begin(__rg); | |
| auto const __last = ranges::end(__rg); | |
| if (__first == __last) | |
| return __format::__write(__out, _M_close); | |
| __fc.advance_to(__out); | |
| __out = _M_fval.format(*__first, __fc); | |
| for (++__first; __first != __last; ++__first) | |
| { | |
| __out = __format::__write(__out, _M_sep); | |
| __fc.advance_to(__out); | |
| __out = _M_fval.format(*__first, __fc); | |
| } | |
| return __format::__write(__out, _M_close); | |
| } | |
| __format::_Spec<_CharT> _M_spec{}; | |
| _String_view _M_open = _Seps::_S_squares().substr(0, 1); | |
| _String_view _M_close = _Seps::_S_squares().substr(1, 1); | |
| _String_view _M_sep = _Seps::_S_comma(); | |
| formatter<_Tp, _CharT> _M_fval; | |
| }; | |
| // In standard this is shown as inheriting from specialization of | |
| // exposition only specialization for range-default-formatter for | |
| // each range_format. We opt for simpler implementation. | |
| // [format.range.fmtmap], [format.range.fmtset], [format.range.fmtstr], | |
| // specializations for maps, sets, and strings | |
| template<ranges::input_range _Rg, __format::__char _CharT> | |
| requires (format_kind<_Rg> != range_format::disabled) | |
| && formattable<ranges::range_reference_t<_Rg>, _CharT> | |
| struct formatter<_Rg, _CharT> | |
| { | |
| private: | |
| static const bool _S_range_format_is_string = | |
| (format_kind<_Rg> == range_format::string) | |
| || (format_kind<_Rg> == range_format::debug_string); | |
| using _Vt = remove_cvref_t< | |
| ranges::range_reference_t< | |
| __format::__maybe_const_range<_Rg, _CharT>>>; | |
| static consteval bool _S_is_correct() | |
| { | |
| if constexpr (_S_range_format_is_string) | |
| static_assert(same_as<_Vt, _CharT>); | |
| return true; | |
| } | |
| static_assert(_S_is_correct()); | |
| public: | |
| constexpr formatter() noexcept | |
| { | |
| using _Seps = __format::_Separators<_CharT>; | |
| if constexpr (format_kind<_Rg> == range_format::map) | |
| { | |
| static_assert(__format::__is_map_formattable<_Vt>); | |
| _M_under.set_brackets(_Seps::_S_braces().substr(0, 1), | |
| _Seps::_S_braces().substr(1, 1)); | |
| _M_under.underlying().set_brackets({}, {}); | |
| _M_under.underlying().set_separator(_Seps::_S_colon()); | |
| } | |
| else if constexpr (format_kind<_Rg> == range_format::set) | |
| _M_under.set_brackets(_Seps::_S_braces().substr(0, 1), | |
| _Seps::_S_braces().substr(1, 1)); | |
| } | |
| constexpr void | |
| set_separator(basic_string_view<_CharT> __sep) noexcept | |
| requires (format_kind<_Rg> == range_format::sequence) | |
| { _M_under.set_separator(__sep); } | |
| constexpr void | |
| set_brackets(basic_string_view<_CharT> __open, | |
| basic_string_view<_CharT> __close) noexcept | |
| requires (format_kind<_Rg> == range_format::sequence) | |
| { _M_under.set_brackets(__open, __close); } | |
| // We deviate from standard, that declares this as template accepting | |
| // unconstrained ParseContext type, which seems unimplementable. | |
| constexpr typename basic_format_parse_context<_CharT>::iterator | |
| parse(basic_format_parse_context<_CharT>& __pc) | |
| { | |
| auto __res = _M_under.parse(__pc); | |
| if constexpr (format_kind<_Rg> == range_format::debug_string) | |
| _M_under.set_debug_format(); | |
| return __res; | |
| } | |
| // We deviate from standard, that declares this as template accepting | |
| // unconstrained FormatContext type, which seems unimplementable. | |
| template<typename _Out> | |
| _GLIBCXX_CONSTEXPR_FORMAT | |
| typename basic_format_context<_Out, _CharT>::iterator | |
| format(__format::__maybe_const_range<_Rg, _CharT>& __rg, | |
| basic_format_context<_Out, _CharT>& __fc) const | |
| { | |
| if constexpr (_S_range_format_is_string) | |
| return _M_under._M_format_range(__rg, __fc); | |
| else | |
| return _M_under.format(__rg, __fc); | |
| } | |
| private: | |
| using _Formatter_under | |
| = __conditional_t<_S_range_format_is_string, | |
| __format::__formatter_str<_CharT>, | |
| range_formatter<_Vt, _CharT>>; | |
| _Formatter_under _M_under; | |
| }; | |
| #if __glibcxx_print >= 202406L | |
| template<ranges::input_range _Rg> | |
| requires (format_kind<_Rg> != range_format::disabled) | |
| constexpr bool enable_nonlocking_formatter_optimization<_Rg> = false; | |
| #endif | |
| #endif // C++23 formatting ranges | |
| #undef _GLIBCXX_WIDEN | |
| #undef _GLIBCXX_CONSTEXPR_FORMAT | |
| _GLIBCXX_END_NAMESPACE_VERSION | |
| } // namespace std | |
| #endif // __cpp_lib_format | |
| #pragma GCC diagnostic pop | |
| #endif // _GLIBCXX_FORMAT |