// format standard header // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // NOTE: // The contents of this header are derived in part from libfmt under the following license: // Copyright (c) 2012 - present, Victor Zverovich // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // // --- Optional exception to the license --- // // As an exception, if, as a result of your compiling your source code, portions // of this Software are embedded into a machine-executable object form of such // source code, you may redistribute such embedded portions in such object form // without including the above copyright and permission notices. #ifndef _FORMAT_ #define _FORMAT_ #include #if _STL_COMPILER_PREPROCESSOR #ifndef __cpp_lib_concepts _EMIT_STL_WARNING(STL4038, "The contents of are available only with C++20 or later."); #else // ^^^ !defined(__cpp_lib_concepts) / defined(__cpp_lib_concepts) vvv #include <__msvc_format_ucd_tables.hpp> #include <__msvc_print.hpp> #include #include #include #include #include #include #include #include #include #include #include #include #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) #pragma warning(disable : _STL_DISABLED_WARNINGS) _STL_DISABLE_CLANG_WARNINGS #pragma push_macro("new") #undef new extern "C" _NODISCARD __std_win_error __stdcall __std_get_cvt(__std_code_page _Codepage, _Cvtvec* _Pcvt) noexcept; _STD_BEGIN #if _HAS_CXX23 #define _FMT_P2286_BEGIN inline namespace __p2286 { #define _FMT_P2286_END } #else // ^^^ C++23 / C++20 vvv #define _FMT_P2286_BEGIN #define _FMT_P2286_END #endif // ^^^ C++20 ^^^ template _NODISCARD constexpr const _CharT* _Choose_literal(const char* const _Str, const wchar_t* const _WStr) noexcept { if constexpr (is_same_v<_CharT, char>) { return _Str; } else { return _WStr; } } // _STATICALLY_WIDEN is used by since C++20 and by since C++23. // It's defined here, so that both headers can use this definition. #define _STATICALLY_WIDEN(_CharT, _Literal) (_Choose_literal<_CharT>(_Literal, L##_Literal)) _EXPORT_STD template class vector; _EXPORT_STD class format_error : public runtime_error { using runtime_error::runtime_error; }; [[noreturn]] inline void _Throw_format_error(const char* const _Message) { _THROW(format_error{_Message}); } enum class _Fmt_align : uint8_t { _None, _Left, _Right, _Center }; enum class _Fmt_sign : uint8_t { _None, _Plus, _Minus, _Space }; enum class _Basic_format_arg_type : uint8_t { _None, _Int_type, _UInt_type, _Long_long_type, _ULong_long_type, _Bool_type, _Char_type, _Float_type, _Double_type, _Long_double_type, _Pointer_type, _CString_type, _String_type, _Custom_type, }; static_assert(static_cast(_Basic_format_arg_type::_Custom_type) < 16, "must fit in 4-bit bitfield"); _NODISCARD constexpr bool _Is_integral_fmt_type(_Basic_format_arg_type _Ty) { return _Ty > _Basic_format_arg_type::_None && _Ty <= _Basic_format_arg_type::_Char_type; } _NODISCARD constexpr bool _Is_arithmetic_fmt_type(_Basic_format_arg_type _Ty) { return _Ty > _Basic_format_arg_type::_None && _Ty <= _Basic_format_arg_type::_Long_double_type; } struct _Auto_id_tag { explicit _Auto_id_tag() = default; }; // clang-format off template concept _Parse_arg_id_callbacks = requires(_Ty _At) { { _At._On_auto_id() } -> same_as; { _At._On_manual_id(size_t{}) } -> same_as; }; template concept _Parse_replacement_field_callbacks = requires(_Ty _At, const _CharT* _First, const _CharT* _Last) { { _At._Parse_context }; { _At._On_text(_First, _Last) } -> same_as; { _At._On_replacement_field(size_t{}, static_cast(nullptr)) } -> same_as; { _At._On_format_specs(size_t{}, _First, _Last) } -> same_as; }; template concept _Parse_align_callbacks = requires(_Ty _At, basic_string_view<_CharT> _Sv, _Fmt_align _Aln) { { _At._On_fill(_Sv) } -> same_as; { _At._On_align(_Aln) } -> same_as; }; template concept _Parse_width_callbacks = requires(_Ty _At) { { _At._On_width(int{}) } -> same_as; }; template concept _Parse_precision_callbacks = requires(_Ty _At) { { _At._On_precision(int{}) } -> same_as; }; template concept _Width_adapter_callbacks = requires(_Ty _At) { { _At._On_dynamic_width(_Auto_id_tag{}) } -> same_as; { _At._On_dynamic_width(size_t{}) } -> same_as; }; template concept _Precision_adapter_callbacks = requires(_Ty _At) { { _At._On_dynamic_precision(_Auto_id_tag{}) } -> same_as; { _At._On_dynamic_precision(size_t{}) } -> same_as; }; template concept _Parse_spec_callbacks = _Parse_align_callbacks<_Ty, _CharT> && _Parse_width_callbacks<_Ty, _CharT> && _Parse_precision_callbacks<_Ty, _CharT> && _Width_adapter_callbacks<_Ty, _CharT> && _Precision_adapter_callbacks<_Ty, _CharT> && requires(_Ty _At, _Fmt_sign _Sgn) { { _At._On_sign(_Sgn) } -> same_as; { _At._On_hash() } -> same_as; { _At._On_zero() } -> same_as; { _At._On_localized() } -> same_as; { _At._On_type(_CharT{}) } -> same_as; }; // clang-format on template struct _Decode_result { const _CharT* _Next_ptr; bool _Is_unicode_scalar_value; // Also _Is_usv below, see https://www.unicode.org/glossary/#unicode_scalar_value // _Is_unicode_scalar_value is also used for non-Unicode encodings, to indicate that the input can be converted to // Unicode. }; // _Decode_utf decodes UTF-8 or UTF-16 encoded unsigned char or wchar_t strings respectively _NODISCARD constexpr _Decode_result _Decode_utf( const wchar_t* _First, const wchar_t* _Last, char32_t& _Val) noexcept { _STL_INTERNAL_CHECK(_First < _Last); _Val = static_cast(*_First); if (_Val < 0xD800) { return {_First + 1, true}; } else if (_Val <= 0xDBFF) { // 0xD800 <= _Val <= 0xDBFF: High surrogate if (_First + 1 == _Last) { _Val = 0xFFFD; return {_Last, false}; } if (_First[1] < 0xDC00 || _First[1] > 0xDFFF) { // unpaired high surrogate _Val = 0xFFFD; return {_First + 1, false}; } _Val = (_Val - 0xD800) << 10; _Val += _First[1] - 0xDC00; _Val += 0x10000; return {_First + 2, true}; } else if (_Val <= 0xDFFF) { // unpaired low surrogate _Val = 0xFFFD; return {_First + 1, false}; } return {_First + 1, true}; } _NODISCARD constexpr _Decode_result _Decode_utf(const char* _First, const char* _Last, char32_t& _Val) noexcept { _STL_INTERNAL_CHECK(_First < _Last); // Decode a UTF-8 encoded codepoint starting at _First and not exceeding _Last, returning // one past the end of the character decoded. Any invalid codepoints will result in // _Val == U+FFFD and _Decode_utf will return one past the // maximal subpart of the ill-formed subsequence. So, most invalid UTF-8 will result in // one U+FFFD for each byte of invalid data. Truncated but otherwise valid UTF-8 may // result in one U+FFFD for more than one input byte. _Val = static_cast(static_cast(*_First)); // All UTF-8 text is at least one byte. // The zero extended values of the "prefix" bytes for // a multi-byte sequence are the lowest numeric value (in two's complement) // that any leading byte could have for a code unit of that size, so // we just sum the comparisons to get the number of trailing bytes. int _Num_bytes; if (_Val <= 0x7F) { return {_First + 1, true}; } else if (_Val >= 0xC2 && _Val <= 0xDF) { _Num_bytes = 2; } else if (_Val >= 0xE0 && _Val <= 0xEF) { _Num_bytes = 3; } else if (_Val >= 0xF0 && _Val <= 0xF4) { _Num_bytes = 4; } else { // definitely not valid _Val = 0xFFFD; return {_First + 1, false}; } if (_First + 1 == _Last) { // We got a multibyte sequence and the next byte is off the end, we need // to check just the next byte here since we need to look for overlong sequences. // We want to return one past the end of a truncated sequence if everything is // otherwise valid, so we can't check if _First + _Num_bytes is off the end. _Val = 0xFFFD; return {_Last, false}; } switch (_Val) { case 0xE0: // we know _First[1] is in range because we just checked above, // and a leader of 0xE0 implies _Num_bytes == 3 if (static_cast(_First[1]) < 0xA0) { // note, we just increment forward one-byte, // even though _Num_bytes would imply the next // codepoint starts at _First + 2, this is because // we don't consume trailing bytes of ill-formed subsequences _Val = 0xFFFD; return {_First + 1, false}; } break; case 0xED: if (static_cast(_First[1]) > 0x9F) { _Val = 0xFFFD; return {_First + 1, false}; } break; case 0xF0: if (static_cast(_First[1]) < 0x90) { _Val = 0xFFFD; return {_First + 1, false}; } break; case 0xF4: if (static_cast(_First[1]) > 0x8F) { _Val = 0xFFFD; return {_First + 1, false}; } break; } // mask out the "value bits" in the leading byte, // for one-byte codepoints there is no leader, // two-byte codepoints have the same number of value // bits as trailing bytes (including the leading zero) switch (_Num_bytes) { case 2: _Val &= 0b1'1111u; break; case 3: _Val &= 0b1111u; break; case 4: _Val &= 0b111u; break; } for (int _Idx = 1; _Idx < _Num_bytes; ++_Idx) { if (_First + _Idx >= _Last || static_cast(_First[_Idx]) < 0x80 || static_cast(_First[_Idx]) > 0xBF) { // truncated sequence _Val = 0xFFFD; return {_First + _Idx, false}; } // we know we're always in range due to the above check. _Val = (_Val << 6) | (static_cast(_First[_Idx]) & 0b11'1111u); } return {_First + _Num_bytes, true}; } _NODISCARD constexpr _Decode_result _Decode_utf( const char32_t* _First, const char32_t* _Last, char32_t& _Val) noexcept { _STL_INTERNAL_CHECK(_First < _Last); (void) _Last; _Val = *_First; const bool _Is_usv = _Val < 0xD800 || (_Val > 0xDFFF && _Val <= 0x10FFFF); return {_First + 1, _Is_usv}; } template class _Unicode_codepoint_iterator { private: const _CharT* _First = nullptr; const _CharT* _Last = nullptr; const _CharT* _Next = nullptr; char32_t _Val = 0; public: using value_type = char32_t; using difference_type = ptrdiff_t; constexpr _Unicode_codepoint_iterator(const _CharT* _First_val, const _CharT* _Last_val) noexcept : _First(_First_val), _Last(_Last_val) { _Next = _Decode_utf(_First, _Last, _Val)._Next_ptr; } constexpr _Unicode_codepoint_iterator() = default; constexpr _Unicode_codepoint_iterator& operator++() noexcept { _First = _Next; if (_First != _Last) { _Next = _Decode_utf(_First, _Last, _Val)._Next_ptr; } return *this; } constexpr _Unicode_codepoint_iterator operator++(int) noexcept { auto _Old = *this; ++*this; return _Old; } _NODISCARD constexpr value_type operator*() const noexcept { return _Val; } _NODISCARD constexpr const _CharT* _Position() const noexcept { return _First; } _NODISCARD constexpr bool operator==(default_sentinel_t) const noexcept { return _First == _Last; } _NODISCARD constexpr bool operator==(const _Unicode_codepoint_iterator& _Other) const noexcept { _STL_INTERNAL_CHECK(_Last == _Other._Last); return _First == _Other._First && _Last == _Other._Last; } }; // Implements a DFA matching the regex on the left side of rule GB11. The DFA is: // // +---+ ExtPic +---+ ZWJ +---+ // | 1 +---------------> 2 +---------------> 3 | // +---+ ++-^+ +---+ // | | // +-+ // Extend // // Note state 3 is never explicitly transitioned to, since it's the "accept" state, we just // transition back to state 1 and return true. class _GB11_LeftHand_regex { private: enum _State_t : bool { _Start, _ExtPic }; _State_t _State = _Start; public: _NODISCARD constexpr bool operator==(const _GB11_LeftHand_regex&) const noexcept = default; _NODISCARD constexpr bool _Match( const _Grapheme_Break_property_values _Left_gbp, _Extended_Pictographic_property_values _Left_ExtPic) noexcept { switch (_State) { case _Start: if (_Left_ExtPic == _Extended_Pictographic_property_values::_Extended_Pictographic_value) { _State = _ExtPic; } return false; case _ExtPic: if (_Left_gbp == _Grapheme_Break_property_values::_ZWJ_value) { _State = _Start; return true; } else if (_Left_gbp != _Grapheme_Break_property_values::_Extend_value) { _State = _Start; return false; } return false; default: _STL_INTERNAL_CHECK(false); return false; } } }; template class _Grapheme_break_property_iterator { private: using _Wrapped_iter_type = _Unicode_codepoint_iterator<_CharT>; _Wrapped_iter_type _WrappedIter; _GB11_LeftHand_regex _GB11_rx; public: _NODISCARD constexpr bool operator==(default_sentinel_t) const noexcept { return _WrappedIter == default_sentinel; } _NODISCARD constexpr bool operator==(const _Grapheme_break_property_iterator&) const noexcept = default; using difference_type = ptrdiff_t; using value_type = iter_value_t<_Wrapped_iter_type>; constexpr _Grapheme_break_property_iterator(const _CharT* _First, const _CharT* _Last) : _WrappedIter(_First, _Last) {} constexpr _Grapheme_break_property_iterator() = default; constexpr _Grapheme_break_property_iterator& operator++() noexcept { auto _Left_gbp = _Grapheme_Break_property_data._Get_property_for_codepoint(*_WrappedIter); auto _Left_ExtPic = _Extended_Pictographic_property_data._Get_property_for_codepoint(*_WrappedIter); auto _Right_gbp = _Grapheme_Break_property_values::_No_value; auto _Right_ExtPic = _Extended_Pictographic_property_values::_No_value; size_t _Num_RIs = 0; for (;; _Left_gbp = _Right_gbp, _Left_ExtPic = _Right_ExtPic) { ++_WrappedIter; if (_WrappedIter == default_sentinel) { return *this; // GB2 Any % eot } _Right_gbp = _Grapheme_Break_property_data._Get_property_for_codepoint(*_WrappedIter); _Right_ExtPic = _Extended_Pictographic_property_data._Get_property_for_codepoint(*_WrappedIter); // match GB11 now, so that we're sure to update it for every character, not just ones where // the GB11 rule is considered const bool _GB11_Match = _GB11_rx._Match(_Left_gbp, _Left_ExtPic); // Also update the number of sequential RIs immediately if (_Left_gbp == _Grapheme_Break_property_values::_Regional_Indicator_value) { ++_Num_RIs; } else { _Num_RIs = 0; } if (_Left_gbp == _Grapheme_Break_property_values::_CR_value && _Right_gbp == _Grapheme_Break_property_values::_LF_value) { continue; // GB3 CR x LF } if (_Left_gbp == _Grapheme_Break_property_values::_Control_value || _Left_gbp == _Grapheme_Break_property_values::_CR_value || _Left_gbp == _Grapheme_Break_property_values::_LF_value) { return *this; // GB4 (Control | CR | LF) % Any } if (_Right_gbp == _Grapheme_Break_property_values::_Control_value || _Right_gbp == _Grapheme_Break_property_values::_CR_value || _Right_gbp == _Grapheme_Break_property_values::_LF_value) { return *this; // GB5 Any % (Control | CR | LF) } if (_Left_gbp == _Grapheme_Break_property_values::_L_value && (_Right_gbp == _Grapheme_Break_property_values::_L_value || _Right_gbp == _Grapheme_Break_property_values::_V_value || _Right_gbp == _Grapheme_Break_property_values::_LV_value || _Right_gbp == _Grapheme_Break_property_values::_LVT_value)) { continue; // GB6 L x (L | V | LV | LVT) } if ((_Left_gbp == _Grapheme_Break_property_values::_LV_value || _Left_gbp == _Grapheme_Break_property_values::_V_value) && (_Right_gbp == _Grapheme_Break_property_values::_V_value || _Right_gbp == _Grapheme_Break_property_values::_T_value)) { continue; // GB7 (LV | V) x (V | T) } if ((_Left_gbp == _Grapheme_Break_property_values::_LVT_value || _Left_gbp == _Grapheme_Break_property_values::_T_value) && _Right_gbp == _Grapheme_Break_property_values::_T_value) { continue; // GB8 (LVT | T) x T } if (_Right_gbp == _Grapheme_Break_property_values::_Extend_value || _Right_gbp == _Grapheme_Break_property_values::_ZWJ_value) { continue; // GB9 x (Extend | ZWJ) } if (_Right_gbp == _Grapheme_Break_property_values::_SpacingMark_value) { continue; // GB9a x SpacingMark } if (_Left_gbp == _Grapheme_Break_property_values::_Prepend_value) { continue; // GB9b Prepend x } if (_GB11_Match && _Right_ExtPic == _Extended_Pictographic_property_values::_Extended_Pictographic_value) { continue; // GB11 \p{ExtendedPictographic} Extend* ZWJ x \p{ExtendedPictographic} } if (_Left_gbp == _Grapheme_Break_property_values::_Regional_Indicator_value && _Right_gbp == _Grapheme_Break_property_values::_Regional_Indicator_value && _Num_RIs % 2 != 0) { continue; // GB12 and 13, do not break between RIs if there are an odd number of RIs before the // breakpoint } return *this; } } constexpr _Grapheme_break_property_iterator operator++(int) noexcept { auto _Old = *this; ++*this; return _Old; } _NODISCARD constexpr const _CharT* _Position() const noexcept { return _WrappedIter._Position(); } _NODISCARD constexpr value_type operator*() const noexcept { return *_WrappedIter; } }; template concept _CharT_or_bool = same_as<_Ty, _CharT> || same_as<_Ty, bool>; template concept _Format_supported_charT = _Is_any_of_v<_CharT, char, wchar_t>; _EXPORT_STD template struct formatter; inline void _You_see_this_error_because_arg_id_is_out_of_range() noexcept {} _EXPORT_STD template class basic_format_parse_context { public: using char_type = _CharT; using const_iterator = basic_string_view<_CharT>::const_iterator; using iterator = const_iterator; constexpr explicit basic_format_parse_context( const basic_string_view<_CharT> _Fmt, const size_t _Num_args_ = 0) noexcept : _Format_string(_Fmt), _Num_args(_Num_args_) {} basic_format_parse_context(const basic_format_parse_context&) = delete; basic_format_parse_context& operator=(const basic_format_parse_context&) = delete; _NODISCARD constexpr const_iterator begin() const noexcept { return _Format_string.begin(); } _NODISCARD constexpr const_iterator end() const noexcept { return _Format_string.end(); } _NODISCARD constexpr const _CharT* _Unchecked_begin() const noexcept { return _Format_string._Unchecked_begin(); } _NODISCARD constexpr const _CharT* _Unchecked_end() const noexcept { return _Format_string._Unchecked_end(); } constexpr void advance_to(const const_iterator _It) { _Adl_verify_range(_It, _Format_string.end()); _Adl_verify_range(_Format_string.begin(), _It); const auto _Diff = static_cast(_It._Unwrapped() - _Format_string._Unchecked_begin()); _Format_string.remove_prefix(_Diff); } // While the standard presents an exposition-only enum value for // the indexing mode (manual, automatic, or unknown) we use _Next_arg_id to indicate it. // _Next_arg_id > 0 means automatic // _Next_arg_id == 0 means unknown // _Next_arg_id < 0 means manual _NODISCARD constexpr size_t next_arg_id() { if (_Next_arg_id < 0) { _Throw_format_error("Can not switch from manual to automatic indexing"); } if (_STD is_constant_evaluated()) { if (static_cast(_Next_arg_id) >= _Num_args) { _You_see_this_error_because_arg_id_is_out_of_range(); } } return static_cast(_Next_arg_id++); } constexpr void check_arg_id(const size_t _Id) { if (_STD is_constant_evaluated()) { if (_Id >= _Num_args) { _You_see_this_error_because_arg_id_is_out_of_range(); } } if (_Next_arg_id > 0) { _Throw_format_error("Can not switch from automatic to manual indexing"); } _Next_arg_id = -1; } private: basic_string_view<_CharT> _Format_string; size_t _Num_args; // The standard says this is size_t, however we use ptrdiff_t to save some space // by not having to store the indexing mode. Above is a more detailed explanation // of how this works. ptrdiff_t _Next_arg_id = 0; }; _EXPORT_STD using format_parse_context = basic_format_parse_context; _EXPORT_STD using wformat_parse_context = basic_format_parse_context; template >> concept _Formattable_with = semiregular<_Formatter> && requires(_Formatter& __f, const _Formatter& __cf, _Ty&& __t, _Context __fc, basic_format_parse_context __pc) { { __f.parse(__pc) } -> same_as; { __cf.format(__t, __fc) } -> same_as; }; template inline constexpr bool _Is_basic_string_like_for = false; template inline constexpr bool _Is_basic_string_like_for, _CharT> = true; template inline constexpr bool _Is_basic_string_like_for, _CharT> = true; template struct _Format_arg_traits { using _Char_type = _Context::char_type; // Function template _Type_eraser mirrors the type dispatching mechanism in the construction of basic_format_arg // (N4950 [format.arg]). They determine the mapping from "raw" to "erased" argument type for _Format_arg_store. template <_Formattable_with<_Context> _Ty> static auto _Type_eraser(); template using _Storage_type = decltype(_Type_eraser>()); template static constexpr size_t _Storage_size = sizeof(_Storage_type<_Ty>); }; _EXPORT_STD template class basic_format_args; _FMT_P2286_BEGIN template struct _Format_handler; _FMT_P2286_END _EXPORT_STD template class basic_format_arg { public: using _CharType = _Context::char_type; class handle { private: const void* _Ptr; void(__cdecl* _Format)(basic_format_parse_context<_CharType>& _Parse_ctx, _Context& _Format_ctx, const void*); friend basic_format_arg; public: template explicit handle(_Ty& _Val) noexcept : _Ptr(_STD addressof(_Val)), _Format([](basic_format_parse_context<_CharType>& _Parse_ctx, _Context& _Format_ctx, const void* _Ptr) { using _Td = remove_const_t<_Ty>; // doesn't drop const-qualifier per an unnumbered LWG issue using _Tq = conditional_t<_Formattable_with, const _Ty, _Ty>; typename _Context::template formatter_type<_Td> _Formatter; _Parse_ctx.advance_to(_Formatter.parse(_Parse_ctx)); _Format_ctx.advance_to( _Formatter.format(*const_cast<_Tq*>(static_cast(_Ptr)), _Format_ctx)); }) { // ditto doesn't drop const-qualifier using _Tq = conditional_t<_Formattable_with, const _Ty, _Ty>; static_assert(_Formattable_with<_Tq, _Context>); } void format(basic_format_parse_context<_CharType>& _Parse_ctx, _Context& _Format_ctx) const { _Format(_Parse_ctx, _Format_ctx, _Ptr); } }; // TRANSITION, LLVM-49072 basic_format_arg() noexcept : _Active_state(_Basic_format_arg_type::_None), _No_state() {} explicit operator bool() const noexcept { return _Active_state != _Basic_format_arg_type::_None; } // Function template _Make_from mirrors the exposition-only single-argument constructor template of // basic_format_arg (N4950 [format.arg]). template <_Formattable_with<_Context> _Ty> static basic_format_arg _Make_from(_Ty& _Val) noexcept { using _Erased_type = _Format_arg_traits<_Context>::template _Storage_type<_Ty>; #if !_HAS_CXX23 if constexpr (is_same_v<_Erased_type, basic_string_view<_CharType>>) { return basic_format_arg(_Erased_type{_Val.data(), _Val.size()}); } else #endif // !_HAS_CXX23 { return basic_format_arg(static_cast<_Erased_type>(_Val)); } } template decltype(auto) _Visit(_Visitor&& _Vis) { switch (_Active_state) { case _Basic_format_arg_type::_None: return _STD forward<_Visitor>(_Vis)(_No_state); case _Basic_format_arg_type::_Int_type: return _STD forward<_Visitor>(_Vis)(_Int_state); case _Basic_format_arg_type::_UInt_type: return _STD forward<_Visitor>(_Vis)(_UInt_state); case _Basic_format_arg_type::_Long_long_type: return _STD forward<_Visitor>(_Vis)(_Long_long_state); case _Basic_format_arg_type::_ULong_long_type: return _STD forward<_Visitor>(_Vis)(_ULong_long_state); case _Basic_format_arg_type::_Bool_type: return _STD forward<_Visitor>(_Vis)(_Bool_state); case _Basic_format_arg_type::_Char_type: return _STD forward<_Visitor>(_Vis)(_Char_state); case _Basic_format_arg_type::_Float_type: return _STD forward<_Visitor>(_Vis)(_Float_state); case _Basic_format_arg_type::_Double_type: return _STD forward<_Visitor>(_Vis)(_Double_state); case _Basic_format_arg_type::_Long_double_type: return _STD forward<_Visitor>(_Vis)(_Long_double_state); case _Basic_format_arg_type::_Pointer_type: return _STD forward<_Visitor>(_Vis)(_Pointer_state); case _Basic_format_arg_type::_CString_type: return _STD forward<_Visitor>(_Vis)(_CString_state); case _Basic_format_arg_type::_String_type: return _STD forward<_Visitor>(_Vis)(_String_state); case _Basic_format_arg_type::_Custom_type: return _STD forward<_Visitor>(_Vis)(_Custom_state); default: _STL_VERIFY(false, "basic_format_arg is in impossible state"); int _Dummy{}; return _STD forward<_Visitor>(_Vis)(_Dummy); } } private: friend basic_format_args<_Context>; friend _Format_handler<_CharType>; friend _Format_arg_traits<_Context>; explicit basic_format_arg(const int _Val) noexcept : _Active_state(_Basic_format_arg_type::_Int_type), _Int_state(_Val) {} explicit basic_format_arg(const unsigned int _Val) noexcept : _Active_state(_Basic_format_arg_type::_UInt_type), _UInt_state(_Val) {} explicit basic_format_arg(const long long _Val) noexcept : _Active_state(_Basic_format_arg_type::_Long_long_type), _Long_long_state(_Val) {} explicit basic_format_arg(const unsigned long long _Val) noexcept : _Active_state(_Basic_format_arg_type::_ULong_long_type), _ULong_long_state(_Val) {} explicit basic_format_arg(const bool _Val) noexcept : _Active_state(_Basic_format_arg_type::_Bool_type), _Bool_state(_Val) {} explicit basic_format_arg(const _CharType _Val) noexcept : _Active_state(_Basic_format_arg_type::_Char_type), _Char_state(_Val) {} explicit basic_format_arg(const float _Val) noexcept : _Active_state(_Basic_format_arg_type::_Float_type), _Float_state(_Val) {} explicit basic_format_arg(const double _Val) noexcept : _Active_state(_Basic_format_arg_type::_Double_type), _Double_state(_Val) {} explicit basic_format_arg(const long double _Val) noexcept : _Active_state(_Basic_format_arg_type::_Long_double_type), _Long_double_state(_Val) {} explicit basic_format_arg(const void* _Val) noexcept : _Active_state(_Basic_format_arg_type::_Pointer_type), _Pointer_state(_Val) {} explicit basic_format_arg(const _CharType* _Val) noexcept : _Active_state(_Basic_format_arg_type::_CString_type), _CString_state(_Val) {} explicit basic_format_arg(const basic_string_view<_CharType> _Val) noexcept : _Active_state(_Basic_format_arg_type::_String_type), _String_state(_Val) {} explicit basic_format_arg(const handle _Val) noexcept : _Active_state(_Basic_format_arg_type::_Custom_type), _Custom_state(_Val) {} _Basic_format_arg_type _Active_state = _Basic_format_arg_type::_None; union { monostate _No_state = monostate{}; int _Int_state; unsigned int _UInt_state; long long _Long_long_state; unsigned long long _ULong_long_state; bool _Bool_state; _CharType _Char_state; float _Float_state; double _Double_state; long double _Long_double_state; const void* _Pointer_state; const _CharType* _CString_state; basic_string_view<_CharType> _String_state; handle _Custom_state; }; }; template template <_Formattable_with<_Context> _Ty> auto _Format_arg_traits<_Context>::_Type_eraser() { using _Td = remove_const_t<_Ty>; // See N4950 [format.arg]/6 if constexpr (is_same_v<_Td, bool>) { return bool{}; } else if constexpr (is_same_v<_Td, _Char_type>) { return _Char_type{}; } else if constexpr (is_same_v<_Td, char> && is_same_v<_Char_type, wchar_t>) { return _Char_type{}; } else if constexpr (signed_integral<_Td> && sizeof(_Td) <= sizeof(int)) { return int{}; } else if constexpr (unsigned_integral<_Td> && sizeof(_Td) <= sizeof(unsigned int)) { return static_cast(42); } else if constexpr (signed_integral<_Td> && sizeof(_Td) <= sizeof(long long)) { return static_cast(42); } else if constexpr (unsigned_integral<_Td> && sizeof(_Td) <= sizeof(unsigned long long)) { return static_cast(42); } else if constexpr (is_same_v<_Td, float>) { return float{}; } else if constexpr (is_same_v<_Td, double>) { return double{}; } else if constexpr (is_same_v<_Td, long double>) { return static_cast(42); } else if constexpr (_Is_basic_string_like_for<_Td, _Char_type>) { return basic_string_view<_Char_type>{}; } else if constexpr (_Is_any_of_v, _Char_type*, const _Char_type*>) { return static_cast(nullptr); } else if constexpr (is_void_v> || is_same_v<_Td, nullptr_t>) { return static_cast(nullptr); } else { int _Dummy{}; return typename basic_format_arg<_Context>::handle{_Dummy}; } } _EXPORT_STD template decltype(auto) visit_format_arg(_Visitor&& _Vis, basic_format_arg<_Context> _Arg) { return _Arg._Visit(_STD forward<_Visitor>(_Vis)); } // we need to implement this ourselves because from_chars does not work with wide characters and isn't constexpr template _NODISCARD constexpr const _CharT* _Parse_nonnegative_integer( const _CharT* _First, const _CharT* _Last, unsigned int& _Value) { _STL_INTERNAL_CHECK(_First != _Last && '0' <= *_First && *_First <= '9'); constexpr auto _Max_int = static_cast((numeric_limits::max)()); constexpr auto _Big_int = _Max_int / 10u; _Value = 0; do { if (_Value > _Big_int) { _Value = _Max_int + 1; break; } _Value = _Value * 10 + static_cast(*_First - '0'); ++_First; } while (_First != _Last && '0' <= *_First && *_First <= '9'); if (_Value > _Max_int) { _Throw_format_error("Number is too big"); } return _First; } template _NODISCARD constexpr const _CharT* _Parse_nonnegative_integer(const _CharT* _First, const _CharT* _Last, int& _Value) { unsigned int _Val_unsigned = 0; _First = _Parse_nonnegative_integer(_First, _Last, _Val_unsigned); // Never invalid because _Parse_nonnegative_integer throws an error for values that don't fit in signed integers _Value = static_cast(_Val_unsigned); return _First; } template _Callbacks_type> _NODISCARD constexpr const _CharT* _Parse_arg_id( const _CharT* _First, const _CharT* _Last, _Callbacks_type&& _Callbacks) { _STL_INTERNAL_CHECK(_First != _Last); _CharT _Ch = *_First; // No id provided, format string is using automatic indexing. if (_Ch == '}' || _Ch == ':') { _Callbacks._On_auto_id(); return _First; } if (_Ch >= '0' && _Ch <= '9') { unsigned int _Index = 0; // arg_id is not allowed to have any leading zeros, but is allowed to be // equal to zero (but not '00'). So if _Ch is zero we skip the parsing, leave // _Index set to zero and let the validity checks below ensure that the arg_id // wasn't something like "00", or "023". if (_Ch == '0') { ++_First; } else { _First = _Parse_nonnegative_integer(_First, _Last, _Index); } // The format string shouldn't end right after the index number. // The only things permitted after the index are the end of the replacement field ('}') // or the beginning of the format spec (':'). if (_First == _Last || (*_First != '}' && *_First != ':')) { _Throw_format_error("Invalid format string."); } _Callbacks._On_manual_id(_Index); return _First; } // This is where we would parse named arg ids if std::format were to support them. _Throw_format_error("Invalid format string."); } _NODISCARD constexpr bool _Is_execution_charset_self_synchronizing() { #ifdef _MSVC_EXECUTION_CHARACTER_SET // This is a list of charset codes (as reported by _MSVC_EXECUTION_CHARACTER_SET) that are // self-synchronizing (and "narrow" so 1200 (utf-16) and 1201 (unicodeFFFE) are not listed). // We care about this because if a charset is self-synchronizing then we can search through it // for formatting control characters _without_ generally decoding the format string. switch (_MSVC_EXECUTION_CHARACTER_SET) { // See: https://learn.microsoft.com/en-us/windows/win32/intl/code-page-identifiers case 874: // Thai (Windows) case 1250: // ANSI Central European; Central European (Windows) case 1251: // ANSI Cyrillic; Cyrillic (Windows) case 1252: // ANSI Latin 1; Western European (Windows) case 1253: // ANSI Greek; Greek (Windows) case 1254: // ANSI Turkish; Turkish (Windows) case 1255: // ANSI Hebrew; Hebrew (Windows) case 1256: // ANSI Arabic; Arabic (Windows) case 1257: // ANSI Baltic; Baltic (Windows) case 1258: // ANSI/OEM Vietnamese; Vietnamese (Windows) case 65001: // Unicode (UTF-8) return true; default: return false; } #else // ^^^ no workaround / EDG workaround vvv return false; // TRANSITION, VSO-1468747 (EDG) - safe fallback response #endif // ^^^ EDG workaround ^^^ } // Generated per N4950 [format.string.std]/13, by tools/unicode_properties_parse/format_width_estimate_intervals.cpp // in the https://github.com/microsoft/stl repository. inline constexpr char32_t _Width_estimate_intervals_v2[] = { // 0x1100u, 0x1160u, 0x231Au, 0x231Cu, 0x2329u, 0x232Bu, 0x23E9u, 0x23EDu, 0x23F0u, 0x23F1u, 0x23F3u, 0x23F4u, 0x25FDu, 0x25FFu, 0x2614u, 0x2616u, 0x2648u, 0x2654u, 0x267Fu, 0x2680u, 0x2693u, 0x2694u, 0x26A1u, 0x26A2u, 0x26AAu, 0x26ACu, 0x26BDu, 0x26BFu, 0x26C4u, 0x26C6u, 0x26CEu, 0x26CFu, 0x26D4u, 0x26D5u, 0x26EAu, 0x26EBu, 0x26F2u, 0x26F4u, 0x26F5u, 0x26F6u, 0x26FAu, 0x26FBu, 0x26FDu, 0x26FEu, 0x2705u, 0x2706u, 0x270Au, 0x270Cu, 0x2728u, 0x2729u, 0x274Cu, 0x274Du, 0x274Eu, 0x274Fu, 0x2753u, 0x2756u, 0x2757u, 0x2758u, 0x2795u, 0x2798u, 0x27B0u, 0x27B1u, 0x27BFu, 0x27C0u, 0x2B1Bu, 0x2B1Du, 0x2B50u, 0x2B51u, 0x2B55u, 0x2B56u, 0x2E80u, 0x2E9Au, 0x2E9Bu, 0x2EF4u, 0x2F00u, 0x2FD6u, 0x2FF0u, 0x2FFCu, 0x3000u, 0x303Fu, 0x3041u, 0x3097u, 0x3099u, 0x3100u, 0x3105u, 0x3130u, 0x3131u, 0x318Fu, 0x3190u, 0x31E4u, 0x31F0u, 0x321Fu, 0x3220u, 0x3248u, 0x3250u, 0xA48Du, 0xA490u, 0xA4C7u, 0xA960u, 0xA97Du, 0xAC00u, 0xD7A4u, 0xF900u, 0xFB00u, 0xFE10u, 0xFE1Au, 0xFE30u, 0xFE53u, 0xFE54u, 0xFE67u, 0xFE68u, 0xFE6Cu, 0xFF01u, 0xFF61u, 0xFFE0u, 0xFFE7u, 0x16FE0u, 0x16FE5u, 0x16FF0u, 0x16FF2u, 0x17000u, 0x187F8u, 0x18800u, 0x18CD6u, 0x18D00u, 0x18D09u, 0x1AFF0u, 0x1AFF4u, 0x1AFF5u, 0x1AFFCu, 0x1AFFDu, 0x1AFFFu, 0x1B000u, 0x1B123u, 0x1B132u, 0x1B133u, 0x1B150u, 0x1B153u, 0x1B155u, 0x1B156u, 0x1B164u, 0x1B168u, 0x1B170u, 0x1B2FCu, 0x1F004u, 0x1F005u, 0x1F0CFu, 0x1F0D0u, 0x1F18Eu, 0x1F18Fu, 0x1F191u, 0x1F19Bu, 0x1F200u, 0x1F203u, 0x1F210u, 0x1F23Cu, 0x1F240u, 0x1F249u, 0x1F250u, 0x1F252u, 0x1F260u, 0x1F266u, 0x1F300u, 0x1F650u, 0x1F680u, 0x1F6C6u, 0x1F6CCu, 0x1F6CDu, 0x1F6D0u, 0x1F6D3u, 0x1F6D5u, 0x1F6D8u, 0x1F6DCu, 0x1F6E0u, 0x1F6EBu, 0x1F6EDu, 0x1F6F4u, 0x1F6FDu, 0x1F7E0u, 0x1F7ECu, 0x1F7F0u, 0x1F7F1u, 0x1F900u, 0x1FA00u, 0x1FA70u, 0x1FA7Du, 0x1FA80u, 0x1FA89u, 0x1FA90u, 0x1FABEu, 0x1FABFu, 0x1FAC6u, 0x1FACEu, 0x1FADCu, 0x1FAE0u, 0x1FAE9u, 0x1FAF0u, 0x1FAF9u, 0x20000u, 0x2FFFEu, 0x30000u, 0x3FFFEu}; _NODISCARD constexpr int _Unicode_width_estimate(const char32_t _Ch) noexcept { // Computes the width estimation for Unicode characters from N4950 [format.string.std]/13 // The two branches are functionally equivalent; `12` is chosen for performance here. constexpr char32_t _Linear_search_threshold = _Width_estimate_intervals_v2[12]; if (_Ch < _Linear_search_threshold) { int _Result = 1; for (const auto& _Bound : _Width_estimate_intervals_v2) { if (_Ch < _Bound) { return _Result; } _Result ^= 0b11u; // Flip between 1 and 2 on each iteration } return 1; } else { const ptrdiff_t _Upper_bound_index = _STD upper_bound(_Width_estimate_intervals_v2, _STD end(_Width_estimate_intervals_v2), _Ch) - _Width_estimate_intervals_v2; return 1 + (_Upper_bound_index & 1); } } template class _Fmt_codec; template class _Fmt_codec_base {}; template <> class _Fmt_codec_base { protected: _Cvtvec _Cvt; _NODISCARD int _Double_byte_encoding_code_units_in_next_character( const char* const _First, const char* const _Last) const { // Returns the number of code units that compose the first encoded character in [_First, _Last), // or -1 if [_First, _Last) doesn't contain an entire encoded character or *_First is not a valid lead byte. wchar_t _Wide; mbstate_t _St{}; const auto _Len = static_cast(_Last - _First); const int _Result = _Mbrtowc(&_Wide, _First, _Len, &_St, &_Cvt); if (_Result > 0) { return _Result; } else if (_Result < 0) { // invalid or incomplete encoded character return -1; } else { // next code unit is '\0' return 1; } } _Fmt_codec_base() { #ifdef _MSVC_EXECUTION_CHARACTER_SET constexpr __std_code_page _Format_codepage{_MSVC_EXECUTION_CHARACTER_SET}; #else // ^^^ no workaround / workaround vvv // TRANSITION, VSO-1468747 (EDG) - constructor isn't constexpr, so value is unimportant constexpr __std_code_page _Format_codepage{65001}; #endif // ^^^ workaround ^^^ [[maybe_unused]] const __std_win_error _Result = __std_get_cvt(_Format_codepage, &_Cvt); _STL_INTERNAL_CHECK(_Result == __std_win_error::_Success); } }; template class _Fmt_codec : private _Fmt_codec_base<_Statically_Utf8> { private: _NODISCARD static constexpr int _Utf8_code_units_in_next_character( const char* const _First, const char* const _Last) noexcept { char32_t _Ch; const auto [_Next, _Is_usv] = _Decode_utf(_First, _Last, _Ch); _STL_INTERNAL_CHECK(_Next - _First <= 4); return _Is_usv ? static_cast(_Next - _First) : -1; } public: _NODISCARD constexpr const char* _Find_encoded(const char* _First, const char* const _Last, const char _Val) const { // Returns the first occurrence of _Val as an encoded character (and not, for example, as a // continuation byte) in [_First, _Last). if constexpr (_Statically_Utf8) { return _STD _Find_unchecked(_First, _Last, _Val); } else { if (this->_Cvt._Mbcurmax == 1 || this->_Cvt._Mbcurmax == 4) { // As above and in _Mbrtowc, assume 4-byte encodings are UTF-8 return _STD _Find_unchecked(_First, _Last, _Val); } while (_First != _Last && *_First != _Val) { const int _Units = this->_Double_byte_encoding_code_units_in_next_character(_First, _Last); if (_Units < 0) { _Throw_format_error("Invalid encoded character in format string."); } _First += _Units; } return _First; } } _NODISCARD constexpr int _Units_in_next_character( const char* const _First, const char* const _Last) const noexcept { // Returns the number of code units that compose the first encoded character in // [_First, _Last), or -1 if [_First, _Last) doesn't contain an entire encoded character or // *_First is not a valid lead byte. _STL_INTERNAL_CHECK(_First < _Last); if constexpr (_Statically_Utf8) { return _Utf8_code_units_in_next_character(_First, _Last); } else { switch (this->_Cvt._Mbcurmax) { default: _STL_INTERNAL_CHECK(!"Bad number of encoding units for this code page"); [[fallthrough]]; case 1: return 1; // all characters have only one code unit case 2: return this->_Double_byte_encoding_code_units_in_next_character(_First, _Last); case 4: // Assume UTF-8 (as does _Mbrtowc) return _Utf8_code_units_in_next_character(_First, _Last); } } } #if _HAS_CXX23 _NODISCARD _Decode_result _Decode(const char* const _First, const char* const _Last, char32_t& _Val) const { _STL_INTERNAL_CHECK(_First < _Last); if constexpr (_Is_ordinary_literal_encoding_utf8()) { return _Decode_utf(_First, _Last, _Val); } else if constexpr (_Is_execution_charset_self_synchronizing()) { wchar_t _Wide; const auto _Res = __std_fs_convert_narrow_to_wide(__std_code_page{_MSVC_EXECUTION_CHARACTER_SET}, _First, 1, &_Wide, 1); _Val = _Wide; return {_First + 1, _Res._Len != 0}; } else { if (*_First == '\0') { _Val = U'\0'; return {_First + 1, true}; } wchar_t _Wide; mbstate_t _St{}; const auto _Len = static_cast(_Last - _First); const int _Result = _Mbrtowc(&_Wide, _First, _Len, &_St, &this->_Cvt); if (_Result < 0) { return {_First + 1, false}; } else { _Val = _Wide; return {_First + _Result, true}; } } } #endif // _HAS_CXX23 }; template class _Fmt_codec { public: _NODISCARD constexpr const wchar_t* _Find_encoded( const wchar_t* const _First, const wchar_t* const _Last, const wchar_t _Val) const { return _STD _Find_unchecked(_First, _Last, _Val); } _NODISCARD constexpr int _Units_in_next_character( const wchar_t* _First, const wchar_t* const _Last) const noexcept { char32_t _Ch; const auto [_Next, _Is_usv] = _Decode_utf(_First, _Last, _Ch); _STL_INTERNAL_CHECK(_Next - _First <= 2); return _Is_usv ? static_cast(_Next - _First) : -1; } #if _HAS_CXX23 _NODISCARD constexpr _Decode_result _Decode( const wchar_t* const _First, const wchar_t* const _Last, char32_t& _Val) const noexcept { return _Decode_utf(_First, _Last, _Val); } #endif // _HAS_CXX23 }; template _NODISCARD constexpr _Fmt_codec<_CharT> _Get_fmt_codec() { return {}; } template requires (is_same_v<_CharT, char> && !_Is_execution_charset_self_synchronizing()) _NODISCARD const _Fmt_codec<_CharT>& _Get_fmt_codec() { using _CodecType = _Fmt_codec<_CharT>; static once_flag _Flag; alignas(_CodecType) static unsigned char _Storage[sizeof(_CodecType)]; _STD call_once(_Flag, [] { _STD construct_at(reinterpret_cast<_CodecType*>(&_Storage)); }); return *_STD launder(reinterpret_cast(&_Storage)); } template _Callbacks_type> _NODISCARD constexpr const _CharT* _Parse_align( const _CharT* _First, const _CharT* _Last, _Callbacks_type&& _Callbacks) { // align and fill _STL_INTERNAL_CHECK(_First != _Last && *_First != '}'); auto _Parsed_align = _Fmt_align::_None; const int _Units = _Get_fmt_codec<_CharT>()._Units_in_next_character(_First, _Last); if (_Units < 0) { // invalid fill character encoding _Throw_format_error("Invalid format string."); } auto _Align_pt = _First + _Units; if (_Align_pt == _Last) { _Align_pt = _First; } for (;;) { switch (*_Align_pt) { case '<': _Parsed_align = _Fmt_align::_Left; break; case '>': _Parsed_align = _Fmt_align::_Right; break; case '^': _Parsed_align = _Fmt_align::_Center; break; } if (_Parsed_align != _Fmt_align::_None) { if (_Align_pt != _First) { if (*_First == '{') { _Throw_format_error("invalid fill character '{'"); } _Callbacks._On_fill({_First, static_cast(_Align_pt - _First)}); _First = _Align_pt + 1; } else { ++_First; } _Callbacks._On_align(_Parsed_align); break; } else if (_Align_pt == _First) { break; } _Align_pt = _First; } return _First; } // Adapts a type modeling _Width_adapter_callbacks to model _Parse_arg_id_callbacks. // Used in _Parse_width so that _Parse_arg_id can be used to parse dynamic widths. template _Callbacks_type> struct _Width_adapter { _Callbacks_type& _Callbacks; constexpr explicit _Width_adapter(_Callbacks_type& _Handler) : _Callbacks(_Handler) {} constexpr void _On_auto_id() { _Callbacks._On_dynamic_width(_Auto_id_tag{}); } constexpr void _On_manual_id(const size_t _Id) { _Callbacks._On_dynamic_width(_Id); } }; // Adapts a type modeling _Precision_adapter_callbacks to model _Parse_arg_id_callbacks. // Used in _Parse_precision so that _Parse_arg_id can be used to parse dynamic precisions. template _Callbacks_type> struct _Precision_adapter { _Callbacks_type& _Callbacks; constexpr explicit _Precision_adapter(_Callbacks_type& _Handler) : _Callbacks(_Handler) {} constexpr void _On_auto_id() { _Callbacks._On_dynamic_precision(_Auto_id_tag{}); } constexpr void _On_manual_id(const size_t _Id) { _Callbacks._On_dynamic_precision(_Id); } }; // _Parse_arg_id expects a handler when it finds an argument id, however // _Parse_replacement_field actually needs to know the value of that argument ID to pass on // to _Handler._On_replacement_field or _Handler._On_format_specs. This _Parse_arg_id wrapper // stores the value of the arg id for later use, so _Parse_replacement_field has access to it. template struct _Id_adapter { basic_format_parse_context<_CharT>& _Parse_context; size_t _Arg_id = static_cast(-1); constexpr void _On_auto_id() { _Arg_id = _Parse_context.next_arg_id(); _STL_INTERNAL_CHECK(_Arg_id != static_cast(-1)); } constexpr void _On_manual_id(const size_t _Id) { _Parse_context.check_arg_id(_Id); _Arg_id = _Id; _STL_INTERNAL_CHECK(_Arg_id != static_cast(-1)); } }; template _Callbacks_type> _NODISCARD constexpr const _CharT* _Parse_width( const _CharT* _First, const _CharT* _Last, _Callbacks_type&& _Callbacks) { _STL_INTERNAL_CHECK(_First != _Last); if ('1' <= *_First && *_First <= '9') { int _Value = 0; _First = _Parse_nonnegative_integer(_First, _Last, _Value); _Callbacks._On_width(_Value); } else if (*_First == '{') { ++_First; if (_First != _Last) { _First = _Parse_arg_id(_First, _Last, _Width_adapter<_CharT, _Callbacks_type>{_Callbacks}); } if (_First == _Last || *_First != '}') { _Throw_format_error("Invalid format string."); } ++_First; } return _First; } template _Callbacks_type> _NODISCARD constexpr const _CharT* _Parse_precision( const _CharT* _First, const _CharT* _Last, _Callbacks_type&& _Callbacks) { ++_First; _CharT _Ch = '\0'; if (_First != _Last) { _Ch = *_First; } if ('0' <= _Ch && _Ch <= '9') { int _Precision = 0; _First = _Parse_nonnegative_integer(_First, _Last, _Precision); _Callbacks._On_precision(_Precision); } else if (_Ch == '{') { ++_First; if (_First != _Last) { _First = _Parse_arg_id(_First, _Last, _Precision_adapter<_CharT, _Callbacks_type>{_Callbacks}); } if (_First == _Last || *_First != '}') { _Throw_format_error("Invalid format string."); } ++_First; } else { _Throw_format_error("Missing precision specifier."); } return _First; } template _Callbacks_type> _NODISCARD constexpr const _CharT* _Parse_format_specs( const _CharT* _First, const _CharT* _Last, _Callbacks_type&& _Callbacks) { if (_First == _Last || *_First == '}') { return _First; } _First = _Parse_align(_First, _Last, _Callbacks); if (_First == _Last) { return _First; } switch (*_First) { case '+': _Callbacks._On_sign(_Fmt_sign::_Plus); ++_First; break; case '-': _Callbacks._On_sign(_Fmt_sign::_Minus); ++_First; break; case ' ': _Callbacks._On_sign(_Fmt_sign::_Space); ++_First; break; default: break; } if (_First == _Last) { return _First; } if (*_First == '#') { _Callbacks._On_hash(); if (++_First == _Last) { return _First; } } if (*_First == '0') { _Callbacks._On_zero(); if (++_First == _Last) { return _First; } } _First = _Parse_width(_First, _Last, _Callbacks); if (_First == _Last) { return _First; } if (*_First == '.') { _First = _Parse_precision(_First, _Last, _Callbacks); if (_First == _Last) { return _First; } } if (*_First == 'L') { _Callbacks._On_localized(); if (++_First == _Last) { return _First; } } // If there's anything remaining we assume it's a type. if (*_First != '}') { _Callbacks._On_type(*_First); ++_First; } else { // call the type callback so it gets a default type, this is required // since _Specs_checker needs to be able to tell that it got a default type // to raise an error for default formatted bools with a sign modifier _Callbacks._On_type(static_cast<_CharT>('\0')); } return _First; } template _HandlerT> _NODISCARD constexpr const _CharT* _Parse_replacement_field( const _CharT* _First, const _CharT* _Last, _HandlerT&& _Handler) { ++_First; if (_First == _Last) { _Throw_format_error("Invalid format string."); } if (*_First == '}') { // string was "{}", and we have a replacement field _Handler._On_replacement_field(_Handler._Parse_context.next_arg_id(), _First); } else if (*_First == '{') { // string was "{{", so we have a literal "{" to print _Handler._On_text(_First, _First + 1); } else { _Id_adapter<_CharT> _Adapter{_Handler._Parse_context}; _First = _Parse_arg_id(_First, _Last, _Adapter); _CharT _Ch{}; if (_First != _Last) { _Ch = *_First; } if (_Ch == '}') { _Handler._On_replacement_field(_Adapter._Arg_id, _First); } else if (_Ch == ':') { _First = _Handler._On_format_specs(_Adapter._Arg_id, _First + 1, _Last); if (_First == _Last || *_First != '}') { _Throw_format_error("Unknown format specifier."); } } else { _Throw_format_error("Missing '}' in format string."); } } return _First + 1; } template _HandlerT> constexpr void _Parse_format_string(basic_string_view<_CharT> _Format_str, _HandlerT&& _Handler) { auto _First = _Format_str.data(); auto _Last = _First + _Format_str.size(); const auto& _Codec = _Get_fmt_codec<_CharT>(); while (_First != _Last) { const _CharT* _OpeningCurl = _First; if (*_First != '{') { _OpeningCurl = _Codec._Find_encoded(_First, _Last, _CharT{'{'}); for (;;) { const _CharT* _ClosingCurl = _Codec._Find_encoded(_First, _OpeningCurl, _CharT{'}'}); // In this case there are neither closing nor opening curls in [_First, _OpeningCurl) // Write the whole thing out. if (_ClosingCurl == _OpeningCurl) { _Handler._On_text(_First, _OpeningCurl); break; } // We know _ClosingCurl isn't past the end because // the above condition was not met. ++_ClosingCurl; if (_ClosingCurl == _OpeningCurl || *_ClosingCurl != '}') { _Throw_format_error("Unmatched '}' in format string."); } // We found two closing curls, so output only one of them _Handler._On_text(_First, _ClosingCurl); // skip over the second closing curl _First = _ClosingCurl + 1; } // We are done, there were no replacement fields. if (_OpeningCurl == _Last) { return; } } // Parse the replacement field starting at _OpeningCurl and ending sometime before _Last. _First = _Parse_replacement_field(_OpeningCurl, _Last, _Handler); } } template struct _Basic_format_specs { int _Width = 0; int _Precision = -1; char _Type = '\0'; _Fmt_align _Alignment = _Fmt_align::_None; _Fmt_sign _Sgn = _Fmt_sign::_None; bool _Alt = false; bool _Localized = false; bool _Leading_zero = false; uint8_t _Fill_length = 1; // At most one codepoint (so one char32_t or four utf-8 char8_t). _CharT _Fill[4 / sizeof(_CharT)] = {_CharT{' '}}; }; // Adds width and precision references to _Basic_format_specs. // This is required for std::formatter implementations because we must // parse the format specs without having access to the format args (via a format context). template struct _Dynamic_format_specs : _Basic_format_specs<_CharT> { int _Dynamic_width_index = -1; int _Dynamic_precision_index = -1; }; // Model of _Parse_spec_callbacks that fills a _Basic_format_specs with the parsed data. template class _Specs_setter { public: constexpr explicit _Specs_setter(_Basic_format_specs<_CharT>& _Specs_) : _Specs(_Specs_) {} constexpr void _On_align(const _Fmt_align _Aln) { _Specs._Alignment = _Aln; } constexpr void _On_fill(const basic_string_view<_CharT> _Sv) { if (_Sv.size() > _STD size(_Specs._Fill)) { _Throw_format_error("Invalid fill (too long)."); } const auto _Pos = _STD _Copy_unchecked(_Sv._Unchecked_begin(), _Sv._Unchecked_end(), _Specs._Fill); _STD fill(_Pos, _STD end(_Specs._Fill), _CharT{}); _Specs._Fill_length = static_cast(_Sv.size()); } constexpr void _On_sign(const _Fmt_sign _Sgn) { _Specs._Sgn = _Sgn; } constexpr void _On_hash() { _Specs._Alt = true; } constexpr void _On_zero() { _Specs._Leading_zero = true; } constexpr void _On_width(const int _Width) { _Specs._Width = _Width; } constexpr void _On_precision(const int _Precision) { _Specs._Precision = _Precision; } constexpr void _On_localized() { _Specs._Localized = true; } constexpr void _On_type(const _CharT _Type) { _Specs._Type = static_cast(_Type); } protected: _Basic_format_specs<_CharT>& _Specs; }; template _NODISCARD constexpr basic_format_arg<_Context> _Get_arg(const _Context& _Ctx, const size_t _Arg_id) { // note: while this is parameterized on the _Arg_id type in libfmt we don't // need to do that in std::format because it's only called with either an integer // id or a named id (which we do not support in std::format) auto _Arg = _Ctx.arg(_Arg_id); if (!_Arg) { _Throw_format_error("Argument not found."); } return _Arg; } template inline constexpr bool _Is_signed_or_unsigned_large_integer_t = _Is_any_of_v, int, unsigned int, long, unsigned long, long long, unsigned long long>; // Checks that the type and value of an argument associated with a dynamic // width specifier are valid. class _Width_checker { public: template _NODISCARD constexpr unsigned long long operator()(const _Ty _Value) const { if constexpr (_Is_signed_or_unsigned_large_integer_t<_Ty>) { if constexpr (is_signed_v<_Ty>) { if (_Value < 0) { _Throw_format_error("Negative width."); } } return static_cast(_Value); } else { _Throw_format_error("Width is not an integer."); } } }; // Checks that the type and value of an argument associated with a dynamic // precision specifier are valid. class _Precision_checker { public: template _NODISCARD constexpr unsigned long long operator()(const _Ty _Value) const { if constexpr (_Is_signed_or_unsigned_large_integer_t<_Ty>) { if constexpr (is_signed_v<_Ty>) { if (_Value < 0) { _Throw_format_error("Negative precision."); } } return static_cast(_Value); } else { _Throw_format_error("Precision is not an integer."); } } }; // Fetch the value of an argument associated with a dynamic // width or precision specifier. This will be called with either // _Width_checker or _Precision_checker as "_Handler". template _NODISCARD constexpr int _Get_dynamic_specs(const _FormatArg _Arg) { _STL_INTERNAL_STATIC_ASSERT(_Is_any_of_v<_Handler, _Width_checker, _Precision_checker>); const unsigned long long _Val = _STD visit_format_arg(_Handler{}, _Arg); if (_Val > static_cast((numeric_limits::max)())) { _Throw_format_error("Number is too big."); } return static_cast(_Val); } // Parses standard format specs into a _Basic_format_specs using _Specs_setter, and // additionally handles dynamic width and precision. This is separate from _Specs_setter // because it needs to know about the current basic_format_parse_context and basic_format_context // in order to fetch the width from the arguments. template class _Specs_handler : public _Specs_setter { public: using _CharT = _Context::char_type; constexpr _Specs_handler(_Basic_format_specs<_CharT>& _Specs_, _ParseContext& _Parse_ctx_, _Context& _Ctx_) : _Specs_setter<_CharT>(_Specs_), _Parse_ctx(_Parse_ctx_), _Ctx(_Ctx_) {} template constexpr void _On_dynamic_width(const _Id _Arg_id) { this->_Specs._Width = _Get_dynamic_specs<_Width_checker>(_Get_arg(_Arg_id)); } template constexpr void _On_dynamic_precision(const _Id _Arg_id) { this->_Specs._Precision = _Get_dynamic_specs<_Precision_checker>(_Get_arg(_Arg_id)); } private: _ParseContext& _Parse_ctx; _Context& _Ctx; _NODISCARD constexpr basic_format_arg<_Context> _Get_arg(_Auto_id_tag) { return _STD _Get_arg(_Ctx, _Parse_ctx.next_arg_id()); } _NODISCARD constexpr basic_format_arg<_Context> _Get_arg(const size_t _Arg_id) { _Parse_ctx.check_arg_id(_Arg_id); return _STD _Get_arg(_Ctx, _Arg_id); } }; template class _Dynamic_specs_handler : public _Specs_setter { public: using _CharT = _ParseContext::char_type; constexpr _Dynamic_specs_handler(_Dynamic_format_specs<_CharT>& _Specs_, _ParseContext& _Parse_ctx_) : _Specs_setter<_CharT>(_Specs_), _Dynamic_specs(_Specs_), _Parse_ctx(_Parse_ctx_) {} constexpr void _On_dynamic_width(const size_t _Arg_id) { _Parse_ctx.check_arg_id(_Arg_id); _Dynamic_specs._Dynamic_width_index = _Verify_dynamic_arg_index_in_range(_Arg_id); } constexpr void _On_dynamic_width(_Auto_id_tag) { _Dynamic_specs._Dynamic_width_index = _Verify_dynamic_arg_index_in_range(_Parse_ctx.next_arg_id()); } constexpr void _On_dynamic_precision(const size_t _Arg_id) { _Parse_ctx.check_arg_id(_Arg_id); _Dynamic_specs._Dynamic_precision_index = _Verify_dynamic_arg_index_in_range(_Arg_id); } constexpr void _On_dynamic_precision(_Auto_id_tag) { _Dynamic_specs._Dynamic_precision_index = _Verify_dynamic_arg_index_in_range(_Parse_ctx.next_arg_id()); } private: _Dynamic_format_specs<_CharT>& _Dynamic_specs; _ParseContext& _Parse_ctx; _NODISCARD static constexpr int _Verify_dynamic_arg_index_in_range(const size_t _Idx) { if (_Idx > static_cast((numeric_limits::max)())) { _Throw_format_error("Dynamic width or precision index too large."); } return static_cast(_Idx); } }; // Checks that the type of the argument printed by a replacement // field with format specs actually satisfies the requirements for // that format spec. If the requirements are met then calls the base class // handler method. template class _Specs_checker : public _Handler { private: _Basic_format_arg_type _Arg_type; // we'll see this if we get a modifier that requires an integer presentation type // for types that can have either integer or non-integer presentation types (charT or bool) bool _Need_arithmetic_presentation_type = false; public: constexpr explicit _Specs_checker(const _Handler& _Handler_inst, const _Basic_format_arg_type _Arg_type_) : _Handler(_Handler_inst), _Arg_type(_Arg_type_) {} constexpr void _Require_numeric_argument() const { if (!_Is_arithmetic_fmt_type(_Arg_type)) { _Throw_format_error("Format specifier requires numeric argument."); } } constexpr void _Check_precision() const { if (_Is_integral_fmt_type(_Arg_type) || _Arg_type == _Basic_format_arg_type::_Pointer_type) { _Throw_format_error("Precision not allowed for this argument type."); } } // _On_align has no checking, since we don't implement numeric alignments. constexpr void _On_localized() { _Require_numeric_argument(); _Handler::_On_localized(); } constexpr void _On_hash() { _Need_arithmetic_presentation_type = true; _Require_numeric_argument(); _Handler::_On_hash(); } constexpr void _On_sign(_Fmt_sign _Sgn) { _Need_arithmetic_presentation_type = true; _Require_numeric_argument(); _Handler::_On_sign(_Sgn); } constexpr void _On_zero() { _Need_arithmetic_presentation_type = true; _Require_numeric_argument(); _Handler::_On_zero(); } constexpr void _On_precision(int _Precision) { _Check_precision(); _Handler::_On_precision(_Precision); } template constexpr void _On_dynamic_precision(const _Ty _Val) { _Check_precision(); _Handler::_On_dynamic_precision(_Val); } template constexpr void _On_type(_CharT _Type) { if (_Type < 0 || _Type > (numeric_limits::max)()) { _Throw_format_error("Invalid type specification."); } const char _Narrow_type = static_cast(_Type); enum class _Presentation_type_category { _Default, _Integer, _Floating, _String, _Pointer, _Char, #if _HAS_CXX23 _Escape, #endif // _HAS_CXX23 }; auto _Cat = _Presentation_type_category::_Default; switch (_Narrow_type) { case '\0': break; #if _HAS_CXX23 case '?': _Cat = _Presentation_type_category::_Escape; break; #endif // _HAS_CXX23 case 's': _Cat = _Presentation_type_category::_String; break; case 'c': _Cat = _Presentation_type_category::_Char; break; case 'd': case 'B': case 'b': case 'X': case 'x': case 'o': _Cat = _Presentation_type_category::_Integer; break; case 'A': case 'a': case 'E': case 'e': case 'F': case 'f': case 'G': case 'g': _Cat = _Presentation_type_category::_Floating; break; case 'p': _Cat = _Presentation_type_category::_Pointer; break; default: _Throw_format_error("Invalid presentation type specifier"); } switch (_Arg_type) { case _Basic_format_arg_type::_None: _STL_INTERNAL_CHECK(!"Invalid argument type."); break; case _Basic_format_arg_type::_Bool_type: if (_Cat == _Presentation_type_category::_Default) { _Cat = _Presentation_type_category::_String; } // note, we don't get a call if there isn't a type, but none is valid for everything. if (_Cat != _Presentation_type_category::_String && _Cat != _Presentation_type_category::_Integer) { _Throw_format_error("Invalid presentation type for bool"); } break; case _Basic_format_arg_type::_Char_type: if (_Cat == _Presentation_type_category::_Default) { _Cat = _Presentation_type_category::_Char; } if (_Cat != _Presentation_type_category::_Char && _Cat != _Presentation_type_category::_Integer #if _HAS_CXX23 && _Cat != _Presentation_type_category::_Escape #endif // _HAS_CXX23 ) { if constexpr (is_same_v<_CharT, char>) { _Throw_format_error("Invalid presentation type for char"); } else { _Throw_format_error("Invalid presentation type for wchar_t"); } } break; case _Basic_format_arg_type::_Int_type: case _Basic_format_arg_type::_UInt_type: case _Basic_format_arg_type::_Long_long_type: case _Basic_format_arg_type::_ULong_long_type: if (_Cat == _Presentation_type_category::_Default) { _Cat = _Presentation_type_category::_Integer; } if (_Cat != _Presentation_type_category::_Integer && _Cat != _Presentation_type_category::_Char) { _Throw_format_error("Invalid presentation type for integer"); } break; case _Basic_format_arg_type::_Float_type: case _Basic_format_arg_type::_Double_type: case _Basic_format_arg_type::_Long_double_type: if (_Cat == _Presentation_type_category::_Default) { _Cat = _Presentation_type_category::_Floating; } if (_Cat != _Presentation_type_category::_Floating) { _Throw_format_error("Invalid presentation type for floating-point"); } break; case _Basic_format_arg_type::_CString_type: case _Basic_format_arg_type::_String_type: if (_Cat == _Presentation_type_category::_Default) { _Cat = _Presentation_type_category::_String; } if (_Cat != _Presentation_type_category::_String #if _HAS_CXX23 && _Cat != _Presentation_type_category::_Escape #endif // _HAS_CXX23 ) { _Throw_format_error("Invalid presentation type for string"); } break; case _Basic_format_arg_type::_Pointer_type: if (_Cat == _Presentation_type_category::_Default) { _Cat = _Presentation_type_category::_Pointer; } if (_Cat != _Presentation_type_category::_Pointer) { _Throw_format_error("Invalid presentation type for pointer"); } break; case _Basic_format_arg_type::_Custom_type: // there's no checking we can do here for custom types // (however if a custom type uses a standard formatter // to do its spec parsing it should get the above checks) break; } if (_Need_arithmetic_presentation_type && _Cat != _Presentation_type_category::_Integer && _Cat != _Presentation_type_category::_Floating) { _Throw_format_error("Modifier requires an integer presentation type for bool"); } _Handler::_On_type(_Type); } }; struct _Format_arg_index { // TRANSITION, Should be templated on number of arguments for even less storage constexpr _Format_arg_index() = default; constexpr explicit _Format_arg_index(const size_t _Index_) noexcept : _Index(_Index_) { _Type(_Basic_format_arg_type::_None); } _NODISCARD constexpr _Basic_format_arg_type _Type() const noexcept { return static_cast<_Basic_format_arg_type>(_Type_); } constexpr void _Type(_Basic_format_arg_type _Val) noexcept { _Type_ = static_cast(_Val); } size_t _Index : (sizeof(size_t) * 8 - 4){}; size_t _Type_ : 4 {}; }; template class _Format_arg_store { private: using _CharType = _Context::char_type; using _Traits = _Format_arg_traits<_Context>; friend basic_format_args<_Context>; static constexpr size_t _Num_args = sizeof...(_Args); static constexpr size_t _Storage_length = (_Traits::template _Storage_size<_Args> + ...); // The actual storage representation: _Num_args offsets into _Storage, followed immediately by the untyped // _Storage which holds copies of the object representations of arguments (with no regard for alignment). // These must be allocated consecutively, since basic_format_args thinks it can store a pointer to // _Index_array and use arithmetic to access the bytes of _Storage. _Format_arg_index _Index_array[_Num_args]; unsigned char _Storage[_Storage_length]; #pragma warning(push) #pragma warning(disable : 6386) // Buffer overrun while writing to '%s' ... template void _Store_impl( const size_t _Arg_index, const _Basic_format_arg_type _Arg_type, const type_identity_t<_Ty>& _Val) noexcept { _STL_INTERNAL_CHECK(_Arg_index < _Num_args); const auto _Store_index = _Index_array[_Arg_index]._Index; _CSTD memcpy(_Storage + _Store_index, _STD addressof(_Val), sizeof(_Ty)); _Index_array[_Arg_index]._Type(_Arg_type); if (_Arg_index + 1 < _Num_args) { // Set the starting index of the next arg, as that is dynamic, must be called with increasing index _Index_array[_Arg_index + 1] = _Format_arg_index{_Store_index + sizeof(_Ty)}; } } #pragma warning(pop) template void _Store(const size_t _Arg_index, _Ty&& _Val) noexcept { using _Erased_type = _Traits::template _Storage_type<_Ty>; _Basic_format_arg_type _Arg_type; if constexpr (is_same_v<_Erased_type, bool>) { _Arg_type = _Basic_format_arg_type::_Bool_type; } else if constexpr (is_same_v<_Erased_type, _CharType>) { _Arg_type = _Basic_format_arg_type::_Char_type; } else if constexpr (is_same_v<_Erased_type, int>) { _Arg_type = _Basic_format_arg_type::_Int_type; } else if constexpr (is_same_v<_Erased_type, unsigned int>) { _Arg_type = _Basic_format_arg_type::_UInt_type; } else if constexpr (is_same_v<_Erased_type, long long>) { _Arg_type = _Basic_format_arg_type::_Long_long_type; } else if constexpr (is_same_v<_Erased_type, unsigned long long>) { _Arg_type = _Basic_format_arg_type::_ULong_long_type; } else if constexpr (is_same_v<_Erased_type, float>) { _Arg_type = _Basic_format_arg_type::_Float_type; } else if constexpr (is_same_v<_Erased_type, double>) { _Arg_type = _Basic_format_arg_type::_Double_type; } else if constexpr (is_same_v<_Erased_type, long double>) { _Arg_type = _Basic_format_arg_type::_Long_double_type; } else if constexpr (is_same_v<_Erased_type, const void*>) { _Arg_type = _Basic_format_arg_type::_Pointer_type; } else if constexpr (is_same_v<_Erased_type, const _CharType*>) { _Arg_type = _Basic_format_arg_type::_CString_type; } else if constexpr (is_same_v<_Erased_type, basic_string_view<_CharType>>) { _Arg_type = _Basic_format_arg_type::_String_type; } else { _STL_INTERNAL_STATIC_ASSERT(is_same_v<_Erased_type, typename basic_format_arg<_Context>::handle>); _Arg_type = _Basic_format_arg_type::_Custom_type; } #if !_HAS_CXX23 // Workaround towards N4950 [format.arg]/6.8 in C++20 if constexpr (is_same_v<_Erased_type, basic_string_view<_CharType>>) { _Store_impl<_Erased_type>(_Arg_index, _Arg_type, _Erased_type{_Val.data(), _Val.size()}); } else #endif // !_HAS_CXX23 { _Store_impl<_Erased_type>(_Arg_index, _Arg_type, static_cast<_Erased_type>(_Val)); } } public: _Format_arg_store(_Args&... _Vals) noexcept { _Index_array[0] = {}; size_t _Arg_index = 0; (_Store(_Arg_index++, _Vals), ...); } }; template class _Format_arg_store<_Context> {}; _EXPORT_STD template class basic_format_args { public: basic_format_args() noexcept = default; basic_format_args(const _Format_arg_store<_Context>&) noexcept {} template basic_format_args(const _Format_arg_store<_Context, _Args...>& _Store) noexcept : _Num_args(sizeof...(_Args)), _Index_array(_Store._Index_array) {} _NODISCARD basic_format_arg<_Context> get(const size_t _Index) const noexcept { if (_Index >= _Num_args) { return basic_format_arg<_Context>{}; } using _CharType = _Context::char_type; // The explanatory comment in _Format_arg_store explains how the following works. const auto _Packed_index = _Index_array[_Index]; const auto _Arg_storage = reinterpret_cast(_Index_array + _Num_args) + _Packed_index._Index; switch (_Packed_index._Type()) { case _Basic_format_arg_type::_None: default: _STL_ASSERT(false, "Invalid basic_format_arg type"); return basic_format_arg<_Context>{}; case _Basic_format_arg_type::_Int_type: return basic_format_arg<_Context>{_Get_value_from_memory(_Arg_storage)}; case _Basic_format_arg_type::_UInt_type: return basic_format_arg<_Context>{_Get_value_from_memory(_Arg_storage)}; case _Basic_format_arg_type::_Long_long_type: return basic_format_arg<_Context>{_Get_value_from_memory(_Arg_storage)}; case _Basic_format_arg_type::_ULong_long_type: return basic_format_arg<_Context>{_Get_value_from_memory(_Arg_storage)}; case _Basic_format_arg_type::_Bool_type: return basic_format_arg<_Context>{_Get_value_from_memory(_Arg_storage)}; case _Basic_format_arg_type::_Char_type: return basic_format_arg<_Context>{_Get_value_from_memory<_CharType>(_Arg_storage)}; case _Basic_format_arg_type::_Float_type: return basic_format_arg<_Context>{_Get_value_from_memory(_Arg_storage)}; case _Basic_format_arg_type::_Double_type: return basic_format_arg<_Context>{_Get_value_from_memory(_Arg_storage)}; case _Basic_format_arg_type::_Long_double_type: return basic_format_arg<_Context>{_Get_value_from_memory(_Arg_storage)}; case _Basic_format_arg_type::_Pointer_type: return basic_format_arg<_Context>{_Get_value_from_memory(_Arg_storage)}; case _Basic_format_arg_type::_CString_type: return basic_format_arg<_Context>{_Get_value_from_memory(_Arg_storage)}; case _Basic_format_arg_type::_String_type: return basic_format_arg<_Context>{_Get_value_from_memory>(_Arg_storage)}; case _Basic_format_arg_type::_Custom_type: return basic_format_arg<_Context>{ _Get_value_from_memory::handle>(_Arg_storage)}; } } _NODISCARD size_t _Estimate_required_capacity() const noexcept { using _CharType = _Context::char_type; size_t _Result = 0; for (size_t _Idx = 0; _Idx < _Num_args; ++_Idx) { const auto _Packed_index = _Index_array[_Idx]; const auto _Arg_type = _Packed_index._Type(); if (_Arg_type == _Basic_format_arg_type::_String_type) { const auto _Arg_storage = reinterpret_cast(_Index_array + _Num_args) + _Packed_index._Index; const auto _View = _Get_value_from_memory>(_Arg_storage); _Result += _View.size(); } else if (_Arg_type == _Basic_format_arg_type::_CString_type) { _Result += 32; // estimate for length of null-terminated strings } else { _Result += 8; // estimate for length of all other arguments } } return _Result; } private: template _NODISCARD static auto _Get_value_from_memory(const unsigned char* const _Val) noexcept { auto& _Temp = *reinterpret_cast(_Val); return _STD bit_cast<_Ty>(_Temp); } size_t _Num_args = 0; const _Format_arg_index* _Index_array = nullptr; }; template basic_format_args(_Format_arg_store<_Context, _Args...>) -> basic_format_args<_Context>; // _Lazy_locale is used instead of a std::locale so that the locale is only // constructed when needed, and is never constructed if the format string does not // contain locale-sensitive format specifiers. Note that this means that a new locale // will be constructed for _every_ locale-sensitive format specifier in the format string, // making that case slower than if we had stored a "real" locale in the basic_format_context. class _Lazy_locale { private: const locale* _Loc = nullptr; public: _Lazy_locale() = default; explicit _Lazy_locale(const locale& _Loc_) : _Loc(&_Loc_) {} explicit _Lazy_locale(const locale&&) = delete; _NODISCARD locale _Get() const { if (_Loc) { return *_Loc; } else { return locale{}; } } }; _EXPORT_STD template requires output_iterator<_Out, const _CharT&> class basic_format_context { private: _Out _OutputIt; basic_format_args _Args; _Lazy_locale _Loc; public: using iterator = _Out; using char_type = _CharT; template using formatter_type = formatter<_Ty, _CharT>; constexpr basic_format_context(_Out _OutputIt_, basic_format_args _Ctx_args) : _OutputIt(_STD move(_OutputIt_)), _Args(_Ctx_args) {} constexpr basic_format_context( _Out _OutputIt_, basic_format_args _Ctx_args, const _Lazy_locale& _Loc_) : _OutputIt(_STD move(_OutputIt_)), _Args(_Ctx_args), _Loc(_Loc_) {} _NODISCARD basic_format_arg arg(size_t _Id) const noexcept { return _Args.get(_Id); } _NODISCARD locale locale() { return _Loc._Get(); } _NODISCARD iterator out() { return _STD move(_OutputIt); } void advance_to(iterator _It) { _OutputIt = _STD move(_It); } _NODISCARD const basic_format_args& _Get_args() const noexcept { return _Args; } _NODISCARD _Lazy_locale _Get_lazy_locale() const { return _Loc; } }; template class _Fmt_buffer { private: _Ty* _Ptr_ = nullptr; size_t _Size_ = 0; size_t _Capacity_ = 0; protected: explicit _Fmt_buffer(const size_t _Size) noexcept : _Size_(_Size), _Capacity_(_Size) {} ~_Fmt_buffer() = default; _Fmt_buffer(_Ty* _Data, const size_t _Size, const size_t _Capacity) noexcept : _Ptr_(_Data), _Size_(_Size), _Capacity_(_Capacity) {} void _Set(_Ty* _Buf_data, const size_t _Buf_capacity) noexcept { _Ptr_ = _Buf_data; _Capacity_ = _Buf_capacity; } virtual void _Grow(size_t _Capacity) = 0; public: using value_type = _Ty; _Fmt_buffer(const _Fmt_buffer&) = delete; void operator=(const _Fmt_buffer&) = delete; _NODISCARD _Ty* begin() noexcept { return _Ptr_; } _NODISCARD _Ty* end() noexcept { return _Ptr_ + _Size_; } _NODISCARD size_t _Size() const noexcept { return _Size_; } _NODISCARD size_t _Capacity() const noexcept { return _Capacity_; } void _Clear() noexcept { _Size_ = 0; } void _Try_reserve(const size_t _New_capacity) { if (_New_capacity > _Capacity_) { _Grow(_New_capacity); } } void push_back(const _Ty _Value) { _Try_reserve(_Size_ + 1); _Ptr_[_Size_++] = _Value; } }; struct _Fmt_buffer_traits { explicit _Fmt_buffer_traits(ptrdiff_t) {} _NODISCARD size_t _Count() const noexcept { return 0; } _NODISCARD size_t _Limit(const size_t _Size) noexcept { return _Size; } }; class _Fmt_fixed_buffer_traits { private: ptrdiff_t _Count_ = 0; ptrdiff_t _Limit_; public: explicit _Fmt_fixed_buffer_traits(const ptrdiff_t _Limit) noexcept : _Limit_(_Limit) {} _NODISCARD size_t _Count() const noexcept { return static_cast(_Count_); } _NODISCARD size_t _Limit(const size_t _Size) noexcept { size_t _Avail = static_cast(_Limit_ > _Count_ ? _Limit_ - _Count_ : 0); _Count_ += _Size; return _Size < _Avail ? _Size : _Avail; } }; inline constexpr size_t _Fmt_buffer_size = 256; template struct _Back_insert_iterator_container_type { using type = void; }; template struct _Back_insert_iterator_container_type> { using type = _Container; }; template struct _Back_insert_iterator_container_access : back_insert_iterator<_Container> { explicit _Back_insert_iterator_container_access(back_insert_iterator<_Container> _Iter) : back_insert_iterator<_Container>(_Iter) {} using back_insert_iterator<_Container>::container; }; template class _Fmt_iterator_buffer final : public _Traits, public _Fmt_buffer<_Ty> { private: _OutputIt _Output; _Ty _Data[_Fmt_buffer_size]; void _Grow(size_t) final { if (this->_Size() == _Fmt_buffer_size) { _Flush(); } } void _Flush() { auto _Size = this->_Size(); this->_Clear(); const auto _End = _Data + this->_Limit(_Size); // extracts back_insert_iterator's underlying container type, or void if not. using _Container = _Back_insert_iterator_container_type<_OutputIt>::type; if constexpr (_Is_specialization_v<_Container, basic_string> || _Is_specialization_v<_Container, vector>) { auto& _Cont = *_Back_insert_iterator_container_access<_Container>{_Output}.container; _Cont.insert(_Cont.end(), _Data, _End); } else { _Output = _STD _Copy_unchecked(_Data, _End, _STD move(_Output)); } } public: explicit _Fmt_iterator_buffer(_OutputIt _Out, ptrdiff_t _Size = _Fmt_buffer_size) : _Traits(_Size), _Fmt_buffer<_Ty>(_Data, 0, _Fmt_buffer_size), _Output(_STD move(_Out)) {} ~_Fmt_iterator_buffer() { if (this->_Size() != 0) { _Flush(); } } _NODISCARD _OutputIt _Out() { _Flush(); return _STD move(_Output); } _NODISCARD ptrdiff_t _Count() const noexcept { return static_cast(_Traits::_Count() + this->_Size()); } }; template class _Fmt_iterator_buffer<_Ty*, _Ty> final : public _Fmt_buffer<_Ty> { private: void _Grow(size_t) final {} public: explicit _Fmt_iterator_buffer(_Ty* _Out, ptrdiff_t = 0) : _Fmt_buffer<_Ty>(_Out, 0, ~size_t{}) {} _NODISCARD _Ty* _Out() noexcept { return this->end(); } }; template class _Fmt_counting_buffer final : public _Fmt_buffer<_Ty> { private: _Ty _Data[_Fmt_buffer_size]; size_t _Count_ = 0; void _Grow(size_t) final { if (this->_Size() != _Fmt_buffer_size) { return; } _Count_ += this->_Size(); this->_Clear(); } public: _Fmt_counting_buffer() : _Fmt_buffer<_Ty>(_Data, 0, _Fmt_buffer_size) {} _NODISCARD size_t _Count() const noexcept { return _Count_ + this->_Size(); } }; using _Fmt_it = back_insert_iterator<_Fmt_buffer>; using _Fmt_wit = back_insert_iterator<_Fmt_buffer>; _EXPORT_STD using format_context = basic_format_context<_Fmt_it, char>; _EXPORT_STD using wformat_context = basic_format_context<_Fmt_wit, wchar_t>; _FMT_P2286_BEGIN template _NODISCARD _OutputIt _Fmt_write(_OutputIt _Out, monostate) { _STL_INTERNAL_CHECK(false); return _Out; } // This size is derived from the maximum length of an arithmetic type. The contenders for widest are: // (a) long long has a max length of 20 characters: LLONG_MIN is "-9223372036854775807". // (b) unsigned long long has a max length of 20 characters: ULLONG_MAX is "18446744073709551615". // (c) double has a max length of 24 characters: -DBL_MAX is "-1.7976931348623158e+308". // That's 17 characters for numeric_limits::max_digits10, // plus 1 character for the sign, // plus 1 character for the decimal point, // plus 1 character for 'e', // plus 1 character for the exponent's sign, // plus 3 characters for the max exponent. inline constexpr size_t _Format_min_buffer_length = 24; template requires (is_arithmetic_v<_Arithmetic> && !_CharT_or_bool<_Arithmetic, _CharT>) _NODISCARD _OutputIt _Fmt_write(_OutputIt _Out, _Arithmetic _Value); template _NODISCARD _OutputIt _Fmt_write(_OutputIt _Out, bool _Value); template _NODISCARD _OutputIt _Fmt_write(_OutputIt _Out, _CharT _Value); template _NODISCARD _OutputIt _Fmt_write(_OutputIt _Out, const void* _Value); template _NODISCARD _OutputIt _Fmt_write(_OutputIt _Out, const _CharT* _Value); template _NODISCARD _OutputIt _Fmt_write(_OutputIt _Out, basic_string_view<_CharT> _Value); template _NODISCARD _OutputIt _Widen_and_copy(const char* _First, const char* const _Last, _OutputIt _Out) { for (; _First != _Last; ++_First, (void) ++_Out) { *_Out = static_cast<_CharT>(*_First); } return _Out; } template requires (is_arithmetic_v<_Arithmetic> && !_CharT_or_bool<_Arithmetic, _CharT>) _NODISCARD _OutputIt _Fmt_write(_OutputIt _Out, const _Arithmetic _Value) { // TRANSITION, Reusable buffer char _Buffer[_Format_min_buffer_length]; char* _End = _Buffer; if constexpr (is_floating_point_v<_Arithmetic>) { if ((_STD isnan)(_Value)) { if ((_STD signbit)(_Value)) { *_End++ = '-'; } _CSTD memcpy(_End, "nan", 3); _End += 3; } } if (_End == _Buffer) { const to_chars_result _Result = _STD to_chars(_Buffer, _STD end(_Buffer), _Value); _STL_INTERNAL_CHECK(_Result.ec == errc{}); _End = _Result.ptr; } return _Widen_and_copy<_CharT>(_Buffer, _End, _STD move(_Out)); } template _NODISCARD _OutputIt _Fmt_write(_OutputIt _Out, const bool _Value) { if constexpr (is_same_v<_CharT, wchar_t>) { return _Fmt_write(_STD move(_Out), _Value ? L"true" : L"false"); } else { return _Fmt_write(_STD move(_Out), _Value ? "true" : "false"); } } template _NODISCARD _OutputIt _Fmt_write(_OutputIt _Out, const _CharT _Value) { *_Out++ = _Value; return _Out; } template _NODISCARD _OutputIt _Fmt_write(_OutputIt _Out, const void* const _Value) { // TRANSITION, Reusable buffer char _Buffer[_Format_min_buffer_length]; const auto [_End, _Ec] = _STD to_chars(_Buffer, _STD end(_Buffer), reinterpret_cast(_Value), 16); _STL_INTERNAL_CHECK(_Ec == errc{}); *_Out++ = '0'; *_Out++ = 'x'; return _Widen_and_copy<_CharT>(_Buffer, _End, _STD move(_Out)); } template _NODISCARD _OutputIt _Fmt_write(_OutputIt _Out, const _CharT* _Value) { if (!_Value) { _Throw_format_error("String pointer is null."); } while (*_Value) { *_Out++ = *_Value++; } return _Out; } template _NODISCARD _OutputIt _Fmt_write(_OutputIt _Out, const basic_string_view<_CharT> _Value) { return _RANGES copy(_Value, _STD move(_Out)).out; } template _NODISCARD _OutputIt _Write_aligned( _OutputIt _Out, const int _Width, const _Specs_type& _Specs, const _Fmt_align _Default_align, _Func&& _Fn) { int _Fill_left = 0; int _Fill_right = 0; auto _Alignment = _Specs._Alignment; if (_Alignment == _Fmt_align::_None) { _Alignment = _Default_align; } if (_Width < _Specs._Width) { switch (_Alignment) { case _Fmt_align::_Left: _Fill_right = _Specs._Width - _Width; break; case _Fmt_align::_Right: _Fill_left = _Specs._Width - _Width; break; case _Fmt_align::_Center: _Fill_left = (_Specs._Width - _Width) / 2; _Fill_right = _Specs._Width - _Width - _Fill_left; break; case _Fmt_align::_None: _STL_ASSERT(false, "Invalid alignment"); break; } } const basic_string_view _Fill_char{_Specs._Fill, _Specs._Fill_length}; for (; _Fill_left > 0; --_Fill_left) { _Out = _RANGES copy(_Fill_char, _STD move(_Out)).out; } _Out = _Fn(_STD move(_Out)); for (; _Fill_right > 0; --_Fill_right) { _Out = _RANGES copy(_Fill_char, _STD move(_Out)).out; } return _Out; } template _NODISCARD constexpr string_view _Get_integral_prefix(const char _Type, const _Integral _Value) noexcept { switch (_Type) { case 'b': return "0b"sv; case 'B': return "0B"sv; case 'x': return "0x"sv; case 'X': return "0X"sv; case 'o': if (_Value != _Integral{0}) { return "0"sv; } return {}; default: return {}; } } template _NODISCARD _OutputIt _Write_sign(_OutputIt _Out, const _Fmt_sign _Sgn, const bool _Is_negative) { if (_Is_negative) { *_Out++ = '-'; } else { switch (_Sgn) { case _Fmt_sign::_Plus: *_Out++ = '+'; break; case _Fmt_sign::_Space: *_Out++ = ' '; break; case _Fmt_sign::_None: case _Fmt_sign::_Minus: break; } } return _Out; } inline void _Buffer_to_uppercase(char* _First, const char* _Last) noexcept { for (; _First != _Last; ++_First) { if (*_First >= 'a' && *_First <= 'z') { *_First -= 'a' - 'A'; } } } template using _Make_standard_integer = conditional_t, make_signed_t<_Ty>, make_unsigned_t<_Ty>>; template _NODISCARD constexpr bool _In_bounds(const _Ty _Value) { return _STD in_range<_Make_standard_integer<_CharT>>(static_cast<_Make_standard_integer<_Ty>>(_Value)); } _NODISCARD inline int _Count_separators(size_t _Digits, const string_view _Groups) { if (_Groups.empty()) { return 0; } // Calculate the amount of separators that are going to be inserted based on the groupings of the locale. int _Separators = 0; auto _Group_it = _Groups.begin(); while (_Digits > static_cast(*_Group_it)) { _Digits -= static_cast(*_Group_it); ++_Separators; if (_Group_it + 1 != _Groups.end()) { ++_Group_it; } } return _Separators; } template _NODISCARD _OutputIt _Write_separated_integer(const char* _First, const char* const _Last, const string_view _Groups, const _CharT _Separator, int _Separators, _OutputIt _Out) { auto _Group_it = _Groups.begin(); auto _Repeats = 0; auto _Grouped = 0; for (int _Section = 0; _Section < _Separators; ++_Section) { _Grouped += *_Group_it; if (_Group_it + 1 != _Groups.end()) { ++_Group_it; } else { ++_Repeats; } } _Out = _Widen_and_copy<_CharT>(_First, _Last - _Grouped, _STD move(_Out)); _First = _Last - _Grouped; for (; _Separators > 0; --_Separators) { if (_Repeats > 0) { --_Repeats; } else { --_Group_it; } *_Out++ = _Separator; _Out = _Widen_and_copy<_CharT>(_First, _First + *_Group_it, _STD move(_Out)); _First += *_Group_it; } _STL_INTERNAL_CHECK(_First == _Last); return _Out; } template _NODISCARD _OutputIt _Fmt_write(_OutputIt _Out, monostate, const _Basic_format_specs<_CharT>&, _Lazy_locale) { _STL_INTERNAL_CHECK(false); return _Out; } template _NODISCARD _OutputIt _Write_integral( _OutputIt _Out, _Integral _Value, _Basic_format_specs<_CharT> _Specs, _Lazy_locale _Locale); #if _HAS_CXX23 template _NODISCARD _OutputIt _Write_escaped( _OutputIt _Out, basic_string_view<_CharT> _Value, _Basic_format_specs<_CharT> _Specs, char _Delim); #endif // _HAS_CXX23 template requires (!_CharT_or_bool<_Integral, _CharT>) _NODISCARD _OutputIt _Fmt_write( _OutputIt _Out, _Integral _Value, const _Basic_format_specs<_CharT>& _Specs, _Lazy_locale _Locale); template _NODISCARD _OutputIt _Fmt_write(_OutputIt _Out, bool _Value, _Basic_format_specs<_CharT> _Specs, _Lazy_locale _Locale); template _NODISCARD _OutputIt _Fmt_write( _OutputIt _Out, _CharT _Value, _Basic_format_specs<_CharT> _Specs, _Lazy_locale _Locale); template _NODISCARD _OutputIt _Fmt_write( _OutputIt _Out, _Float _Value, const _Basic_format_specs<_CharT>& _Specs, _Lazy_locale _Locale); template _NODISCARD _OutputIt _Fmt_write( _OutputIt _Out, const void* _Value, const _Basic_format_specs<_CharT>& _Specs, _Lazy_locale); template _NODISCARD _OutputIt _Fmt_write( _OutputIt _Out, const _CharT* _Value, const _Basic_format_specs<_CharT>& _Specs, _Lazy_locale _Locale); template _NODISCARD _OutputIt _Fmt_write( _OutputIt _Out, basic_string_view<_CharT> _Value, const _Basic_format_specs<_CharT>& _Specs, _Lazy_locale); #pragma warning(push) #pragma warning(disable : 4296) // '<': expression is always false template _NODISCARD _OutputIt _Write_integral( _OutputIt _Out, const _Integral _Value, _Basic_format_specs<_CharT> _Specs, _Lazy_locale _Locale) { if (_Specs._Type == 'c') { if (!_In_bounds<_CharT>(_Value)) { if constexpr (is_same_v<_CharT, char>) { _Throw_format_error("integral cannot be stored in char"); } else { _Throw_format_error("integral cannot be stored in wchar_t"); } } _Specs._Alt = false; return _Fmt_write(_STD move(_Out), static_cast<_CharT>(_Value), _Specs, _Locale); } _STL_INTERNAL_CHECK(_Specs._Precision == -1); if (_Specs._Sgn == _Fmt_sign::_None) { _Specs._Sgn = _Fmt_sign::_Minus; } int _Base = 10; switch (_Specs._Type) { case 'B': case 'b': _Base = 2; break; case 'X': case 'x': _Base = 16; break; case 'o': _Base = 8; break; } // long long -1 representation in binary is 64 bits + sign char _Buffer[65]; const auto [_End, _Ec] = _STD to_chars(_Buffer, _STD end(_Buffer), _Value, _Base); _STL_INTERNAL_CHECK(_Ec == errc{}); auto _Buffer_start = _Buffer; auto _Width = static_cast(_End - _Buffer_start); if (_Value >= _Integral{0}) { if (_Specs._Sgn != _Fmt_sign::_Minus) { _Width += 1; } } else { // Remove the '-', it will be dealt with directly _Buffer_start += 1; } if (_Specs._Type == 'X') { _Buffer_to_uppercase(_Buffer_start, _End); } string_view _Prefix; if (_Specs._Alt) { _Prefix = _Get_integral_prefix(_Specs._Type, _Value); _Width += static_cast(_Prefix.size()); } auto _Separators = 0; string _Groups; if (_Specs._Localized) { _Groups = _STD use_facet>(_Locale._Get()).grouping(); _Separators = _Count_separators(static_cast(_End - _Buffer_start), _Groups); // TRANSITION, separators may be wider for wide chars _Width += _Separators; } const bool _Write_leading_zeroes = _Specs._Leading_zero && _Specs._Alignment == _Fmt_align::_None; auto _Writer = [&, _End = _End](_OutputIt _Out) { _Out = _Write_sign(_STD move(_Out), _Specs._Sgn, _Value < _Integral{0}); _Out = _Widen_and_copy<_CharT>(_Prefix.data(), _Prefix.data() + _Prefix.size(), _STD move(_Out)); if (_Write_leading_zeroes && _Width < _Specs._Width) { _Out = _RANGES fill_n(_STD move(_Out), _Specs._Width - _Width, _CharT{'0'}); } if (_Separators > 0) { return _Write_separated_integer(_Buffer_start, _End, _Groups, _STD use_facet>(_Locale._Get()).thousands_sep(), // _Separators, _STD move(_Out)); } return _Widen_and_copy<_CharT>(_Buffer_start, _End, _STD move(_Out)); }; if (_Write_leading_zeroes) { return _Writer(_STD move(_Out)); } return _Write_aligned(_STD move(_Out), _Width, _Specs, _Fmt_align::_Right, _Writer); } #pragma warning(pop) template requires (!_CharT_or_bool<_Integral, _CharT>) _NODISCARD _OutputIt _Fmt_write( _OutputIt _Out, const _Integral _Value, const _Basic_format_specs<_CharT>& _Specs, _Lazy_locale _Locale) { return _Write_integral(_STD move(_Out), _Value, _Specs, _Locale); } template _NODISCARD _OutputIt _Fmt_write( _OutputIt _Out, const bool _Value, _Basic_format_specs<_CharT> _Specs, _Lazy_locale _Locale) { if (_Specs._Type != '\0' && _Specs._Type != 's') { return _Write_integral(_STD move(_Out), static_cast(_Value), _Specs, _Locale); } _STL_INTERNAL_CHECK(_Specs._Precision == -1); if (_Specs._Localized) { _Specs._Localized = false; const auto& _Facet = _STD use_facet>(_Locale._Get()); return _Fmt_write(_STD move(_Out), _Value ? static_cast>(_Facet.truename()) : static_cast>(_Facet.falsename()), _Specs, _Locale); } if constexpr (is_same_v<_CharT, wchar_t>) { return _Fmt_write(_STD move(_Out), _Value ? L"true" : L"false", _Specs, _Locale); } else { return _Fmt_write(_STD move(_Out), _Value ? "true" : "false", _Specs, _Locale); } } template _NODISCARD _OutputIt _Fmt_write( _OutputIt _Out, const _CharT _Value, _Basic_format_specs<_CharT> _Specs, _Lazy_locale _Locale) { if (_Specs._Type != '\0' && _Specs._Type != 'c' #if _HAS_CXX23 && _Specs._Type != '?' #endif // _HAS_CXX23 ) { return _Write_integral(_STD move(_Out), _Value, _Specs, _Locale); } _STL_INTERNAL_CHECK(_Specs._Precision == -1); #if _HAS_CXX23 if (_Specs._Type == '?') { return _Write_escaped(_STD move(_Out), basic_string_view<_CharT>{&_Value, 1}, _Specs, '\''); } #endif // _HAS_CXX23 return _Fmt_write(_STD move(_Out), basic_string_view<_CharT>{&_Value, 1}, _Specs, _Locale); } template _NODISCARD _OutputIt _Fmt_write( _OutputIt _Out, const _Float _Value, const _Basic_format_specs<_CharT>& _Specs, _Lazy_locale _Locale) { auto _Sgn = _Specs._Sgn; if (_Sgn == _Fmt_sign::_None) { _Sgn = _Fmt_sign::_Minus; } auto _To_upper = false; auto _Format = chars_format::general; auto _Exponent = 'e'; auto _Precision = _Specs._Precision; switch (_Specs._Type) { case 'A': _To_upper = true; [[fallthrough]]; case 'a': _Format = chars_format::hex; _Exponent = 'p'; break; case 'E': _To_upper = true; [[fallthrough]]; case 'e': if (_Precision == -1) { _Precision = 6; } _Format = chars_format::scientific; break; case 'F': _To_upper = true; [[fallthrough]]; case 'f': if (_Precision == -1) { _Precision = 6; } _Format = chars_format::fixed; break; case 'G': _To_upper = true; [[fallthrough]]; case 'g': if (_Precision == -1) { _Precision = 6; } _Format = chars_format::general; break; } // Consider the powers of 2 in decimal: // 2^-1 = 0.5 // 2^-2 = 0.25 // 2^-3 = 0.125 // 2^-4 = 0.0625 // Each power of 2 consumes one more decimal digit. This is because: // 2^-N * 5^-N = 10^-N // 2^-N = 10^-N * 5^N // Example: 2^-4 = 10^-4 * 5^4 = 0.0001 * 625 // Therefore, the min subnormal 2^-1074 consumes 1074 digits of precision (digits after the decimal point). // We need 3 more characters for a potential negative sign, the zero integer part, and the decimal point. // Therefore, the precision can be clamped to 1074. // The largest number consumes 309 digits before the decimal point. With a precision of 1074, and it being // negative, it would use a buffer of size 1074+309+2. We need to add an additional number to the max // exponent to accommodate the ones place. constexpr auto _Max_precision = 1074; constexpr auto _Buffer_size = _Max_precision + DBL_MAX_10_EXP + 3; char _Buffer[_Buffer_size]; to_chars_result _Result; auto _Extra_precision = 0; if (_Precision > _Max_precision) { _Extra_precision = _Precision - _Max_precision; _Precision = _Max_precision; } const auto _Is_negative = (_STD signbit)(_Value); if ((_STD isnan)(_Value)) { _Result.ptr = _Buffer; if (_Is_negative) { ++_Result.ptr; // pretend to skip over a '-' that to_chars would put in _Buffer[0] } _CSTD memcpy(_Result.ptr, "nan", 3); _Result.ptr += 3; } else { if (_Precision == -1) { _Result = _STD to_chars(_Buffer, _STD end(_Buffer), _Value, _Format); } else { _Result = _STD to_chars(_Buffer, _STD end(_Buffer), _Value, _Format, _Precision); } _STL_INTERNAL_CHECK(_Result.ec == errc{}); } auto _Buffer_start = _Buffer; auto _Width = static_cast(_Result.ptr - _Buffer_start); if (_Is_negative) { // Remove the '-', it will be dealt with directly _Buffer_start += 1; } else { if (_Sgn != _Fmt_sign::_Minus) { _Width += 1; } } _STL_INTERNAL_CHECK(_Exponent == 'e' || _Exponent == 'p'); if (_To_upper) { _Buffer_to_uppercase(_Buffer_start, _Result.ptr); _Exponent -= 'a' - 'A'; } const auto _Is_finite = (_STD isfinite)(_Value); auto _Append_decimal = false; auto _Exponent_start = _Result.ptr; auto _Radix_point = _Result.ptr; auto _Integral_end = _Result.ptr; auto _Zeroes_to_append = 0; auto _Separators = 0; string _Groups; if (_Is_finite) { if (_Specs._Alt || _Specs._Localized) { for (auto _It = _Buffer_start; _It < _Result.ptr; ++_It) { if (*_It == '.') { _Radix_point = _It; } else if (*_It == _Exponent) { _Exponent_start = _It; } } _Integral_end = (_STD min)(_Radix_point, _Exponent_start); if (_Specs._Alt && _Radix_point == _Result.ptr) { // TRANSITION, decimal point may be wider ++_Width; _Append_decimal = true; } if (_Specs._Localized) { _Groups = _STD use_facet>(_Locale._Get()).grouping(); _Separators = _Count_separators(static_cast(_Integral_end - _Buffer_start), _Groups); } } switch (_Format) { case chars_format::hex: case chars_format::scientific: if (_Extra_precision != 0) { // Trailing zeroes are in front of the exponent while (*--_Exponent_start != _Exponent) { } } [[fallthrough]]; case chars_format::fixed: _Zeroes_to_append = _Extra_precision; break; case chars_format::general: if (_Specs._Alt && (_Specs._Type == 'g' || _Specs._Type == 'G')) { auto _Digits = static_cast(_Exponent_start - _Buffer_start); if (!_Append_decimal) { --_Digits; } _Zeroes_to_append = _Extra_precision + _Precision - _Digits; // Leading zeroes are not significant if we used fixed point notation. if (_Exponent_start == _Result.ptr && _STD abs(_Value) < 1.0 && _Value != 0.0) { for (auto _It = _Buffer_start; _It < _Result.ptr; ++_It) { if (*_It == '0') { ++_Zeroes_to_append; } else if (*_It != '.') { break; } } } } break; default: _STL_UNREACHABLE; } } _Width += _Zeroes_to_append; const bool _Write_leading_zeroes = _Specs._Leading_zero && _Specs._Alignment == _Fmt_align::_None && _Is_finite; auto _Writer = [&](_OutputIt _Out) { _Out = _Write_sign(_STD move(_Out), _Sgn, _Is_negative); if (_Write_leading_zeroes && _Width < _Specs._Width) { _Out = _RANGES fill_n(_STD move(_Out), _Specs._Width - _Width, _CharT{'0'}); } if (_Specs._Localized) { const auto& _Facet = _STD use_facet>(_Locale._Get()); _Out = _Write_separated_integer( _Buffer_start, _Integral_end, _Groups, _Facet.thousands_sep(), _Separators, _STD move(_Out)); if (_Radix_point != _Result.ptr || _Append_decimal) { *_Out++ = _Facet.decimal_point(); _Append_decimal = false; } _Buffer_start = _Integral_end; if (_Radix_point != _Result.ptr) { ++_Buffer_start; } } _Out = _Widen_and_copy<_CharT>(_Buffer_start, _Exponent_start, _STD move(_Out)); if (_Specs._Alt && _Append_decimal) { *_Out++ = '.'; } for (; _Zeroes_to_append > 0; --_Zeroes_to_append) { *_Out++ = '0'; } return _Widen_and_copy<_CharT>(_Exponent_start, _Result.ptr, _STD move(_Out)); }; if (_Write_leading_zeroes) { return _Writer(_STD move(_Out)); } return _Write_aligned(_STD move(_Out), _Width, _Specs, _Fmt_align::_Right, _Writer); } template _NODISCARD _OutputIt _Fmt_write( _OutputIt _Out, const void* const _Value, const _Basic_format_specs<_CharT>& _Specs, _Lazy_locale) { _STL_INTERNAL_CHECK(_Specs._Type == '\0' || _Specs._Type == 'p'); _STL_INTERNAL_CHECK(_Specs._Sgn == _Fmt_sign::_None); _STL_INTERNAL_CHECK(!_Specs._Alt); _STL_INTERNAL_CHECK(_Specs._Precision == -1); _STL_INTERNAL_CHECK(!_Specs._Leading_zero); _STL_INTERNAL_CHECK(!_Specs._Localized); // Since the bit width of 0 is 0x0, special-case it instead of complicating the math even more. int _Width = 3; if (_Value != nullptr) { // Compute the bit width of the pointer (i.e. how many bits it takes to be represented). // Add 3 to the bit width so we always round up on the division. // Divide that by the amount of bits a hexit represents (log2(16) = log2(2^4) = 4). // Add 2 for the 0x prefix. _Width = 2 + (_STD bit_width(reinterpret_cast(_Value)) + 3) / 4; } return _Write_aligned(_STD move(_Out), _Width, _Specs, _Fmt_align::_Right, [=](_OutputIt _Out) { return _Fmt_write<_CharT>(_STD move(_Out), _Value); }); } template _NODISCARD _OutputIt _Fmt_write( _OutputIt _Out, const _CharT* _Value, const _Basic_format_specs<_CharT>& _Specs, _Lazy_locale _Locale) { return _Fmt_write(_STD move(_Out), basic_string_view<_CharT>{_Value}, _Specs, _Locale); } // width iterator for UTF-8 and UTF-16 template class _Measure_string_prefix_iterator_utf { private: _Grapheme_break_property_iterator<_CharT> _WrappedIter; public: _NODISCARD constexpr bool operator==(const _Measure_string_prefix_iterator_utf&) const noexcept = default; _NODISCARD constexpr bool operator==(default_sentinel_t) const noexcept { return _WrappedIter == default_sentinel; } using difference_type = ptrdiff_t; using value_type = int; constexpr _Measure_string_prefix_iterator_utf(const _CharT* _First, const _CharT* _Last) : _WrappedIter(_First, _Last) {} constexpr _Measure_string_prefix_iterator_utf() = default; _NODISCARD constexpr value_type operator*() const { return _Unicode_width_estimate(*_WrappedIter); } constexpr _Measure_string_prefix_iterator_utf& operator++() noexcept { ++_WrappedIter; return *this; } constexpr _Measure_string_prefix_iterator_utf operator++(int) noexcept { auto _Old = *this; ++*this; return _Old; } _NODISCARD constexpr const _CharT* _Position() const { return _WrappedIter._Position(); } }; class _Measure_string_prefix_iterator_legacy { private: _Fmt_codec _Codec = _Get_fmt_codec(); const char* _First = nullptr; const char* _Last = nullptr; int _Units = 0; void _Update_units() { if (_First < _Last) { _Units = _Codec._Units_in_next_character(_First, _Last); } else { _Units = -1; } } public: _NODISCARD bool operator==(default_sentinel_t) const noexcept { return _First == _Last; } _NODISCARD bool operator==(const _Measure_string_prefix_iterator_legacy& _Rhs) const noexcept { return _First == _Rhs._First && _Last == _Rhs._Last; } using difference_type = ptrdiff_t; using value_type = int; _Measure_string_prefix_iterator_legacy(const char* _First_val, const char* _Last_val) : _First(_First_val), _Last(_Last_val) { _Update_units(); } _Measure_string_prefix_iterator_legacy() = default; _Measure_string_prefix_iterator_legacy& operator++() noexcept { _First += _Units; _Update_units(); return *this; } _Measure_string_prefix_iterator_legacy operator++(int) noexcept { auto _Old = *this; ++*this; return _Old; } _NODISCARD value_type operator*() const noexcept { return _Units; } _NODISCARD const char* _Position() const noexcept { return _First; } }; template using _Measure_string_prefix_iterator = conditional_t && !_Is_execution_charset_self_synchronizing(), _Measure_string_prefix_iterator_legacy, _Measure_string_prefix_iterator_utf<_CharT>>; template _NODISCARD const _CharT* _Measure_string_prefix(const basic_string_view<_CharT> _Value, int& _Width) { // Returns a pointer past-the-end of the largest prefix of _Value that fits in _Width, or all // of _Value if _Width is negative. Updates _Width to the estimated width of that prefix. const int _Max_width = _Width; const auto _First = _Value.data(); const auto _Last = _First + _Value.size(); _Measure_string_prefix_iterator<_CharT> _Pfx_iter(_First, _Last); int _Estimated_width = 0; // the estimated width of [_First, _Pfx_iter) constexpr auto _Max_int = (numeric_limits::max)(); while (_Pfx_iter != default_sentinel) { if (_Estimated_width == _Max_width && _Max_width >= 0) { // We're at our maximum length break; } const int _Character_width = *_Pfx_iter; if (_Max_int - _Character_width < _Estimated_width) { // avoid overflow // Either _Max_width isn't set, or adding this character will exceed it. if (_Max_width < 0) { // unset; saturate width estimate and take all characters _Width = _Max_int; return _Last; } break; } _Estimated_width += _Character_width; if (_Estimated_width > _Max_width && _Max_width >= 0) { // with this character, we exceed the maximum length _Estimated_width -= _Character_width; break; } ++_Pfx_iter; } _Width = _Estimated_width; return _Pfx_iter._Position(); } #if _HAS_CXX23 _NODISCARD constexpr bool _Is_printable(char32_t _Val) { return __printable_property_data._Get_property_for_codepoint(_Val) == __printable_property_values::_Yes_value; } _NODISCARD constexpr bool _Is_grapheme_extend(char32_t _Val) { // TRANSITION, should reuse _Grapheme_Break_property_data to save space. // (Grapheme_Extend=Yes is Grapheme_Cluster_Break=Extend minus Emoji_Modifier=Yes.) return _Grapheme_Extend_property_data._Get_property_for_codepoint(_Val) == _Grapheme_Extend_property_values::_Grapheme_Extend_value; } template _NODISCARD _OutputIt _Write_escaped(_OutputIt _Out, basic_string_view<_CharT> _Value, char _Delim) { auto _First = _Value.data(); const auto _Last = _First + _Value.size(); const auto& _Codec = _Get_fmt_codec<_CharT>(); _STL_INTERNAL_CHECK(_Delim == '"' || _Delim == '\''); *_Out++ = static_cast<_CharT>(_Delim); bool _Escape_grapheme_extend = true; char _Buffer[8]; while (_First != _Last) { const auto _Ch = *_First; if (_Ch == static_cast<_CharT>('\t')) { _Out = _Fmt_write(_STD move(_Out), _STATICALLY_WIDEN(_CharT, R"(\t)")); _Escape_grapheme_extend = true; ++_First; } else if (_Ch == static_cast<_CharT>('\n')) { _Out = _Fmt_write(_STD move(_Out), _STATICALLY_WIDEN(_CharT, R"(\n)")); _Escape_grapheme_extend = true; ++_First; } else if (_Ch == static_cast<_CharT>('\r')) { _Out = _Fmt_write(_STD move(_Out), _STATICALLY_WIDEN(_CharT, R"(\r)")); _Escape_grapheme_extend = true; ++_First; } else if (_Ch == static_cast<_CharT>(_Delim)) { *_Out++ = static_cast<_CharT>('\\'); *_Out++ = static_cast<_CharT>(_Delim); _Escape_grapheme_extend = true; ++_First; } else if (_Ch == static_cast<_CharT>('\\')) { _Out = _Fmt_write(_STD move(_Out), _STATICALLY_WIDEN(_CharT, R"(\\)")); _Escape_grapheme_extend = true; ++_First; } else { char32_t _Decoded_ch; const auto [_Next, _Is_usv] = _Codec._Decode(_First, _Last, _Decoded_ch); if (_Is_usv) { const bool _Needs_escape = !_Is_printable(_Decoded_ch) || (_Escape_grapheme_extend && _Is_grapheme_extend(_Decoded_ch)); if (_Needs_escape) { _Out = _Fmt_write(_STD move(_Out), _STATICALLY_WIDEN(_CharT, R"(\u{)")); const auto [_End, _Ec] = _STD to_chars(_Buffer, _STD end(_Buffer), static_cast(_Decoded_ch), 16); _STL_INTERNAL_CHECK(_Ec == errc{}); _Out = _Widen_and_copy<_CharT>(_Buffer, _End, _STD move(_Out)); *_Out++ = static_cast<_CharT>('}'); _Escape_grapheme_extend = true; } else { _Out = _STD _Copy_unchecked(_First, _Next, _STD move(_Out)); _Escape_grapheme_extend = false; } _First = _Next; } else { for (; _First != _Next; ++_First) { _Out = _Fmt_write(_STD move(_Out), _STATICALLY_WIDEN(_CharT, R"(\x{)")); const auto [_End, _Ec] = _STD to_chars(_Buffer, _STD end(_Buffer), static_cast>(*_First), 16); _STL_INTERNAL_CHECK(_Ec == errc{}); _Out = _Widen_and_copy<_CharT>(_Buffer, _End, _STD move(_Out)); *_Out++ = static_cast<_CharT>('}'); } _Escape_grapheme_extend = true; } } } *_Out++ = static_cast<_CharT>(_Delim); return _STD move(_Out); } template _NODISCARD _OutputIt _Write_escaped( _OutputIt _Out, basic_string_view<_CharT> _Value, _Basic_format_specs<_CharT> _Specs, char _Delim) { if (_Specs._Precision < 0 && _Specs._Width <= 0) { return _Write_escaped(_STD move(_Out), _Value, _Delim); } basic_string<_CharT> _Temp; { _Fmt_iterator_buffer>, _CharT> _Buf(back_insert_iterator{_Temp}); (void) _Write_escaped(back_insert_iterator<_Fmt_buffer<_CharT>>{_Buf}, _Value, _Delim); } int _Width = _Specs._Precision; const _CharT* _Last = _Measure_string_prefix<_CharT>(_Temp, _Width); return _Write_aligned(_STD move(_Out), _Width, _Specs, _Fmt_align::_Left, [&_Temp, _Last](_OutputIt _Out) { return _Fmt_write(_STD move(_Out), basic_string_view<_CharT>{_Temp.data(), _Last}); }); } #endif // _HAS_CXX23 template _NODISCARD _OutputIt _Fmt_write( _OutputIt _Out, const basic_string_view<_CharT> _Value, const _Basic_format_specs<_CharT>& _Specs, _Lazy_locale) { _STL_INTERNAL_CHECK(_Specs._Type == '\0' || _Specs._Type == 'c' || _Specs._Type == 's' || _Specs._Type == '?'); _STL_INTERNAL_CHECK(_Specs._Sgn == _Fmt_sign::_None); _STL_INTERNAL_CHECK(!_Specs._Alt); _STL_INTERNAL_CHECK(!_Specs._Leading_zero); #if _HAS_CXX23 if (_Specs._Type == '?') { return _Write_escaped(_STD move(_Out), _Value, _Specs, '"'); } #endif // _HAS_CXX23 if (_Specs._Precision < 0 && _Specs._Width <= 0) { return _Fmt_write(_STD move(_Out), _Value); } int _Width = _Specs._Precision; const _CharT* _Last = _Measure_string_prefix(_Value, _Width); return _Write_aligned(_STD move(_Out), _Width, _Specs, _Fmt_align::_Left, [=](_OutputIt _Out) { return _Fmt_write(_STD move(_Out), basic_string_view<_CharT>{_Value.data(), _Last}); }); } // This is the visitor that's used for "simple" replacement fields. // It could be a generic lambda, but that's bad for throughput. // A simple replacement field is a replacement field that's just "{}", // without any format specs. template struct _Default_arg_formatter { using _Context = basic_format_context<_OutputIt, _CharT>; _OutputIt _Out; basic_format_args<_Context> _Args; _Lazy_locale _Loc; template _OutputIt operator()(_Ty _Val) && { return _Fmt_write<_CharT>(_STD move(_Out), _Val); } _OutputIt operator()(basic_format_arg<_Context>::handle _Handle) && { basic_format_parse_context<_CharT> _Parse_ctx({}); basic_format_context<_OutputIt, _CharT> _Format_ctx(_STD move(_Out), _Args, _Loc); _Handle.format(_Parse_ctx, _Format_ctx); return _Format_ctx.out(); } }; // Visitor used for replacement fields that contain specs template struct _Arg_formatter { using _Context = basic_format_context<_OutputIt, _CharT>; _Context* _Ctx = nullptr; _Basic_format_specs<_CharT>* _Specs = nullptr; _OutputIt operator()(basic_format_arg<_Context>::handle) { _STL_VERIFY(false, "The custom handler should be structurally unreachable for _Arg_formatter"); _STL_INTERNAL_CHECK(_Ctx); return _Ctx->out(); } template _OutputIt operator()(_Ty _Val) { _STL_INTERNAL_CHECK(_Specs); _STL_INTERNAL_CHECK(_Ctx); return _Fmt_write(_Ctx->out(), _Val, *_Specs, _Ctx->_Get_lazy_locale()); } }; // Special compile time version of _Parse_format_specs. This version is parameterized on // the type of the argument associated with the format specifier, since we don't really // care about avoiding code bloat for code that never runs at runtime, and we can't form // the erased basic_format_args structure at compile time. template consteval _ParseContext::iterator _Compile_time_parse_format_specs(_ParseContext& _Pc) { using _CharT = _ParseContext::char_type; using _Context = basic_format_context>, _CharT>; using _ArgTraits = _Format_arg_traits<_Context>; using _FormattedTypeMapping = _ArgTraits::template _Storage_type<_Ty>; // If the type is going to use a custom formatter we should just use that, // instead of trying to instantiate a custom formatter for its erased handle // type using _FormattedType = conditional_t::handle>, _Ty, _FormattedTypeMapping>; formatter<_FormattedType, _CharT> _Formatter{}; return _Formatter.parse(_Pc); } // set of format parsing actions that only checks for validity template struct _Format_checker { using _ParseContext = basic_format_parse_context<_CharT>; using _ParseFunc = _ParseContext::iterator (*)(_ParseContext&); static constexpr size_t _Num_args = sizeof...(_Args); _ParseContext _Parse_context; _ParseFunc _Parse_funcs[_Num_args > 0 ? _Num_args : 1]; consteval explicit _Format_checker(basic_string_view<_CharT> _Fmt) noexcept : _Parse_context(_Fmt, _Num_args), _Parse_funcs{&_Compile_time_parse_format_specs<_Args, _ParseContext>...} {} constexpr void _On_text(const _CharT*, const _CharT*) const noexcept {} constexpr void _On_replacement_field(size_t, const _CharT*) const noexcept {} constexpr const _CharT* _On_format_specs(const size_t _Id, const _CharT* _First, const _CharT*) { _Parse_context.advance_to(_Parse_context.begin() + (_First - _Parse_context.begin()._Unwrapped())); if (_Id < _Num_args) { auto _Iter = _Parse_funcs[_Id](_Parse_context); // TRANSITION, VSO-1451773 (workaround: named variable) return _Iter._Unwrapped(); } else { return _First; } } }; // The top level set of parsing "actions". template struct _Format_handler { using _OutputIt = back_insert_iterator<_Fmt_buffer<_CharT>>; using _Context = basic_format_context<_OutputIt, _CharT>; basic_format_parse_context<_CharT> _Parse_context; _Context _Ctx; explicit _Format_handler(_OutputIt _Out, basic_string_view<_CharT> _Str, basic_format_args<_Context> _Format_args) : _Parse_context(_Str), _Ctx(_STD move(_Out), _Format_args) {} explicit _Format_handler(_OutputIt _Out, basic_string_view<_CharT> _Str, basic_format_args<_Context> _Format_args, const _Lazy_locale& _Loc) : _Parse_context(_Str), _Ctx(_STD move(_Out), _Format_args, _Loc) {} void _On_text(const _CharT* _First, const _CharT* _Last) { _Ctx.advance_to(_RANGES _Copy_unchecked(_First, _Last, _Ctx.out()).out); } void _On_replacement_field(const size_t _Id, const _CharT*) { auto _Arg = _Get_arg(_Ctx, _Id); _Ctx.advance_to(_STD visit_format_arg( _Default_arg_formatter<_OutputIt, _CharT>{_Ctx.out(), _Ctx._Get_args(), _Ctx._Get_lazy_locale()}, _Arg)); } const _CharT* _On_format_specs(const size_t _Id, const _CharT* _First, const _CharT* _Last) { _Parse_context.advance_to(_Parse_context.begin() + (_First - &*_Parse_context.begin())); auto _Arg = _Get_arg(_Ctx, _Id); if (_Arg._Active_state == _Basic_format_arg_type::_Custom_type) { _Arg._Custom_state.format(_Parse_context, _Ctx); return _Parse_context.begin()._Unwrapped(); } _Basic_format_specs<_CharT> _Specs; _Specs_checker<_Specs_handler, _Context>> _Handler( _Specs_handler, _Context>{_Specs, _Parse_context, _Ctx}, _Arg._Active_state); _First = _Parse_format_specs(_First, _Last, _Handler); if (_First == _Last || *_First != '}') { _Throw_format_error("Missing '}' in format string."); } _Ctx.advance_to(_STD visit_format_arg( _Arg_formatter<_OutputIt, _CharT>{._Ctx = _STD addressof(_Ctx), ._Specs = _STD addressof(_Specs)}, _Arg)); return _First; } }; _FMT_P2286_END // Generic formatter definition, the deleted default constructor // makes it "disabled" as per N4950 [format.formatter.spec]/5 _EXPORT_STD template struct formatter { formatter() = delete; formatter(const formatter&) = delete; formatter operator=(const formatter&) = delete; }; _FMT_P2286_BEGIN template struct _Formatter_base { using _Pc = basic_format_parse_context<_CharT>; constexpr _Pc::iterator parse(_Pc& _ParseCtx) { _Specs_checker<_Dynamic_specs_handler<_Pc>> _Handler(_Dynamic_specs_handler<_Pc>{_Specs, _ParseCtx}, _ArgType); const auto _It = _Parse_format_specs(_ParseCtx._Unchecked_begin(), _ParseCtx._Unchecked_end(), _Handler); if (_It != _ParseCtx._Unchecked_end() && *_It != '}') { _Throw_format_error("Missing '}' in format string."); } return _ParseCtx.begin() + (_It - _ParseCtx._Unchecked_begin()); } template _FormatContext::iterator format(const _Ty& _Val, _FormatContext& _FormatCtx) const { _Dynamic_format_specs<_CharT> _Format_specs = _Specs; if (_Specs._Dynamic_width_index >= 0) { _Format_specs._Width = _Get_dynamic_specs<_Width_checker>(_FormatCtx.arg(static_cast(_Specs._Dynamic_width_index))); } if (_Specs._Dynamic_precision_index >= 0) { _Format_specs._Precision = _Get_dynamic_specs<_Precision_checker>( _FormatCtx.arg(static_cast(_Specs._Dynamic_precision_index))); } return _STD visit_format_arg( _Arg_formatter{ ._Ctx = _STD addressof(_FormatCtx), ._Specs = _STD addressof(_Format_specs)}, basic_format_arg<_FormatContext>::_Make_from(_Val)); } private: _Dynamic_format_specs<_CharT> _Specs; }; _FMT_P2286_END #define _FORMAT_SPECIALIZE_FOR(_Type, _ArgType) \ template <_Format_supported_charT _CharT> \ struct formatter<_Type, _CharT> : _Formatter_base<_Type, _CharT, _ArgType> {} _FORMAT_SPECIALIZE_FOR(int, _Basic_format_arg_type::_Int_type); _FORMAT_SPECIALIZE_FOR(unsigned int, _Basic_format_arg_type::_UInt_type); _FORMAT_SPECIALIZE_FOR(long long, _Basic_format_arg_type::_Long_long_type); _FORMAT_SPECIALIZE_FOR(unsigned long long, _Basic_format_arg_type::_ULong_long_type); _FORMAT_SPECIALIZE_FOR(bool, _Basic_format_arg_type::_Bool_type); _FORMAT_SPECIALIZE_FOR(float, _Basic_format_arg_type::_Float_type); _FORMAT_SPECIALIZE_FOR(double, _Basic_format_arg_type::_Double_type); _FORMAT_SPECIALIZE_FOR(long double, _Basic_format_arg_type::_Long_double_type); _FORMAT_SPECIALIZE_FOR(nullptr_t, _Basic_format_arg_type::_Pointer_type); _FORMAT_SPECIALIZE_FOR(void*, _Basic_format_arg_type::_Pointer_type); _FORMAT_SPECIALIZE_FOR(const void*, _Basic_format_arg_type::_Pointer_type); _FORMAT_SPECIALIZE_FOR(short, _Basic_format_arg_type::_Int_type); _FORMAT_SPECIALIZE_FOR(unsigned short, _Basic_format_arg_type::_UInt_type); _FORMAT_SPECIALIZE_FOR(long, _Basic_format_arg_type::_Int_type); _FORMAT_SPECIALIZE_FOR(unsigned long, _Basic_format_arg_type::_UInt_type); _FORMAT_SPECIALIZE_FOR(char, _Basic_format_arg_type::_Char_type); _FORMAT_SPECIALIZE_FOR(signed char, _Basic_format_arg_type::_Int_type); _FORMAT_SPECIALIZE_FOR(unsigned char, _Basic_format_arg_type::_UInt_type); #undef _FORMAT_SPECIALIZE_FOR // not using the macro because we'd like to avoid the formatter specialization template <> struct formatter : _Formatter_base {}; // We could use the macro for these specializations, but it's confusing to refer to symbols that are defined // inside the macro in the macro's "call". template <_Format_supported_charT _CharT> struct formatter<_CharT*, _CharT> : _Formatter_base<_CharT*, _CharT, _Basic_format_arg_type::_CString_type> {}; template <_Format_supported_charT _CharT> struct formatter : _Formatter_base {}; template <_Format_supported_charT _CharT, size_t _Nx> struct formatter<_CharT[_Nx], _CharT> : _Formatter_base<_CharT[_Nx], _CharT, _Basic_format_arg_type::_CString_type> {}; template <_Format_supported_charT _CharT, class _Traits, class _Allocator> struct formatter, _CharT> : _Formatter_base, _CharT, _Basic_format_arg_type::_String_type> {}; template <_Format_supported_charT _CharT, class _Traits> struct formatter, _CharT> : _Formatter_base, _CharT, _Basic_format_arg_type::_String_type> {}; _EXPORT_STD template struct basic_format_string { public: template requires convertible_to> consteval basic_format_string(const _Ty& _Str_val) : _Str(_Str_val) { if (_Is_execution_charset_self_synchronizing()) { _Parse_format_string(_Str, _Format_checker<_CharT, remove_cvref_t<_Args>...>{_Str}); } } _NODISCARD constexpr basic_string_view<_CharT> get() const noexcept { return _Str; } private: basic_string_view<_CharT> _Str; }; _EXPORT_STD template using format_string = basic_format_string...>; _EXPORT_STD template using wformat_string = basic_format_string...>; _EXPORT_STD using format_args = basic_format_args; _EXPORT_STD using wformat_args = basic_format_args; _EXPORT_STD template _NODISCARD auto make_format_args(_Args&&... _Vals) { static_assert((_Formattable_with, _Context> && ...), "Cannot format an argument. To make type T formattable, provide a formatter specialization. " "See N4950 [format.arg.store]/2 and [formatter.requirements]."); return _Format_arg_store<_Context, _Args...>{_Vals...}; } _EXPORT_STD template _NODISCARD auto make_wformat_args(_Args&&... _Vals) { static_assert((_Formattable_with, wformat_context> && ...), "Cannot format an argument. To make type T formattable, provide a formatter specialization. " "See N4950 [format.arg.store]/2 and [formatter.requirements]."); return _Format_arg_store{_Vals...}; } _FMT_P2286_BEGIN _EXPORT_STD template _OutputIt> _OutputIt vformat_to(_OutputIt _Out, const string_view _Fmt, const format_args _Args) { // Make `_Parse_format_string` type-dependent to defer instantiation: using _Dependent_char = decltype((void) _Out, char{}); if constexpr (is_same_v<_OutputIt, _Fmt_it>) { _Format_handler<_Dependent_char> _Handler(_Out, _Fmt, _Args); _Parse_format_string(_Fmt, _Handler); return _Out; } else { _Fmt_iterator_buffer<_OutputIt, char> _Buf(_STD move(_Out)); _Format_handler<_Dependent_char> _Handler(_Fmt_it{_Buf}, _Fmt, _Args); _Parse_format_string(_Fmt, _Handler); return _Buf._Out(); } } _EXPORT_STD template _OutputIt> _OutputIt vformat_to(_OutputIt _Out, const wstring_view _Fmt, const wformat_args _Args) { // Make `_Parse_format_string` type-dependent to defer instantiation: using _Dependent_char = decltype((void) _Out, wchar_t{}); if constexpr (is_same_v<_OutputIt, _Fmt_wit>) { _Format_handler<_Dependent_char> _Handler(_Out, _Fmt, _Args); _Parse_format_string(_Fmt, _Handler); return _Out; } else { _Fmt_iterator_buffer<_OutputIt, wchar_t> _Buf(_STD move(_Out)); _Format_handler<_Dependent_char> _Handler(_Fmt_wit{_Buf}, _Fmt, _Args); _Parse_format_string(_Fmt, _Handler); return _Buf._Out(); } } _EXPORT_STD template _OutputIt> _OutputIt vformat_to(_OutputIt _Out, const locale& _Loc, const string_view _Fmt, const format_args _Args) { // Make `_Parse_format_string` type-dependent to defer instantiation: using _Dependent_char = decltype((void) _Out, char{}); if constexpr (is_same_v<_OutputIt, _Fmt_it>) { _Format_handler<_Dependent_char> _Handler(_Out, _Fmt, _Args, _Lazy_locale{_Loc}); _Parse_format_string(_Fmt, _Handler); return _Out; } else { _Fmt_iterator_buffer<_OutputIt, char> _Buf(_STD move(_Out)); _Format_handler<_Dependent_char> _Handler(_Fmt_it{_Buf}, _Fmt, _Args, _Lazy_locale{_Loc}); _Parse_format_string(_Fmt, _Handler); return _Buf._Out(); } } _EXPORT_STD template _OutputIt> _OutputIt vformat_to(_OutputIt _Out, const locale& _Loc, const wstring_view _Fmt, const wformat_args _Args) { // Make `_Parse_format_string` type-dependent to defer instantiation: using _Dependent_char = decltype((void) _Out, wchar_t{}); if constexpr (is_same_v<_OutputIt, _Fmt_wit>) { _Format_handler<_Dependent_char> _Handler(_Out, _Fmt, _Args, _Lazy_locale{_Loc}); _Parse_format_string(_Fmt, _Handler); return _Out; } else { _Fmt_iterator_buffer<_OutputIt, wchar_t> _Buf(_STD move(_Out)); _Format_handler<_Dependent_char> _Handler(_Fmt_wit{_Buf}, _Fmt, _Args, _Lazy_locale{_Loc}); _Parse_format_string(_Fmt, _Handler); return _Buf._Out(); } } _EXPORT_STD template _OutputIt, class... _Types> _OutputIt format_to(_OutputIt _Out, const format_string<_Types...> _Fmt, _Types&&... _Args) { return _STD vformat_to(_STD move(_Out), _Fmt.get(), _STD make_format_args(_Args...)); } _EXPORT_STD template _OutputIt, class... _Types> _OutputIt format_to(_OutputIt _Out, const wformat_string<_Types...> _Fmt, _Types&&... _Args) { return _STD vformat_to(_STD move(_Out), _Fmt.get(), _STD make_wformat_args(_Args...)); } _EXPORT_STD template _OutputIt, class... _Types> _OutputIt format_to(_OutputIt _Out, const locale& _Loc, const format_string<_Types...> _Fmt, _Types&&... _Args) { return _STD vformat_to(_STD move(_Out), _Loc, _Fmt.get(), _STD make_format_args(_Args...)); } _EXPORT_STD template _OutputIt, class... _Types> _OutputIt format_to(_OutputIt _Out, const locale& _Loc, const wformat_string<_Types...> _Fmt, _Types&&... _Args) { return _STD vformat_to(_STD move(_Out), _Loc, _Fmt.get(), _STD make_wformat_args(_Args...)); } _EXPORT_STD template // improves throughput, see GH-2329 _NODISCARD string vformat(const string_view _Fmt, const format_args _Args) { string _Str; _Str.reserve(_Fmt.size() + _Args._Estimate_required_capacity()); _STD vformat_to(back_insert_iterator{_Str}, _Fmt, _Args); return _Str; } _EXPORT_STD template // improves throughput, see GH-2329 _NODISCARD wstring vformat(const wstring_view _Fmt, const wformat_args _Args) { wstring _Str; _Str.reserve(_Fmt.size() + _Args._Estimate_required_capacity()); _STD vformat_to(back_insert_iterator{_Str}, _Fmt, _Args); return _Str; } _EXPORT_STD template // improves throughput, see GH-2329 _NODISCARD string vformat(const locale& _Loc, const string_view _Fmt, const format_args _Args) { string _Str; _Str.reserve(_Fmt.size() + _Args._Estimate_required_capacity()); _STD vformat_to(back_insert_iterator{_Str}, _Loc, _Fmt, _Args); return _Str; } _EXPORT_STD template // improves throughput, see GH-2329 _NODISCARD wstring vformat(const locale& _Loc, const wstring_view _Fmt, const wformat_args _Args) { wstring _Str; _Str.reserve(_Fmt.size() + _Args._Estimate_required_capacity()); _STD vformat_to(back_insert_iterator{_Str}, _Loc, _Fmt, _Args); return _Str; } _EXPORT_STD template _NODISCARD string format(const format_string<_Types...> _Fmt, _Types&&... _Args) { return _STD vformat(_Fmt.get(), _STD make_format_args(_Args...)); } _EXPORT_STD template _NODISCARD wstring format(const wformat_string<_Types...> _Fmt, _Types&&... _Args) { return _STD vformat(_Fmt.get(), _STD make_wformat_args(_Args...)); } _EXPORT_STD template _NODISCARD string format(const locale& _Loc, const format_string<_Types...> _Fmt, _Types&&... _Args) { return _STD vformat(_Loc, _Fmt.get(), _STD make_format_args(_Args...)); } _EXPORT_STD template _NODISCARD wstring format(const locale& _Loc, const wformat_string<_Types...> _Fmt, _Types&&... _Args) { return _STD vformat(_Loc, _Fmt.get(), _STD make_wformat_args(_Args...)); } _FMT_P2286_END _EXPORT_STD template struct format_to_n_result { _OutputIt out; iter_difference_t<_OutputIt> size; }; _FMT_P2286_BEGIN _EXPORT_STD template _OutputIt, class... _Types> format_to_n_result<_OutputIt> format_to_n( _OutputIt _Out, const iter_difference_t<_OutputIt> _Max, const format_string<_Types...> _Fmt, _Types&&... _Args) { _Fmt_iterator_buffer<_OutputIt, char, _Fmt_fixed_buffer_traits> _Buf(_STD move(_Out), _Max); _STD vformat_to(_Fmt_it{_Buf}, _Fmt.get(), _STD make_format_args(_Args...)); return {.out = _Buf._Out(), .size = _Buf._Count()}; } _EXPORT_STD template _OutputIt, class... _Types> format_to_n_result<_OutputIt> format_to_n( _OutputIt _Out, const iter_difference_t<_OutputIt> _Max, const wformat_string<_Types...> _Fmt, _Types&&... _Args) { _Fmt_iterator_buffer<_OutputIt, wchar_t, _Fmt_fixed_buffer_traits> _Buf(_STD move(_Out), _Max); _STD vformat_to(_Fmt_wit{_Buf}, _Fmt.get(), _STD make_wformat_args(_Args...)); return {.out = _Buf._Out(), .size = _Buf._Count()}; } _EXPORT_STD template _OutputIt, class... _Types> format_to_n_result<_OutputIt> format_to_n(_OutputIt _Out, const iter_difference_t<_OutputIt> _Max, const locale& _Loc, const format_string<_Types...> _Fmt, _Types&&... _Args) { _Fmt_iterator_buffer<_OutputIt, char, _Fmt_fixed_buffer_traits> _Buf(_STD move(_Out), _Max); _STD vformat_to(_Fmt_it{_Buf}, _Loc, _Fmt.get(), _STD make_format_args(_Args...)); return {.out = _Buf._Out(), .size = _Buf._Count()}; } _EXPORT_STD template _OutputIt, class... _Types> format_to_n_result<_OutputIt> format_to_n(_OutputIt _Out, const iter_difference_t<_OutputIt> _Max, const locale& _Loc, const wformat_string<_Types...> _Fmt, _Types&&... _Args) { _Fmt_iterator_buffer<_OutputIt, wchar_t, _Fmt_fixed_buffer_traits> _Buf(_STD move(_Out), _Max); _STD vformat_to(_Fmt_wit{_Buf}, _Loc, _Fmt.get(), _STD make_wformat_args(_Args...)); return {.out = _Buf._Out(), .size = _Buf._Count()}; } _EXPORT_STD template _NODISCARD size_t formatted_size(const format_string<_Types...> _Fmt, _Types&&... _Args) { _Fmt_counting_buffer _Buf; _STD vformat_to(_Fmt_it{_Buf}, _Fmt.get(), _STD make_format_args(_Args...)); return _Buf._Count(); } _EXPORT_STD template _NODISCARD size_t formatted_size(const wformat_string<_Types...> _Fmt, _Types&&... _Args) { _Fmt_counting_buffer _Buf; _STD vformat_to(_Fmt_wit{_Buf}, _Fmt.get(), _STD make_wformat_args(_Args...)); return _Buf._Count(); } _EXPORT_STD template _NODISCARD size_t formatted_size(const locale& _Loc, const format_string<_Types...> _Fmt, _Types&&... _Args) { _Fmt_counting_buffer _Buf; _STD vformat_to(_Fmt_it{_Buf}, _Loc, _Fmt.get(), _STD make_format_args(_Args...)); return _Buf._Count(); } _EXPORT_STD template _NODISCARD size_t formatted_size(const locale& _Loc, const wformat_string<_Types...> _Fmt, _Types&&... _Args) { _Fmt_counting_buffer _Buf; _STD vformat_to(_Fmt_wit{_Buf}, _Loc, _Fmt.get(), _STD make_wformat_args(_Args...)); return _Buf._Count(); } _FMT_P2286_END #if _HAS_CXX23 enum class _Add_newline : bool { _Nope, _Yes }; _NODISCARD inline string _Unescape_braces(const _Add_newline _Add_nl, const string_view _Old_str) { string _Unescaped_str; if (_Old_str.empty()) { if (_Add_nl == _Add_newline::_Yes) { _Unescaped_str.push_back('\n'); } return _Unescaped_str; } size_t _Unescaped_str_expected_size = _Old_str.size(); if (_Add_nl == _Add_newline::_Yes) { ++_Unescaped_str_expected_size; } _Unescaped_str.resize_and_overwrite(_Unescaped_str_expected_size, [_Add_nl, _Old_str](char* _Dest_ptr, size_t) { char _Prev_char = _Old_str.front(); size_t _Num_chars_written = 1; *_Dest_ptr++ = _Prev_char; for (const auto _Curr_char : _Old_str.substr(1)) { if ((_Curr_char == '{' && _Prev_char == '{') || (_Curr_char == '}' && _Prev_char == '}')) { _Prev_char = '\0'; } else { *_Dest_ptr++ = _Curr_char; ++_Num_chars_written; _Prev_char = _Curr_char; } } if (_Add_nl == _Add_newline::_Yes) { *_Dest_ptr = '\n'; ++_Num_chars_written; } return _Num_chars_written; }); return _Unescaped_str; } template struct _Fill_align_and_width_specs { // used by thread::id and stacktrace_entry formatters int _Width = -1; int _Dynamic_width_index = -1; _Fmt_align _Alignment = _Fmt_align::_None; uint8_t _Fill_length = 1; // At most one codepoint (so one char32_t or four utf-8 char8_t). _CharT _Fill[4 / sizeof(_CharT)] = {' '}; }; template class _Fill_align_and_width_specs_setter { public: constexpr explicit _Fill_align_and_width_specs_setter( _Fill_align_and_width_specs<_CharT>& _Specs_, basic_format_parse_context<_CharT>& _Parse_ctx_) : _Specs(_Specs_), _Parse_ctx(_Parse_ctx_) {} constexpr void _On_align(const _Fmt_align _Aln) { _Specs._Alignment = _Aln; } constexpr void _On_fill(const basic_string_view<_CharT> _Sv) { if (_Sv.size() > _STD size(_Specs._Fill)) { _Throw_format_error("Invalid fill (too long)."); } const auto _Pos = _STD _Copy_unchecked(_Sv._Unchecked_begin(), _Sv._Unchecked_end(), _Specs._Fill); _STD fill(_Pos, _STD end(_Specs._Fill), _CharT{}); _Specs._Fill_length = static_cast(_Sv.size()); } constexpr void _On_width(const int _Width) { _Specs._Width = _Width; } constexpr void _On_dynamic_width(const size_t _Arg_id) { _Parse_ctx.check_arg_id(_Arg_id); _Specs._Dynamic_width_index = _Verify_dynamic_arg_index_in_range(_Arg_id); } constexpr void _On_dynamic_width(_Auto_id_tag) { _Specs._Dynamic_width_index = _Verify_dynamic_arg_index_in_range(_Parse_ctx.next_arg_id()); } private: _Fill_align_and_width_specs<_CharT>& _Specs; basic_format_parse_context<_CharT>& _Parse_ctx; _NODISCARD static constexpr int _Verify_dynamic_arg_index_in_range(const size_t _Idx) { if (!_STD in_range(_Idx)) { _Throw_format_error("Dynamic width index is too large."); } return static_cast(_Idx); } }; template _NODISCARD constexpr const _CharT* _Parse_fill_align_and_width_specs( const _CharT* _Begin, const _CharT* _End, _Callbacks_type&& _Callbacks) { if (_Begin == _End || *_Begin == '}') { return _Begin; } _Begin = _Parse_align(_Begin, _End, _Callbacks); if (_Begin == _End) { return _Begin; } return _Parse_width(_Begin, _End, _Callbacks); } template struct _Fill_align_and_width_formatter { public: _NODISCARD constexpr auto parse(basic_format_parse_context<_CharT>& _Parse_ctx) { _Fill_align_and_width_specs_setter<_CharT> _Callback{_Specs, _Parse_ctx}; const auto _It = _Parse_fill_align_and_width_specs(_Parse_ctx._Unchecked_begin(), _Parse_ctx._Unchecked_end(), _Callback); if (_It != _Parse_ctx._Unchecked_end() && *_It != '}') { _Throw_format_error("Missing '}' in format string."); } return _Parse_ctx.begin() + (_It - _Parse_ctx._Unchecked_begin()); } template _NODISCARD constexpr auto _Format( _FormatContext& _Format_ctx, const int _Width, _Fmt_align _Default_align, _Func&& _Fn) const { _Fill_align_and_width_specs _Format_specs = _Specs; if (_Specs._Dynamic_width_index >= 0) { _Format_specs._Width = _Get_dynamic_specs<_Width_checker>(_Format_ctx.arg(static_cast(_Specs._Dynamic_width_index))); } return _Write_aligned(_Format_ctx.out(), _Width, _Format_specs, _Default_align, _STD forward<_Func>(_Fn)); } private: _Fill_align_and_width_specs<_CharT> _Specs; }; #endif // _HAS_CXX23 #undef _FMT_P2286_END #undef _FMT_P2286_BEGIN _STD_END #pragma pop_macro("new") _STL_RESTORE_CLANG_WARNINGS #pragma warning(pop) #pragma pack(pop) #endif // defined(__cpp_lib_concepts) #endif // _STL_COMPILER_PREPROCESSOR #endif // _FORMAT_