diff --git a/NOTICE.txt b/NOTICE.txt index 0e92d4512ee..152fcefc111 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -137,3 +137,33 @@ In addition, certain files include the notices provided below. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// + +---------------------- + +// 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. diff --git a/docs/cgmanifest.json b/docs/cgmanifest.json index 6e1b6634269..44596388928 100644 --- a/docs/cgmanifest.json +++ b/docs/cgmanifest.json @@ -18,6 +18,15 @@ } } }, + { + "component": { + "type": "git", + "git": { + "repositoryUrl": "https://github.com/fmtlib/fmt", + "commitHash": "273d8865e31659f69528623754c1742b2819ad26" + } + } + }, { "component": { "type": "git", diff --git a/stl/CMakeLists.txt b/stl/CMakeLists.txt index 0d460ef834e..751c6ad78aa 100644 --- a/stl/CMakeLists.txt +++ b/stl/CMakeLists.txt @@ -141,6 +141,7 @@ set(HEADERS ${CMAKE_CURRENT_LIST_DIR}/inc/experimental/unordered_set ${CMAKE_CURRENT_LIST_DIR}/inc/experimental/vector ${CMAKE_CURRENT_LIST_DIR}/inc/filesystem + ${CMAKE_CURRENT_LIST_DIR}/inc/format ${CMAKE_CURRENT_LIST_DIR}/inc/forward_list ${CMAKE_CURRENT_LIST_DIR}/inc/fstream ${CMAKE_CURRENT_LIST_DIR}/inc/functional diff --git a/stl/inc/__msvc_all_public_headers.hpp b/stl/inc/__msvc_all_public_headers.hpp index 9889036b41b..a6f4d4cfa2f 100644 --- a/stl/inc/__msvc_all_public_headers.hpp +++ b/stl/inc/__msvc_all_public_headers.hpp @@ -87,6 +87,7 @@ #include #include #include +#include #include #include #include diff --git a/stl/inc/algorithm b/stl/inc/algorithm index a748bbb800d..75a4b7df914 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -383,39 +383,6 @@ namespace ranges { }; inline constexpr _For_each_n_fn for_each_n{_Not_quite_object::_Construct_tag{}}; - - // VARIABLE ranges::find - class _Find_fn : private _Not_quite_object { - public: - using _Not_quite_object::_Not_quite_object; - - // clang-format off - template _Se, class _Ty, class _Pj = identity> - requires indirect_binary_predicate, const _Ty*> - _NODISCARD constexpr _It operator()(_It _First, _Se _Last, const _Ty& _Val, _Pj _Proj = {}) const { - _Adl_verify_range(_First, _Last); - auto _UResult = _RANGES _Find_unchecked( - _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _Val, _Pass_fn(_Proj)); - - _Seek_wrapped(_First, _STD move(_UResult)); - return _First; - } - - template - requires indirect_binary_predicate, _Pj>, const _Ty*> - _NODISCARD constexpr borrowed_iterator_t<_Rng> operator()( - _Rng&& _Range, const _Ty& _Val, _Pj _Proj = {}) const { - auto _First = _RANGES begin(_Range); - auto _UResult = - _RANGES _Find_unchecked(_Get_unwrapped(_STD move(_First)), _Uend(_Range), _Val, _Pass_fn(_Proj)); - - _Seek_wrapped(_First, _STD move(_UResult)); - return _First; - } - // clang-format on - }; - - inline constexpr _Find_fn find{_Not_quite_object::_Construct_tag{}}; } // namespace ranges #endif // __cpp_lib_concepts @@ -1366,60 +1333,6 @@ namespace ranges { inline constexpr _None_of_fn none_of{_Not_quite_object::_Construct_tag{}}; - // ALIAS TEMPLATE copy_result - template - using copy_result = in_out_result<_In, _Out>; - - // VARIABLE ranges::copy - // clang-format off - template _Se, weakly_incrementable _Out> - requires indirectly_copyable<_It, _Out> - _NODISCARD constexpr copy_result<_It, _Out> _Copy_unchecked(_It _First, _Se _Last, _Out _Result) { - if constexpr (_Ptr_copy_cat<_It, _Out>::_Trivially_copyable && sized_sentinel_for<_Se, _It>) { - if (!_STD is_constant_evaluated()) { - auto _Final = _RANGES next(_First, _STD move(_Last)); - _Result = _Copy_memmove(_STD move(_First), _Final, _STD move(_Result)); - return {_STD move(_Final), _STD move(_Result)}; - } - } - - for (; _First != _Last; ++_First, (void) ++_Result) { - *_Result = *_First; - } - - return {_STD move(_First), _STD move(_Result)}; - } - // clang-format on - - class _Copy_fn : private _Not_quite_object { - public: - using _Not_quite_object::_Not_quite_object; - - // clang-format off - template _Se, weakly_incrementable _Out> - requires indirectly_copyable<_It, _Out> - constexpr copy_result<_It, _Out> operator()(_It _First, _Se _Last, _Out _Result) const { - _Adl_verify_range(_First, _Last); - auto _UResult = _RANGES _Copy_unchecked( - _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _STD move(_Result)); - _Seek_wrapped(_First, _STD move(_UResult.in)); - return {_STD move(_First), _STD move(_UResult.out)}; - } - - template - requires indirectly_copyable, _Out> - constexpr copy_result, _Out> operator()(_Rng&& _Range, _Out _Result) const { - auto _First = _RANGES begin(_Range); - auto _UResult = - _RANGES _Copy_unchecked(_Get_unwrapped(_STD move(_First)), _Uend(_Range), _STD move(_Result)); - _Seek_wrapped(_First, _STD move(_UResult.in)); - return {_STD move(_First), _STD move(_UResult.out)}; - } - // clang-format on - }; - - inline constexpr _Copy_fn copy{_Not_quite_object::_Construct_tag{}}; - // ALIAS TEMPLATE copy_n_result template using copy_n_result = in_out_result<_In, _Out>; @@ -3520,42 +3433,6 @@ namespace ranges { inline constexpr _Fill_fn fill{_Not_quite_object::_Construct_tag{}}; - // VARIABLE ranges::fill_n - class _Fill_n_fn : private _Not_quite_object { - public: - using _Not_quite_object::_Not_quite_object; - - template _It> - constexpr _It operator()(_It _First, iter_difference_t<_It> _Count, const _Ty& _Value) const { - if (_Count > 0) { - auto _UFirst = _Get_unwrapped_n(_STD move(_First), _Count); - if (!_STD is_constant_evaluated()) { - if constexpr (_Fill_memset_is_safe) { - _Fill_memset(_UFirst, _Value, static_cast(_Count)); - _Seek_wrapped(_First, _UFirst + _Count); // no need to move since _UFirst is a pointer - return _First; - } else if constexpr (_Fill_zero_memset_is_safe) { - if (_Is_all_bits_zero(_Value)) { - _Fill_zero_memset(_UFirst, static_cast(_Count)); - _Seek_wrapped(_First, _UFirst + _Count); // no need to move since _UFirst is a pointer - return _First; - } - } - } - - for (; _Count > 0; ++_UFirst, (void) --_Count) { - *_UFirst = _Value; - } - - _Seek_wrapped(_First, _STD move(_UFirst)); - } - - return _First; - } - }; - - inline constexpr _Fill_n_fn fill_n{_Not_quite_object::_Construct_tag{}}; - // VARIABLE ranges::generate class _Generate_fn : private _Not_quite_object { public: diff --git a/stl/inc/bit b/stl/inc/bit index 56e6cdcb37f..d98eb56c550 100644 --- a/stl/inc/bit +++ b/stl/inc/bit @@ -113,7 +113,7 @@ _NODISCARD constexpr int _Popcount_fallback(_Ty _Val) noexcept { return static_cast(_Val & static_cast<_Ty>(_Digits + _Digits - 1)); } -#if defined(_M_IX86) || defined(_M_X64) +#if defined(_M_IX86) || (defined(_M_X64) && !defined(_M_ARM64EC)) extern "C" { extern int __isa_available; @@ -207,7 +207,7 @@ _NODISCARD int _Checked_x86_x64_popcount(const _Ty _Val) noexcept { #endif // _M_IX86 } } -#endif // defined(_M_IX86) || defined(_M_X64) +#endif // defined(_M_IX86) || (defined(_M_X64) && !defined(_M_ARM64EC)) #if defined(_M_ARM) || defined(_M_ARM64) @@ -255,17 +255,17 @@ _NODISCARD int _Checked_arm_arm64_countl_zero(const _Ty _Val) noexcept { template , int> _Enabled> _NODISCARD constexpr int countl_zero(const _Ty _Val) noexcept { - if (_STD is_constant_evaluated()) { - return _Countl_zero_fallback(_Val); - } else { -#if defined(_M_IX86) || defined(_M_X64) +#if defined(_M_IX86) || (defined(_M_X64) && !defined(_M_ARM64EC)) + if (!_STD is_constant_evaluated()) { return _Checked_x86_x64_countl_zero(_Val); + } #elif defined(_M_ARM) || defined(_M_ARM64) + if (!_STD is_constant_evaluated()) { return _Checked_arm_arm64_countl_zero(_Val); -#else -#error Unsupported Hardware -#endif } +#endif // defined(_M_ARM) || defined(_M_ARM64) + + return _Countl_zero_fallback(_Val); } template , int> = 0> @@ -285,11 +285,11 @@ _NODISCARD constexpr int countr_one(const _Ty _Val) noexcept { template , int> _Enabled = 0> _NODISCARD constexpr int popcount(const _Ty _Val) noexcept { -#if defined(_M_IX86) || defined(_M_X64) +#if defined(_M_IX86) || (defined(_M_X64) && !defined(_M_ARM64EC)) if (!_STD is_constant_evaluated()) { return _Checked_x86_x64_popcount(_Val); } -#endif // defined(_M_IX86) || defined(_M_X64) +#endif // defined(_M_IX86) || (defined(_M_X64) && !defined(_M_ARM64EC)) return _Popcount_fallback(_Val); } diff --git a/stl/inc/chrono b/stl/inc/chrono index 2c52c87919c..94027aadf50 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -28,6 +28,11 @@ #include #include #include + +#ifdef __cpp_lib_concepts +#include +#include +#endif // defined(__cpp_lib_concepts) #endif // _HAS_CXX20 #pragma pack(push, _CRT_PACKING) @@ -5171,6 +5176,183 @@ namespace chrono { basic_istream<_CharT, _Traits>& _Is, const _Time_parse_iomanip<_CharT, _Traits, _Alloc, _Parsable>& _Tpi) { return _CHRONO from_stream(_Is, _Tpi._Fmt.c_str(), _Tpi._Tp, _Tpi._Abbrev, _Tpi._Offset); } + +#ifdef __cpp_lib_concepts + // [time.format] + + // clang-format off + template + concept _Chrono_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, basic_string_view<_CharT> _Sv, _Fmt_align _Aln) { + { _At._On_conversion_spec(_CharT{}, _CharT{}) } -> same_as; + { _At._On_lit_char(_CharT{}) } -> same_as; + }; + // clang-format on + + template + struct _Chrono_specs { + _CharT _Lit_char = _CharT{0}; // any character other than '{', '}', or '%' + char _Modifier = '\0'; // either 'E' or 'O' + char _Type = '\0'; + }; + + template + struct _Chrono_format_specs { + int _Width = 0; + int _Precision = -1; + int _Dynamic_width_index = -1; + int _Dynamic_precision_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)] = {_CharT{' '}}; + // recursive definition in grammar, so could have any number of these with literal chars + vector<_Chrono_specs<_CharT>> _Chrono_specs_list; + }; + + // Model of _Chrono_parse_spec_callbacks that fills a _Chrono_format_specs with the parsed data + template + class _Chrono_specs_setter { + public: + constexpr explicit _Chrono_specs_setter(_Chrono_format_specs<_CharT>& _Specs_) : _Specs(_Specs_) {} + + // same as _Specs_setter + constexpr void _On_align(_Fmt_align _Aln) { + _Specs._Alignment = _Aln; + } + + // same as _Specs_setter + constexpr void _On_fill(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(int _Width) { + _Specs._Width = _Width; + } + + constexpr void _On_precision(int _Prec) { + _Specs._Precision = _Prec; + } + + constexpr void _On_conversion_spec(_CharT _Modifier, _CharT _Type) { + // NOTE: same performance note from _Basic_format_specs also applies here + const char _Char_mod = static_cast(_Modifier); + const char _Char_type = static_cast(_Type); + if (_Char_mod != '\0' && _Char_mod != 'E' && _Char_mod != 'O') { + _THROW(format_error("Invalid modifier specification.")); + } + + if (_Type < 0 || _Type > (numeric_limits::max)()) { + _THROW(format_error("Invalid type specification.")); + } + + _Chrono_specs<_CharT> _Conv_spec{._Modifier = _Char_mod, ._Type = _Char_type}; + _Specs._Chrono_specs_list.push_back(_Conv_spec); + } + + constexpr void _On_lit_char(_CharT _Lit_ch) { + _Chrono_specs<_CharT> _Lit_char_spec{._Lit_char = _Lit_ch}; + _Specs._Chrono_specs_list.push_back(_Lit_char_spec); + } + + protected: + _Chrono_format_specs<_CharT>& _Specs; + }; + + template _Callbacks_type> + _NODISCARD constexpr const _CharT* _Parse_conversion_specs( + const _CharT* _Begin, const _CharT* _End, _Callbacks_type&& _Callbacks) { + _STL_INTERNAL_CHECK(*_Begin == '%'); + ++_Begin; + if (_Begin == _End || *_Begin == '}') { + _THROW(format_error("Invalid format string.")); + } + + _CharT _Mod = '\0'; + _CharT _Ch = *_Begin; + + if (_Ch == 'E' || _Ch == 'O') { // includes modifier + _Mod = _Ch; + ++_Begin; + if (_Begin == _End || *_Begin == '}') { + _THROW(format_error("Invalid format string - missing type after modifier.")); + } + } + + _CharT _Type = *_Begin; + _Callbacks._On_conversion_spec(_Mod, _Type); + + return ++_Begin; + } + + template _Callbacks_type> + _NODISCARD constexpr const _CharT* _Parse_chrono_format_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; + } + + _Begin = _Parse_width(_Begin, _End, _Callbacks); + if (_Begin == _End) { + return _Begin; + } + + if (*_Begin == '.') { + _Begin = _Parse_precision(_Begin, _End, _Callbacks); + if (_Begin == _End) { + return _Begin; + } + } + + // chrono-spec + while (_Begin != _End && *_Begin != '}') { + if (*_Begin == '%') { // conversion-spec + if (_Begin + 1 == _End) { + _THROW(format_error("Invalid format string - missing type after %")); + } + + const _CharT _Next_ch = *(_Begin + 1); + switch (_Next_ch) { + case 'n': + _Callbacks._On_lit_char('\n'); + _Begin += 2; + break; + case 't': + _Callbacks._On_lit_char('\t'); + _Begin += 2; + break; + case '%': + _Callbacks._On_lit_char('%'); + _Begin += 2; + break; + default: // some other type + _Begin = _Parse_conversion_specs(_Begin, _End, _Callbacks); + break; + } + } else { // literal-char + _Callbacks._On_lit_char(*_Begin); + ++_Begin; + } + } + + return _Begin; + } +#endif // __cpp_lib_concepts #endif // _HAS_CXX20 } // namespace chrono diff --git a/stl/inc/format b/stl/inc/format new file mode 100644 index 00000000000..939581aff94 --- /dev/null +++ b/stl/inc/format @@ -0,0 +1,2822 @@ +// 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. + +#pragma once +#ifndef _FORMAT_ +#define _FORMAT_ +#include +#if _STL_COMPILER_PREPROCESSOR +#ifndef __cpp_lib_concepts +#pragma message("The contents of are available only with C++20 concepts support.") +#else // ^^^ !defined(__cpp_lib_concepts) / defined(__cpp_lib_concepts) vvv + +#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 + +_STD_BEGIN + +class format_error : public runtime_error { + using runtime_error::runtime_error; +}; + +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::_ULong_long_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 {}; + +// 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* _Begin, const _CharT* _End) { + { _At._Parse_context }; + { _At._On_text(_Begin, _End) } -> same_as; + { _At._On_replacement_field(size_t{}, static_cast(nullptr)) } -> same_as; + { _At._On_format_specs(size_t{}, _Begin, _End) } -> 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, basic_string_view<_CharT> _Sv, _Fmt_align _Aln, _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 +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>; + +template +struct formatter; + +inline void _You_see_this_error_because_arg_id_is_out_of_range() noexcept {} + +template +class basic_format_parse_context { +public: + using char_type = _CharT; + using const_iterator = typename basic_string_view<_CharT>::const_iterator; + using iterator = const_iterator; + + constexpr explicit basic_format_parse_context( + 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")); + } + + 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; +}; + +using format_parse_context = basic_format_parse_context; +using wformat_parse_context = basic_format_parse_context; + +template +class basic_format_arg { +public: + using _CharType = typename _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(const _Ty& _Val) noexcept + : _Ptr(_STD addressof(_Val)), + _Format([](basic_format_parse_context<_CharType>& _Parse_ctx, _Context& _Format_ctx, const void* _Ptr) { + typename _Context::template formatter_type<_Ty> _Formatter; + _Parse_ctx.advance_to(_Formatter.parse(_Parse_ctx)); + _Format_ctx.advance_to(_Formatter.format(*static_cast(_Ptr), _Format_ctx)); + }) {} + + 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 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) {} + + explicit operator bool() const noexcept { + return _Active_state != _Basic_format_arg_type::_None; + } + + _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 +decltype(auto) visit_format_arg(_Visitor&& _Vis, basic_format_arg<_Context> _Arg) { + switch (_Arg._Active_state) { + case _Basic_format_arg_type::_None: + return _STD forward<_Visitor>(_Vis)(_Arg._No_state); + case _Basic_format_arg_type::_Int_type: + return _STD forward<_Visitor>(_Vis)(_Arg._Int_state); + case _Basic_format_arg_type::_UInt_type: + return _STD forward<_Visitor>(_Vis)(_Arg._UInt_state); + case _Basic_format_arg_type::_Long_long_type: + return _STD forward<_Visitor>(_Vis)(_Arg._Long_long_state); + case _Basic_format_arg_type::_ULong_long_type: + return _STD forward<_Visitor>(_Vis)(_Arg._ULong_long_state); + case _Basic_format_arg_type::_Bool_type: + return _STD forward<_Visitor>(_Vis)(_Arg._Bool_state); + case _Basic_format_arg_type::_Char_type: + return _STD forward<_Visitor>(_Vis)(_Arg._Char_state); + case _Basic_format_arg_type::_Float_type: + return _STD forward<_Visitor>(_Vis)(_Arg._Float_state); + case _Basic_format_arg_type::_Double_type: + return _STD forward<_Visitor>(_Vis)(_Arg._Double_state); + case _Basic_format_arg_type::_Long_double_type: + return _STD forward<_Visitor>(_Vis)(_Arg._Long_double_state); + case _Basic_format_arg_type::_Pointer_type: + return _STD forward<_Visitor>(_Vis)(_Arg._Pointer_state); + case _Basic_format_arg_type::_CString_type: + return _STD forward<_Visitor>(_Vis)(_Arg._CString_state); + case _Basic_format_arg_type::_String_type: + return _STD forward<_Visitor>(_Vis)(_Arg._String_state); + case _Basic_format_arg_type::_Custom_type: + return _STD forward<_Visitor>(_Vis)(_Arg._Custom_state); + default: + _STL_VERIFY(false, "basic_format_arg is in impossible state"); + return _STD forward<_Visitor>(_Vis)(0); + } +} + +// 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* _Begin, const _CharT* _End, unsigned int& _Value) { + _STL_INTERNAL_CHECK(_Begin != _End && '0' <= *_Begin && *_Begin <= '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(*_Begin - '0'); + ++_Begin; + } while (_Begin != _End && '0' <= *_Begin && *_Begin <= '9'); + + if (_Value > _Max_int) { + _THROW(format_error("Number is too big")); + } + + return _Begin; +} + +template +_NODISCARD constexpr const _CharT* _Parse_nonnegative_integer(const _CharT* _Begin, const _CharT* _End, int& _Value) { + unsigned int _Val_unsigned = 0; + + _Begin = _Parse_nonnegative_integer(_Begin, _End, _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 _Begin; +} + +template _Callbacks_type> +_NODISCARD constexpr const _CharT* _Parse_arg_id( + const _CharT* _Begin, const _CharT* _End, _Callbacks_type&& _Callbacks) { + _STL_INTERNAL_CHECK(_Begin != _End); + _CharT _Ch = *_Begin; + // No id provided, format string is using automatic indexing. + if (_Ch == '}' || _Ch == ':') { + _Callbacks._On_auto_id(); + return _Begin; + } + + 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') { + ++_Begin; + } else { + _Begin = _Parse_nonnegative_integer(_Begin, _End, _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 (_Begin == _End || (*_Begin != '}' && *_Begin != ':')) { + _THROW(format_error("Invalid format string.")); + } + + _Callbacks._On_manual_id(_Index); + return _Begin; + } + // 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_utf8() { +#pragma warning(push) +#pragma warning(disable : 4309) // 'initializing' : truncation of constant value +#pragma warning(disable : 4566) // character represented by universal-character-name '\u4E00' cannot be represented in + // the current code page +#pragma warning(disable : 6201) // Index '2' is out of valid index range '0' to '1' for possibly stack allocated buffer + // '_Test_char' +#pragma warning(disable : 6239) // ( && ) always evaluates to the result of . + // Did you intend to use the bitwise-and operator? + constexpr char _Test_char[] = "\u4e00"; + return sizeof(_Test_char) == 4 && _Test_char[0] == '\xe4' && _Test_char[1] == '\xb8' && _Test_char[2] == '\x80'; +#pragma warning(pop) +} + +inline constexpr bool _Is_execution_charset_utf8_v = _Is_execution_charset_utf8(); + +_NODISCARD constexpr int _Utf8_code_units_in_next_character( + const char* const _First, const char* const _Last) noexcept { + // Returns a count of the number of UTF-8 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. + const auto _Ch = static_cast(*_First); + if (_Ch < 0b1000'0000u) { + return 1; + } + + const auto _Len = static_cast(_Last - _First); + + if (_Ch < 0b1110'0000u) { + // check for non-lead byte or partial 2-byte encoded character + return (_Ch >= 0b1100'0000u && _Len >= 2) ? 2 : -1; + } + + if (_Ch < 0b1111'0000u) { + // check for partial 3-byte encoded character + return (_Len >= 3) ? 3 : -1; + } + + // check for partial 4-byte encoded character + return (_Len >= 4) ? 4 : -1; +} + +_NODISCARD inline int _Double_byte_encoding_code_units_in_next_character( + const char* const _First, const char* const _Last, const _Cvtvec& _Cvt) { + // Returns a count of 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; + } +} + +_NODISCARD inline int _Code_units_in_next_character(const char* _First, const char* _Last, const _Cvtvec& _Cvt) { + // Returns a count of 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 (_Is_execution_charset_utf8_v) { + return _Utf8_code_units_in_next_character(_First, _Last); + } else { + switch (_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 _Double_byte_encoding_code_units_in_next_character(_First, _Last, _Cvt); + + case 4: // Assume UTF-8 (as does _Mbrtowc) + return _Utf8_code_units_in_next_character(_First, _Last); + } + } +} + +_NODISCARD inline int _Code_units_in_next_character(const wchar_t* _First, const wchar_t* _Last, const _Cvtvec&) { + // Returns a count of 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 an unpaired surrogate. + _STL_INTERNAL_CHECK(_First < _Last); + + if (*_First < 0xD800u || *_First >= 0xE000u) { + return 1; + } + + if (*_First >= 0xDC00u) { // unpaired low surrogate + return -1; + } + + if (++_First == _Last || *_First < 0xDC00u || *_First >= 0xE000u) { // unpaired high surrogate + return -1; + } + + return 2; // surrogate pair +} + +template _Callbacks_type> +_NODISCARD const _CharT* _Parse_align(const _CharT* _Begin, const _CharT* _End, _Callbacks_type&& _Callbacks) { + // align and fill + _STL_INTERNAL_CHECK(_Begin != _End && *_Begin != '}'); + auto _Parsed_align = _Fmt_align::_None; + + const int _Units = _Code_units_in_next_character(_Begin, _End, _Getcvt()); + if (_Units < 0) { // invalid fill character encoding + _THROW(format_error("Invalid format string.")); + } + auto _Align_pt = _Begin + _Units; + + if (_Align_pt == _End) { + _Align_pt = _Begin; + } + + 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 != _Begin) { + if (*_Begin == '{') { + _THROW(format_error("invalid fill character '{'")); + } + _Callbacks._On_fill({_Begin, static_cast(_Align_pt - _Begin)}); + _Begin = _Align_pt + 1; + } else { + ++_Begin; + } + _Callbacks._On_align(_Parsed_align); + break; + } else if (_Align_pt == _Begin) { + break; + } + _Align_pt = _Begin; + } + + return _Begin; +} + +// 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* _Begin, const _CharT* _End, _Callbacks_type&& _Callbacks) { + _STL_INTERNAL_CHECK(_Begin != _End); + if ('1' <= *_Begin && *_Begin <= '9') { + int _Value = 0; + _Begin = _Parse_nonnegative_integer(_Begin, _End, _Value); + _Callbacks._On_width(_Value); + } else if (*_Begin == '{') { + ++_Begin; + if (_Begin != _End) { + _Begin = _Parse_arg_id(_Begin, _End, _Width_adapter<_CharT, _Callbacks_type>{_Callbacks}); + } + + if (_Begin == _End || *_Begin != '}') { + _THROW(format_error("Invalid format string.")); + } + ++_Begin; + } + return _Begin; +} + +template _Callbacks_type> +_NODISCARD constexpr const _CharT* _Parse_precision( + const _CharT* _Begin, const _CharT* _End, _Callbacks_type&& _Callbacks) { + ++_Begin; + _CharT _Ch = '\0'; + if (_Begin != _End) { + _Ch = *_Begin; + } + + if ('0' <= _Ch && _Ch <= '9') { + int _Precision = 0; + _Begin = _Parse_nonnegative_integer(_Begin, _End, _Precision); + _Callbacks._On_precision(_Precision); + } else if (_Ch == '{') { + ++_Begin; + if (_Begin != _End) { + _Begin = _Parse_arg_id(_Begin, _End, _Precision_adapter<_CharT, _Callbacks_type>{_Callbacks}); + } + + if (_Begin == _End || *_Begin != '}') { + _THROW(format_error("Invalid format string.")); + } + ++_Begin; + } else { + _THROW(format_error("Missing precision specifier.")); + } + + return _Begin; +} + +template _Callbacks_type> +_NODISCARD constexpr const _CharT* _Parse_format_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; + } + + switch (*_Begin) { + case '+': + _Callbacks._On_sign(_Fmt_sign::_Plus); + ++_Begin; + break; + case '-': + _Callbacks._On_sign(_Fmt_sign::_Minus); + ++_Begin; + break; + case ' ': + _Callbacks._On_sign(_Fmt_sign::_Space); + ++_Begin; + break; + default: + break; + } + + if (_Begin == _End) { + return _Begin; + } + + if (*_Begin == '#') { + _Callbacks._On_hash(); + if (++_Begin == _End) { + return _Begin; + } + } + + if (*_Begin == '0') { + _Callbacks._On_zero(); + if (++_Begin == _End) { + return _Begin; + } + } + + _Begin = _Parse_width(_Begin, _End, _Callbacks); + if (_Begin == _End) { + return _Begin; + } + + if (*_Begin == '.') { + _Begin = _Parse_precision(_Begin, _End, _Callbacks); + if (_Begin == _End) { + return _Begin; + } + } + + if (*_Begin == 'L') { + _Callbacks._On_localized(); + if (++_Begin == _End) { + return _Begin; + } + } + + // If there's anything remaining we assume it's a type. + if (*_Begin != '}') { + _Callbacks._On_type(*_Begin); + ++_Begin; + } + + return _Begin; +} + +template _HandlerT> +_NODISCARD constexpr const _CharT* _Parse_replacement_field( + const _CharT* _Begin, const _CharT* _End, _HandlerT&& _Handler) { + ++_Begin; + if (_Begin == _End) { + _THROW(format_error("Invalid format string.")); + } + + if (*_Begin == '}') { + // string was "{}", and we have a replacement field + _Handler._On_replacement_field(_Handler._Parse_context.next_arg_id(), _Begin); + } else if (*_Begin == '{') { + // string was "{{", so we have a literal "{" to print + _Handler._On_text(_Begin, _Begin + 1); + } else { + _Id_adapter<_CharT> _Adapter{_Handler._Parse_context}; + _Begin = _Parse_arg_id(_Begin, _End, _Adapter); + _CharT _Ch = _CharT{}; + if (_Begin != _End) { + _Ch = *_Begin; + } + + if (_Ch == '}') { + _Handler._On_replacement_field(_Adapter._Arg_id, _Begin); + } else if (_Ch == ':') { + _Begin = _Handler._On_format_specs(_Adapter._Arg_id, _Begin + 1, _End); + if (_Begin == _End || *_Begin != '}') { + _THROW(format_error("Unknown format specifier.")); + } + } else { + _THROW(format_error("Missing '}' in format string.")); + } + } + + return _Begin + 1; +} + +template +_NODISCARD const _CharT* _Find_encoded( + const _CharT* _First, const _CharT* _Last, const _CharT _Val, const _Cvtvec& _Cvt) { + // Returns the first occurrence of _Val as an encoded character (and not, for example, as a + // continuation byte) in [_First, _Last). + if constexpr (_Is_execution_charset_utf8_v) { + return _Find_unchecked(_First, _Last, _Val); + } else { + if (_Cvt._Mbcurmax == 1 || _Cvt._Mbcurmax == 4) { + // As above and in _Mbrtowc, assume 4-byte encodings are UTF-8 + return _Find_unchecked(_First, _Last, _Val); + } + + while (_First != _Last && *_First != _Val) { + const int _Units = _Code_units_in_next_character(_First, _Last, _Cvt); + if (_Units < 0) { + _THROW(format_error("Invalid encoded character in format string.")); + } + _First += _Units; + } + + return _First; + } +} + +template _HandlerT> +void _Parse_format_string(basic_string_view<_CharT> _Format_str, _HandlerT&& _Handler) { + auto _Begin = _Format_str.data(); + auto _End = _Begin + _Format_str.size(); + const _Cvtvec& _Cvt = _Getcvt(); + + while (_Begin != _End) { + const _CharT* _OpeningCurl = _Begin; + if (*_Begin != '{') { + _OpeningCurl = _Find_encoded(_Begin, _End, _CharT{'{'}, _Cvt); + + for (;;) { + const _CharT* _ClosingCurl = _Find_encoded(_Begin, _OpeningCurl, _CharT{'}'}, _Cvt); + + // In this case there are neither closing nor opening curls in [_Begin, _OpeningCurl) + // Write the whole thing out. + if (_ClosingCurl == _OpeningCurl) { + _Handler._On_text(_Begin, _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(_Begin, _ClosingCurl); + + // skip over the second closing curl + _Begin = _ClosingCurl + 1; + } + + // We are done, there were no replacement fields. + if (_OpeningCurl == _End) { + return; + } + } + // Parse the replacement field starting at _OpeningCurl and ending sometime before _End. + _Begin = _Parse_replacement_field(_OpeningCurl, _End, _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) { + if (_Type < 0 || _Type > (numeric_limits::max)()) { + _THROW(format_error("Invalid type specification.")); + } + _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; +} + +// 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_integral_v<_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_integral_v<_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 = typename _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 = typename _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(const _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(const _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); + } +}; + +class _Numeric_specs_checker { +private: + _Basic_format_arg_type _Arg_type = _Basic_format_arg_type::_None; + +public: + constexpr explicit _Numeric_specs_checker(_Basic_format_arg_type _Arg_type_) : _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_sign() const { + _Require_numeric_argument(); + if (_Is_integral_fmt_type(_Arg_type) && _Arg_type != _Basic_format_arg_type::_Int_type + && _Arg_type != _Basic_format_arg_type::_Long_long_type + && _Arg_type != _Basic_format_arg_type::_Char_type) { + _THROW(format_error("Format specifier requires signed 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.")); + } + } +}; + +// Uses _Numeric_specs_checker to check 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: + _Numeric_specs_checker _Numeric_checker; + +public: + constexpr explicit _Specs_checker(const _Handler& _Handler_inst, const _Basic_format_arg_type _Arg_type_) + : _Handler(_Handler_inst), _Numeric_checker(_Arg_type_) {} + + // _On_align has no checking, since we don't implement numeric alignments. + + constexpr void _On_hash() { + // Note that '#' is not valid for CharT or bool unless you + // pass a numeric presentation type, but we encounter '#' before + // the presentation type so we can not check that requirement here + _Numeric_checker._Require_numeric_argument(); + _Handler::_On_hash(); + } + + constexpr void _On_zero() { + // Note 0 is again not valid for CharT or bool unless a numeric + // presentation type is used. + _Numeric_checker._Require_numeric_argument(); + _Handler::_On_zero(); + } + + constexpr void _On_precision(int _Precision) { + _Numeric_checker._Check_precision(); + _Handler::_On_precision(_Precision); + } +}; + +// clang-format off +template +concept _Has_formatter = requires(const _Ty& _Val, _Context& _Ctx) { + _STD declval>().format(_Val, _Ctx); +}; +// clang-format on + +template +_NODISCARD constexpr size_t _Get_format_arg_type_storage_size(_Basic_format_arg_type _Type) noexcept { + switch (_Type) { + case _Basic_format_arg_type::_Int_type: + return sizeof(int); + case _Basic_format_arg_type::_UInt_type: + return sizeof(unsigned int); + case _Basic_format_arg_type::_Long_long_type: + return sizeof(long long); + case _Basic_format_arg_type::_ULong_long_type: + return sizeof(unsigned long long); + case _Basic_format_arg_type::_Bool_type: + return sizeof(bool); + case _Basic_format_arg_type::_Char_type: + return sizeof(_CharT); + case _Basic_format_arg_type::_Float_type: + return sizeof(float); + case _Basic_format_arg_type::_Double_type: + return sizeof(double); + case _Basic_format_arg_type::_Long_double_type: + return sizeof(long double); + case _Basic_format_arg_type::_Pointer_type: + return sizeof(void*); + case _Basic_format_arg_type::_CString_type: + return sizeof(const _CharT*); + case _Basic_format_arg_type::_String_type: + return sizeof(basic_string_view<_CharT>); + case _Basic_format_arg_type::_Custom_type: + return sizeof(void*) + sizeof(void (*)()); + case _Basic_format_arg_type::_None: + default: + _STL_INTERNAL_CHECK(false); + return 0; + } +} + +// See N4878 [format.arg]/5 +// clang-format off +template + requires _Has_formatter<_Context, _Ty> +/* consteval */ constexpr auto _Get_format_arg_storage_type(const _Ty&) noexcept { + // clang-format on + using _Char_type = typename _Context::char_type; + if constexpr (is_same_v<_Ty, monostate>) { + return monostate{}; + } else if constexpr (is_same_v<_Ty, bool>) { + return bool{}; + } else if constexpr (is_same_v<_Ty, _Char_type>) { + return _Char_type{}; + } else if constexpr (is_same_v<_Ty, char> && is_same_v<_Char_type, wchar_t>) { + return _Char_type{}; + } else if constexpr (signed_integral<_Ty> && sizeof(_Ty) <= sizeof(int)) { + return int{}; + } else if constexpr (unsigned_integral<_Ty> && sizeof(_Ty) <= sizeof(unsigned int)) { + return static_cast(42); + } else if constexpr (signed_integral<_Ty> && sizeof(_Ty) <= sizeof(long long)) { + return static_cast(42); + } else if constexpr (unsigned_integral<_Ty> && sizeof(_Ty) <= sizeof(unsigned long long)) { + return static_cast(42); + } else if constexpr (is_same_v<_Ty, float>) { + return float{}; + } else if constexpr (is_same_v<_Ty, double>) { + return double{}; + } else if constexpr (is_same_v<_Ty, long double>) { + return static_cast(42); + } else { + return typename basic_format_arg<_Context>::handle{42}; + } +} + +template +/* consteval */ constexpr auto _Get_format_arg_storage_type(const typename _Context::char_type*) noexcept { + return static_cast(nullptr); +} + +template +/* consteval */ constexpr auto _Get_format_arg_storage_type( + basic_string_view) noexcept { + return basic_string_view{}; +} + +template +/* consteval */ constexpr auto _Get_format_arg_storage_type( + const basic_string&) noexcept { + return basic_string_view{}; +} + +template +/* consteval */ constexpr auto _Get_format_arg_storage_type(nullptr_t) noexcept { + return static_cast(nullptr); +} + +// clang-format off +template + requires is_void_v<_Ty> +/* consteval */ constexpr auto _Get_format_arg_storage_type(const _Ty*) noexcept { + // clang-format on + return static_cast(nullptr); +} + +template +inline constexpr size_t _Get_format_arg_storage_size = sizeof( + _Get_format_arg_storage_type<_Context>(_STD declval())); + +struct _Format_arg_store_packed_index { + // TRANSITION, Should be templated on number of arguments for even less storage + using _Index_type = size_t; + + constexpr _Format_arg_store_packed_index() = default; + constexpr explicit _Format_arg_store_packed_index(const size_t _Index_) : _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<_Index_type>(_Val); + } + + _Index_type _Index : (sizeof(_Index_type) * 8 - 4); + _Index_type _Type_ : 4; +}; + +template +class basic_format_args; + +template +class _Format_arg_store { +private: + using _CharType = typename _Context::char_type; + using _Index_type = _Format_arg_store_packed_index; + + friend basic_format_args<_Context>; + + static constexpr size_t _Num_args = sizeof...(_Args); + static constexpr size_t _Index_length = _Num_args * sizeof(_Index_type); + static constexpr size_t _Storage_length = (_Get_format_arg_storage_size<_Context, _Args> + ... + 0); + + // we store the data in memory as _Format_arg_store_packed_index[_Num_args] + unsigned char[_Storage_length] + alignas(_Index_type) unsigned char _Storage[_Index_length + _Storage_length]; + + template + void _Store_impl(const size_t _Arg_index, const _Basic_format_arg_type _Arg_type, _Ty _Val) noexcept { + const auto _Index_array = reinterpret_cast<_Index_type*>(_Storage); + const auto _Store_index = _Index_array[_Arg_index]._Index; + const auto _Length = _Get_format_arg_type_storage_size<_CharType>(_Arg_type); + + _CSTD memcpy(_Storage + _Index_length + _Store_index, _STD addressof(_Val), _Length); + _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_store_packed_index{_Store_index + _Length}; + } + } + + // See N4885 [format.arg]/5 + // clang-format off + template + requires _Has_formatter<_Context, _Ty> + void _Store(const size_t _Arg_index, const _Ty& _Val) noexcept { + // clang-format on + if constexpr (is_same_v<_Ty, bool>) { + _Store_impl(_Arg_index, _Basic_format_arg_type::_Bool_type, _Val); + } else if constexpr (is_same_v<_Ty, _CharType>) { + _Store_impl<_CharType>(_Arg_index, _Basic_format_arg_type::_Char_type, _Val); + } else if constexpr (is_same_v<_Ty, char> && is_same_v<_CharType, wchar_t>) { + _Store_impl<_CharType>(_Arg_index, _Basic_format_arg_type::_Char_type, static_cast(_Val)); + } else if constexpr (signed_integral<_Ty> && sizeof(_Ty) <= sizeof(int)) { + _Store_impl(_Arg_index, _Basic_format_arg_type::_Int_type, static_cast(_Val)); + } else if constexpr (unsigned_integral<_Ty> && sizeof(_Ty) <= sizeof(unsigned int)) { + _Store_impl(_Arg_index, _Basic_format_arg_type::_UInt_type, static_cast(_Val)); + } else if constexpr (signed_integral<_Ty> && sizeof(_Ty) <= sizeof(long long)) { + _Store_impl(_Arg_index, _Basic_format_arg_type::_Long_long_type, static_cast(_Val)); + } else if constexpr (unsigned_integral<_Ty> && sizeof(_Ty) <= sizeof(unsigned long long)) { + _Store_impl( + _Arg_index, _Basic_format_arg_type::_ULong_long_type, static_cast(_Val)); + } else if constexpr (is_same_v<_Ty, float>) { + _Store_impl(_Arg_index, _Basic_format_arg_type::_Float_type, _Val); + } else if constexpr (is_same_v<_Ty, double>) { + _Store_impl(_Arg_index, _Basic_format_arg_type::_Double_type, _Val); + } else if constexpr (is_same_v<_Ty, long double>) { + _Store_impl(_Arg_index, _Basic_format_arg_type::_Long_double_type, _Val); + } else { + using _Handle_type = typename basic_format_arg<_Context>::handle; + _Store_impl<_Handle_type>(_Arg_index, _Basic_format_arg_type::_Custom_type, _Handle_type{_Val}); + } + } + + void _Store(const size_t _Arg_index, const _CharType* _Val) noexcept { + _Store_impl(_Arg_index, _Basic_format_arg_type::_CString_type, _Val); + } + + template + void _Store(const size_t _Arg_index, basic_string_view<_CharType, _Traits> _Val) noexcept { + _Store_impl>( + _Arg_index, _Basic_format_arg_type::_String_type, basic_string_view<_CharType>{_Val}); + } + + template + void _Store(const size_t _Arg_index, const basic_string<_CharType, _Traits, _Alloc>& _Val) noexcept { + _Store_impl>( + _Arg_index, _Basic_format_arg_type::_String_type, basic_string_view<_CharType>{_Val.data(), _Val.size()}); + } + + void _Store(const size_t _Arg_index, nullptr_t) noexcept { + _Store_impl(_Arg_index, _Basic_format_arg_type::_Pointer_type, static_cast(nullptr)); + } + + // clang-format off + template + requires is_void_v<_Ty> + void _Store(const size_t _Arg_index, const _Ty* _Ptr) noexcept { + // clang-format on + _Store_impl(_Arg_index, _Basic_format_arg_type::_Pointer_type, static_cast(_Ptr)); + } + +public: + _Format_arg_store(const _Args&... _Vals) noexcept { + // Note: _Storage is uninitialized, so manually initialize the first index + _STD construct_at(reinterpret_cast<_Format_arg_store_packed_index*>(_Storage)); + size_t _Arg_index = 0; + (_Store(_Arg_index++, _Vals), ...); + } +}; + +template +class _Format_arg_store<_Context> {}; + +template +class basic_format_args { +private: + template + _NODISCARD static auto _Get_value_from_memory(const unsigned char* _Val) noexcept { + auto& _Temp = *reinterpret_cast(_Val); + return _Bit_cast<_Ty>(_Temp); + } + +public: + basic_format_args() noexcept; + 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)), _Storage(_Store._Storage) {} + + _NODISCARD basic_format_arg<_Context> get(const size_t _Index) const noexcept { + if (_Index >= _Num_args) { + return basic_format_arg<_Context>{}; + } + + using _CharType = typename _Context::char_type; + const auto _Packed_index = reinterpret_cast(_Storage)[_Index]; + const auto _Index_length = _Num_args * sizeof(_Format_arg_store_packed_index); + const unsigned char* _Arg_storage = _Storage + _Index_length + _Packed_index._Index; + + switch (_Packed_index._Type()) { + case _Basic_format_arg_type::_None: + 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)}; + default: + _STL_ASSERT(false, "Invalid basic_format_arg type"); + return basic_format_arg<_Context>{}; + } + } + +private: + size_t _Num_args = 0; + const unsigned char* _Storage = nullptr; +}; + +// clang-format off +template + requires output_iterator<_Out, const _CharT&> +class basic_format_context { + // clang-format on +private: + _Out _OutputIt; + basic_format_args _Args; + 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, const locale& _Loc_) + : _OutputIt(_STD move(_OutputIt_)), _Args(_Ctx_args), _Loc(_Loc_) {} + + _NODISCARD basic_format_arg arg(size_t _Id) const { + return _Args.get(_Id); + } + _NODISCARD locale locale() { + return _Loc; + } + + _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; + } +}; + +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; + +// clang-format off +template + requires (is_arithmetic_v<_Arithmetic> && !_CharT_or_bool<_Arithmetic, _CharT>) +_NODISCARD _OutputIt _Fmt_write(_OutputIt _Out, _Arithmetic _Value); +// clang-format on + +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); + +#pragma warning(push) +#pragma warning(disable : 4365) // 'argument': conversion from 'char' to 'const wchar_t', signed/unsigned mismatch +// clang-format off +template + requires (is_arithmetic_v<_Arithmetic> && !_CharT_or_bool<_Arithmetic, _CharT>) +_NODISCARD _OutputIt _Fmt_write(_OutputIt _Out, const _Arithmetic _Value) { + // clang-format on + // TRANSITION, Reusable buffer + array _Buffer; + const auto [_End, _Ec] = _STD to_chars(_Buffer.data(), _Buffer.data() + _Buffer.size(), _Value); + _STL_ASSERT(_Ec == errc{}, "to_chars failed"); + return _RANGES _Copy_unchecked(_Buffer.data(), _End, _STD move(_Out)).out; +} +#pragma warning(pop) + +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; +} + +#pragma warning(push) +#pragma warning(disable : 4365) // 'argument': conversion from 'char' to 'const wchar_t', signed/unsigned mismatch +template +_NODISCARD _OutputIt _Fmt_write(_OutputIt _Out, const void* const _Value) { + // TRANSITION, Reusable buffer + array _Buffer; + const auto [_End, _Ec] = + _STD to_chars(_Buffer.data(), _Buffer.data() + _Buffer.size(), reinterpret_cast(_Value), 16); + _STL_ASSERT(_Ec == errc{}, "to_chars failed"); + *_Out++ = '0'; + *_Out++ = 'x'; + return _RANGES _Copy_unchecked(_Buffer.data(), _End, _STD move(_Out)).out; +} +#pragma warning(pop) + +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 _Basic_format_specs<_CharT>& _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<_CharT> _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* _Begin, const char* _End) { + for (; _Begin != _End; ++_Begin) { + *_Begin = static_cast(_CSTD toupper(*_Begin)); + } +} + +template +_NODISCARD constexpr bool _In_bounds(const _Ty _Value) { + if constexpr (is_unsigned_v<_CharT> && is_unsigned_v<_Ty>) { + return _Value <= (numeric_limits<_CharT>::max)(); + } else if constexpr (is_unsigned_v<_CharT>) { + return _Value >= 0 && static_cast>(_Value) <= (numeric_limits<_CharT>::max)(); + } else if constexpr (is_unsigned_v<_Ty>) { + return _Value <= static_cast>((numeric_limits<_CharT>::max)()); + } else { + return (numeric_limits<_CharT>::min)() <= _Value && _Value <= (numeric_limits<_CharT>::max)(); + } +} + +_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* _Start, const char* const _End, 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 = _RANGES _Copy_unchecked(_Start, _End - _Grouped, _STD move(_Out)).out; + _Start = _End - _Grouped; + + for (; _Separators > 0; --_Separators) { + if (_Repeats > 0) { + --_Repeats; + } else { + --_Group_it; + } + + *_Out++ = _Separator; + _Out = _RANGES _Copy_unchecked(_Start, _Start + *_Group_it, _STD move(_Out)).out; + _Start += *_Group_it; + } + _STL_INTERNAL_CHECK(_Start == _End); + return _Out; +} + +template +_NODISCARD _OutputIt _Fmt_write(_OutputIt _Out, monostate, const _Basic_format_specs<_CharT>&, locale) { + _STL_INTERNAL_CHECK(false); + return _Out; +} + +template +_NODISCARD _OutputIt _Write_integral( + _OutputIt _Out, _Integral _Value, _Basic_format_specs<_CharT> _Specs, locale _Locale); + +// clang-format off +template + requires (!_CharT_or_bool<_Integral, _CharT>) +_NODISCARD _OutputIt _Fmt_write( + _OutputIt _Out, _Integral _Value, const _Basic_format_specs<_CharT>& _Specs, locale _Locale); +// clang-format on + +template +_NODISCARD _OutputIt _Fmt_write(_OutputIt _Out, bool _Value, _Basic_format_specs<_CharT> _Specs, locale _Locale); + +template +_NODISCARD _OutputIt _Fmt_write(_OutputIt _Out, _CharT _Value, _Basic_format_specs<_CharT> _Specs, locale _Locale); + +template +_NODISCARD _OutputIt _Fmt_write( + _OutputIt _Out, _Float _Value, const _Basic_format_specs<_CharT>& _Specs, locale _Locale); + +template +_NODISCARD _OutputIt _Fmt_write(_OutputIt _Out, const void* _Value, const _Basic_format_specs<_CharT>& _Specs, locale); + +template +_NODISCARD _OutputIt _Fmt_write( + _OutputIt _Out, const _CharT* _Value, const _Basic_format_specs<_CharT>& _Specs, locale _Locale); + +template +_NODISCARD _OutputIt _Fmt_write( + _OutputIt _Out, basic_string_view<_CharT> _Value, const _Basic_format_specs<_CharT>& _Specs, locale); + +#pragma warning(push) +#pragma warning(disable : 4365) // 'argument': conversion from 'char' to 'const wchar_t', signed/unsigned mismatch +template +_NODISCARD _OutputIt _Write_integral( + _OutputIt _Out, const _Integral _Value, _Basic_format_specs<_CharT> _Specs, locale _Locale) { + if (_Specs._Type == 'c') { + if (!_In_bounds<_CharT>(_Value)) { + _THROW(format_error("integral cannot be stored in charT")); + } + _Specs._Alt = false; + return _Fmt_write(_STD move(_Out), static_cast<_CharT>(_Value), _Specs, _Locale); + } + + if (_Specs._Precision != -1) { + _THROW(format_error("integral cannot have a precision")); + } + + if (_Specs._Sgn == _Fmt_sign::_None) { + _Specs._Sgn = _Fmt_sign::_Minus; + } + + int _Base = 10; + bool _To_upper = false; + + switch (_Specs._Type) { + case '\0': + case 'd': + break; + case 'B': + _To_upper = true; + [[fallthrough]]; + case 'b': + _Base = 2; + break; + case 'X': + _To_upper = true; + [[fallthrough]]; + case 'x': + _Base = 16; + break; + case 'o': + _Base = 8; + break; + default: + _THROW(format_error("invalid integral type")); + } + + // long long -1 representation in binary is 64 bits + sign + array _Buffer; + const auto [_End, _Ec] = _STD to_chars(_Buffer.data(), _Buffer.data() + _Buffer.size(), _Value, _Base); + _STL_ASSERT(_Ec == errc{}, "to_chars failed"); + + auto _Buffer_start = _Buffer.data(); + 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 (_To_upper) { + _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).grouping(); + _Separators = _Count_separators(_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) { +#pragma warning(push) +#pragma warning(disable : 4296) // '<': expression is always false + _Out = _Write_sign(_STD move(_Out), _Specs._Sgn, _Value < _Integral{0}); +#pragma warning(pop) + _Out = _RANGES _Copy_unchecked(_Prefix.begin(), _Prefix.end(), _STD move(_Out)).out; + if (_Write_leading_zeroes && _Width < _Specs._Width) { + _Out = _RANGES fill_n(_STD move(_Out), _Specs._Width - _Width, '0'); + } + + if (_Separators > 0) { + return _Write_separated_integer(_Buffer_start, _End, _Groups, + _STD use_facet>(_Locale).thousands_sep(), _Separators, _STD move(_Out)); + } + return _RANGES _Copy_unchecked(_Buffer_start, _End, _STD move(_Out)).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) + +// clang-format off +template + requires (!_CharT_or_bool<_Integral, _CharT>) +_NODISCARD _OutputIt _Fmt_write( + _OutputIt _Out, const _Integral _Value, const _Basic_format_specs<_CharT>& _Specs, locale _Locale) { + // clang-format on + return _Write_integral(_STD move(_Out), _Value, _Specs, _Locale); +} + +template +_NODISCARD _OutputIt _Fmt_write(_OutputIt _Out, const bool _Value, _Basic_format_specs<_CharT> _Specs, locale _Locale) { + if (_Specs._Type != '\0' && _Specs._Type != 's') { + return _Write_integral(_STD move(_Out), static_cast(_Value), _Specs, _Locale); + } + + if (_Specs._Precision != -1) { + _THROW(format_error("bool cannot have a precision")); + } + + if (_Specs._Localized) { + _Specs._Localized = false; + return _Fmt_write(_STD move(_Out), + _Value ? static_cast>(_STD use_facet>(_Locale).truename()) + : static_cast>(_STD use_facet>(_Locale).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, locale _Locale) { + if (_Specs._Type != '\0' && _Specs._Type != 'c') { + return _Write_integral(_STD move(_Out), _Value, _Specs, _Locale); + } + + if (_Specs._Precision != -1) { + _THROW(format_error("charT cannot have a precision")); + } + + // Clear the type so that the string_view writer doesn't fail on 'c'. + _Specs._Type = '\0'; + return _Fmt_write(_STD move(_Out), basic_string_view<_CharT>{&_Value, 1}, _Specs, _Locale); +} + +#pragma warning(push) +#pragma warning(disable : 4365) // 'argument': conversion from 'char' to 'const wchar_t', signed/unsigned mismatch +template +_NODISCARD _OutputIt _Fmt_write( + _OutputIt _Out, const _Float _Value, const _Basic_format_specs<_CharT>& _Specs, 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 = '\0'; + auto _Precision = _Specs._Precision; + + switch (_Specs._Type) { + case '\0': + break; + 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; + _Exponent = 'e'; + 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; + _Exponent = 'e'; + break; + default: + _THROW(format_error("invalid floating point type")); + } + + // 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; + array _Buffer; + to_chars_result _Result; + + auto _Extra_precision = 0; + if (_Precision > _Max_precision) { + _Extra_precision = _Precision - _Max_precision; + _Precision = _Max_precision; + } + + if (_Precision == -1) { + _Result = _STD to_chars(_Buffer.data(), _Buffer.data() + _Buffer.size(), _Value, _Format); + } else { + _Result = _STD to_chars(_Buffer.data(), _Buffer.data() + _Buffer.size(), _Value, _Format, _Precision); + } + + _STL_ASSERT(_Result.ec == errc{}, "to_chars failed"); + + auto _Buffer_start = _Buffer.data(); + auto _Width = static_cast(_Result.ptr - _Buffer_start); + + const auto _Is_negative = (_STD signbit)(_Value); + + if (_Is_negative) { + // Remove the '-', it will be dealt with directly + _Buffer_start += 1; + } else { + if (_Sgn != _Fmt_sign::_Minus) { + _Width += 1; + } + } + + if (_To_upper) { + _Buffer_to_uppercase(_Buffer_start, _Result.ptr); + _Exponent = static_cast(_CSTD toupper(_Exponent)); + } + + 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 ((_Specs._Alt || _Specs._Localized) && _Is_finite) { + 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) { + if (_Radix_point == _Result.ptr) { + // TRANSITION, decimal point may be wider + ++_Width; + _Append_decimal = true; + } + + if (_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; + } + } + } + } + } + + if (_Specs._Localized) { + _Groups = _STD use_facet>(_Locale).grouping(); + _Separators = _Count_separators(_Integral_end - _Buffer_start, _Groups); + } + } + + if (_Is_finite && (_Specs._Type == 'f' || _Specs._Type == 'F')) { + _Zeroes_to_append = _Extra_precision; + } + + _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, '0'); + } + + if (_Specs._Localized) { + _Out = _Write_separated_integer(_Buffer_start, _Integral_end, _Groups, + _STD use_facet>(_Locale).thousands_sep(), _Separators, _STD move(_Out)); + if (_Radix_point != _Result.ptr || _Append_decimal) { + *_Out++ = _STD use_facet>(_Locale).decimal_point(); + _Append_decimal = false; + } + _Buffer_start = _Integral_end; + if (_Radix_point != _Result.ptr) { + ++_Buffer_start; + } + } + + _Out = _RANGES _Copy_unchecked(_Buffer_start, _Exponent_start, _STD move(_Out)).out; + _Buffer_start = _Exponent_start; + + if (_Specs._Alt && _Append_decimal) { + *_Out++ = '.'; + } + + for (; _Zeroes_to_append > 0; --_Zeroes_to_append) { + *_Out++ = '0'; + } + + return _RANGES _Copy_unchecked(_Buffer_start, _Result.ptr, _STD move(_Out)).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 +_NODISCARD _OutputIt _Fmt_write( + _OutputIt _Out, const void* const _Value, const _Basic_format_specs<_CharT>& _Specs, locale) { + if (_Specs._Type != '\0' && _Specs._Type != 'p') { + _THROW(format_error("invalid const void* type")); + } + + if (_Specs._Sgn != _Fmt_sign::_None) { + _THROW(format_error("const void* cannot have a sign")); + } + + if (_Specs._Alt) { + _THROW(format_error("const void* cannot have an alternative representation")); + } + + if (_Specs._Precision != -1) { + _THROW(format_error("const void* cannot have a precision")); + } + + if (_Specs._Leading_zero) { + _THROW(format_error("const void* cannot have a leading zero")); + } + + if (_Specs._Localized) { + _THROW(format_error("const void* cannot be localized")); + } + + // 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. + auto _Width = 2 + static_cast(_STD bit_width(reinterpret_cast(_Value)) + 3) / 4; + + // Since the bit width of 0 is 0, special case it instead of complicating the math even more. + if (_Value == nullptr) { + _Width = 3; + } + + return _Write_aligned(_STD move(_Out), _Width, _Specs, _Fmt_align::_Left, + [=](_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, locale _Locale) { + return _Fmt_write(_STD move(_Out), basic_string_view<_CharT>{_Value}, _Specs, _Locale); +} + +inline constexpr char16_t _Width_estimate_low_intervals[] = { // Per N4885 [format.string.std]/11 + 0x1100u, 0x1160u, 0x2329u, 0x232Bu, 0x2E80u, 0x303Fu, 0x3040u, 0xA4D0u, 0xAC00u, 0xD7A4u, 0xF900u, 0xFB00u, 0xFE10u, + 0xFE1Au, 0xFE30u, 0xFE70u, 0xFF00u, 0xFF61u, 0xFFE0u, 0xFFE7u}; + +inline constexpr char32_t _Width_estimate_high_intervals[] = { // Per N4885 [format.string.std]/11 + 0x1F300u, 0x1F650u, 0x1F900u, 0x1FA00u, 0x20000u, 0x2FFFEu, 0x30000u, 0x3FFFEu}; + +template +_NODISCARD constexpr int _Unicode_width_estimate(const char32_t _Ch) noexcept { + // Computes the width estimation for Unicode characters from N4885 [format.string.std]/11 + int _Result = 1; + for (const auto& _Bound : _Bounds) { + if (_Ch < _Bound) { + return _Result; + } + _Result ^= 1; + } + + return 1; +} + +_NODISCARD inline int _Estimate_utf8_character_width(const char* const _Ptr, const int _Units) noexcept { + // Return an estimate for the width of the character composed of _Units code units, + // whose first code unit is denoted by _Ptr. + auto _Ch = static_cast(*_Ptr); + switch (_Units) { + default: + case 1: + case 2: + return 1; + case 3: + _Ch &= 0b1111u; + break; + case 4: + _Ch &= 0b111u; + break; + } + + for (int _Idx = 1; _Idx < _Units; ++_Idx) { + _Ch = _Ch << 6 | (_Ptr[_Idx] & 0b11'1111u); + } + + if (_Units == 3) { + return _Unicode_width_estimate<_Width_estimate_low_intervals>(_Ch); + } + + return _Unicode_width_estimate<_Width_estimate_high_intervals>(_Ch); +} + +_NODISCARD inline int _Estimate_character_width(const char* _Ptr, const int _Units, const _Cvtvec& _Cvt) { + // Return an estimate for the width of the character composed of _Units code units, + // whose first code unit is denoted by _Ptr. + if constexpr (_Is_execution_charset_utf8_v) { + return _Estimate_utf8_character_width(_Ptr, _Units); + } else { + if (_Cvt._Mbcurmax != 4) { + // not a Unicode encoding; estimate width == number of code units + return _Units; + } + + // assume UTF-8 + return _Estimate_utf8_character_width(_Ptr, _Units); + } +} + +_NODISCARD inline int _Estimate_character_width(const wchar_t* _Ptr, const int _Units, const _Cvtvec&) { + // Return an estimate for the width of the character composed of _Units code units, + // whose first code unit is denoted by _Ptr. + auto _Ch = static_cast(*_Ptr); + if (_Units == 1) { + return _Unicode_width_estimate<_Width_estimate_low_intervals>(_Ch); + } + + // surrogate pair + _Ch = (_Ch - 0xD8000u) << 10; + _Ch += static_cast(_Ptr[1]) - 0xDC00u; + _Ch += 0x10000u; + return _Unicode_width_estimate<_Width_estimate_high_intervals>(_Ch); +} + +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; + auto _Pos = _Value.data(); + const auto _Last = _Pos + _Value.size(); + int _Estimated_width = 0; // the estimated width of [_Value.data(), _Pos) + const _Cvtvec& _Cvt = _Getcvt(); + constexpr auto _Max_int = (numeric_limits::max)(); + + while (_Pos != _Last) { + if (_Estimated_width == _Max_width && _Max_width >= 0) { + // We're at our maximum length + break; + } + + // TRANSITION, extended grapheme clustering + const int _Units = _Code_units_in_next_character(_Pos, _Last, _Cvt); + const int _Character_width = _Estimate_character_width(_Pos, _Units, _Cvt); + + 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 + _Estimated_width = _Max_int; + _Pos = _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; + } + + _Pos += _Units; + } + + _Width = _Estimated_width; + return _Pos; +} + +template +_NODISCARD _OutputIt _Fmt_write( + _OutputIt _Out, const basic_string_view<_CharT> _Value, const _Basic_format_specs<_CharT>& _Specs, locale) { + if (_Specs._Type != '\0' && _Specs._Type != 's') { + _THROW(format_error("invalid string type")); + } + + if (_Specs._Sgn != _Fmt_sign::_None) { + _THROW(format_error("string cannot have a sign")); + } + + if (_Specs._Alt) { + _THROW(format_error("string cannot have an alternative representation")); + } + + if (_Specs._Leading_zero) { + _THROW(format_error("string cannot have a leading zero")); + } + + if (_Specs._Localized) { + _THROW(format_error("string cannot be localized")); + } + + 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; + locale _Loc; + + template + _OutputIt operator()(_Ty _Val) && { + return _Fmt_write<_CharT>(_STD move(_Out), _Val); + } + + _OutputIt operator()(typename 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()(typename 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->locale()); + } +}; + +// The top level set of parsing "actions". +template +struct _Format_handler { + 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, const locale& _Loc) + : _Parse_context(_Str), _Ctx(_STD move(_Out), _Format_args, _Loc) {} + + void _On_text(const _CharT* _Begin, const _CharT* _End) { + _Ctx.advance_to(_RANGES _Copy_unchecked(_Begin, _End, _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.locale()}, _Arg)); + } + + const _CharT* _On_format_specs(const size_t _Id, const _CharT* _Begin, const _CharT* _End) { + _Parse_context.advance_to(_Parse_context.begin() + (_Begin - &*_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); + _Begin = _Parse_format_specs(_Begin, _End, _Handler); + if (_Begin == _End || *_Begin != '}') { + _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 _Begin; + } +}; + +// Generic formatter definition, the deleted default constructor +// makes it "disabled" as per N4868 [formatter.formatter.spec]/5 +template +struct formatter { + formatter() = delete; + formatter(const formatter&) = delete; + formatter operator=(const formatter&) = delete; +}; + +template +struct _Formatter_base { + using _Pc = basic_format_parse_context<_CharT>; + + typename _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 + typename _FormatContext::iterator format(const _Ty& _Val, _FormatContext& _FormatCtx) { + if (_Specs._Dynamic_width_index >= 0) { + _Specs._Width = + _Get_dynamic_specs<_Width_checker>(_FormatCtx.arg(static_cast(_Specs._Dynamic_width_index))); + } + + if (_Specs._Dynamic_precision_index >= 0) { + _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(_Specs)}, + basic_format_arg<_FormatContext>{_Val}); + } + +private: + _Dynamic_format_specs<_CharT> _Specs; +}; + +#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 + : _Formatter_base {}; + +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> {}; + +using format_context = basic_format_context, string::value_type>; +using wformat_context = basic_format_context, wstring::value_type>; +using format_args = basic_format_args; +using wformat_args = basic_format_args; + +template +_NODISCARD auto make_format_args(const _Args&... _Vals) { + return _Format_arg_store<_Context, _Args...>{_Vals...}; +} + +template +_NODISCARD auto make_wformat_args(const _Args&... _Vals) { + return _Format_arg_store<_Context, _Args...>{_Vals...}; +} + +template +using format_args_t = basic_format_args>; + +template _OutputIt> +_OutputIt vformat_to( + _OutputIt _Out, const string_view _Fmt, const format_args_t, char> _Args) { + _Format_handler<_OutputIt, char, basic_format_context<_OutputIt, char>> _Handler( + _STD move(_Out), _Fmt, _Args, locale::classic()); + _Parse_format_string(_Fmt, _Handler); + return _Handler._Ctx.out(); +} + +template _OutputIt> +_OutputIt vformat_to( + _OutputIt _Out, const wstring_view _Fmt, const format_args_t, wchar_t> _Args) { + _Format_handler<_OutputIt, wchar_t, basic_format_context<_OutputIt, wchar_t>> _Handler( + _STD move(_Out), _Fmt, _Args, locale::classic()); + _Parse_format_string(_Fmt, _Handler); + return _Handler._Ctx.out(); +} + +template _OutputIt> +_OutputIt vformat_to(_OutputIt _Out, const locale& _Loc, const string_view _Fmt, + const format_args_t, char> _Args) { + _Format_handler<_OutputIt, char, basic_format_context<_OutputIt, char>> _Handler( + _STD move(_Out), _Fmt, _Args, _Loc); + _Parse_format_string(_Fmt, _Handler); + return _Handler._Ctx.out(); +} + +template _OutputIt> +_OutputIt vformat_to(_OutputIt _Out, const locale& _Loc, const wstring_view _Fmt, + const format_args_t, wchar_t> _Args) { + _Format_handler<_OutputIt, wchar_t, basic_format_context<_OutputIt, wchar_t>> _Handler( + _STD move(_Out), _Fmt, _Args, _Loc); + _Parse_format_string(_Fmt, _Handler); + return _Handler._Ctx.out(); +} + +template _OutputIt, class... _Types> +_OutputIt format_to(_OutputIt _Out, const string_view _Fmt, const _Types&... _Args) { + using _Context = basic_format_context<_OutputIt, char>; + return _STD vformat_to(_STD move(_Out), _Fmt, _STD make_format_args<_Context>(_Args...)); +} + +template _OutputIt, class... _Types> +_OutputIt format_to(_OutputIt _Out, const wstring_view _Fmt, const _Types&... _Args) { + using _Context = basic_format_context<_OutputIt, wchar_t>; + return _STD vformat_to(_STD move(_Out), _Fmt, _STD make_format_args<_Context>(_Args...)); +} + +template _OutputIt, class... _Types> +_OutputIt format_to(_OutputIt _Out, const locale& _Loc, const string_view _Fmt, const _Types&... _Args) { + using _Context = basic_format_context<_OutputIt, char>; + return _STD vformat_to(_STD move(_Out), _Loc, _Fmt, _STD make_format_args<_Context>(_Args...)); +} + +template _OutputIt, class... _Types> +_OutputIt format_to(_OutputIt _Out, const locale& _Loc, const wstring_view _Fmt, const _Types&... _Args) { + using _Context = basic_format_context<_OutputIt, wchar_t>; + return _STD vformat_to(_STD move(_Out), _Loc, _Fmt, _STD make_format_args<_Context>(_Args...)); +} + +_NODISCARD inline string vformat(const string_view _Fmt, const format_args _Args) { + string _Str; + _STD vformat_to(_STD back_inserter(_Str), _Fmt, _Args); + return _Str; +} + +_NODISCARD inline wstring vformat(const wstring_view _Fmt, const wformat_args _Args) { + wstring _Str; + _STD vformat_to(_STD back_inserter(_Str), _Fmt, _Args); + return _Str; +} + +_NODISCARD inline string vformat(const locale& _Loc, const string_view _Fmt, const format_args _Args) { + string _Str; + _STD vformat_to(_STD back_inserter(_Str), _Loc, _Fmt, _Args); + return _Str; +} + +_NODISCARD inline wstring vformat(const locale& _Loc, const wstring_view _Fmt, const wformat_args _Args) { + wstring _Str; + _STD vformat_to(_STD back_inserter(_Str), _Loc, _Fmt, _Args); + return _Str; +} + +template +_NODISCARD string format(const string_view _Fmt, const _Types&... _Args) { + return _STD vformat(_Fmt, _STD make_format_args(_Args...)); +} + +template +_NODISCARD wstring format(const wstring_view _Fmt, const _Types&... _Args) { + return _STD vformat(_Fmt, _STD make_wformat_args(_Args...)); +} + +template +_NODISCARD string format(const locale& _Loc, const string_view _Fmt, const _Types&... _Args) { + return _STD vformat(_Loc, _Fmt, _STD make_format_args(_Args...)); +} + +template +_NODISCARD wstring format(const locale& _Loc, const wstring_view _Fmt, const _Types&... _Args) { + return _STD vformat(_Loc, _Fmt, _STD make_wformat_args(_Args...)); +} + +template +struct _Format_to_n_iterator { + _OutputIt _Out; + iter_difference_t<_OutputIt> _Max; + iter_difference_t<_OutputIt> _Count = 0; + + using difference_type = iter_difference_t<_OutputIt>; + + _Format_to_n_iterator& operator=(_CharT _Ch) { + if (_Count < _Max) { + *_Out++ = _Ch; + } + ++_Count; + return *this; + } + + _NODISCARD _Format_to_n_iterator& operator*() { + return *this; + } + + _Format_to_n_iterator& operator++() { + return *this; + } + + _Format_to_n_iterator& operator++(int) { + return *this; + } +}; + +template +struct format_to_n_result { + _OutputIt out; + iter_difference_t<_OutputIt> size; +}; + +template _OutputIt, class... _Types> +format_to_n_result<_OutputIt> format_to_n( + _OutputIt _Out, const iter_difference_t<_OutputIt> _Max, const string_view _Fmt, const _Types&... _Args) { + _Format_to_n_iterator<_OutputIt, char> _It{._Out = _STD move(_Out), ._Max = _Max}; + + using _Context = basic_format_context; + auto _Arg_store = _STD make_format_args<_Context>(_Args...); + _It = _STD vformat_to(_STD move(_It), _Fmt, _STD move(_Arg_store)); + return {.out = _STD move(_It._Out), .size = _It._Count}; +} + +template _OutputIt, class... _Types> +format_to_n_result<_OutputIt> format_to_n( + _OutputIt _Out, const iter_difference_t<_OutputIt> _Max, const wstring_view _Fmt, const _Types&... _Args) { + _Format_to_n_iterator<_OutputIt, wchar_t> _It{._Out = _STD move(_Out), ._Max = _Max}; + + using _Context = basic_format_context; + auto _Arg_store = _STD make_format_args<_Context>(_Args...); + _It = _STD vformat_to(_STD move(_It), _Fmt, _STD move(_Arg_store)); + return {.out = _STD move(_It._Out), .size = _It._Count}; +} + +template _OutputIt, class... _Types> +format_to_n_result<_OutputIt> format_to_n(_OutputIt _Out, const iter_difference_t<_OutputIt> _Max, const locale& _Loc, + const string_view _Fmt, const _Types&... _Args) { + _Format_to_n_iterator<_OutputIt, char> _It{._Out = _STD move(_Out), ._Max = _Max}; + + using _Context = basic_format_context; + auto _Arg_store = _STD make_format_args<_Context>(_Args...); + _It = _STD vformat_to(_STD move(_It), _Loc, _Fmt, _STD move(_Arg_store)); + return {.out = _STD move(_It._Out), .size = _It._Count}; +} + +template _OutputIt, class... _Types> +format_to_n_result<_OutputIt> format_to_n(_OutputIt _Out, const iter_difference_t<_OutputIt> _Max, const locale& _Loc, + const wstring_view _Fmt, const _Types&... _Args) { + _Format_to_n_iterator<_OutputIt, wchar_t> _It{._Out = _STD move(_Out), ._Max = _Max}; + + using _Context = basic_format_context; + auto _Arg_store = _STD make_format_args<_Context>(_Args...); + _It = _STD vformat_to(_STD move(_It), _Loc, _Fmt, _STD move(_Arg_store)); + return {.out = _STD move(_It._Out), .size = _It._Count}; +} + +struct _Counting_iterator { + size_t _Count = 0; + + using difference_type = ptrdiff_t; + + template + _Counting_iterator& operator=(_CharT) { + ++_Count; + return *this; + } + + _NODISCARD _Counting_iterator& operator*() { + return *this; + } + + _Counting_iterator& operator++() { + return *this; + } + + _Counting_iterator& operator++(int) { + return *this; + } +}; + +template +_NODISCARD size_t formatted_size(const string_view _Fmt, const _Types&... _Args) { + using _Context = basic_format_context<_Counting_iterator, char>; + auto _Arg_store = _STD make_format_args<_Context>(_Args...); + return _STD vformat_to(_Counting_iterator{}, _Fmt, _STD move(_Arg_store))._Count; +} + +template +_NODISCARD size_t formatted_size(const wstring_view _Fmt, const _Types&... _Args) { + using _Context = basic_format_context<_Counting_iterator, wchar_t>; + auto _Arg_store = _STD make_format_args<_Context>(_Args...); + return _STD vformat_to(_Counting_iterator{}, _Fmt, _STD move(_Arg_store))._Count; +} + +template +_NODISCARD size_t formatted_size(const locale& _Loc, const string_view _Fmt, const _Types&... _Args) { + using _Context = basic_format_context<_Counting_iterator, char>; + auto _Arg_store = _STD make_format_args<_Context>(_Args...); + return _STD vformat_to(_Counting_iterator{}, _Loc, _Fmt, _STD move(_Arg_store))._Count; +} + +template +_NODISCARD size_t formatted_size(const locale& _Loc, const wstring_view _Fmt, const _Types&... _Args) { + using _Context = basic_format_context<_Counting_iterator, wchar_t>; + auto _Arg_store = _STD make_format_args<_Context>(_Args...); + return _STD vformat_to(_Counting_iterator{}, _Loc, _Fmt, _STD move(_Arg_store))._Count; +} + +_STD_END + +#pragma pop_macro("new") +_STL_RESTORE_CLANG_WARNINGS +#pragma warning(pop) +#pragma pack(pop) + +#endif // __cpp_lib_concepts +#endif // _STL_COMPILER_PREPROCESSOR +#endif // _FORMAT_ diff --git a/stl/inc/header-units.json b/stl/inc/header-units.json index f4c76b822dc..4d65c03055e 100644 --- a/stl/inc/header-units.json +++ b/stl/inc/header-units.json @@ -52,6 +52,7 @@ "exception", "execution", "filesystem", + "format", "forward_list", "fstream", "functional", diff --git a/stl/inc/queue b/stl/inc/queue index bcd510658e4..6efee980d07 100644 --- a/stl/inc/queue +++ b/stl/inc/queue @@ -22,47 +22,6 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN // CLASS TEMPLATE queue template > -class queue; - -template -_NODISCARD bool operator==(const queue<_Ty, _Container>& _Left, const queue<_Ty, _Container>& _Right) { - return _Left.c == _Right.c; -} - -template -_NODISCARD bool operator!=(const queue<_Ty, _Container>& _Left, const queue<_Ty, _Container>& _Right) { - return _Left.c != _Right.c; -} - -template -_NODISCARD bool operator<(const queue<_Ty, _Container>& _Left, const queue<_Ty, _Container>& _Right) { - return _Left.c < _Right.c; -} - -template -_NODISCARD bool operator>(const queue<_Ty, _Container>& _Left, const queue<_Ty, _Container>& _Right) { - return _Left.c > _Right.c; -} - -template -_NODISCARD bool operator<=(const queue<_Ty, _Container>& _Left, const queue<_Ty, _Container>& _Right) { - return _Left.c <= _Right.c; -} - -template -_NODISCARD bool operator>=(const queue<_Ty, _Container>& _Left, const queue<_Ty, _Container>& _Right) { - return _Left.c >= _Right.c; -} - -#ifdef __cpp_lib_concepts -template -_NODISCARD compare_three_way_result_t<_Container> operator<=>( - const queue<_Ty, _Container>& _Left, const queue<_Ty, _Container>& _Right) { - return _Left.c <=> _Right.c; -} -#endif // __cpp_lib_concepts - -template class queue { public: using value_type = typename _Container::value_type; @@ -149,20 +108,9 @@ public: _Swap_adl(c, _Right.c); } - // clang-format off - friend bool operator== <>(const queue&, const queue&); - friend bool operator!= <>(const queue&, const queue&); - friend bool operator< <>(const queue&, const queue&); - friend bool operator> <>(const queue&, const queue&); - friend bool operator<= <>(const queue&, const queue&); - friend bool operator>= <>(const queue&, const queue&); - // clang-format on - -#ifdef __cpp_lib_concepts - template - friend compare_three_way_result_t<_Container2> operator<=>( - const queue<_Ty2, _Container2>&, const queue<_Ty2, _Container2>&); -#endif // __cpp_lib_concepts + _NODISCARD const _Container& _Get_container() const noexcept { // TRANSITION, VSO-1307828 + return c; + } protected: _Container c{}; @@ -179,6 +127,44 @@ template queue; #endif // _HAS_CXX17 +template +_NODISCARD bool operator==(const queue<_Ty, _Container>& _Left, const queue<_Ty, _Container>& _Right) { + return _Left._Get_container() == _Right._Get_container(); +} + +template +_NODISCARD bool operator!=(const queue<_Ty, _Container>& _Left, const queue<_Ty, _Container>& _Right) { + return _Left._Get_container() != _Right._Get_container(); +} + +template +_NODISCARD bool operator<(const queue<_Ty, _Container>& _Left, const queue<_Ty, _Container>& _Right) { + return _Left._Get_container() < _Right._Get_container(); +} + +template +_NODISCARD bool operator>(const queue<_Ty, _Container>& _Left, const queue<_Ty, _Container>& _Right) { + return _Left._Get_container() > _Right._Get_container(); +} + +template +_NODISCARD bool operator<=(const queue<_Ty, _Container>& _Left, const queue<_Ty, _Container>& _Right) { + return _Left._Get_container() <= _Right._Get_container(); +} + +template +_NODISCARD bool operator>=(const queue<_Ty, _Container>& _Left, const queue<_Ty, _Container>& _Right) { + return _Left._Get_container() >= _Right._Get_container(); +} + +#ifdef __cpp_lib_concepts +template +_NODISCARD compare_three_way_result_t<_Container> operator<=>( + const queue<_Ty, _Container>& _Left, const queue<_Ty, _Container>& _Right) { + return _Left._Get_container() <=> _Right._Get_container(); +} +#endif // __cpp_lib_concepts + template ::value, int> = 0> void swap(queue<_Ty, _Container>& _Left, queue<_Ty, _Container>& _Right) noexcept(noexcept(_Left.swap(_Right))) { _Left.swap(_Right); diff --git a/stl/inc/stack b/stl/inc/stack index 2cc4d964f56..1ea9f63aec7 100644 --- a/stl/inc/stack +++ b/stl/inc/stack @@ -20,47 +20,6 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN // CLASS TEMPLATE stack template > -class stack; - -template -_NODISCARD bool operator==(const stack<_Ty, _Container>& _Left, const stack<_Ty, _Container>& _Right) { - return _Left.c == _Right.c; -} - -template -_NODISCARD bool operator!=(const stack<_Ty, _Container>& _Left, const stack<_Ty, _Container>& _Right) { - return _Left.c != _Right.c; -} - -template -_NODISCARD bool operator<(const stack<_Ty, _Container>& _Left, const stack<_Ty, _Container>& _Right) { - return _Left.c < _Right.c; -} - -template -_NODISCARD bool operator>(const stack<_Ty, _Container>& _Left, const stack<_Ty, _Container>& _Right) { - return _Left.c > _Right.c; -} - -template -_NODISCARD bool operator<=(const stack<_Ty, _Container>& _Left, const stack<_Ty, _Container>& _Right) { - return _Left.c <= _Right.c; -} - -template -_NODISCARD bool operator>=(const stack<_Ty, _Container>& _Left, const stack<_Ty, _Container>& _Right) { - return _Left.c >= _Right.c; -} - -#ifdef __cpp_lib_concepts -template -_NODISCARD compare_three_way_result_t<_Container> operator<=>( - const stack<_Ty, _Container>& _Left, const stack<_Ty, _Container>& _Right) { - return _Left.c <=> _Right.c; -} -#endif // __cpp_lib_concepts - -template class stack { public: using value_type = typename _Container::value_type; @@ -139,20 +98,9 @@ public: _Swap_adl(c, _Right.c); } - // clang-format off - friend bool operator== <>(const stack&, const stack&); - friend bool operator!= <>(const stack&, const stack&); - friend bool operator< <>(const stack&, const stack&); - friend bool operator> <>(const stack&, const stack&); - friend bool operator<= <>(const stack&, const stack&); - friend bool operator>= <>(const stack&, const stack&); - // clang-format on - -#ifdef __cpp_lib_concepts - template - friend compare_three_way_result_t<_Container2> operator<=>( - const stack<_Ty2, _Container2>&, const stack<_Ty2, _Container2>&); -#endif // __cpp_lib_concepts + _NODISCARD const _Container& _Get_container() const noexcept { // TRANSITION, VSO-1307828 + return c; + } protected: _Container c{}; @@ -169,6 +117,44 @@ template stack; #endif // _HAS_CXX17 +template +_NODISCARD bool operator==(const stack<_Ty, _Container>& _Left, const stack<_Ty, _Container>& _Right) { + return _Left._Get_container() == _Right._Get_container(); +} + +template +_NODISCARD bool operator!=(const stack<_Ty, _Container>& _Left, const stack<_Ty, _Container>& _Right) { + return _Left._Get_container() != _Right._Get_container(); +} + +template +_NODISCARD bool operator<(const stack<_Ty, _Container>& _Left, const stack<_Ty, _Container>& _Right) { + return _Left._Get_container() < _Right._Get_container(); +} + +template +_NODISCARD bool operator>(const stack<_Ty, _Container>& _Left, const stack<_Ty, _Container>& _Right) { + return _Left._Get_container() > _Right._Get_container(); +} + +template +_NODISCARD bool operator<=(const stack<_Ty, _Container>& _Left, const stack<_Ty, _Container>& _Right) { + return _Left._Get_container() <= _Right._Get_container(); +} + +template +_NODISCARD bool operator>=(const stack<_Ty, _Container>& _Left, const stack<_Ty, _Container>& _Right) { + return _Left._Get_container() >= _Right._Get_container(); +} + +#ifdef __cpp_lib_concepts +template +_NODISCARD compare_three_way_result_t<_Container> operator<=>( + const stack<_Ty, _Container>& _Left, const stack<_Ty, _Container>& _Right) { + return _Left._Get_container() <=> _Right._Get_container(); +} +#endif // __cpp_lib_concepts + template ::value, int> = 0> void swap(stack<_Ty, _Container>& _Left, stack<_Ty, _Container>& _Right) noexcept(noexcept(_Left.swap(_Right))) { _Left.swap(_Right); diff --git a/stl/inc/variant b/stl/inc/variant index 036e1d602aa..a6359e0e6b7 100644 --- a/stl/inc/variant +++ b/stl/inc/variant @@ -1731,9 +1731,6 @@ constexpr _Ret visit(_Callable&& _Obj, _Variants&&... _Args) { } #endif // _HAS_CXX20 -// CLASS monostate [variant.monostate] -struct monostate {}; - // monostate RELATIONAL OPERATORS [variant.monostate.relops] _NODISCARD constexpr bool operator==(monostate, monostate) noexcept { return true; diff --git a/stl/inc/xcharconv_ryu.h b/stl/inc/xcharconv_ryu.h index 5ed23a839db..282e18dc1bf 100644 --- a/stl/inc/xcharconv_ryu.h +++ b/stl/inc/xcharconv_ryu.h @@ -39,12 +39,21 @@ #if _STL_COMPILER_PREPROCESSOR #include +#include +#include #include #include +#include -#ifdef _M_X64 +#if defined(_M_X64) && !defined(_M_ARM64EC) +#define _HAS_CHARCONV_INTRINSICS 1 +#else // ^^^ intrinsics available ^^^ / vvv intrinsics unavailable vvv +#define _HAS_CHARCONV_INTRINSICS 0 +#endif // ^^^ intrinsics unavailable ^^^ + +#if _HAS_CHARCONV_INTRINSICS #include // for _umul128() and __shiftright128() -#endif // _M_X64 +#endif // ^^^ intrinsics available ^^^ #if !_HAS_CXX17 #error The contents of are only available with C++17. (Also, you should not include this internal header.) @@ -135,7 +144,7 @@ inline constexpr int __DOUBLE_POW5_BITCOUNT = 121; // vvvvvvvvvv DERIVED FROM d2s_intrinsics.h vvvvvvvvvv -#ifdef _M_X64 +#if _HAS_CHARCONV_INTRINSICS _NODISCARD inline uint64_t __ryu_umul128(const uint64_t __a, const uint64_t __b, uint64_t* const __productHi) { return _umul128(__a, __b, __productHi); @@ -321,7 +330,7 @@ _NODISCARD inline bool __multipleOfPowerOf2(const uint64_t __value, const uint32 inline constexpr int __POW10_ADDITIONAL_BITS = 120; -#ifdef _M_X64 +#if _HAS_CHARCONV_INTRINSICS // Returns the low 64 bits of the high 128 bits of the 256-bit product of a and b. _NODISCARD inline uint64_t __umul256_hi128_lo64( const uint64_t __aHi, const uint64_t __aLo, const uint64_t __bHi, const uint64_t __bLo) { @@ -370,7 +379,7 @@ _NODISCARD inline uint32_t __mulShift_mod1e9(const uint64_t __m, const uint64_t* const uint64_t __s1high = __high2 + __c2; // 192 _STL_INTERNAL_CHECK(__j >= 128); _STL_INTERNAL_CHECK(__j <= 180); -#ifdef _M_X64 +#if _HAS_CHARCONV_INTRINSICS const uint32_t __dist = static_cast(__j - 128); // __dist: [0, 52] const uint64_t __shiftedhigh = __s1high >> __dist; const uint64_t __shiftedlow = __ryu_shiftright128(__s1low, __s1high, __dist); @@ -389,7 +398,10 @@ _NODISCARD inline uint32_t __mulShift_mod1e9(const uint64_t __m, const uint64_t* #endif // ^^^ intrinsics unavailable ^^^ } -inline void __append_n_digits(const uint32_t __olength, uint32_t __digits, char* const __result) { +#define _WIDEN(_TYPE, _CHAR) static_cast<_TYPE>(is_same_v<_TYPE, char> ? _CHAR : L##_CHAR) + +template +void __append_n_digits(const uint32_t __olength, uint32_t __digits, _CharT* const __result) { uint32_t __i = 0; while (__digits >= 10000) { #ifdef __clang__ // TRANSITION, LLVM-38217 @@ -400,21 +412,21 @@ inline void __append_n_digits(const uint32_t __olength, uint32_t __digits, char* __digits /= 10000; const uint32_t __c0 = (__c % 100) << 1; const uint32_t __c1 = (__c / 100) << 1; - _CSTD memcpy(__result + __olength - __i - 2, __DIGIT_TABLE + __c0, 2); - _CSTD memcpy(__result + __olength - __i - 4, __DIGIT_TABLE + __c1, 2); + _CSTD memcpy(__result + __olength - __i - 2, __DIGIT_TABLE<_CharT> + __c0, 2 * sizeof(_CharT)); + _CSTD memcpy(__result + __olength - __i - 4, __DIGIT_TABLE<_CharT> + __c1, 2 * sizeof(_CharT)); __i += 4; } if (__digits >= 100) { const uint32_t __c = (__digits % 100) << 1; __digits /= 100; - _CSTD memcpy(__result + __olength - __i - 2, __DIGIT_TABLE + __c, 2); + _CSTD memcpy(__result + __olength - __i - 2, __DIGIT_TABLE<_CharT> + __c, 2 * sizeof(_CharT)); __i += 2; } if (__digits >= 10) { const uint32_t __c = __digits << 1; - _CSTD memcpy(__result + __olength - __i - 2, __DIGIT_TABLE + __c, 2); + _CSTD memcpy(__result + __olength - __i - 2, __DIGIT_TABLE<_CharT> + __c, 2 * sizeof(_CharT)); } else { - __result[0] = static_cast('0' + __digits); + __result[0] = static_cast<_CharT>(_WIDEN(_CharT, '0') + __digits); } } @@ -429,43 +441,45 @@ inline void __append_d_digits(const uint32_t __olength, uint32_t __digits, char* __digits /= 10000; const uint32_t __c0 = (__c % 100) << 1; const uint32_t __c1 = (__c / 100) << 1; - _CSTD memcpy(__result + __olength + 1 - __i - 2, __DIGIT_TABLE + __c0, 2); - _CSTD memcpy(__result + __olength + 1 - __i - 4, __DIGIT_TABLE + __c1, 2); + _CSTD memcpy(__result + __olength + 1 - __i - 2, __DIGIT_TABLE + __c0, 2); + _CSTD memcpy(__result + __olength + 1 - __i - 4, __DIGIT_TABLE + __c1, 2); __i += 4; } if (__digits >= 100) { const uint32_t __c = (__digits % 100) << 1; __digits /= 100; - _CSTD memcpy(__result + __olength + 1 - __i - 2, __DIGIT_TABLE + __c, 2); + _CSTD memcpy(__result + __olength + 1 - __i - 2, __DIGIT_TABLE + __c, 2); __i += 2; } if (__digits >= 10) { const uint32_t __c = __digits << 1; - __result[2] = __DIGIT_TABLE[__c + 1]; + __result[2] = __DIGIT_TABLE[__c + 1]; __result[1] = '.'; - __result[0] = __DIGIT_TABLE[__c]; + __result[0] = __DIGIT_TABLE[__c]; } else { __result[1] = '.'; __result[0] = static_cast('0' + __digits); } } -inline void __append_c_digits(const uint32_t __count, uint32_t __digits, char* const __result) { +template +void __append_c_digits(const uint32_t __count, uint32_t __digits, _CharT* const __result) { uint32_t __i = 0; for (; __i < __count - 1; __i += 2) { const uint32_t __c = (__digits % 100) << 1; __digits /= 100; - _CSTD memcpy(__result + __count - __i - 2, __DIGIT_TABLE + __c, 2); + _CSTD memcpy(__result + __count - __i - 2, __DIGIT_TABLE<_CharT> + __c, 2 * sizeof(_CharT)); } if (__i < __count) { - const char __c = static_cast('0' + (__digits % 10)); + const _CharT __c = static_cast<_CharT>(_WIDEN(_CharT, '0') + (__digits % 10)); __result[__count - __i - 1] = __c; } } -inline void __append_nine_digits(uint32_t __digits, char* const __result) { +template +void __append_nine_digits(uint32_t __digits, _CharT* const __result) { if (__digits == 0) { - _CSTD memset(__result, '0', 9); + _STD fill_n(__result, 9, _WIDEN(_CharT, '0')); return; } @@ -478,10 +492,10 @@ inline void __append_nine_digits(uint32_t __digits, char* const __result) { __digits /= 10000; const uint32_t __c0 = (__c % 100) << 1; const uint32_t __c1 = (__c / 100) << 1; - _CSTD memcpy(__result + 7 - __i, __DIGIT_TABLE + __c0, 2); - _CSTD memcpy(__result + 5 - __i, __DIGIT_TABLE + __c1, 2); + _CSTD memcpy(__result + 7 - __i, __DIGIT_TABLE<_CharT> + __c0, 2 * sizeof(_CharT)); + _CSTD memcpy(__result + 5 - __i, __DIGIT_TABLE<_CharT> + __c1, 2 * sizeof(_CharT)); } - __result[0] = static_cast('0' + __digits); + __result[0] = static_cast<_CharT>(_WIDEN(_CharT, '0') + __digits); } _NODISCARD inline uint32_t __indexForExponent(const uint32_t __e) { @@ -497,9 +511,10 @@ _NODISCARD inline uint32_t __lengthForIndex(const uint32_t __idx) { return (__log10Pow2(16 * static_cast(__idx)) + 1 + 16 + 8) / 9; } -_NODISCARD inline to_chars_result __d2fixed_buffered_n(char* _First, char* const _Last, const double __d, +template +_NODISCARD pair<_CharT*, errc> __d2fixed_buffered_n(_CharT* _First, _CharT* const _Last, const double __d, const uint32_t __precision) { - char* const _Original_first = _First; + _CharT* const _Original_first = _First; const uint64_t __bits = __double_to_bits(__d); @@ -513,10 +528,10 @@ _NODISCARD inline to_chars_result __d2fixed_buffered_n(char* _First, char* const return { _Last, errc::value_too_large }; } - *_First++ = '0'; + *_First++ = _WIDEN(_CharT, '0'); if (__precision > 0) { - *_First++ = '.'; - _CSTD memset(_First, '0', __precision); + *_First++ = _WIDEN(_CharT, '.'); + _STD fill_n(_First, __precision, _WIDEN(_CharT, '0')); _First += __precision; } return { _First, errc{} }; @@ -568,13 +583,13 @@ _NODISCARD inline to_chars_result __d2fixed_buffered_n(char* _First, char* const if (_First == _Last) { return { _Last, errc::value_too_large }; } - *_First++ = '0'; + *_First++ = _WIDEN(_CharT, '0'); } if (__precision > 0) { if (_First == _Last) { return { _Last, errc::value_too_large }; } - *_First++ = '.'; + *_First++ = _WIDEN(_CharT, '.'); } if (__e2 < 0) { const int32_t __idx = -__e2 / 16; @@ -587,14 +602,14 @@ _NODISCARD inline to_chars_result __d2fixed_buffered_n(char* _First, char* const if (_Last - _First < static_cast(__precision)) { return { _Last, errc::value_too_large }; } - _CSTD memset(_First, '0', __precision); + _STD fill_n(_First, __precision, _WIDEN(_CharT, '0')); _First += __precision; } else if (__i < __MIN_BLOCK_2[__idx]) { __i = __MIN_BLOCK_2[__idx]; if (_Last - _First < static_cast(9 * __i)) { return { _Last, errc::value_too_large }; } - _CSTD memset(_First, '0', 9 * __i); + _STD fill_n(_First, 9 * __i, _WIDEN(_CharT, '0')); _First += 9 * __i; } for (; __i < __blocks; ++__i) { @@ -607,7 +622,7 @@ _NODISCARD inline to_chars_result __d2fixed_buffered_n(char* _First, char* const if (_Last - _First < static_cast(__fill)) { return { _Last, errc::value_too_large }; } - _CSTD memset(_First, '0', __fill); + _STD fill_n(_First, __fill, _WIDEN(_CharT, '0')); _First += __fill; break; } @@ -647,31 +662,31 @@ _NODISCARD inline to_chars_result __d2fixed_buffered_n(char* _First, char* const } } if (__roundUp != 0) { - char* _Round = _First; - char* _Dot = _Last; + _CharT* _Round = _First; + _CharT* _Dot = _Last; while (true) { if (_Round == _Original_first) { - _Round[0] = '1'; + _Round[0] = _WIDEN(_CharT, '1'); if (_Dot != _Last) { - _Dot[0] = '0'; - _Dot[1] = '.'; + _Dot[0] = _WIDEN(_CharT, '0'); + _Dot[1] = _WIDEN(_CharT, '.'); } if (_First == _Last) { return { _Last, errc::value_too_large }; } - *_First++ = '0'; + *_First++ = _WIDEN(_CharT, '0'); break; } --_Round; - const char __c = _Round[0]; - if (__c == '.') { + const _CharT __c = _Round[0]; + if (__c == _WIDEN(_CharT, '.')) { _Dot = _Round; - } else if (__c == '9') { - _Round[0] = '0'; + } else if (__c == _WIDEN(_CharT, '9')) { + _Round[0] = _WIDEN(_CharT, '0'); __roundUp = 1; } else { if (__roundUp == 1 || __c % 2 != 0) { - _Round[0] = __c + 1; + _Round[0] = static_cast<_CharT>(__c + 1); } break; } @@ -681,7 +696,7 @@ _NODISCARD inline to_chars_result __d2fixed_buffered_n(char* _First, char* const if (_Last - _First < static_cast(__precision)) { return { _Last, errc::value_too_large }; } - _CSTD memset(_First, '0', __precision); + _STD fill_n(_First, __precision, _WIDEN(_CharT, '0')); _First += __precision; } return { _First, errc{} }; @@ -919,11 +934,11 @@ _NODISCARD inline to_chars_result __d2exp_buffered_n(char* _First, char* const _ if (__exp >= 100) { const int32_t __c = __exp % 10; - _CSTD memcpy(_First, __DIGIT_TABLE + 2 * (__exp / 10), 2); + _CSTD memcpy(_First, __DIGIT_TABLE + 2 * (__exp / 10), 2); _First[2] = static_cast('0' + __c); _First += 3; } else { - _CSTD memcpy(_First, __DIGIT_TABLE + 2 * __exp, 2); + _CSTD memcpy(_First, __DIGIT_TABLE + 2 * __exp, 2); _First += 2; } @@ -1175,7 +1190,8 @@ _NODISCARD inline __floating_decimal_32 __f2d(const uint32_t __ieeeMantissa, con return __fd; } -_NODISCARD inline to_chars_result _Large_integer_to_chars(char* const _First, char* const _Last, +template +_NODISCARD pair<_CharT*, errc> _Large_integer_to_chars(_CharT* const _First, _CharT* const _Last, const uint32_t _Mantissa2, const int32_t _Exponent2) { // Print the integer _Mantissa2 * 2^_Exponent2 exactly. @@ -1283,7 +1299,7 @@ _NODISCARD inline to_chars_result _Large_integer_to_chars(char* const _First, ch return { _Last, errc::value_too_large }; } - char* _Result = _First; + _CharT* _Result = _First; // Print _Data[0]. While it's up to 10 digits, // which is more than Ryu generates, the code below can handle this. @@ -1299,7 +1315,8 @@ _NODISCARD inline to_chars_result _Large_integer_to_chars(char* const _First, ch return { _Result, errc{} }; } -_NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _Last, const __floating_decimal_32 __v, +template +_NODISCARD pair<_CharT*, errc> __to_chars(_CharT* const _First, _CharT* const _Last, const __floating_decimal_32 __v, chars_format _Fmt, const uint32_t __ieeeMantissa, const uint32_t __ieeeExponent) { // Step 5: Print the decimal representation. uint32_t _Output = __v.__mantissa; @@ -1385,7 +1402,7 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La return { _Last, errc::value_too_large }; } - char* _Mid; + _CharT* _Mid; if (_Ryu_exponent > 0) { // case "172900" bool _Can_use_ryu; @@ -1448,35 +1465,35 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La _Output /= 10000; const uint32_t __c0 = (__c % 100) << 1; const uint32_t __c1 = (__c / 100) << 1; - _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE + __c0, 2); - _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE + __c1, 2); + _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE<_CharT> + __c0, 2 * sizeof(_CharT)); + _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE<_CharT> + __c1, 2 * sizeof(_CharT)); } if (_Output >= 100) { const uint32_t __c = (_Output % 100) << 1; _Output /= 100; - _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE + __c, 2); + _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE<_CharT> + __c, 2 * sizeof(_CharT)); } if (_Output >= 10) { const uint32_t __c = _Output << 1; - _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE + __c, 2); + _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE<_CharT> + __c, 2 * sizeof(_CharT)); } else { - *--_Mid = static_cast('0' + _Output); + *--_Mid = static_cast<_CharT>(_WIDEN(_CharT, '0') + _Output); } if (_Ryu_exponent > 0) { // case "172900" with _Can_use_ryu // Performance note: it might be more efficient to do this immediately after setting _Mid. - _CSTD memset(_First + __olength, '0', static_cast(_Ryu_exponent)); + _STD fill_n(_First + __olength, _Ryu_exponent, _WIDEN(_CharT, '0')); } else if (_Ryu_exponent == 0) { // case "1729" // Done! } else if (_Whole_digits > 0) { // case "17.29" // Performance note: moving digits might not be optimal. - _CSTD memmove(_First, _First + 1, static_cast(_Whole_digits)); - _First[_Whole_digits] = '.'; + _CSTD memmove(_First, _First + 1, static_cast(_Whole_digits) * sizeof(_CharT)); + _First[_Whole_digits] = _WIDEN(_CharT, '.'); } else { // case "0.001729" // Performance note: a larger memset() followed by overwriting '.' might be more efficient. - _First[0] = '0'; - _First[1] = '.'; - _CSTD memset(_First + 2, '0', static_cast(-_Whole_digits)); + _First[0] = _WIDEN(_CharT, '0'); + _First[1] = _WIDEN(_CharT, '.'); + _STD fill_n(_First + 2, -_Whole_digits, _WIDEN(_CharT, '0')); } return { _First + _Total_fixed_length, errc{} }; @@ -1487,7 +1504,7 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La if (_Last - _First < static_cast(_Total_scientific_length)) { return { _Last, errc::value_too_large }; } - char* const __result = _First; + _CharT* const __result = _First; // Print the decimal digits. uint32_t __i = 0; @@ -1500,50 +1517,55 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La _Output /= 10000; const uint32_t __c0 = (__c % 100) << 1; const uint32_t __c1 = (__c / 100) << 1; - _CSTD memcpy(__result + __olength - __i - 1, __DIGIT_TABLE + __c0, 2); - _CSTD memcpy(__result + __olength - __i - 3, __DIGIT_TABLE + __c1, 2); + _CSTD memcpy(__result + __olength - __i - 1, __DIGIT_TABLE<_CharT> + __c0, 2 * sizeof(_CharT)); + _CSTD memcpy(__result + __olength - __i - 3, __DIGIT_TABLE<_CharT> + __c1, 2 * sizeof(_CharT)); __i += 4; } if (_Output >= 100) { const uint32_t __c = (_Output % 100) << 1; _Output /= 100; - _CSTD memcpy(__result + __olength - __i - 1, __DIGIT_TABLE + __c, 2); + _CSTD memcpy(__result + __olength - __i - 1, __DIGIT_TABLE<_CharT> + __c, 2 * sizeof(_CharT)); __i += 2; } if (_Output >= 10) { const uint32_t __c = _Output << 1; // We can't use memcpy here: the decimal dot goes between these two digits. - __result[2] = __DIGIT_TABLE[__c + 1]; - __result[0] = __DIGIT_TABLE[__c]; + __result[2] = __DIGIT_TABLE<_CharT>[__c + 1]; + __result[0] = __DIGIT_TABLE<_CharT>[__c]; } else { - __result[0] = static_cast('0' + _Output); + __result[0] = static_cast<_CharT>(_WIDEN(_CharT, '0') + _Output); } // Print decimal point if needed. uint32_t __index; if (__olength > 1) { - __result[1] = '.'; + __result[1] = _WIDEN(_CharT, '.'); __index = __olength + 1; } else { __index = 1; } // Print the exponent. - __result[__index++] = 'e'; + __result[__index++] = _WIDEN(_CharT, 'e'); if (_Scientific_exponent < 0) { - __result[__index++] = '-'; + __result[__index++] = _WIDEN(_CharT, '-'); _Scientific_exponent = -_Scientific_exponent; } else { - __result[__index++] = '+'; + __result[__index++] = _WIDEN(_CharT, '+'); } - _CSTD memcpy(__result + __index, __DIGIT_TABLE + 2 * _Scientific_exponent, 2); + _CSTD memcpy(__result + __index, __DIGIT_TABLE<_CharT> + 2 * _Scientific_exponent, 2 * sizeof(_CharT)); __index += 2; return { _First + _Total_scientific_length, errc{} }; } -_NODISCARD inline to_chars_result __f2s_buffered_n(char* const _First, char* const _Last, const float __f, +_NODISCARD inline to_chars_result _Convert_to_chars_result(const pair& _Pair) { + return {_Pair.first, _Pair.second}; +} + +template +_NODISCARD pair<_CharT*, errc> __f2s_buffered_n(_CharT* const _First, _CharT* const _Last, const float __f, const chars_format _Fmt) { // Step 1: Decode the floating-point number, and unify normalized and subnormal cases. @@ -1556,7 +1578,11 @@ _NODISCARD inline to_chars_result __f2s_buffered_n(char* const _First, char* con return { _Last, errc::value_too_large }; } - _CSTD memcpy(_First, "0e+00", 5); + if constexpr (is_same_v<_CharT, char>) { + _CSTD memcpy(_First, "0e+00", 5); + } else { + _CSTD memcpy(_First, L"0e+00", 5 * sizeof(wchar_t)); + } return { _First + 5, errc{} }; } @@ -1566,7 +1592,7 @@ _NODISCARD inline to_chars_result __f2s_buffered_n(char* const _First, char* con return { _Last, errc::value_too_large }; } - *_First = '0'; + *_First = _WIDEN(_CharT, '0'); return { _First + 1, errc{} }; } @@ -1635,7 +1661,7 @@ _NODISCARD inline to_chars_result __f2s_buffered_n(char* const _First, char* con // c. Split only the first factor into 31-bit pieces, which also guarantees // no internal overflow, but requires extra work since the intermediate // results are not perfectly aligned. -#ifdef _M_X64 +#if _HAS_CHARCONV_INTRINSICS _NODISCARD inline uint64_t __mulShift(const uint64_t __m, const uint64_t* const __mul, const int32_t __j) { // __m is maximum 55 bits @@ -1897,7 +1923,8 @@ _NODISCARD inline __floating_decimal_64 __d2d(const uint64_t __ieeeMantissa, con return __fd; } -_NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _Last, const __floating_decimal_64 __v, +template +_NODISCARD pair<_CharT*, errc> __to_chars(_CharT* const _First, _CharT* const _Last, const __floating_decimal_64 __v, chars_format _Fmt, const double __f) { // Step 5: Print the decimal representation. uint64_t _Output = __v.__mantissa; @@ -1989,7 +2016,7 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La return { _Last, errc::value_too_large }; } - char* _Mid; + _CharT* _Mid; if (_Ryu_exponent > 0) { // case "172900" bool _Can_use_ryu; @@ -2072,10 +2099,10 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La const uint32_t __d0 = (__d % 100) << 1; const uint32_t __d1 = (__d / 100) << 1; - _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE + __c0, 2); - _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE + __c1, 2); - _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE + __d0, 2); - _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE + __d1, 2); + _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE<_CharT> + __c0, 2 * sizeof(_CharT)); + _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE<_CharT> + __c1, 2 * sizeof(_CharT)); + _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE<_CharT> + __d0, 2 * sizeof(_CharT)); + _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE<_CharT> + __d1, 2 * sizeof(_CharT)); } uint32_t __output2 = static_cast(_Output); while (__output2 >= 10000) { @@ -2087,35 +2114,35 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La __output2 /= 10000; const uint32_t __c0 = (__c % 100) << 1; const uint32_t __c1 = (__c / 100) << 1; - _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE + __c0, 2); - _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE + __c1, 2); + _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE<_CharT> + __c0, 2 * sizeof(_CharT)); + _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE<_CharT> + __c1, 2 * sizeof(_CharT)); } if (__output2 >= 100) { const uint32_t __c = (__output2 % 100) << 1; __output2 /= 100; - _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE + __c, 2); + _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE<_CharT> + __c, 2 * sizeof(_CharT)); } if (__output2 >= 10) { const uint32_t __c = __output2 << 1; - _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE + __c, 2); + _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE<_CharT> + __c, 2 * sizeof(_CharT)); } else { - *--_Mid = static_cast('0' + __output2); + *--_Mid = static_cast<_CharT>(_WIDEN(_CharT, '0') + __output2); } if (_Ryu_exponent > 0) { // case "172900" with _Can_use_ryu // Performance note: it might be more efficient to do this immediately after setting _Mid. - _CSTD memset(_First + __olength, '0', static_cast(_Ryu_exponent)); + _STD fill_n(_First + __olength, _Ryu_exponent, _WIDEN(_CharT, '0')); } else if (_Ryu_exponent == 0) { // case "1729" // Done! } else if (_Whole_digits > 0) { // case "17.29" // Performance note: moving digits might not be optimal. - _CSTD memmove(_First, _First + 1, static_cast(_Whole_digits)); - _First[_Whole_digits] = '.'; + _CSTD memmove(_First, _First + 1, static_cast(_Whole_digits) * sizeof(_CharT)); + _First[_Whole_digits] = _WIDEN(_CharT, '.'); } else { // case "0.001729" // Performance note: a larger memset() followed by overwriting '.' might be more efficient. - _First[0] = '0'; - _First[1] = '.'; - _CSTD memset(_First + 2, '0', static_cast(-_Whole_digits)); + _First[0] = _WIDEN(_CharT, '0'); + _First[1] = _WIDEN(_CharT, '.'); + _STD fill_n(_First + 2, -_Whole_digits, _WIDEN(_CharT, '0')); } return { _First + _Total_fixed_length, errc{} }; @@ -2126,7 +2153,7 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La if (_Last - _First < static_cast(_Total_scientific_length)) { return { _Last, errc::value_too_large }; } - char* const __result = _First; + _CharT* const __result = _First; // Print the decimal digits. uint32_t __i = 0; @@ -2147,10 +2174,10 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La const uint32_t __c1 = (__c / 100) << 1; const uint32_t __d0 = (__d % 100) << 1; const uint32_t __d1 = (__d / 100) << 1; - _CSTD memcpy(__result + __olength - __i - 1, __DIGIT_TABLE + __c0, 2); - _CSTD memcpy(__result + __olength - __i - 3, __DIGIT_TABLE + __c1, 2); - _CSTD memcpy(__result + __olength - __i - 5, __DIGIT_TABLE + __d0, 2); - _CSTD memcpy(__result + __olength - __i - 7, __DIGIT_TABLE + __d1, 2); + _CSTD memcpy(__result + __olength - __i - 1, __DIGIT_TABLE<_CharT> + __c0, 2 * sizeof(_CharT)); + _CSTD memcpy(__result + __olength - __i - 3, __DIGIT_TABLE<_CharT> + __c1, 2 * sizeof(_CharT)); + _CSTD memcpy(__result + __olength - __i - 5, __DIGIT_TABLE<_CharT> + __d0, 2 * sizeof(_CharT)); + _CSTD memcpy(__result + __olength - __i - 7, __DIGIT_TABLE<_CharT> + __d1, 2 * sizeof(_CharT)); __i += 8; } uint32_t __output2 = static_cast(_Output); @@ -2163,50 +2190,50 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La __output2 /= 10000; const uint32_t __c0 = (__c % 100) << 1; const uint32_t __c1 = (__c / 100) << 1; - _CSTD memcpy(__result + __olength - __i - 1, __DIGIT_TABLE + __c0, 2); - _CSTD memcpy(__result + __olength - __i - 3, __DIGIT_TABLE + __c1, 2); + _CSTD memcpy(__result + __olength - __i - 1, __DIGIT_TABLE<_CharT> + __c0, 2 * sizeof(_CharT)); + _CSTD memcpy(__result + __olength - __i - 3, __DIGIT_TABLE<_CharT> + __c1, 2 * sizeof(_CharT)); __i += 4; } if (__output2 >= 100) { const uint32_t __c = (__output2 % 100) << 1; __output2 /= 100; - _CSTD memcpy(__result + __olength - __i - 1, __DIGIT_TABLE + __c, 2); + _CSTD memcpy(__result + __olength - __i - 1, __DIGIT_TABLE<_CharT> + __c, 2 * sizeof(_CharT)); __i += 2; } if (__output2 >= 10) { const uint32_t __c = __output2 << 1; // We can't use memcpy here: the decimal dot goes between these two digits. - __result[2] = __DIGIT_TABLE[__c + 1]; - __result[0] = __DIGIT_TABLE[__c]; + __result[2] = __DIGIT_TABLE<_CharT>[__c + 1]; + __result[0] = __DIGIT_TABLE<_CharT>[__c]; } else { - __result[0] = static_cast('0' + __output2); + __result[0] = static_cast<_CharT>(_WIDEN(_CharT, '0') + __output2); } // Print decimal point if needed. uint32_t __index; if (__olength > 1) { - __result[1] = '.'; + __result[1] = _WIDEN(_CharT, '.'); __index = __olength + 1; } else { __index = 1; } // Print the exponent. - __result[__index++] = 'e'; + __result[__index++] = _WIDEN(_CharT, 'e'); if (_Scientific_exponent < 0) { - __result[__index++] = '-'; + __result[__index++] = _WIDEN(_CharT, '-'); _Scientific_exponent = -_Scientific_exponent; } else { - __result[__index++] = '+'; + __result[__index++] = _WIDEN(_CharT, '+'); } if (_Scientific_exponent >= 100) { const int32_t __c = _Scientific_exponent % 10; - _CSTD memcpy(__result + __index, __DIGIT_TABLE + 2 * (_Scientific_exponent / 10), 2); - __result[__index + 2] = static_cast('0' + __c); + _CSTD memcpy(__result + __index, __DIGIT_TABLE<_CharT> + 2 * (_Scientific_exponent / 10), 2 * sizeof(_CharT)); + __result[__index + 2] = static_cast<_CharT>(_WIDEN(_CharT, '0') + __c); __index += 3; } else { - _CSTD memcpy(__result + __index, __DIGIT_TABLE + 2 * _Scientific_exponent, 2); + _CSTD memcpy(__result + __index, __DIGIT_TABLE<_CharT> + 2 * _Scientific_exponent, 2 * sizeof(_CharT)); __index += 2; } @@ -2245,7 +2272,8 @@ _NODISCARD inline bool __d2d_small_int(const uint64_t __ieeeMantissa, const uint return true; } -_NODISCARD inline to_chars_result __d2s_buffered_n(char* const _First, char* const _Last, const double __f, +template +_NODISCARD pair<_CharT*, errc> __d2s_buffered_n(_CharT* const _First, _CharT* const _Last, const double __f, const chars_format _Fmt) { // Step 1: Decode the floating-point number, and unify normalized and subnormal cases. @@ -2258,7 +2286,11 @@ _NODISCARD inline to_chars_result __d2s_buffered_n(char* const _First, char* con return { _Last, errc::value_too_large }; } - _CSTD memcpy(_First, "0e+00", 5); + if constexpr (is_same_v<_CharT, char>) { + _CSTD memcpy(_First, "0e+00", 5); + } else { + _CSTD memcpy(_First, L"0e+00", 5 * sizeof(wchar_t)); + } return { _First + 5, errc{} }; } @@ -2268,7 +2300,7 @@ _NODISCARD inline to_chars_result __d2s_buffered_n(char* const _First, char* con return { _Last, errc::value_too_large }; } - *_First = '0'; + *_First = _WIDEN(_CharT, '0'); return { _First + 1, errc{} }; } @@ -2331,9 +2363,9 @@ template _NODISCARD to_chars_result _Floating_to_chars_ryu( char* const _First, char* const _Last, const _Floating _Value, const chars_format _Fmt) noexcept { if constexpr (is_same_v<_Floating, float>) { - return __f2s_buffered_n(_First, _Last, _Value, _Fmt); + return _Convert_to_chars_result(__f2s_buffered_n(_First, _Last, _Value, _Fmt)); } else { - return __d2s_buffered_n(_First, _Last, _Value, _Fmt); + return _Convert_to_chars_result(__d2s_buffered_n(_First, _Last, _Value, _Fmt)); } } @@ -2376,11 +2408,14 @@ _NODISCARD to_chars_result _Floating_to_chars_fixed_precision( return {_Last, errc::value_too_large}; } - return __d2fixed_buffered_n(_First, _Last, _Value, static_cast(_Precision)); + return _Convert_to_chars_result(__d2fixed_buffered_n(_First, _Last, _Value, static_cast(_Precision))); } _STD_END +#undef _HAS_CHARCONV_INTRINSICS +#undef _WIDEN + #pragma pop_macro("new") _STL_RESTORE_CLANG_WARNINGS #pragma warning(pop) diff --git a/stl/inc/xcharconv_ryu_tables.h b/stl/inc/xcharconv_ryu_tables.h index 508d3479fdf..87bc0c2b34c 100644 --- a/stl/inc/xcharconv_ryu_tables.h +++ b/stl/inc/xcharconv_ryu_tables.h @@ -63,7 +63,9 @@ _STD_BEGIN // A table of all two-digit numbers. This is used to speed up decimal digit // generation by copying pairs of digits into the final output. -inline constexpr char __DIGIT_TABLE[200] = { +template inline constexpr _CharT __DIGIT_TABLE[] = {_CharT{}}; + +template <> inline constexpr char __DIGIT_TABLE[200] = { '0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9', '1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9', '2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9', @@ -76,6 +78,19 @@ inline constexpr char __DIGIT_TABLE[200] = { '9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9' }; +template <> inline constexpr wchar_t __DIGIT_TABLE[200] = { + L'0',L'0',L'0',L'1',L'0',L'2',L'0',L'3',L'0',L'4',L'0',L'5',L'0',L'6',L'0',L'7',L'0',L'8',L'0',L'9', + L'1',L'0',L'1',L'1',L'1',L'2',L'1',L'3',L'1',L'4',L'1',L'5',L'1',L'6',L'1',L'7',L'1',L'8',L'1',L'9', + L'2',L'0',L'2',L'1',L'2',L'2',L'2',L'3',L'2',L'4',L'2',L'5',L'2',L'6',L'2',L'7',L'2',L'8',L'2',L'9', + L'3',L'0',L'3',L'1',L'3',L'2',L'3',L'3',L'3',L'4',L'3',L'5',L'3',L'6',L'3',L'7',L'3',L'8',L'3',L'9', + L'4',L'0',L'4',L'1',L'4',L'2',L'4',L'3',L'4',L'4',L'4',L'5',L'4',L'6',L'4',L'7',L'4',L'8',L'4',L'9', + L'5',L'0',L'5',L'1',L'5',L'2',L'5',L'3',L'5',L'4',L'5',L'5',L'5',L'6',L'5',L'7',L'5',L'8',L'5',L'9', + L'6',L'0',L'6',L'1',L'6',L'2',L'6',L'3',L'6',L'4',L'6',L'5',L'6',L'6',L'6',L'7',L'6',L'8',L'6',L'9', + L'7',L'0',L'7',L'1',L'7',L'2',L'7',L'3',L'7',L'4',L'7',L'5',L'7',L'6',L'7',L'7',L'7',L'8',L'7',L'9', + L'8',L'0',L'8',L'1',L'8',L'2',L'8',L'3',L'8',L'4',L'8',L'5',L'8',L'6',L'8',L'7',L'8',L'8',L'8',L'9', + L'9',L'0',L'9',L'1',L'9',L'2',L'9',L'3',L'9',L'4',L'9',L'5',L'9',L'6',L'9',L'7',L'9',L'8',L'9',L'9' +}; + // ^^^^^^^^^^ DERIVED FROM digit_table.h ^^^^^^^^^^ // vvvvvvvvvv DERIVED FROM d2s_full_table.h vvvvvvvvvv diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index 2643b4cbe56..9b1c2b0bda7 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -253,7 +253,7 @@ protected: }; // STATIC numpunct::id OBJECT -#if !(defined _CRTBLD && defined _BUILDING_SATELLITE_2) // TRANSITION, VSO-578955 +#if !defined(_CRTBLD) || defined(CRTDLL2) || !defined(_DLL) || defined(_M_CEE_PURE) // TRANSITION, VSO-578955 #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdllimport-static-field-def" @@ -265,7 +265,7 @@ __PURE_APPDOMAIN_GLOBAL locale::id numpunct<_Elem>::id; #ifdef __clang__ #pragma clang diagnostic pop #endif // __clang__ -#endif // !(defined _CRTBLD && defined _BUILDING_SATELLITE_2) +#endif // !defined(_CRTBLD) || defined(CRTDLL2) || !defined(_DLL) || defined(_M_CEE_PURE) // CLASS TEMPLATE num_get template >> diff --git a/stl/inc/xmemory b/stl/inc/xmemory index 49828d83b0e..b4bb3212eed 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -1481,27 +1481,6 @@ struct _NODISCARD _Uninitialized_backout { } }; -#ifdef __cpp_lib_concepts -namespace ranges { - // STRUCT TEMPLATE in_out_result - template - struct in_out_result { - /* [[no_unique_address]] */ _In in; - /* [[no_unique_address]] */ _Out out; - - template <_Convertible_from _IIn, _Convertible_from _OOut> - constexpr operator in_out_result<_IIn, _OOut>() const& { - return {in, out}; - } - - template <_Convertible_from<_In> _IIn, _Convertible_from<_Out> _OOut> - constexpr operator in_out_result<_IIn, _OOut>() && { - return {_STD move(in), _STD move(out)}; - } - }; -} // namespace ranges -#endif // __cpp_lib_concepts - // FUNCTION TEMPLATE _Uninitialized_move_unchecked template _CONSTEXPR20_DYNALLOC _NoThrowFwdIt _Uninitialized_move_unchecked( diff --git a/stl/inc/xutility b/stl/inc/xutility index 2e17ebc0b5a..0b923cfd235 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -1013,7 +1013,13 @@ using indirect_result_t = invoke_result_t<_Fn, iter_reference_t<_Its>...>; template _Proj> struct projected { using value_type = remove_cvref_t>; +#if defined(__clang__) || defined(__EDG__) indirect_result_t<_Proj&, _It> operator*() const; +#else // ^^^ no workaround / workaround vvv + indirect_result_t<_Proj&, _It> operator*() const { + _CSTD abort(); // TRANSITION, VSO-1308657 + } +#endif // ^^^ workaround ^^^ }; #ifdef __clang__ @@ -4210,6 +4216,85 @@ _FwdIt2 copy(_ExPo&&, _FwdIt1 _First, _FwdIt1 _Last, _FwdIt2 _Dest) noexcept /* } #endif // _HAS_CXX17 +#ifdef __cpp_lib_concepts +namespace ranges { + // CONCEPT _Convertible_from + template + concept _Convertible_from = convertible_to<_From, _To>; + + // STRUCT TEMPLATE in_out_result + template + struct in_out_result { + /* [[no_unique_address]] */ _In in; + /* [[no_unique_address]] */ _Out out; + + template <_Convertible_from _IIn, _Convertible_from _OOut> + constexpr operator in_out_result<_IIn, _OOut>() const& { + return {in, out}; + } + + template <_Convertible_from<_In> _IIn, _Convertible_from<_Out> _OOut> + constexpr operator in_out_result<_IIn, _OOut>() && { + return {_STD move(in), _STD move(out)}; + } + }; + + // ALIAS TEMPLATE copy_result + template + using copy_result = in_out_result<_In, _Out>; + + // VARIABLE ranges::copy + // clang-format off + template _Se, weakly_incrementable _Out> + requires indirectly_copyable<_It, _Out> + _NODISCARD constexpr copy_result<_It, _Out> _Copy_unchecked(_It _First, _Se _Last, _Out _Result) { + // clang-format on + if constexpr (_Ptr_copy_cat<_It, _Out>::_Trivially_copyable && sized_sentinel_for<_Se, _It>) { + if (!_STD is_constant_evaluated()) { + auto _Final = _RANGES next(_First, _STD move(_Last)); + _Result = _Copy_memmove(_STD move(_First), _Final, _STD move(_Result)); + return {_STD move(_Final), _STD move(_Result)}; + } + } + + for (; _First != _Last; ++_First, (void) ++_Result) { + *_Result = *_First; + } + + return {_STD move(_First), _STD move(_Result)}; + } + + class _Copy_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, weakly_incrementable _Out> + requires indirectly_copyable<_It, _Out> + constexpr copy_result<_It, _Out> operator()(_It _First, _Se _Last, _Out _Result) const { + _Adl_verify_range(_First, _Last); + auto _UResult = _RANGES _Copy_unchecked( + _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _STD move(_Result)); + _Seek_wrapped(_First, _STD move(_UResult.in)); + return {_STD move(_First), _STD move(_UResult.out)}; + } + + template + requires indirectly_copyable, _Out> + constexpr copy_result, _Out> operator()(_Rng&& _Range, _Out _Result) const { + auto _First = _RANGES begin(_Range); + auto _UResult = + _RANGES _Copy_unchecked(_Get_unwrapped(_STD move(_First)), _Uend(_Range), _STD move(_Result)); + _Seek_wrapped(_First, _STD move(_UResult.in)); + return {_STD move(_First), _STD move(_UResult.out)}; + } + // clang-format on + }; + + inline constexpr _Copy_fn copy{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE copy_n template _CONSTEXPR20 _OutIt copy_n(_InIt _First, _Diff _Count_raw, _OutIt _Dest) { @@ -4565,6 +4650,46 @@ _FwdIt fill_n(_ExPo&&, _FwdIt _Dest, _Diff _Count_raw, const _Ty& _Val) noexcept } #endif // _HAS_CXX17 +#ifdef __cpp_lib_concepts +namespace ranges { + // VARIABLE ranges::fill_n + class _Fill_n_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + template _It> + constexpr _It operator()(_It _First, iter_difference_t<_It> _Count, const _Ty& _Value) const { + if (_Count > 0) { + auto _UFirst = _Get_unwrapped_n(_STD move(_First), _Count); + if (!_STD is_constant_evaluated()) { + if constexpr (_Fill_memset_is_safe) { + _Fill_memset(_UFirst, _Value, static_cast(_Count)); + _Seek_wrapped(_First, _UFirst + _Count); // no need to move since _UFirst is a pointer + return _First; + } else if constexpr (_Fill_zero_memset_is_safe) { + if (_Is_all_bits_zero(_Value)) { + _Fill_zero_memset(_UFirst, static_cast(_Count)); + _Seek_wrapped(_First, _UFirst + _Count); // no need to move since _UFirst is a pointer + return _First; + } + } + } + + for (; _Count > 0; ++_UFirst, (void) --_Count) { + *_UFirst = _Value; + } + + _Seek_wrapped(_First, _STD move(_UFirst)); + } + + return _First; + } + }; + + inline constexpr _Fill_n_fn fill_n{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE equal // _Can_memcmp_elements<_Elem1, _Elem2> reports whether `_Elem1 == _Elem2` can be optimized to memcmp. @@ -4780,10 +4905,6 @@ namespace ranges { } } - // CONCEPT _Convertible_from - template - concept _Convertible_from = convertible_to<_From, _To>; - // STRUCT TEMPLATE in_in_result template struct in_in_result { @@ -5229,11 +5350,13 @@ _NODISCARD _FwdIt find(_ExPo&& _Exec, _FwdIt _First, const _FwdIt _Last, const _ #ifdef __cpp_lib_concepts namespace ranges { + // VARIABLE ranges::find // clang-format off // concept-constrained for strict enforcement as it is used by several algorithms template _Se, class _Ty, class _Pj = identity> requires indirect_binary_predicate, const _Ty*> _NODISCARD constexpr _It _Find_unchecked(_It _First, const _Se _Last, const _Ty& _Val, _Pj _Proj = {}) { + // clang-format on if constexpr (_Memchr_in_find_is_safe<_It, _Ty> && sized_sentinel_for<_Se, _It> && same_as<_Pj, identity>) { if (!_STD is_constant_evaluated()) { if (!_Within_limits(_First, _Val)) { @@ -5241,8 +5364,8 @@ namespace ranges { } const auto _First_ptr = _STD to_address(_First); - const auto _Result = static_cast>*>(_CSTD memchr(_First_ptr, - static_cast(_Val), static_cast(_Last - _First))); + const auto _Result = static_cast>*>( + _CSTD memchr(_First_ptr, static_cast(_Val), static_cast(_Last - _First))); if (_Result) { if constexpr (is_pointer_v<_It>) { return _Result; @@ -5263,7 +5386,38 @@ namespace ranges { return _First; } - // clang-format on + + class _Find_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, class _Ty, class _Pj = identity> + requires indirect_binary_predicate, const _Ty*> + _NODISCARD constexpr _It operator()(_It _First, _Se _Last, const _Ty& _Val, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Last); + auto _UResult = _RANGES _Find_unchecked( + _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _Val, _Pass_fn(_Proj)); + + _Seek_wrapped(_First, _STD move(_UResult)); + return _First; + } + + template + requires indirect_binary_predicate, _Pj>, const _Ty*> + _NODISCARD constexpr borrowed_iterator_t<_Rng> operator()( + _Rng&& _Range, const _Ty& _Val, _Pj _Proj = {}) const { + auto _First = _RANGES begin(_Range); + auto _UResult = + _RANGES _Find_unchecked(_Get_unwrapped(_STD move(_First)), _Uend(_Range), _Val, _Pass_fn(_Proj)); + + _Seek_wrapped(_First, _STD move(_UResult)); + return _First; + } + // clang-format on + }; + + inline constexpr _Find_fn find{_Not_quite_object::_Construct_tag{}}; } // namespace ranges #endif // __cpp_lib_concepts @@ -5821,6 +5975,11 @@ struct _Nontrivial_dummy_type { }; _STL_INTERNAL_STATIC_ASSERT(!is_trivially_default_constructible_v<_Nontrivial_dummy_type>); +#ifdef __cpp_lib_variant +// CLASS monostate [variant.monostate] +struct monostate {}; +#endif // __cpp_lib_variant + _STD_END #pragma pop_macro("new") _STL_RESTORE_CLANG_WARNINGS diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index fd88225e9db..5a920d16407 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -165,6 +165,7 @@ // P0608R3 Improving variant's Converting Constructor/Assignment // P0616R0 Using move() In // P0631R8 Math Constants +// P0645R10 Text Formatting // P0646R1 list/forward_list remove()/remove_if()/unique() Return size_type // P0653R2 to_address() // P0655R1 visit() @@ -1226,9 +1227,14 @@ #define __cpp_lib_constexpr_vector 201907L #endif // defined(__cpp_constexpr_dynamic_alloc) && !defined(__clang__) -#define __cpp_lib_destroying_delete 201806L -#define __cpp_lib_endian 201907L -#define __cpp_lib_erase_if 202002L +#define __cpp_lib_destroying_delete 201806L +#define __cpp_lib_endian 201907L +#define __cpp_lib_erase_if 202002L + +#ifdef __cpp_lib_concepts // TRANSITION, GH-395 +#define __cpp_lib_format 201907L +#endif // __cpp_lib_concepts + #define __cpp_lib_generic_unordered_lookup 201811L #define __cpp_lib_int_pow2 202002L #define __cpp_lib_integer_comparison_functions 202002L diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index 39c03dca856..e667375acea 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -301,10 +301,6 @@ std/utilities/variant/variant.variant/variant.assign/T.pass.cpp FAIL std/utilities/variant/variant.variant/variant.ctor/conv.pass.cpp FAIL std/utilities/variant/variant.variant/variant.ctor/T.pass.cpp FAIL -# C++20 P0645R10 " Text Formatting" -std/language.support/support.limits/support.limits.general/format.version.pass.cpp FAIL -std/utilities/format/format.error/format.error.pass.cpp FAIL - # C++20 P0784R7 "More constexpr containers" std/utilities/memory/allocator.traits/allocator.traits.members/construct.pass.cpp FAIL std/utilities/memory/allocator.traits/allocator.traits.members/destroy.pass.cpp FAIL diff --git a/tests/libcxx/skipped_tests.txt b/tests/libcxx/skipped_tests.txt index 070c57f94f0..3ac38303153 100644 --- a/tests/libcxx/skipped_tests.txt +++ b/tests/libcxx/skipped_tests.txt @@ -301,10 +301,6 @@ utilities\variant\variant.variant\variant.assign\T.pass.cpp utilities\variant\variant.variant\variant.ctor\conv.pass.cpp utilities\variant\variant.variant\variant.ctor\T.pass.cpp -# C++20 P0645R10 " Text Formatting" -language.support\support.limits\support.limits.general\format.version.pass.cpp -utilities\format\format.error\format.error.pass.cpp - # C++20 P0784R7 "More constexpr containers" utilities\memory\allocator.traits\allocator.traits.members\construct.pass.cpp utilities\memory\allocator.traits\allocator.traits.members\destroy.pass.cpp diff --git a/tests/std/include/test_format_support.hpp b/tests/std/include/test_format_support.hpp new file mode 100644 index 00000000000..89dd541e85c --- /dev/null +++ b/tests/std/include/test_format_support.hpp @@ -0,0 +1,127 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once + +#include +#include +#include +#include +#include + +// copied from the string_view tests +template +struct choose_literal; // not defined + +template <> +struct choose_literal { + static constexpr const char* choose(const char* s, const wchar_t*) { + return s; + } +}; + +template <> +struct choose_literal { + static constexpr const wchar_t* choose(const char*, const wchar_t* s) { + return s; + } +}; + +#define TYPED_LITERAL(CharT, Literal) (choose_literal::choose(Literal, L##Literal)) + +template +struct noop_testing_callbacks { + constexpr void _On_align(std::_Fmt_align) {} + constexpr void _On_fill(std::basic_string_view) {} + constexpr void _On_width(unsigned int) {} + constexpr void _On_dynamic_width(std::size_t) {} + constexpr void _On_dynamic_width(std::_Auto_id_tag) {} + constexpr void _On_precision(unsigned int) {} + constexpr void _On_dynamic_precision(std::size_t) {} + constexpr void _On_dynamic_precision(std::_Auto_id_tag) {} + constexpr void _On_sign(std::_Fmt_sign) {} + constexpr void _On_hash() {} + constexpr void _On_zero() {} + constexpr void _On_localized() {} + constexpr void _On_type(CharT) {} +}; + +template +struct testing_callbacks { + std::_Fmt_align expected_alignment = std::_Fmt_align::_None; + std::_Fmt_sign expected_sign = std::_Fmt_sign::_None; + std::basic_string_view expected_fill; + int expected_width = -1; + std::size_t expected_dynamic_width = static_cast(-1); + bool expected_auto_dynamic_width = false; + int expected_precision = -1; + std::size_t expected_dynamic_precision = static_cast(-1); + bool expected_auto_dynamic_precision = false; + bool expected_hash = false; + bool expected_zero = false; + bool expected_localized = false; + CharT expected_type = '\0'; + + constexpr void _On_align(std::_Fmt_align aln) { + assert(aln == expected_alignment); + } + constexpr void _On_fill(std::basic_string_view str_view) { + assert(str_view == expected_fill); + } + constexpr void _On_width(int width) { + assert(width == expected_width); + } + constexpr void _On_dynamic_width(std::size_t id) { + assert(id == expected_dynamic_width); + } + constexpr void _On_dynamic_width(std::_Auto_id_tag) { + assert(expected_auto_dynamic_width); + } + constexpr void _On_precision(int pre) { + assert(pre == expected_precision); + } + constexpr void _On_dynamic_precision(std::size_t id) { + assert(id == expected_dynamic_precision); + } + constexpr void _On_dynamic_precision(std::_Auto_id_tag) { + assert(expected_auto_dynamic_precision); + } + constexpr void _On_sign(std::_Fmt_sign sgn) { + assert(sgn == expected_sign); + } + constexpr void _On_hash() { + assert(expected_hash); + } + constexpr void _On_zero() { + assert(expected_zero); + } + constexpr void _On_localized() { + assert(expected_localized); + } + constexpr void _On_type(CharT type) { + assert(type == expected_type); + } +}; +template +testing_callbacks(std::_Fmt_align, std::basic_string_view) -> testing_callbacks; + +struct testing_arg_id_callbacks { + constexpr void _On_auto_id() {} + constexpr void _On_manual_id(std::size_t) {} +}; + +template +void test_parse_helper(const CharT* (*func)(const CharT*, const CharT*, callback_type&&), + std::basic_string_view view, bool err_expected = false, + typename std::basic_string_view::size_type expected_end_position = std::basic_string_view::npos, + callback_type&& callbacks = {}) { + try { + auto end = func(view.data(), view.data() + view.size(), std::move(callbacks)); + if (expected_end_position != std::basic_string_view::npos) { + assert(end == view.data() + expected_end_position); + } + assert(!err_expected); + } catch (const std::format_error&) { + assert(err_expected); + } +} diff --git a/tests/std/test.lst b/tests/std/test.lst index 332340ef722..cba3615d387 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -232,6 +232,7 @@ tests\P0339R6_polymorphic_allocator tests\P0355R7_calendars_and_time_zones_clocks tests\P0355R7_calendars_and_time_zones_dates tests\P0355R7_calendars_and_time_zones_dates_literals +tests\P0355R7_calendars_and_time_zones_formatting tests\P0355R7_calendars_and_time_zones_hms tests\P0355R7_calendars_and_time_zones_io tests\P0355R7_calendars_and_time_zones_time_point_and_durations @@ -258,6 +259,14 @@ tests\P0607R0_inline_variables tests\P0608R3_improved_variant_converting_constructor tests\P0616R0_using_move_in_numeric tests\P0631R8_numbers_math_constants +tests\P0645R10_text_formatting_args +tests\P0645R10_text_formatting_custom_formatting +tests\P0645R10_text_formatting_death +tests\P0645R10_text_formatting_formatting +tests\P0645R10_text_formatting_legacy_text_encoding +tests\P0645R10_text_formatting_parse_contexts +tests\P0645R10_text_formatting_parsing +tests\P0645R10_text_formatting_utf8 tests\P0660R10_jthread_and_cv_any tests\P0660R10_stop_token tests\P0660R10_stop_token_death diff --git a/tests/std/tests/P0067R5_charconv/test.cpp b/tests/std/tests/P0067R5_charconv/test.cpp index 4201ccf9856..8eaf8e2a9b2 100644 --- a/tests/std/tests/P0067R5_charconv/test.cpp +++ b/tests/std/tests/P0067R5_charconv/test.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,7 @@ #include "float_hex_precision_to_chars_test_cases.hpp" #include "float_scientific_precision_to_chars_test_cases.hpp" #include "float_to_chars_test_cases.hpp" +#include "wchar_test_cases.hpp" #include using namespace std; @@ -1079,6 +1081,44 @@ void test_right_shift_64_bits_with_rounding() { assert(_Right_shift_with_rounding(0xffff'ffff'ffff'ffffULL, 64, false) == 1); } +void wchar_tests() { + static_assert(size(__DIGIT_TABLE) == size(__DIGIT_TABLE)); + auto& fac = use_facet>(locale{}); + for (size_t i = 0; i < size(__DIGIT_TABLE); ++i) { + assert(fac.widen(__DIGIT_TABLE[i]) == __DIGIT_TABLE[i]); + } + + wchar_t buffer[32]; + for (const auto& t : double_to_wide_test_cases) { + const auto result = __d2s_buffered_n(begin(buffer), end(buffer), t.value, t.fmt); + assert(result.second == errc{}); + const wstring_view sv(t.correct); + assert(equal(buffer, result.first, sv.begin(), sv.end())); + } + + for (const auto& t : float_to_wide_test_cases) { + const auto result = __f2s_buffered_n(begin(buffer), end(buffer), t.value, t.fmt); + assert(result.second == errc{}); + const wstring_view sv(t.correct); + assert(equal(buffer, result.first, sv.begin(), sv.end())); + } + + for (const auto& t : wide_digit_pairs_test_cases) { + const auto result = + __d2fixed_buffered_n(begin(buffer), end(buffer), t.value, static_cast(t.precision)); + assert(result.second == errc{}); + const wstring_view sv(t.correct); + assert(equal(buffer, result.first, sv.begin(), sv.end())); + } +} + +// GH-1569 - Test instantiation of wchar_t helpers. +template pair std::__to_chars( + wchar_t* const, wchar_t* const, const __floating_decimal_32, chars_format, const uint32_t, const uint32_t); +template pair std::__to_chars( + wchar_t* const, wchar_t* const, const __floating_decimal_64, chars_format, const double); +template pair std::__d2fixed_buffered_n(wchar_t*, wchar_t* const, const double, const uint32_t); + int main(int argc, char** argv) { const auto start = chrono::steady_clock::now(); @@ -1092,6 +1132,8 @@ int main(int argc, char** argv) { test_right_shift_64_bits_with_rounding(); + wchar_tests(); + const auto finish = chrono::steady_clock::now(); const long long ms = chrono::duration_cast(finish - start).count(); diff --git a/tests/std/tests/P0067R5_charconv/test.hpp b/tests/std/tests/P0067R5_charconv/test.hpp index b20c766395e..3d0275b9899 100644 --- a/tests/std/tests/P0067R5_charconv/test.hpp +++ b/tests/std/tests/P0067R5_charconv/test.hpp @@ -31,6 +31,12 @@ struct FloatToCharsTestCase { const char* correct; }; +struct FloatToWideTestCase { + float value; + chars_format fmt; + const wchar_t* correct; +}; + struct FloatPrecisionToCharsTestCase { float value; chars_format fmt; @@ -52,9 +58,22 @@ struct DoubleToCharsTestCase { const char* correct; }; +struct DoubleToWideTestCase { + double value; + chars_format fmt; + const wchar_t* correct; +}; + struct DoublePrecisionToCharsTestCase { double value; chars_format fmt; int precision; const char* correct; }; + +struct DoublePrecisionToWideTestCase { + double value; + chars_format fmt; + int precision; + const wchar_t* correct; +}; diff --git a/tests/std/tests/P0067R5_charconv/wchar_test_cases.hpp b/tests/std/tests/P0067R5_charconv/wchar_test_cases.hpp new file mode 100644 index 00000000000..795ac517987 --- /dev/null +++ b/tests/std/tests/P0067R5_charconv/wchar_test_cases.hpp @@ -0,0 +1,167 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once + +#include + +#include "test.hpp" +using namespace std; + +// The wchar_t machinery is currently limited to a subset of the Ryu code. It is known to not handle: negative numbers, +// infinity, NaN, or hex formatting. + +inline constexpr DoublePrecisionToWideTestCase wide_digit_pairs_test_cases[] = { + {0.0001020304, chars_format::fixed, 10, L"0.0001020304"}, + {0.0506070809, chars_format::fixed, 10, L"0.0506070809"}, + {0.1011121314, chars_format::fixed, 10, L"0.1011121314"}, + {0.1516171819, chars_format::fixed, 10, L"0.1516171819"}, + {0.2021222324, chars_format::fixed, 10, L"0.2021222324"}, + {0.2526272829, chars_format::fixed, 10, L"0.2526272829"}, + {0.3031323334, chars_format::fixed, 10, L"0.3031323334"}, + {0.3536373839, chars_format::fixed, 10, L"0.3536373839"}, + {0.4041424344, chars_format::fixed, 10, L"0.4041424344"}, + {0.4546474849, chars_format::fixed, 10, L"0.4546474849"}, + {0.5051525354, chars_format::fixed, 10, L"0.5051525354"}, + {0.5556575859, chars_format::fixed, 10, L"0.5556575859"}, + {0.6061626364, chars_format::fixed, 10, L"0.6061626364"}, + {0.6566676869, chars_format::fixed, 10, L"0.6566676869"}, + {0.7071727374, chars_format::fixed, 10, L"0.7071727374"}, + {0.7576777879, chars_format::fixed, 10, L"0.7576777879"}, + {0.8081828384, chars_format::fixed, 10, L"0.8081828384"}, + {0.8586878889, chars_format::fixed, 10, L"0.8586878889"}, + {0.9091929394, chars_format::fixed, 10, L"0.9091929394"}, + {0.9596979899, chars_format::fixed, 10, L"0.9596979899"}, +}; + +inline constexpr DoubleToWideTestCase double_to_wide_test_cases[] = { + // Test special cases (zero, inf, nan) and an ordinary case. Also test negative signs. + {0.0, chars_format::scientific, L"0e+00"}, + //{-0.0, chars_format::scientific, L"-0e+00"}, + //{double_inf, chars_format::scientific, L"inf"}, + //{-double_inf, chars_format::scientific, L"-inf"}, + //{double_nan, chars_format::scientific, L"nan"}, + //{-double_nan, chars_format::scientific, L"-nan(ind)"}, + //{double_nan_payload, chars_format::scientific, L"nan"}, + //{-double_nan_payload, chars_format::scientific, L"-nan"}, + {2.018, chars_format::scientific, L"2.018e+00"}, + //{-2.018, chars_format::scientific, L"-2.018e+00"}, + {0.2018, chars_format::scientific, L"2.018e-01"}, + //{-0.2018, chars_format::scientific, L"-2.018e-01"}, + + // Ditto for fixed, which doesn't emit exponents. + {0.0, chars_format::fixed, L"0"}, + //{-0.0, chars_format::fixed, L"-0"}, + //{double_inf, chars_format::fixed, L"inf"}, + //{-double_inf, chars_format::fixed, L"-inf"}, + //{double_nan, chars_format::fixed, L"nan"}, + //{-double_nan, chars_format::fixed, L"-nan(ind)"}, + //{double_nan_payload, chars_format::fixed, L"nan"}, + //{-double_nan_payload, chars_format::fixed, L"-nan"}, + {2.018, chars_format::fixed, L"2.018"}, + //{-2.018, chars_format::fixed, L"-2.018"}, + + // Ditto for general, which selects fixed for the scientific exponent 0. + {0.0, chars_format::general, L"0"}, + //{-0.0, chars_format::general, L"-0"}, + //{double_inf, chars_format::general, L"inf"}, + //{-double_inf, chars_format::general, L"-inf"}, + //{double_nan, chars_format::general, L"nan"}, + //{-double_nan, chars_format::general, L"-nan(ind)"}, + //{double_nan_payload, chars_format::general, L"nan"}, + //{-double_nan_payload, chars_format::general, L"-nan"}, + {2.018, chars_format::general, L"2.018"}, + //{-2.018, chars_format::general, L"-2.018"}, + + // Ditto for plain, which selects fixed because it's shorter for these values. + {0.0, chars_format{}, L"0"}, + //{-0.0, chars_format{}, L"-0"}, + //{double_inf, chars_format{}, L"inf"}, + //{-double_inf, chars_format{}, L"-inf"}, + //{double_nan, chars_format{}, L"nan"}, + //{-double_nan, chars_format{}, L"-nan(ind)"}, + //{double_nan_payload, chars_format{}, L"nan"}, + //{-double_nan_payload, chars_format{}, L"-nan"}, + {2.018, chars_format{}, L"2.018"}, + //{-2.018, chars_format{}, L"-2.018"}, + + // Ditto for hex. + //{0.0, chars_format::hex, L"0p+0"}, + //{-0.0, chars_format::hex, L"-0p+0"}, + //{double_inf, chars_format::hex, L"inf"}, + //{-double_inf, chars_format::hex, L"-inf"}, + //{double_nan, chars_format::hex, L"nan"}, + //{-double_nan, chars_format::hex, L"-nan(ind)"}, + //{double_nan_payload, chars_format::hex, L"nan"}, + //{-double_nan_payload, chars_format::hex, L"-nan"}, + //{0x1.729p+0, chars_format::hex, L"1.729p+0"}, + //{-0x1.729p+0, chars_format::hex, L"-1.729p+0"}, + //{0x1.729p-1, chars_format::hex, L"1.729p-1"}, + //{-0x1.729p-1, chars_format::hex, L"-1.729p-1"}, +}; + +inline constexpr FloatToWideTestCase float_to_wide_test_cases[] = { + // Test special cases (zero, inf, nan) and an ordinary case. Also test negative signs. + {0.0f, chars_format::scientific, L"0e+00"}, + //{-0.0f, chars_format::scientific, L"-0e+00"}, + //{float_inf, chars_format::scientific, L"inf"}, + //{-float_inf, chars_format::scientific, L"-inf"}, + //{float_nan, chars_format::scientific, L"nan"}, + //{-float_nan, chars_format::scientific, L"-nan(ind)"}, + //{float_nan_payload, chars_format::scientific, L"nan"}, + //{-float_nan_payload, chars_format::scientific, L"-nan"}, + {2.018f, chars_format::scientific, L"2.018e+00"}, + //{-2.018f, chars_format::scientific, L"-2.018e+00"}, + {0.2018f, chars_format::scientific, L"2.018e-01"}, + //{-0.2018f, chars_format::scientific, L"-2.018e-01"}, + + // Ditto for fixed, which doesn't emit exponents. + {0.0f, chars_format::fixed, L"0"}, + //{-0.0f, chars_format::fixed, L"-0"}, + //{float_inf, chars_format::fixed, L"inf"}, + //{-float_inf, chars_format::fixed, L"-inf"}, + //{float_nan, chars_format::fixed, L"nan"}, + //{-float_nan, chars_format::fixed, L"-nan(ind)"}, + //{float_nan_payload, chars_format::fixed, L"nan"}, + //{-float_nan_payload, chars_format::fixed, L"-nan"}, + {2.018f, chars_format::fixed, L"2.018"}, + //{-2.018f, chars_format::fixed, L"-2.018"}, + + // Ditto for general, which selects fixed for the scientific exponent 0. + {0.0f, chars_format::general, L"0"}, + //{-0.0f, chars_format::general, L"-0"}, + //{float_inf, chars_format::general, L"inf"}, + //{-float_inf, chars_format::general, L"-inf"}, + //{float_nan, chars_format::general, L"nan"}, + //{-float_nan, chars_format::general, L"-nan(ind)"}, + //{float_nan_payload, chars_format::general, L"nan"}, + //{-float_nan_payload, chars_format::general, L"-nan"}, + {2.018f, chars_format::general, L"2.018"}, + //{-2.018f, chars_format::general, L"-2.018"}, + + // Ditto for plain, which selects fixed because it's shorter for these values. + {0.0f, chars_format{}, L"0"}, + //{-0.0f, chars_format{}, L"-0"}, + //{float_inf, chars_format{}, L"inf"}, + //{-float_inf, chars_format{}, L"-inf"}, + //{float_nan, chars_format{}, L"nan"}, + //{-float_nan, chars_format{}, L"-nan(ind)"}, + //{float_nan_payload, chars_format{}, L"nan"}, + //{-float_nan_payload, chars_format{}, L"-nan"}, + {2.018f, chars_format{}, L"2.018"}, + //{-2.018f, chars_format{}, L"-2.018"}, + + // Ditto for hex. + //{0.0f, chars_format::hex, L"0p+0"}, + //{-0.0f, chars_format::hex, L"-0p+0"}, + //{float_inf, chars_format::hex, L"inf"}, + //{-float_inf, chars_format::hex, L"-inf"}, + //{float_nan, chars_format::hex, L"nan"}, + //{-float_nan, chars_format::hex, L"-nan(ind)"}, + //{float_nan_payload, chars_format::hex, L"nan"}, + //{-float_nan_payload, chars_format::hex, L"-nan"}, + //{0x1.729p+0f, chars_format::hex, L"1.729p+0"}, + //{-0x1.729p+0f, chars_format::hex, L"-1.729p+0"}, + //{0x1.729p-1f, chars_format::hex, L"1.729p-1"}, + //{-0x1.729p-1f, chars_format::hex, L"-1.729p-1"}, +}; diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/env.lst b/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp new file mode 100644 index 00000000000..2ae3a52bb3e --- /dev/null +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp @@ -0,0 +1,197 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace chrono; + +// copied from the string_view tests +template +struct choose_literal; // not defined + +template <> +struct choose_literal { + static constexpr const char* choose(const char* s, const wchar_t*) { + return s; + } +}; + +template <> +struct choose_literal { + static constexpr const wchar_t* choose(const char*, const wchar_t* s) { + return s; + } +}; + +#define TYPED_LITERAL(CharT, Literal) (choose_literal::choose(Literal, L##Literal)) + +template +struct testing_callbacks { + _Fmt_align expected_alignment = _Fmt_align::_None; + basic_string_view expected_fill; + int expected_width = -1; + size_t expected_dynamic_width = static_cast(-1); + bool expected_auto_dynamic_width = false; + int expected_precision = -1; + size_t expected_dynamic_precision = static_cast(-1); + bool expected_auto_dynamic_precision = false; + vector<_Chrono_specs>& expected_chrono_specs; + size_t curr_index = 0; + + void _On_align(_Fmt_align aln) { + assert(aln == expected_alignment); + } + void _On_fill(basic_string_view str_view) { + assert(str_view == expected_fill); + } + void _On_width(int width) { + assert(width == expected_width); + } + void _On_dynamic_width(size_t id) { + assert(id == expected_dynamic_width); + } + void _On_dynamic_width(_Auto_id_tag) { + assert(expected_auto_dynamic_width); + } + void _On_precision(int pre) { + assert(pre == expected_precision); + } + void _On_dynamic_precision(size_t id) { + assert(id == expected_dynamic_precision); + } + void _On_dynamic_precision(_Auto_id_tag) { + assert(expected_auto_dynamic_precision); + } + void _On_conversion_spec(CharT mod, CharT type) { + assert(static_cast(mod) == expected_chrono_specs[curr_index]._Modifier); + assert(static_cast(type) == expected_chrono_specs[curr_index]._Type); + assert(expected_chrono_specs[curr_index]._Lit_char == CharT{0}); // not set + ++curr_index; + } + void _On_lit_char(CharT ch) { + assert(ch == expected_chrono_specs[curr_index]._Lit_char); + assert(expected_chrono_specs[curr_index]._Modifier == '\0'); // not set + assert(expected_chrono_specs[curr_index]._Type == '\0'); // not set + ++curr_index; + } +}; + +template +void test_parse_helper(const CharT* (*func)(const CharT*, const CharT*, callback_type&&), basic_string_view view, + bool err_expected = false, + typename basic_string_view::size_type expected_end_position = basic_string_view::npos, + callback_type&& callbacks = {}) { + try { + auto end = func(view.data(), view.data() + view.size(), move(callbacks)); + if (expected_end_position != basic_string_view::npos) { + assert(end == view.data() + expected_end_position); + } + assert(!err_expected); + } catch (const format_error&) { + assert(err_expected); + } +} + +template +bool test_parse_conversion_spec() { + auto parse_conv_spec_fn = _Parse_conversion_specs>; + using view_typ = basic_string_view; + using chrono_spec = _Chrono_specs; + + view_typ s0(TYPED_LITERAL(CharT, "%B")); + view_typ s1(TYPED_LITERAL(CharT, "%Ec")); + view_typ s2(TYPED_LITERAL(CharT, "%Od")); + view_typ s3(TYPED_LITERAL(CharT, "%E")); + view_typ s4(TYPED_LITERAL(CharT, "%")); + view_typ s5(TYPED_LITERAL(CharT, "%}")); + view_typ s6(TYPED_LITERAL(CharT, "%E}")); + + vector v0{{._Type = 'B'}}; + test_parse_helper(parse_conv_spec_fn, s0, false, view_typ::npos, {.expected_chrono_specs = v0}); + + vector v1{{._Modifier = 'E', ._Type = 'c'}}; + test_parse_helper(parse_conv_spec_fn, s1, false, view_typ::npos, {.expected_chrono_specs = v1}); + + vector v2{{._Modifier = 'O', ._Type = 'd'}}; + test_parse_helper(parse_conv_spec_fn, s2, false, view_typ::npos, {.expected_chrono_specs = v2}); + + vector v{}; + test_parse_helper(parse_conv_spec_fn, s3, true, view_typ::npos, {.expected_chrono_specs = v}); + test_parse_helper(parse_conv_spec_fn, s4, true, view_typ::npos, {.expected_chrono_specs = v}); + test_parse_helper(parse_conv_spec_fn, s5, true, view_typ::npos, {.expected_chrono_specs = v}); + test_parse_helper(parse_conv_spec_fn, s6, true, view_typ::npos, {.expected_chrono_specs = v}); + + return true; +} + +template +bool test_parse_chrono_format_specs() { + auto parse_chrono_format_specs_fn = _Parse_chrono_format_specs>; + using view_typ = basic_string_view; + using chrono_spec = _Chrono_specs; + + view_typ s0(TYPED_LITERAL(CharT, "%Oe")); + view_typ s1(TYPED_LITERAL(CharT, "lit")); + view_typ s2(TYPED_LITERAL(CharT, "%H:%M}")); + view_typ s3(TYPED_LITERAL(CharT, "6%H}")); + view_typ s4(TYPED_LITERAL(CharT, "*<6hi")); + view_typ s5(TYPED_LITERAL(CharT, "*^4.4%ymm")); + view_typ s6(TYPED_LITERAL(CharT, "%H%")); + view_typ s7(TYPED_LITERAL(CharT, "%H%}")); + view_typ s8(TYPED_LITERAL(CharT, "A%nB%tC%%D")); + + vector v0{{._Modifier = 'O', ._Type = 'e'}}; + test_parse_helper(parse_chrono_format_specs_fn, s0, false, s0.size(), {.expected_chrono_specs = v0}); + + vector v1{{._Lit_char = 'l'}, {._Lit_char = 'i'}, {._Lit_char = 't'}}; + test_parse_helper(parse_chrono_format_specs_fn, s1, false, s1.size(), {.expected_chrono_specs = v1}); + + vector v2{{._Type = 'H'}, {._Lit_char = ':'}, {._Type = 'M'}}; + test_parse_helper(parse_chrono_format_specs_fn, s2, false, s2.size() - 1, {.expected_chrono_specs = v2}); + + vector v3{{._Type = 'H'}}; + test_parse_helper( + parse_chrono_format_specs_fn, s3, false, s3.size() - 1, {.expected_width = 6, .expected_chrono_specs = v3}); + + vector v8{{._Lit_char = 'A'}, {._Lit_char = '\n'}, {._Lit_char = 'B'}, {._Lit_char = '\t'}, + {._Lit_char = 'C'}, {._Lit_char = '%'}, {._Lit_char = 'D'}}; + test_parse_helper(parse_chrono_format_specs_fn, s8, false, s8.size(), {.expected_chrono_specs = v8}); + + vector v4{{._Lit_char = 'h'}, {._Lit_char = 'i'}}; + test_parse_helper(parse_chrono_format_specs_fn, s4, false, s4.size(), + {.expected_alignment = _Fmt_align::_Left, + .expected_fill = view_typ(TYPED_LITERAL(CharT, "*")), + .expected_width = 6, + .expected_chrono_specs = v4}); + + vector v5{{._Type = 'y'}, {._Lit_char = 'm'}, {._Lit_char = 'm'}}; + test_parse_helper(parse_chrono_format_specs_fn, s5, false, s5.size(), + {.expected_alignment = _Fmt_align::_Center, + .expected_fill = view_typ(TYPED_LITERAL(CharT, "*")), + .expected_width = 4, + .expected_precision = 4, + .expected_chrono_specs = v5}); + + vector v{{._Type = 'H'}}; // we don't throw a format_error until we parse the %H + test_parse_helper(parse_chrono_format_specs_fn, s6, true, view_typ::npos, {.expected_chrono_specs = v}); + test_parse_helper(parse_chrono_format_specs_fn, s7, true, view_typ::npos, {.expected_chrono_specs = v}); + + return true; +} + +int main() { + test_parse_conversion_spec(); + test_parse_conversion_spec(); + + test_parse_chrono_format_specs(); + test_parse_chrono_format_specs(); +} diff --git a/tests/std/tests/P0645R10_text_formatting_args/env.lst b/tests/std/tests/P0645R10_text_formatting_args/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0645R10_text_formatting_args/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0645R10_text_formatting_args/test.cpp b/tests/std/tests/P0645R10_text_formatting_args/test.cpp new file mode 100644 index 00000000000..c78a683a496 --- /dev/null +++ b/tests/std/tests/P0645R10_text_formatting_args/test.cpp @@ -0,0 +1,220 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +template +constexpr auto get_input_literal() { + if constexpr (same_as) { + return "meow"; + } else { + return L"meow"; + } +} + +template +constexpr auto get_input_sv() { + if constexpr (same_as) { + return "meow"sv; + } else { + return L"meow"sv; + } +} + +enum class Arg_type : uint8_t { + none, + int_type, + unsigned_type, + long_long_type, + unsigned_long_long_type, + bool_type, + char_type, + float_type, + double_type, + long_double_type, + pointer_type, + string_literal_type, + string_type, + handle_type, +}; + +template +auto visitor = [](auto&& arg) { + using T = decay_t; + using char_type = typename Context::char_type; + if constexpr (is_same_v) { + return Arg_type::none; + } else if constexpr (is_same_v) { + return Arg_type::int_type; + } else if constexpr (is_same_v) { + return Arg_type::unsigned_type; + } else if constexpr (is_same_v) { + return Arg_type::long_long_type; + } else if constexpr (is_same_v) { + return Arg_type::unsigned_long_long_type; + } else if constexpr (is_same_v) { + return Arg_type::char_type; + } else if constexpr (is_same_v) { + return Arg_type::float_type; + } else if constexpr (is_same_v) { + return Arg_type::double_type; + } else if constexpr (is_same_v) { + return Arg_type::long_double_type; + } else if constexpr (is_same_v) { + return Arg_type::pointer_type; + } else if constexpr (is_same_v) { + return Arg_type::string_literal_type; + } else if constexpr (is_same_v>) { + return Arg_type::string_type; + } else { + return Arg_type::handle_type; + } +}; + +template +void test_basic_format_arg() { + using char_type = typename Context::char_type; + + { // construction + basic_format_arg default_constructed; + assert(!default_constructed); + + basic_format_arg from_int{5}; + assert(from_int); + + basic_format_arg from_unsigned{5u}; + assert(from_unsigned); + + basic_format_arg from_long_long{5ll}; + assert(from_long_long); + + basic_format_arg from_unsigned_long_long{5ull}; + assert(from_unsigned_long_long); + + basic_format_arg from_float{5.0f}; + assert(from_float); + + basic_format_arg from_double{5.0}; + assert(from_double); + + basic_format_arg from_long_double{5.0L}; + assert(from_long_double); + + basic_format_arg from_pointer{static_cast(nullptr)}; + assert(from_pointer); + + basic_format_arg from_literal{get_input_literal()}; + assert(from_literal); + + basic_format_arg from_string_view{get_input_sv()}; + assert(from_string_view); + } +} +template +void test_empty_format_arg() { + const auto store = make_format_args(); + const basic_format_args args{store}; + const same_as> auto first_arg = args.get(0); + assert(!first_arg); +} + +template +void test_single_format_arg(Type value) { + const auto store = make_format_args(value); + const basic_format_args args{store}; + const same_as> auto first_arg = args.get(0); + assert(first_arg); + assert(visit_format_arg(visitor, first_arg) == Result); + const same_as> auto other_arg = args.get(1); + assert(!other_arg); +} + +template +void test_format_arg_store() { + using char_type = typename Context::char_type; + + test_empty_format_arg(); + + test_single_format_arg(42); + if constexpr (is_same_v) { + test_single_format_arg(42); + } + test_single_format_arg(42); + test_single_format_arg(42); + test_single_format_arg(42); + test_single_format_arg(42); + test_single_format_arg(42); + test_single_format_arg(42); + test_single_format_arg(42); + test_single_format_arg(42); + test_single_format_arg(42); + test_single_format_arg(42); + test_single_format_arg(42); + test_single_format_arg(42); + test_single_format_arg(42); + test_single_format_arg(42); + test_single_format_arg(42); + test_single_format_arg(42); + if constexpr (sizeof(int) == sizeof(ptrdiff_t)) { + test_single_format_arg(42); + } else { + test_single_format_arg(42); + } + + test_single_format_arg(42); + test_single_format_arg(42); + test_single_format_arg(42); + test_single_format_arg(42); + test_single_format_arg(42); + if constexpr (is_same_v) { + test_single_format_arg(42); + } else { + test_single_format_arg(42); + } + test_single_format_arg(42); + if constexpr (is_same_v) { + test_single_format_arg(42); + } else { + test_single_format_arg(42); + } + test_single_format_arg(42); + test_single_format_arg(42); + test_single_format_arg(42); + test_single_format_arg(42); + test_single_format_arg(42); + test_single_format_arg(42); + if constexpr (sizeof(unsigned int) == sizeof(size_t)) { + test_single_format_arg(42); + } else { + test_single_format_arg(42); + } + + test_single_format_arg(42.f); + test_single_format_arg(42.); + test_single_format_arg(42.); + + test_single_format_arg(nullptr); + + test_single_format_arg(get_input_literal()); + test_single_format_arg, Arg_type::string_type>(get_input_sv()); +} + +static_assert(sizeof(_Format_arg_store_packed_index) == sizeof(_Format_arg_store_packed_index::_Index_type)); +static_assert(_Get_format_arg_storage_size == sizeof(basic_format_arg::handle)); + +int main() { + test_basic_format_arg(); + test_basic_format_arg(); + test_format_arg_store(); + test_format_arg_store(); +} diff --git a/tests/std/tests/P0645R10_text_formatting_custom_formatting/env.lst b/tests/std/tests/P0645R10_text_formatting_custom_formatting/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0645R10_text_formatting_custom_formatting/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0645R10_text_formatting_custom_formatting/test.cpp b/tests/std/tests/P0645R10_text_formatting_custom_formatting/test.cpp new file mode 100644 index 00000000000..a087e83d50a --- /dev/null +++ b/tests/std/tests/P0645R10_text_formatting_custom_formatting/test.cpp @@ -0,0 +1,149 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +using namespace std; + +// copied from the string_view tests +template +struct choose_literal; // not defined + +template <> +struct choose_literal { + static constexpr const char* choose(const char* s, const wchar_t*) { + return s; + } +}; + +template <> +struct choose_literal { + static constexpr const wchar_t* choose(const char*, const wchar_t* s) { + return s; + } +}; + +#define TYPED_LITERAL(CharT, Literal) (choose_literal::choose(Literal, L##Literal)) +#define STR(Str) TYPED_LITERAL(charT, Str) + +struct basic_custom_formattable_type { + string_view string_content; +}; + +template <> +struct std::formatter { + basic_format_parse_context::iterator parse(basic_format_parse_context& parse_ctx) { + if (parse_ctx.begin() != parse_ctx.end()) { + throw format_error{"only empty specs please"}; + } + return parse_ctx.end(); + } + format_context::iterator format(const basic_custom_formattable_type& val, format_context& ctx) { + ctx.advance_to(copy(val.string_content.begin(), val.string_content.end(), ctx.out())); + return ctx.out(); + } +}; + +template +struct custom_formattable_type { + T val; +}; + +template +struct std::formatter, charT> : std::formatter { + template + auto format(const custom_formattable_type& val, FC& format_ctx) { + return std::formatter::format(val.val, format_ctx); + } +}; + +constexpr void test_disabled_formatter_is_disabled() { + using F = formatter; + + static_assert(!is_default_constructible_v); + static_assert(!is_copy_constructible_v); + static_assert(!is_move_constructible_v); + static_assert(!is_copy_assignable_v); + static_assert(!is_move_assignable_v); +} + +template +void test_custom_equiv_with_format(const charT* fmt_string, const T& val) { + assert(format(fmt_string, custom_formattable_type{val}) == format(fmt_string, val)); +} + +template +void test_custom_equiv_with_format_mixed(const charT* fmt_string, const T& val) { + assert(format(fmt_string, custom_formattable_type{val}, val) == format(fmt_string, val, val)); +} + +template +void test_numeric_custom_formattable_type() { + test_custom_equiv_with_format(STR("{}"), 0); + test_custom_equiv_with_format(STR("{}"), 42); + test_custom_equiv_with_format(STR("{:+}"), 0); + test_custom_equiv_with_format(STR("{}"), numeric_limits::min()); + test_custom_equiv_with_format(STR("{}"), numeric_limits::max()); + test_custom_equiv_with_format(STR("{:3}"), 1); + if constexpr (!is_floating_point_v && !_Is_any_of_v) { + test_custom_equiv_with_format(STR("{:#x}"), 255); + test_custom_equiv_with_format(STR("{:#X}"), 255); + } +} + +template +void test_numeric_mixed_args_custom_formattable_type() { + test_custom_equiv_with_format_mixed(STR("{}{}"), 0); + test_custom_equiv_with_format_mixed(STR("{1}{0}"), 42); + test_custom_equiv_with_format_mixed(STR("{:+}{:-}"), 0); + test_custom_equiv_with_format_mixed(STR("{}{}"), numeric_limits::min()); + test_custom_equiv_with_format_mixed(STR("{}{}"), numeric_limits::max()); + test_custom_equiv_with_format_mixed(STR("{:3}{:4}"), 1); + test_custom_equiv_with_format_mixed(STR("{0}{0}"), 42); + if constexpr (!is_floating_point_v && !_Is_any_of_v) { + test_custom_equiv_with_format_mixed(STR("{:#x}{}"), 255); + test_custom_equiv_with_format_mixed(STR("{:#X}{}"), 255); + } +} + +template +void test_custom_formattable_type() { + test_numeric_custom_formattable_type(); + test_numeric_custom_formattable_type(); + test_numeric_custom_formattable_type(); + test_numeric_custom_formattable_type(); + test_numeric_custom_formattable_type(); +#ifdef _NATIVE_WCHAR_T_DEFINED + test_numeric_custom_formattable_type(); +#endif + test_numeric_custom_formattable_type(); + test_numeric_custom_formattable_type(); +} + +template +void test_mixed_custom_formattable_type() { + test_numeric_mixed_args_custom_formattable_type(); + test_numeric_mixed_args_custom_formattable_type(); + test_numeric_mixed_args_custom_formattable_type(); + test_numeric_mixed_args_custom_formattable_type(); + test_numeric_mixed_args_custom_formattable_type(); +#ifdef _NATIVE_WCHAR_T_DEFINED + test_numeric_mixed_args_custom_formattable_type(); +#endif + test_numeric_mixed_args_custom_formattable_type(); + test_numeric_mixed_args_custom_formattable_type(); +} + +int main() { + assert(format("{}", basic_custom_formattable_type{"f"}) == "f"s); + test_custom_formattable_type(); + test_custom_formattable_type(); + test_mixed_custom_formattable_type(); + test_mixed_custom_formattable_type(); + return 0; +} diff --git a/tests/std/tests/P0645R10_text_formatting_death/env.lst b/tests/std/tests/P0645R10_text_formatting_death/env.lst new file mode 100644 index 00000000000..22f1f0230a4 --- /dev/null +++ b/tests/std/tests/P0645R10_text_formatting_death/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_winsdk_concepts_matrix.lst diff --git a/tests/std/tests/P0645R10_text_formatting_death/test.cpp b/tests/std/tests/P0645R10_text_formatting_death/test.cpp new file mode 100644 index 00000000000..86e74da6037 --- /dev/null +++ b/tests/std/tests/P0645R10_text_formatting_death/test.cpp @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#define _CONTAINER_DEBUG_LEVEL 1 + +#include +#include +#include + +#include +using namespace std; + +void test_case_advance_no_range() { + const auto format_string = "First {} and second {} and third {}"sv; + const auto other_format_string = "something"sv; + basic_format_parse_context context{format_string}; + context.advance_to(other_format_string.begin()); +} + +int main(int argc, char* argv[]) { + std_testing::death_test_executive exec; + +#if _ITERATOR_DEBUG_LEVEL != 0 + exec.add_death_tests({ + test_case_advance_no_range, + }); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + + return exec.run(argc, argv); +} diff --git a/tests/std/tests/P0645R10_text_formatting_formatting/env.lst b/tests/std/tests/P0645R10_text_formatting_formatting/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0645R10_text_formatting_formatting/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp b/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp new file mode 100644 index 00000000000..ab37ef41f5c --- /dev/null +++ b/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp @@ -0,0 +1,1368 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +// copied from the string_view tests +template +struct choose_literal; // not defined + +template <> +struct choose_literal { + static constexpr const char* choose(const char* s, const wchar_t*) { + return s; + } +}; + +template <> +struct choose_literal { + static constexpr const wchar_t* choose(const char*, const wchar_t* s) { + return s; + } +}; + +#define TYPED_LITERAL(CharT, Literal) (choose_literal::choose(Literal, L##Literal)) +#define STR(Str) TYPED_LITERAL(charT, Str) + +// Test against IDL mismatch between the DLL which stores the locale and the code which uses it. +#ifdef _DEBUG +#define DEFAULT_IDL_SETTING 2 +#else +#define DEFAULT_IDL_SETTING 0 +#endif + +template +auto make_testing_format_args(Args&&... vals) { + if constexpr (is_same_v) { + return make_wformat_args(forward(vals)...); + } else { + return make_format_args(forward(vals)...); + } +} + +template +void throw_helper(const charT* fmt, const Args&... vals) { + try { + (void) format(fmt, vals...); + assert(false); + } catch (const format_error&) { + } +} + +template +struct move_only_back_inserter { + back_insert_iterator> it; + using difference_type = ptrdiff_t; + + bool moved_from = false; + + move_only_back_inserter() = default; + explicit move_only_back_inserter(basic_string& str) : it{str} {} + + move_only_back_inserter(const move_only_back_inserter&) = delete; + move_only_back_inserter& operator=(const move_only_back_inserter&) = delete; + + move_only_back_inserter(move_only_back_inserter&& other) : it(other.it) { + assert(!exchange(other.moved_from, true)); + } + move_only_back_inserter& operator=(move_only_back_inserter&& other) { + assert(!exchange(other.moved_from, true)); + it = other.it; + moved_from = false; + return *this; + } + + move_only_back_inserter& operator++() { + assert(!moved_from); + ++it; + return *this; + } + + decltype(auto) operator++(int) { + assert(!moved_from); + return it++; + } + + decltype(auto) operator*() { + assert(!moved_from); + return *it; + } +}; + +template +move_only_back_inserter(basic_string&) -> move_only_back_inserter; + +// tests for format with no format args or replacement fields +template +void test_simple_formatting() { + basic_string output_string; + + vformat_to(back_insert_iterator{output_string}, locale::classic(), STR("f"), make_testing_format_args()); + assert(output_string == STR("f")); + + output_string.clear(); + format_to(move_only_back_inserter{output_string}, STR("{} {} {} {} {} {} {} {} {}"), true, charT{'a'}, 0, 0u, 0.0, + STR("s"), basic_string_view{STR("sv")}, nullptr, static_cast(nullptr)); + assert(output_string == STR("true a 0 0 0 s sv 0x0 0x0")); + + output_string.clear(); + format_to(move_only_back_inserter{output_string}, STR("{:} {:} {:} {:} {:} {:} {:} {:} {:}"), true, charT{'a'}, 0, + 0u, 0.0, STR("s"), basic_string_view{STR("sv")}, nullptr, static_cast(nullptr)); + assert(output_string == STR("true a 0 0 0 s sv 0x0 0x0")); + + output_string.clear(); + format_to_n(move_only_back_inserter{output_string}, 300, STR("{} {} {} {} {} {} {} {} {}"), true, charT{'a'}, 0, 0u, + 0.0, STR("s"), basic_string_view{STR("sv")}, nullptr, static_cast(nullptr)); + assert(output_string == STR("true a 0 0 0 s sv 0x0 0x0")); + + output_string.clear(); + vformat_to( + back_insert_iterator{output_string}, locale::classic(), STR("format"), make_testing_format_args()); + assert(output_string == STR("format")); + + output_string.clear(); + format_to(back_insert_iterator{output_string}, STR("format")); + assert(output_string == STR("format")); + + output_string.clear(); + format_to(back_insert_iterator{output_string}, locale::classic(), STR("format")); + assert(output_string == STR("format")); + + assert(format(STR("f")) == STR("f")); + assert(format(STR("format")) == STR("format")); + assert(format(locale::classic(), STR("f")) == STR("f")); + assert(format(locale::classic(), STR("format")) == STR("format")); +} + +template +void test_escaped_curls() { + basic_string output_string; + + // test escaped opening curls + vformat_to(back_insert_iterator{output_string}, locale::classic(), STR("{{"), make_testing_format_args()); + assert(output_string == STR("{")); + + output_string.clear(); + vformat_to(back_insert_iterator{output_string}, locale::classic(), STR("{{{{"), make_testing_format_args()); + assert(output_string == STR("{{")); + + output_string.clear(); + vformat_to(back_insert_iterator{output_string}, locale::classic(), STR("x{{"), make_testing_format_args()); + assert(output_string == STR("x{")); + + output_string.clear(); + vformat_to(back_insert_iterator{output_string}, locale::classic(), STR("{{ {{"), make_testing_format_args()); + assert(output_string == STR("{ {")); + + output_string.clear(); + vformat_to(back_insert_iterator{output_string}, locale::classic(), STR("x{{x"), make_testing_format_args()); + assert(output_string == STR("x{x")); + + output_string.clear(); + vformat_to(back_insert_iterator{output_string}, locale::classic(), STR("{{x"), make_testing_format_args()); + assert(output_string == STR("{x")); + + // tests escaped closing curls + output_string.clear(); + vformat_to(back_insert_iterator{output_string}, locale::classic(), STR("}}"), make_testing_format_args()); + assert(output_string == STR("}")); + + output_string.clear(); + vformat_to(back_insert_iterator{output_string}, locale::classic(), STR("}}}}"), make_testing_format_args()); + assert(output_string == STR("}}")); + + output_string.clear(); + vformat_to(back_insert_iterator{output_string}, locale::classic(), STR("x}}"), make_testing_format_args()); + assert(output_string == STR("x}")); + + output_string.clear(); + vformat_to(back_insert_iterator{output_string}, locale::classic(), STR("}} }}"), make_testing_format_args()); + assert(output_string == STR("} }")); + + output_string.clear(); + vformat_to(back_insert_iterator{output_string}, locale::classic(), STR("x}}x"), make_testing_format_args()); + assert(output_string == STR("x}x")); + + output_string.clear(); + vformat_to(back_insert_iterator{output_string}, locale::classic(), STR("}}x"), make_testing_format_args()); + assert(output_string == STR("}x")); +} + +template +void test_simple_replacement_field() { + basic_string output_string; + + vformat_to( + back_insert_iterator{output_string}, locale::classic(), STR("{}"), make_testing_format_args(STR("f"))); + assert(output_string == STR("f")); + + output_string.clear(); + format_to(back_insert_iterator{output_string}, STR("{}"), STR("f")); + assert(output_string == STR("f")); + + output_string.clear(); + format_to(back_insert_iterator{output_string}, locale::classic(), STR("{}"), STR("f")); + assert(output_string == STR("f")); + + assert(format(STR("{}"), STR("f")) == STR("f")); + assert(format(locale::classic(), STR("{}"), STR("f")) == STR("f")); + + // Test string_view + output_string.clear(); + vformat_to(back_insert_iterator{output_string}, locale::classic(), STR("{}"), + make_testing_format_args(basic_string_view{STR("f")})); + assert(output_string == STR("f")); + + // Test bool + output_string.clear(); + vformat_to( + back_insert_iterator{output_string}, locale::classic(), STR("{}"), make_testing_format_args(true)); + assert(output_string == STR("true")); + + output_string.clear(); + vformat_to( + back_insert_iterator{output_string}, locale::classic(), STR("{}"), make_testing_format_args(false)); + assert(output_string == STR("false")); + + // Test char + output_string.clear(); + vformat_to(back_insert_iterator{output_string}, locale::classic(), STR("{}"), make_testing_format_args('a')); + assert(output_string == STR("a")); + + // Test const void* + output_string.clear(); + vformat_to(back_insert_iterator{output_string}, locale::classic(), STR("{}"), + make_testing_format_args(static_cast(nullptr))); + assert(output_string == STR("0x0")); + + // Test void* + output_string.clear(); + vformat_to(back_insert_iterator{output_string}, locale::classic(), STR("{}"), + make_testing_format_args(static_cast(nullptr))); + assert(output_string == STR("0x0")); + + // Test nullptr + output_string.clear(); + vformat_to( + back_insert_iterator{output_string}, locale::classic(), STR("{}"), make_testing_format_args(nullptr)); + assert(output_string == STR("0x0")); + + // Test signed integers + output_string.clear(); + vformat_to( + back_insert_iterator{output_string}, locale::classic(), STR("{}"), make_testing_format_args(1234)); + assert(output_string == STR("1234")); + + output_string.clear(); + vformat_to( + back_insert_iterator{output_string}, locale::classic(), STR("{}"), make_testing_format_args(1234ll)); + assert(output_string == STR("1234")); + + output_string.clear(); + vformat_to( + back_insert_iterator{output_string}, locale::classic(), STR("{}"), make_testing_format_args(INT_MIN)); + assert(output_string == STR("-2147483648")); + + output_string.clear(); + vformat_to( + back_insert_iterator{output_string}, locale::classic(), STR("{}"), make_testing_format_args(INT_MAX)); + assert(output_string == STR("2147483647")); + + output_string.clear(); + vformat_to( + back_insert_iterator{output_string}, locale::classic(), STR("{}"), make_testing_format_args(LLONG_MAX)); + assert(output_string == STR("9223372036854775807")); + + output_string.clear(); + vformat_to( + back_insert_iterator{output_string}, locale::classic(), STR("{}"), make_testing_format_args(LLONG_MIN)); + assert(output_string == STR("-9223372036854775808")); + + // Test unsigned integers + output_string.clear(); + vformat_to( + back_insert_iterator{output_string}, locale::classic(), STR("{}"), make_testing_format_args(1234u)); + assert(output_string == STR("1234")); + + output_string.clear(); + vformat_to( + back_insert_iterator{output_string}, locale::classic(), STR("{}"), make_testing_format_args(1234ull)); + assert(output_string == STR("1234")); + + output_string.clear(); + vformat_to( + back_insert_iterator{output_string}, locale::classic(), STR("{}"), make_testing_format_args(UINT_MAX)); + assert(output_string == STR("4294967295")); + + output_string.clear(); + vformat_to( + back_insert_iterator{output_string}, locale::classic(), STR("{}"), make_testing_format_args(ULLONG_MAX)); + assert(output_string == STR("18446744073709551615")); + + // Test float + output_string.clear(); + vformat_to( + back_insert_iterator{output_string}, locale::classic(), STR("{}"), make_testing_format_args(12.34f)); + assert(output_string == STR("12.34")); + + output_string.clear(); + vformat_to( + back_insert_iterator{output_string}, locale::classic(), STR("{}"), make_testing_format_args(FLT_MAX)); + assert(output_string == STR("3.4028235e+38")); + + output_string.clear(); + vformat_to( + back_insert_iterator{output_string}, locale::classic(), STR("{}"), make_testing_format_args(-FLT_MAX)); + assert(output_string == STR("-3.4028235e+38")); + + output_string.clear(); + vformat_to( + back_insert_iterator{output_string}, locale::classic(), STR("{}"), make_testing_format_args(FLT_MIN)); + assert(output_string == STR("1.1754944e-38")); + + output_string.clear(); + vformat_to(back_insert_iterator{output_string}, locale::classic(), STR("{}"), + make_testing_format_args(FLT_EPSILON)); + assert(output_string == STR("1.1920929e-07")); + + output_string.clear(); + vformat_to(back_insert_iterator{output_string}, locale::classic(), STR("{}"), + make_testing_format_args(FLT_TRUE_MIN)); + assert(output_string == STR("1e-45")); + + output_string.clear(); + vformat_to(back_insert_iterator{output_string}, locale::classic(), STR("{}"), + make_testing_format_args(numeric_limits::infinity())); + assert(output_string == STR("inf")); + + output_string.clear(); + vformat_to(back_insert_iterator{output_string}, locale::classic(), STR("{}"), + make_testing_format_args(-numeric_limits::infinity())); + assert(output_string == STR("-inf")); + + output_string.clear(); + vformat_to(back_insert_iterator{output_string}, locale::classic(), STR("{}"), + make_testing_format_args(numeric_limits::quiet_NaN())); + assert(output_string == STR("nan")); + + output_string.clear(); + vformat_to(back_insert_iterator{output_string}, locale::classic(), STR("{}"), make_testing_format_args(0.f)); + assert(output_string == STR("0")); + + output_string.clear(); + vformat_to( + back_insert_iterator{output_string}, locale::classic(), STR("{}"), make_testing_format_args(-0.f)); + assert(output_string == STR("-0")); + + // Test double + output_string.clear(); + vformat_to( + back_insert_iterator{output_string}, locale::classic(), STR("{}"), make_testing_format_args(12.34)); + assert(output_string == STR("12.34")); + + output_string.clear(); + vformat_to( + back_insert_iterator{output_string}, locale::classic(), STR("{}"), make_testing_format_args(DBL_MAX)); + assert(output_string == STR("1.7976931348623157e+308")); + + output_string.clear(); + vformat_to( + back_insert_iterator{output_string}, locale::classic(), STR("{}"), make_testing_format_args(-DBL_MAX)); + assert(output_string == STR("-1.7976931348623157e+308")); + + output_string.clear(); + vformat_to( + back_insert_iterator{output_string}, locale::classic(), STR("{}"), make_testing_format_args(DBL_MIN)); + assert(output_string == STR("2.2250738585072014e-308")); + + output_string.clear(); + vformat_to(back_insert_iterator{output_string}, locale::classic(), STR("{}"), + make_testing_format_args(DBL_EPSILON)); + assert(output_string == STR("2.220446049250313e-16")); + + output_string.clear(); + vformat_to(back_insert_iterator{output_string}, locale::classic(), STR("{}"), + make_testing_format_args(DBL_TRUE_MIN)); + assert(output_string == STR("5e-324")); + + output_string.clear(); + vformat_to(back_insert_iterator{output_string}, locale::classic(), STR("{}"), + make_testing_format_args(numeric_limits::infinity())); + assert(output_string == STR("inf")); + + output_string.clear(); + vformat_to(back_insert_iterator{output_string}, locale::classic(), STR("{}"), + make_testing_format_args(-numeric_limits::infinity())); + assert(output_string == STR("-inf")); + + output_string.clear(); + vformat_to(back_insert_iterator{output_string}, locale::classic(), STR("{}"), + make_testing_format_args(numeric_limits::quiet_NaN())); + assert(output_string == STR("nan")); + + output_string.clear(); + vformat_to(back_insert_iterator{output_string}, locale::classic(), STR("{}"), make_testing_format_args(0.0)); + assert(output_string == STR("0")); + + output_string.clear(); + vformat_to( + back_insert_iterator{output_string}, locale::classic(), STR("{}"), make_testing_format_args(-0.0)); + assert(output_string == STR("-0")); +} + +template +void test_multiple_replacement_fields() { + basic_string output_string; + + vformat_to(back_insert_iterator{output_string}, locale::classic(), STR("{0} {0}"), + make_testing_format_args(STR("f"))); + assert(output_string == STR("f f")); +} + +template +void test_fill_and_align() { + auto writer = [](auto out) { + out++ = 'A'; + out++ = 'B'; + return out; + }; + + _Basic_format_specs specs; + + auto tester = [&] { + basic_string output_string; + (void) _Write_aligned(back_inserter(output_string), 2, specs, _Fmt_align::_Left, writer); + return output_string; + }; + + assert(tester() == STR("AB")); + + specs._Width = 1; + assert(tester() == STR("AB")); + + + specs._Width = 5; + specs._Alignment = _Fmt_align::_Left; + assert(tester() == STR("AB ")); + + specs._Alignment = _Fmt_align::_Right; + assert(tester() == STR(" AB")); + + specs._Alignment = _Fmt_align::_Center; + assert(tester() == STR(" AB ")); + + + specs._Alignment = _Fmt_align::_Left; + specs._Fill[0] = {'*'}; + assert(tester() == STR("AB***")); + + specs._Alignment = _Fmt_align::_Right; + assert(tester() == STR("***AB")); + + specs._Alignment = _Fmt_align::_Center; + assert(tester() == STR("*AB**")); +} + +template +void test_integral_specs() { + assert(format(STR("{:}"), Integral{0}) == STR("0")); + + // Sign + assert(format(STR("{: }"), Integral{0}) == STR(" 0")); + assert(format(STR("{:+}"), Integral{0}) == STR("+0")); + assert(format(STR("{:-}"), Integral{0}) == STR("0")); + + if constexpr (is_signed_v) { + assert(format(STR("{: }"), Integral{-1}) == STR("-1")); + assert(format(STR("{:+}"), Integral{-1}) == STR("-1")); + assert(format(STR("{:-}"), Integral{-1}) == STR("-1")); + } + + assert(format(STR("{: 3}"), Integral{1}) == STR(" 1")); + assert(format(STR("{:+3}"), Integral{1}) == STR(" +1")); + assert(format(STR("{:-3}"), Integral{1}) == STR(" 1")); + + // Alternate form + assert(format(STR("{:#}"), Integral{0}) == STR("0")); + assert(format(STR("{:#d}"), Integral{0}) == STR("0")); + assert(format(STR("{:#c}"), Integral{'a'}) == STR("a")); + + assert(format(STR("{:#b}"), Integral{0}) == STR("0b0")); + assert(format(STR("{:#B}"), Integral{0}) == STR("0B0")); + + assert(format(STR("{:#o}"), Integral{0}) == STR("0")); + assert(format(STR("{:#o}"), Integral{1}) == STR("01")); + + assert(format(STR("{:#x}"), Integral{0}) == STR("0x0")); + assert(format(STR("{:#X}"), Integral{0}) == STR("0X0")); + assert(format(STR("{:#x}"), Integral{255}) == STR("0xff")); + assert(format(STR("{:#X}"), Integral{255}) == STR("0XFF")); + + assert(format(STR("{:+#6x}"), Integral{255}) == STR(" +0xff")); + + if constexpr (is_signed_v) { + assert(format(STR("{:#o}"), Integral{-1}) == STR("-01")); + assert(format(STR("{:#x}"), Integral{-255}) == STR("-0xff")); + assert(format(STR("{:#X}"), Integral{-255}) == STR("-0XFF")); + } + + if constexpr (is_same_v) { + assert(format(STR("{:b}"), numeric_limits::min()) + == STR("-1000000000000000000000000000000000000000000000000000000000000000")); + } + + // Leading zero + assert(format(STR("{:0}"), Integral{0}) == STR("0")); + assert(format(STR("{:03}"), Integral{0}) == STR("000")); + assert(format(STR("{:+03}"), Integral{0}) == STR("+00")); + assert(format(STR("{:<03}"), Integral{0}) == STR("0 ")); + assert(format(STR("{:>03}"), Integral{0}) == STR(" 0")); + assert(format(STR("{:+#06X}"), Integral{5}) == STR("+0X005")); + + // Width + assert(format(STR("{:3}"), Integral{0}) == STR(" 0")); + + // Precision + throw_helper(STR("{:.1}"), Integral{0}); + + // Locale +#if !defined(_DLL) || _ITERATOR_DEBUG_LEVEL == DEFAULT_IDL_SETTING + assert(format(locale{"en-US"}, STR("{:L}"), Integral{0}) == STR("0")); + assert(format(locale{"en-US"}, STR("{:L}"), Integral{100}) == STR("100")); + assert(format(locale{"en-US"}, STR("{:L}"), Integral{1'000}) == STR("1,000")); + assert(format(locale{"en-US"}, STR("{:L}"), Integral{10'000}) == STR("10,000")); + assert(format(locale{"en-US"}, STR("{:L}"), Integral{100'000}) == STR("100,000")); + assert(format(locale{"en-US"}, STR("{:L}"), Integral{1'000'000}) == STR("1,000,000")); + assert(format(locale{"en-US"}, STR("{:L}"), Integral{10'000'000}) == STR("10,000,000")); + assert(format(locale{"en-US"}, STR("{:L}"), Integral{100'000'000}) == STR("100,000,000")); + + assert(format(locale{"en-US"}, STR("{:Lx}"), Integral{0x123'abc}) == STR("123,abc")); + assert(format(locale{"en-US"}, STR("{:6L}"), Integral{1'000}) == STR(" 1,000")); + + assert(format(locale{"hi-IN"}, STR("{:L}"), Integral{10'000'000}) == STR("1,00,00,000")); + assert(format(locale{"hi-IN"}, STR("{:L}"), Integral{100'000'000}) == STR("10,00,00,000")); + + assert(format(locale{"hi-IN"}, STR("{:Lx}"), Integral{0x123'abc}) == STR("1,23,abc")); +#endif // !defined(_DLL) || _ITERATOR_DEBUG_LEVEL == DEFAULT_IDL_SETTING + + // Type + assert(format(STR("{:b}"), Integral{0}) == STR("0")); + assert(format(STR("{:b}"), Integral{100}) == STR("1100100")); + + assert(format(STR("{:d}"), Integral{100}) == STR("100")); + + throw_helper(STR("{:c}"), Integral{numeric_limits::max()} + 1); + if constexpr (is_signed_v) { + throw_helper(STR("{:c}"), Integral{numeric_limits::min()} - 1); + } +} + +template +void test_type(const charT* fmt, T val) { + assert(format(fmt, val) == format(fmt, static_cast(val))); +} + +template +void test_bool_specs() { + assert(format(STR("{:}"), true) == STR("true")); + assert(format(STR("{:}"), false) == STR("false")); + + // Sign + throw_helper(STR("{: }"), true); + throw_helper(STR("{:+}"), true); + throw_helper(STR("{:-}"), true); + + // Alternate form + throw_helper(STR("{:#}"), true); + + // Leading zero + throw_helper(STR("{:0}"), true); + + // Width + assert(format(STR("{:6}"), true) == STR("true ")); + assert(format(STR("{:6}"), false) == STR("false ")); + + // Precision + throw_helper(STR("{:.5}"), true); + + // Locale + assert(format(STR("{:L}"), true) == STR("true")); + assert(format(STR("{:L}"), false) == STR("false")); +#if !defined(_DLL) || _ITERATOR_DEBUG_LEVEL == DEFAULT_IDL_SETTING + assert(format(locale{"en-US"}, STR("{:L}"), true) == STR("true")); + assert(format(locale{"en-US"}, STR("{:L}"), false) == STR("false")); + + struct my_bool : numpunct { + virtual basic_string do_truename() const { + return STR("yes"); + } + + virtual basic_string do_falsename() const { + return STR("no"); + } + }; + + locale loc{locale::classic(), new my_bool}; + + assert(format(loc, STR("{:L}"), true) == STR("yes")); + assert(format(loc, STR("{:L}"), false) == STR("no")); +#endif // !defined(_DLL) || _ITERATOR_DEBUG_LEVEL == DEFAULT_IDL_SETTING + + // Type + assert(format(STR("{:s}"), true) == STR("true")); + throw_helper(STR("{:a}"), true); + + test_type(STR("{:b}"), true); + test_type(STR("{:B}"), true); + test_type(STR("{:c}"), true); + test_type(STR("{:d}"), true); + test_type(STR("{:o}"), true); + test_type(STR("{:x}"), true); + test_type(STR("{:X}"), true); + + test_type(STR("{:b}"), false); + test_type(STR("{:B}"), false); + test_type(STR("{:c}"), false); + test_type(STR("{:d}"), false); + test_type(STR("{:o}"), false); + test_type(STR("{:x}"), false); + test_type(STR("{:X}"), false); +} + +template +void test_char_specs() { + assert(format(STR("{:}"), charT{'X'}) == STR("X")); + + // Sign + throw_helper(STR("{: }"), charT{'X'}); + throw_helper(STR("{:+}"), charT{'X'}); + throw_helper(STR("{:-}"), charT{'X'}); + + // Alternate form + throw_helper(STR("{:#}"), charT{'X'}); + + // Leading zero + throw_helper(STR("{:0}"), charT{'X'}); + + // Width + assert(format(STR("{:3}"), charT{'X'}) == STR("X ")); + + // Precision + throw_helper(STR("{:.5}"), charT{'X'}); + + // Types + assert(format(STR("{:c}"), charT{'X'}) == STR("X")); + throw_helper(STR("{:a}"), charT{'X'}); + + test_type(STR("{:b}"), charT{'X'}); + test_type(STR("{:B}"), charT{'X'}); + test_type(STR("{:c}"), charT{'X'}); + test_type(STR("{:d}"), charT{'X'}); + test_type(STR("{:o}"), charT{'X'}); + test_type(STR("{:x}"), charT{'X'}); + test_type(STR("{:X}"), charT{'X'}); + + test_type(STR("{:+d}"), charT{'X'}); +} + +template +void test_float_specs() { + const Float inf = numeric_limits::infinity(); + const Float nan = numeric_limits::quiet_NaN(); + + assert(format(STR("{:}"), Float{0}) == STR("0")); + assert(format(STR("{:}"), inf) == STR("inf")); + assert(format(STR("{:}"), nan) == STR("nan")); + + // Sign + assert(format(STR("{: }"), Float{0}) == STR(" 0")); + assert(format(STR("{:+}"), Float{0}) == STR("+0")); + assert(format(STR("{:-}"), Float{0}) == STR("0")); + + assert(format(STR("{: }"), Float{-1}) == STR("-1")); + assert(format(STR("{:+}"), Float{-1}) == STR("-1")); + assert(format(STR("{:-}"), Float{-1}) == STR("-1")); + + assert(format(STR("{: 3}"), Float{1}) == STR(" 1")); + assert(format(STR("{:+3}"), Float{1}) == STR(" +1")); + assert(format(STR("{:-3}"), Float{1}) == STR(" 1")); + + assert(format(STR("{: }"), inf) == STR(" inf")); + assert(format(STR("{:+}"), inf) == STR("+inf")); + assert(format(STR("{:-}"), inf) == STR("inf")); + + assert(format(STR("{: }"), -inf) == STR("-inf")); + assert(format(STR("{:+}"), -inf) == STR("-inf")); + assert(format(STR("{:-}"), -inf) == STR("-inf")); + + assert(format(STR("{: }"), nan) == STR(" nan")); + assert(format(STR("{:+}"), nan) == STR("+nan")); + assert(format(STR("{:-}"), nan) == STR("nan")); + + // Alternate form + assert(format(STR("{:#}"), Float{0}) == STR("0.")); + assert(format(STR("{:#a}"), Float{0}) == STR("0.p+0")); + assert(format(STR("{:#A}"), Float{0}) == STR("0.P+0")); + assert(format(STR("{:#.0e}"), Float{0}) == STR("0.e+00")); + assert(format(STR("{:#.0E}"), Float{0}) == STR("0.E+00")); + assert(format(STR("{:#.0f}"), Float{0}) == STR("0.")); + assert(format(STR("{:#.0F}"), Float{0}) == STR("0.")); + assert(format(STR("{:#g}"), Float{0}) == STR("0.00000")); + assert(format(STR("{:#G}"), Float{0}) == STR("0.00000")); + assert(format(STR("{:#g}"), Float{1.2}) == STR("1.20000")); + assert(format(STR("{:#G}"), Float{1.2}) == STR("1.20000")); + assert(format(STR("{:#g}"), Float{1'000'000}) == STR("1.00000e+06")); + assert(format(STR("{:#g}"), Float{12.2}) == STR("12.2000")); + assert(format(STR("{:#.0g}"), Float{0}) == STR("0.")); + assert(format(STR("{:#.0G}"), Float{0}) == STR("0.")); + + + assert(format(STR("{:#.2g}"), 0.5) == STR("0.50")); + assert(format(STR("{:#.1g}"), 0.5) == STR("0.5")); + assert(format(STR("{:#.3g}"), 0.5) == STR("0.500")); + assert(format(STR("{:#.3g}"), 0.05) == STR("0.0500")); + assert(format(STR("{:#.3g}"), 0.0005) == STR("0.000500")); + assert(format(STR("{:#.3g}"), 0.00005) == STR("5.00e-05")); + assert(format(STR("{:#.2g}"), 0.0999) == STR("0.10")); + assert(format(STR("{:#.3g}"), 0.000470) == STR("0.000470")); + + assert(format(STR("{:#} {:#}"), inf, nan) == STR("inf nan")); + assert(format(STR("{:#a} {:#a}"), inf, nan) == STR("inf nan")); + assert(format(STR("{:#A} {:#A}"), inf, nan) == STR("INF NAN")); + assert(format(STR("{:#e} {:#e}"), inf, nan) == STR("inf nan")); + assert(format(STR("{:#E} {:#E}"), inf, nan) == STR("INF NAN")); + assert(format(STR("{:#f} {:#f}"), inf, nan) == STR("inf nan")); + assert(format(STR("{:#F} {:#F}"), inf, nan) == STR("INF NAN")); + assert(format(STR("{:#g} {:#g}"), inf, nan) == STR("inf nan")); + assert(format(STR("{:#G} {:#G}"), inf, nan) == STR("INF NAN")); + + // Width + assert(format(STR("{:3}"), Float{0}) == STR(" 0")); + assert(format(STR("{:#9G}"), Float{12.2}) == STR(" 12.2000")); + assert(format(STR("{:#12g}"), Float{1'000'000}) == STR(" 1.00000e+06")); + + // Precision + Float value = 1234.52734375; + assert(format(STR("{:.4}"), value) == STR("1235")); + assert(format(STR("{:.1}"), inf) == STR("inf")); + assert(format(STR("{:.1}"), nan) == STR("nan")); + + assert(format(STR("{:.4a}"), value) == STR("1.34a2p+10")); + assert(format(STR("{:.4A}"), value) == STR("1.34A2P+10")); + + assert(format(STR("{:.4e}"), value) == STR("1.2345e+03")); + assert(format(STR("{:.4E}"), value) == STR("1.2345E+03")); + + assert(format(STR("{:.4f}"), value) == STR("1234.5273")); + assert(format(STR("{:.4F}"), value) == STR("1234.5273")); + + assert(format(STR("{:.4g}"), value) == STR("1235")); + assert(format(STR("{:.4G}"), value) == STR("1235")); + + assert(format(STR("{:.3000f}"), -numeric_limits::max()).size() + == 3002 + numeric_limits::max_exponent10 + 1); + assert(format(STR("{:.3000f}"), -numeric_limits::denorm_min()).size() == 3003); + assert(format(STR("{:#.3000g}"), -numeric_limits::max()).size() == 3002); + assert(format(STR("{:#.3000g}"), -numeric_limits::denorm_min()).size() + == 3007 - static_cast(is_same_v)); + + for (auto limits : {-numeric_limits::max(), -numeric_limits::denorm_min()}) { + auto fixed3000 = format(STR("{:.3000f}"), limits); + auto fixed1500 = format(STR("{:.1500f}"), limits); + assert(fixed1500 == fixed3000.substr(0, fixed1500.size())); + assert(all_of(fixed3000.begin() + static_cast(fixed1500.size()), fixed3000.end(), + [](auto ch) { return ch == charT{'0'}; })); + } + + for (auto limits : {-numeric_limits::max(), -numeric_limits::denorm_min()}) { + auto general3000 = format(STR("{:#.3000g}"), limits); + auto general1500 = format(STR("{:#.3000g}"), limits); + assert(general1500 == general3000.substr(0, general1500.size())); + assert(all_of(general3000.begin() + static_cast(general1500.size()), general3000.end(), + [](auto ch) { return ch == charT{'0'}; })); + } + + // Leading zero + assert(format(STR("{:06}"), Float{0}) == STR("000000")); + assert(format(STR("{:06}"), Float{1.2}) == STR("0001.2")); + assert(format(STR("{:06}"), nan) == STR(" nan")); + assert(format(STR("{:06}"), inf) == STR(" inf")); + + // Locale +#if !defined(_DLL) || _ITERATOR_DEBUG_LEVEL == DEFAULT_IDL_SETTING + assert(format(locale{"en-US"}, STR("{:L}"), Float{0}) == STR("0")); + assert(format(locale{"en-US"}, STR("{:Lf}"), Float{0}) == STR("0.000000")); + assert(format(locale{"en-US"}, STR("{:L}"), Float{100}) == STR("100")); + assert(format(locale{"en-US"}, STR("{:L}"), Float{100.2345}) == STR("100.2345")); + assert(format(locale{"en-US"}, STR("{:.4Lf}"), value) == STR("1,234.5273")); + assert(format(locale{"en-US"}, STR("{:#.4Lg}"), Float{0}) == STR("0.000")); + assert(format(locale{"en-US"}, STR("{:L}"), nan) == STR("nan")); + assert(format(locale{"en-US"}, STR("{:L}"), inf) == STR("inf")); + + assert(format(locale{"de-DE"}, STR("{:Lf}"), Float{0}) == STR("0,000000")); +#endif // !defined(_DLL) || _ITERATOR_DEBUG_LEVEL == DEFAULT_IDL_SETTING + + // Type + assert(format(STR("{:a}"), value) == STR("1.34a1cp+10")); + assert(format(STR("{:A}"), value) == STR("1.34A1CP+10")); + + assert(format(STR("{:e}"), value) == STR("1.234527e+03")); + assert(format(STR("{:E}"), value) == STR("1.234527E+03")); + + assert(format(STR("{:f}"), value) == STR("1234.527344")); + assert(format(STR("{:F}"), value) == STR("1234.527344")); + + assert(format(STR("{:g}"), value) == STR("1234.53")); + assert(format(STR("{:G}"), value) == STR("1234.53")); + + assert(format(STR("{:a} {:a}"), inf, nan) == STR("inf nan")); + assert(format(STR("{:A} {:A}"), inf, nan) == STR("INF NAN")); + assert(format(STR("{:e} {:e}"), inf, nan) == STR("inf nan")); + assert(format(STR("{:E} {:E}"), inf, nan) == STR("INF NAN")); + assert(format(STR("{:f} {:f}"), inf, nan) == STR("inf nan")); + assert(format(STR("{:F} {:F}"), inf, nan) == STR("INF NAN")); + assert(format(STR("{:g} {:g}"), inf, nan) == STR("inf nan")); + assert(format(STR("{:G} {:G}"), inf, nan) == STR("INF NAN")); +} + +template +void test_pointer_specs() { + assert(format(STR("{:}"), nullptr) == STR("0x0")); + + // Sign + throw_helper(STR("{: }"), nullptr); + throw_helper(STR("{:+}"), nullptr); + throw_helper(STR("{:-}"), nullptr); + + // Alternate form + throw_helper(STR("{:#}"), nullptr); + + // Leading zero + throw_helper(STR("{:0}"), nullptr); + + // Width + assert(format(STR("{:5}"), nullptr) == STR("0x0 ")); + + // Precision + throw_helper(STR("{:.5}"), nullptr); + + // Locale + throw_helper(STR("{:L}"), nullptr); + + // Types + assert(format(STR("{:p}"), nullptr) == STR("0x0")); + throw_helper(STR("{:a}"), nullptr); +} + +template +void test_string_specs() { + auto cstr = STR("scully"); + auto view = basic_string_view{cstr}; + + assert(format(STR("{:}"), cstr) == cstr); + assert(format(STR("{:}"), view) == cstr); + + // Sign + throw_helper(STR("{: }"), cstr); + throw_helper(STR("{:+}"), cstr); + throw_helper(STR("{:-}"), cstr); + + throw_helper(STR("{: }"), view); + throw_helper(STR("{:+}"), view); + throw_helper(STR("{:-}"), view); + + // Alternate form + throw_helper(STR("{:#}"), cstr); + throw_helper(STR("{:#}"), view); + + // Leading zero + throw_helper(STR("{:0}"), cstr); + throw_helper(STR("{:0}"), view); + + // Width + assert(format(STR("{:8}"), cstr) == STR("scully ")); + assert(format(STR("{:8}"), view) == STR("scully ")); + + // Precision + assert(format(STR("{:.2}"), cstr) == STR("sc")); + assert(format(STR("{:5.2}"), cstr) == STR("sc ")); + assert(format(STR("{:5.2}"), cstr) == STR("sc ")); + assert(format(STR("{:>5.2}"), cstr) == STR(" sc")); + + assert(format(STR("{:.2}"), view) == STR("sc")); + assert(format(STR("{:5.2}"), view) == STR("sc ")); + assert(format(STR("{:5.2}"), view) == STR("sc ")); + assert(format(STR("{:>5.2}"), view) == STR(" sc")); + + // Locale + throw_helper(STR("{:L}"), cstr); + throw_helper(STR("{:L}"), view); + + // Types + assert(format(STR("{:s}"), cstr) == cstr); + throw_helper(STR("{:a}"), cstr); + + assert(format(STR("{:s}"), view) == cstr); + throw_helper(STR("{:a}"), view); +} + +template +void test_spec_replacement_field() { + test_integral_specs(); + test_integral_specs(); + test_integral_specs(); + test_integral_specs(); + test_bool_specs(); + test_char_specs(); + test_float_specs(); + test_float_specs(); + test_float_specs(); + test_pointer_specs(); + test_string_specs(); +} + +template +void test_size_helper(const size_t expected_size, const basic_string_view fmt, const Args&... args) { + assert(formatted_size(fmt, args...) == expected_size); + assert(formatted_size(locale::classic(), fmt, args...) == expected_size); + + const auto signed_size = static_cast(expected_size); + basic_string str; + { + str.resize(expected_size); + const auto res = format_to_n(str.begin(), signed_size, fmt, args...); + assert(res.size == signed_size); + assert(res.out - str.begin() == signed_size); + assert(res.out == str.end()); + assert(format(fmt, args...) == str); + + basic_string locale_str; + locale_str.resize(expected_size); + format_to_n(locale_str.begin(), signed_size, locale::classic(), fmt, args...); + assert(str == locale_str); + assert(locale_str.size() == expected_size); + } + basic_string half_str; + { + const auto half_size = expected_size / 2; + half_str.resize(half_size); + const auto res = format_to_n(half_str.begin(), static_cast(half_size), fmt, args...); + assert(res.size == signed_size); + assert(static_cast(res.out - half_str.begin()) == half_size); + assert(res.out == half_str.end()); + } + assert(str.starts_with(half_str)); +} + +template +void test_size() { + test_size_helper(3, STR("{}"), 123); + test_size_helper(6, STR("{}"), 3.1415); + test_size_helper(8, STR("{:8}"), STR("scully")); +} + +void test_multibyte_format_strings() { +#ifndef MSVC_INTERNAL_TESTING // TRANSITION, the Windows version on Contest VMs doesn't always understand ".UTF-8" + { + assert(setlocale(LC_ALL, ".UTF-8") != nullptr); + // Filling with footballs ("\xf0\x9f\x8f\x88" is U+1F3C8 AMERICAN FOOTBALL) + assert(format("{:\xf0\x9f\x8f\x88>4}"sv, 42) == "\xf0\x9f\x8f\x88\xf0\x9f\x8f\x88\x34\x32"); + + assert(format("{:\xf0\x9f\x8f\x88<4.2}", "1") == "\x31\xf0\x9f\x8f\x88\xf0\x9f\x8f\x88\xf0\x9f\x8f\x88"sv); + assert(format("{:\xf0\x9f\x8f\x88^4.2}", "1") == "\xf0\x9f\x8f\x88\x31\xf0\x9f\x8f\x88\xf0\x9f\x8f\x88"sv); + assert(format("{:\xf0\x9f\x8f\x88>4.2}", "1") == "\xf0\x9f\x8f\x88\xf0\x9f\x8f\x88\xf0\x9f\x8f\x88\x31"sv); + } + + { + assert(setlocale(LC_ALL, ".UTF-8") != nullptr); + try { + (void) format("{:\x9f\x8f\x88<10}"sv, 42); // Bad fill character encoding: missing lead byte before \x9f + assert(false); + } catch (const format_error&) { + } + } +#endif // MSVC_INTERNAL_TESTING + + assert(setlocale(LC_ALL, "C") != nullptr); +} + +// The libfmt_ tests are derived from tests in +// libfmt, Copyright (c) 2012 - present, Victor Zverovich +// See NOTICE.txt for more information. + +template +void libfmt_formatter_test_escape() { + assert(format(STR("{{")) == STR("{")); + assert(format(STR("before {{")) == STR("before {")); + assert(format(STR("{{ after")) == STR("{ after")); + assert(format(STR("before {{ after")) == STR("before { after")); + assert(format(STR("}}")) == STR("}")); + assert(format(STR("before }}")) == STR("before }")); + assert(format(STR("}} after")) == STR("} after")); + assert(format(STR("before }} after")) == STR("before } after")); + assert(format(STR("{{}}")) == STR("{}")); + assert(format(STR("{{{0}}}"), 42) == STR("{42}")); +} + +template +void libfmt_formatter_test_args_in_different_position() { + assert(format(STR("{0}"), 42) == STR("42")); + assert(format(STR("before {0}"), 42) == STR("before 42")); + assert(format(STR("{0} after"), 42) == STR("42 after")); + assert(format(STR("before {0} after"), 42) == STR("before 42 after")); + assert(format(STR("{0} = {1}"), STR("answer"), 42) == STR("answer = 42")); + assert(format(STR("{1} is the {0}"), STR("answer"), 42) == STR("42 is the answer")); + assert(format(STR("{0}{1}{0}"), STR("abra"), STR("cad")) == STR("abracadabra")); +} + +template +void libfmt_formatter_test_auto_arg_index() { + assert(format(STR("{}{}{}"), 'a', 'b', 'c') == STR("abc")); + throw_helper(STR("{0}{}"), 'a', 'b'); + throw_helper(STR("{}{0}"), 'a', 'b'); + assert(format(STR("{:.{}}"), 1.2345, 2) == STR("1.2")); + throw_helper(STR("{0}:.{}"), 1.2345, 2); + throw_helper(STR("{:.{0}}"), 1.2345, 2); + throw_helper(STR("{}")); +} + +template +void libfmt_formatter_test_left_align() { + assert(format(STR("{0:<4}"), 42) == STR("42 ")); + assert(format(STR("{0:<4o}"), 042) == STR("42 ")); + assert(format(STR("{0:<4x}"), 0x42) == STR("42 ")); + assert(format(STR("{0:<5}"), -42) == STR("-42 ")); + assert(format(STR("{0:<5}"), 42u) == STR("42 ")); + assert(format(STR("{0:<5}"), -42l) == STR("-42 ")); + assert(format(STR("{0:<5}"), 42ul) == STR("42 ")); + assert(format(STR("{0:<5}"), -42ll) == STR("-42 ")); + assert(format(STR("{0:<5}"), 42ull) == STR("42 ")); + assert(format(STR("{0:<5}"), -42.0) == STR("-42 ")); + assert(format(STR("{0:<5}"), -42.0l) == STR("-42 ")); + assert(format(STR("{0:<5}"), 'c') == STR("c ")); + assert(format(STR("{0:<5}"), STR("abc")) == STR("abc ")); + assert(format(STR("{0:<8}"), reinterpret_cast(0xface)) == STR("0xface ")); +} + +template +void libfmt_formatter_test_right_align() { + assert(format(STR("{0:>4}"), 42) == STR(" 42")); + assert(format(STR("{0:>4o}"), 042) == STR(" 42")); + assert(format(STR("{0:>4x}"), 0x42) == STR(" 42")); + assert(format(STR("{0:>5}"), -42) == STR(" -42")); + assert(format(STR("{0:>5}"), 42u) == STR(" 42")); + assert(format(STR("{0:>5}"), -42l) == STR(" -42")); + assert(format(STR("{0:>5}"), 42ul) == STR(" 42")); + assert(format(STR("{0:>5}"), -42ll) == STR(" -42")); + assert(format(STR("{0:>5}"), 42ull) == STR(" 42")); + assert(format(STR("{0:>5}"), -42.0) == STR(" -42")); + assert(format(STR("{0:>5}"), -42.0l) == STR(" -42")); + assert(format(STR("{0:>5}"), 'c') == STR(" c")); + assert(format(STR("{0:>5}"), STR("abc")) == STR(" abc")); + assert(format(STR("{0:>8}"), reinterpret_cast(0xface)) == STR(" 0xface")); +} + +template +void libfmt_formatter_test_center_align() { + assert(format(STR("{0:^5}"), 42) == STR(" 42 ")); + assert(format(STR("{0:^5o}"), 042) == STR(" 42 ")); + assert(format(STR("{0:^5x}"), 0x42) == STR(" 42 ")); + assert(format(STR("{0:^5}"), -42) == STR(" -42 ")); + assert(format(STR("{0:^5}"), 42u) == STR(" 42 ")); + assert(format(STR("{0:^5}"), -42l) == STR(" -42 ")); + assert(format(STR("{0:^5}"), 42ul) == STR(" 42 ")); + assert(format(STR("{0:^5}"), -42ll) == STR(" -42 ")); + assert(format(STR("{0:^5}"), 42ull) == STR(" 42 ")); + assert(format(STR("{0:^5}"), -42.0) == STR(" -42 ")); + assert(format(STR("{0:^5}"), -42.0l) == STR(" -42 ")); + assert(format(STR("{0:^5}"), 'c') == STR(" c ")); + assert(format(STR("{0:^6}"), STR("abc")) == STR(" abc ")); + assert(format(STR("{0:^8}"), reinterpret_cast(0xface)) == STR(" 0xface ")); +} + +template +void libfmt_formatter_test_fill() { + throw_helper(STR("{0:{<5}"), 'c'); + throw_helper(STR("{0:{<5}}"), 'c'); + assert(format(STR("{0:*>4}"), 42) == STR("**42")); + assert(format(STR("{0:*>5}"), -42) == STR("**-42")); + assert(format(STR("{0:*>5}"), 42u) == STR("***42")); + assert(format(STR("{0:*>5}"), -42l) == STR("**-42")); + assert(format(STR("{0:*>5}"), 42ul) == STR("***42")); + assert(format(STR("{0:*>5}"), -42ll) == STR("**-42")); + assert(format(STR("{0:*>5}"), 42ull) == STR("***42")); + assert(format(STR("{0:*>5}"), -42.0) == STR("**-42")); + assert(format(STR("{0:*>5}"), -42.0l) == STR("**-42")); + assert(format(STR("{0:*<5}"), 'c') == STR("c****")); + assert(format(STR("{0:*<5}"), STR("abc")) == STR("abc**")); + assert(format(STR("{0:*>8}"), reinterpret_cast(0xface)) == STR("**0xface")); + assert(format(STR("{:}="), STR("meow")) == STR("meow=")); + assert(format(basic_string_view(STR("{:\0>4}"), 6), '*') == basic_string(STR("\0\0\0*"), 4)); + throw_helper(STR("{:\x80\x80\x80\x80\x80>}"), 0); +} + +template +void libfmt_formatter_test_plus_sign() { + assert(format(STR("{0:+}"), 42) == STR("+42")); + assert(format(STR("{0:+}"), -42) == STR("-42")); + assert(format(STR("{0:+}"), 42) == STR("+42")); + assert(format(STR("{0:+}"), 42u) == STR("+42")); // behavior differs from libfmt, but conforms + assert(format(STR("{0:+}"), 42l) == STR("+42")); + assert(format(STR("{0:+}"), 42ul) == STR("+42")); // behavior differs from libfmt, but conforms + assert(format(STR("{0:+}"), 42ll) == STR("+42")); + assert(format(STR("{0:+}"), 42ull) == STR("+42")); // behavior differs from libfmt, but conforms + assert(format(STR("{0:+}"), 42.0) == STR("+42")); + assert(format(STR("{0:+}"), 42.0l) == STR("+42")); + throw_helper(STR("{0:+"), 'c'); + throw_helper(STR("{0:+}"), 'c'); + throw_helper(STR("{0:+}"), STR("abc")); + throw_helper(STR("{0:+}"), reinterpret_cast(0x42)); +} + +template +void libfmt_formatter_test_minus_sign() { + assert(format(STR("{0:-}"), 42) == STR("42")); + assert(format(STR("{0:-}"), -42) == STR("-42")); + assert(format(STR("{0:-}"), 42) == STR("42")); + assert(format(STR("{0:-}"), 42u) == STR("42")); // behavior differs from libfmt, but conforms + assert(format(STR("{0:-}"), 42l) == STR("42")); + assert(format(STR("{0:-}"), 42ul) == STR("42")); // behavior differs from libfmt, but conforms + assert(format(STR("{0:-}"), 42ll) == STR("42")); + assert(format(STR("{0:-}"), 42ull) == STR("42")); // behavior differs from libfmt, but conforms + assert(format(STR("{0:-}"), 42.0) == STR("42")); + assert(format(STR("{0:-}"), 42.0l) == STR("42")); + throw_helper(STR("{0:-"), 'c'); + throw_helper(STR("{0:-}"), 'c'); + throw_helper(STR("{0:-}"), STR("abc")); + throw_helper(STR("{0:-}"), reinterpret_cast(0x42)); +} + +template +void libfmt_formatter_test_space_sign() { + assert(format(STR("{0: }"), 42) == STR(" 42")); + assert(format(STR("{0: }"), -42) == STR("-42")); + assert(format(STR("{0: }"), 42) == STR(" 42")); + assert(format(STR("{0: }"), 42u) == STR(" 42")); // behavior differs from libfmt, but conforms + assert(format(STR("{0: }"), 42l) == STR(" 42")); + assert(format(STR("{0: }"), 42ul) == STR(" 42")); // behavior differs from libfmt, but conforms + assert(format(STR("{0: }"), 42ll) == STR(" 42")); + assert(format(STR("{0: }"), 42ull) == STR(" 42")); // behavior differs from libfmt, but conforms + assert(format(STR("{0: }"), 42.0) == STR(" 42")); + assert(format(STR("{0: }"), 42.0l) == STR(" 42")); + throw_helper(STR("{0: "), 'c'); + throw_helper(STR("{0: }"), 'c'); + throw_helper(STR("{0: }"), STR("abc")); + throw_helper(STR("{0: }"), reinterpret_cast(0x42)); +} + +template +void libfmt_formatter_test_hash_flag() { + assert(format(STR("{0:#}"), 42) == STR("42")); + assert(format(STR("{0:#}"), -42) == STR("-42")); + assert(format(STR("{0:#b}"), 42) == STR("0b101010")); + assert(format(STR("{0:#B}"), 42) == STR("0B101010")); + assert(format(STR("{0:#b}"), -42) == STR("-0b101010")); + assert(format(STR("{0:#x}"), 0x42) == STR("0x42")); + assert(format(STR("{0:#X}"), 0x42) == STR("0X42")); + assert(format(STR("{0:#x}"), -0x42) == STR("-0x42")); + assert(format(STR("{0:#o}"), 0) == STR("0")); + assert(format(STR("{0:#o}"), 042) == STR("042")); + assert(format(STR("{0:#o}"), -042) == STR("-042")); + assert(format(STR("{0:#}"), 42u) == STR("42")); + assert(format(STR("{0:#x}"), 0x42u) == STR("0x42")); + assert(format(STR("{0:#o}"), 042u) == STR("042")); + + assert(format(STR("{0:#}"), -42l) == STR("-42")); + assert(format(STR("{0:#x}"), 0x42l) == STR("0x42")); + assert(format(STR("{0:#x}"), -0x42l) == STR("-0x42")); + assert(format(STR("{0:#o}"), 042l) == STR("042")); + assert(format(STR("{0:#o}"), -042l) == STR("-042")); + assert(format(STR("{0:#}"), 42ul) == STR("42")); + assert(format(STR("{0:#x}"), 0x42ul) == STR("0x42")); + assert(format(STR("{0:#o}"), 042ul) == STR("042")); + + assert(format(STR("{0:#}"), -42ll) == STR("-42")); + assert(format(STR("{0:#x}"), 0x42ll) == STR("0x42")); + assert(format(STR("{0:#x}"), -0x42ll) == STR("-0x42")); + assert(format(STR("{0:#o}"), 042ll) == STR("042")); + assert(format(STR("{0:#o}"), -042ll) == STR("-042")); + assert(format(STR("{0:#}"), 42ull) == STR("42")); + assert(format(STR("{0:#x}"), 0x42ull) == STR("0x42")); + assert(format(STR("{0:#o}"), 042ull) == STR("042")); + + assert(format(STR("{0:#}"), -42.0) == STR("-42.")); // behavior differs from libfmt, but conforms + assert(format(STR("{0:#}"), -42.0l) == STR("-42.")); // behavior differs from libfmt, but conforms + assert(format(STR("{:#.0e}"), 42.0) == STR("4.e+01")); + assert(format(STR("{:#.0f}"), 0.01) == STR("0.")); + assert(format(STR("{:#.2g}"), 0.5) == STR("0.50")); + assert(format(STR("{:#.0f}"), 0.5) == STR("0.")); + throw_helper(STR("{0:#"), 'c'); + throw_helper(STR("{0:#}"), 'c'); + throw_helper(STR("{0:#}"), STR("abc")); + throw_helper(STR("{0:#}"), reinterpret_cast(0x42)); +} + +template +void libfmt_formatter_test_zero_flag() { + assert(format(STR("{0:0}"), 42) == STR("42")); + assert(format(STR("{0:05}"), -42) == STR("-0042")); + assert(format(STR("{0:05}"), 42u) == STR("00042")); + assert(format(STR("{0:05}"), -42l) == STR("-0042")); + assert(format(STR("{0:05}"), 42ul) == STR("00042")); + assert(format(STR("{0:05}"), -42ll) == STR("-0042")); + assert(format(STR("{0:05}"), 42ull) == STR("00042")); + assert(format(STR("{0:07}"), -42.0) == STR("-000042")); + assert(format(STR("{0:07}"), -42.0l) == STR("-000042")); + throw_helper(STR("{0:0"), 'c'); + throw_helper(STR("{0:05}"), 'c'); + throw_helper(STR("{0:05}"), STR("abc")); + throw_helper(STR("{0:05}"), reinterpret_cast(0x42)); +} + +template +void libfmt_formatter_test_runtime_width() { + throw_helper(STR("{0:{"), 0); + throw_helper(STR("{0:{}"), 0); + throw_helper(STR("{0:{?}}"), 0); + throw_helper(STR("{0:{1}}"), 0); + throw_helper(STR("{0:{0:}}"), 0); + throw_helper(STR("{0:{1}}"), 0, -1); + throw_helper(STR("{0:{1}}"), 0, (INT_MAX + 1u)); + throw_helper(STR("{0:{1}}"), 0, -1l); + throw_helper(STR("{0:{1}}"), 0, (INT_MAX + 1ul)); + assert(format(STR("{0:{1}}"), 0, '0') + == STR(" 0")); // behavior differs from libfmt, but conforms + throw_helper(STR("{0:{1}}"), 0, 0.0); + + assert(format(STR("{0:{1}}"), -42, 4) == STR(" -42")); + assert(format(STR("{0:{1}}"), 42u, 5) == STR(" 42")); + assert(format(STR("{0:{1}}"), -42l, 6) == STR(" -42")); + assert(format(STR("{0:{1}}"), 42ul, 7) == STR(" 42")); + assert(format(STR("{0:{1}}"), -42ll, 6) == STR(" -42")); + assert(format(STR("{0:{1}}"), 42ull, 7) == STR(" 42")); + assert(format(STR("{0:{1}}"), -1.23, 8) == STR(" -1.23")); + assert(format(STR("{0:{1}}"), -1.23l, 9) == STR(" -1.23")); + assert(format(STR("{0:{1}}"), reinterpret_cast(0xcafe), 10) + == STR("0xcafe ")); // behavior differs from libfmt, but conforms + assert(format(STR("{0:{1}}"), 'x', 11) == STR("x ")); + assert(format(STR("{0:{1}}"), STR("str"), 12) == STR("str ")); +} + +template +void libfmt_formatter_test_runtime_precision() { + throw_helper(STR("{0:.{"), 0); + throw_helper(STR("{0:.{}"), 0); + throw_helper(STR("{0:.{?}}"), 0); + throw_helper(STR("{0:.{1}"), 0, 0); + throw_helper(STR("{0:.{1}}"), 0); + throw_helper(STR("{0:.{0:}}"), 0); + throw_helper(STR("{0:.{1}}"), 0, -1); + throw_helper(STR("{0:.{1}}"), 0, (INT_MAX + 1u)); + throw_helper(STR("{0:.{1}}"), 0, -1l); + throw_helper(STR("{0:.{1}}"), 0, (INT_MAX + 1ul)); + throw_helper(STR("{0:.{1}}"), 0, '0'); + throw_helper(STR("{0:.{1}}"), 0, 0.0); + throw_helper(STR("{0:.{1}}"), 42, 2); + throw_helper(STR("{0:.{1}f}"), 42, 2); + throw_helper(STR("{0:.{1}}"), 42u, 2); + throw_helper(STR("{0:.{1}f}"), 42u, 2); + throw_helper(STR("{0:.{1}}"), 42l, 2); + throw_helper(STR("{0:.{1}f}"), 42l, 2); + throw_helper(STR("{0:.{1}}"), 42ul, 2); + throw_helper(STR("{0:.{1}f}"), 42ul, 2); + throw_helper(STR("{0:.{1}}"), 42ll, 2); + throw_helper(STR("{0:.{1}f}"), 42ll, 2); + throw_helper(STR("{0:.{1}}"), 42ull, 2); + throw_helper(STR("{0:.{1}f}"), 42ull, 2); + throw_helper(STR("{0:3.{1}}"), 'x', 0); + assert(format(STR("{0:.{1}}"), 1.2345, 2) == STR("1.2")); + assert(format(STR("{1:.{0}}"), 2, 1.2345l) == STR("1.2")); + throw_helper(STR("{0:.{1}}"), reinterpret_cast(0xcafe), 2); + throw_helper(STR("{0:.{1}f}"), reinterpret_cast(0xcafe), 2); + assert(format(STR("{0:.{1}}"), STR("str"), 2) == STR("st")); +} + +void test() { + test_simple_formatting(); + test_simple_formatting(); + + test_escaped_curls(); + test_escaped_curls(); + + test_simple_replacement_field(); + test_simple_replacement_field(); + + test_multiple_replacement_fields(); + test_multiple_replacement_fields(); + + test_fill_and_align(); + test_fill_and_align(); + + test_spec_replacement_field(); + test_spec_replacement_field(); + + test_size(); + test_size(); + + test_multibyte_format_strings(); + + libfmt_formatter_test_escape(); + libfmt_formatter_test_escape(); + + libfmt_formatter_test_args_in_different_position(); + libfmt_formatter_test_args_in_different_position(); + + libfmt_formatter_test_auto_arg_index(); + libfmt_formatter_test_auto_arg_index(); + + libfmt_formatter_test_left_align(); + libfmt_formatter_test_left_align(); + + libfmt_formatter_test_right_align(); + libfmt_formatter_test_right_align(); + + libfmt_formatter_test_center_align(); + libfmt_formatter_test_center_align(); + + libfmt_formatter_test_fill(); + libfmt_formatter_test_fill(); + + libfmt_formatter_test_plus_sign(); + libfmt_formatter_test_plus_sign(); + + libfmt_formatter_test_minus_sign(); + libfmt_formatter_test_minus_sign(); + + libfmt_formatter_test_space_sign(); + libfmt_formatter_test_space_sign(); + + libfmt_formatter_test_hash_flag(); + libfmt_formatter_test_hash_flag(); + + libfmt_formatter_test_zero_flag(); + libfmt_formatter_test_zero_flag(); + + libfmt_formatter_test_runtime_width(); + libfmt_formatter_test_runtime_width(); + + libfmt_formatter_test_runtime_precision(); + libfmt_formatter_test_runtime_precision(); +} + +int main() { + test(); +} diff --git a/tests/std/tests/P0645R10_text_formatting_legacy_text_encoding/env.lst b/tests/std/tests/P0645R10_text_formatting_legacy_text_encoding/env.lst new file mode 100644 index 00000000000..9aa5f2a5cdb --- /dev/null +++ b/tests/std/tests/P0645R10_text_formatting_legacy_text_encoding/env.lst @@ -0,0 +1,27 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# This is `concepts_matrix.lst` with `/execution-charset:.932` added. +# clang is excluded since it doesn't support non-UTF-8 execution charsets. + +RUNALL_INCLUDE ..\prefix.lst +RUNALL_CROSSLIST +PM_CL="/w14640 /Zc:threadSafeInit- /EHsc /std:c++latest /execution-charset:.932" +RUNALL_CROSSLIST +PM_CL="/MD /D_ITERATOR_DEBUG_LEVEL=0 /permissive- /Zc:noexceptTypes-" +PM_CL="/MD /D_ITERATOR_DEBUG_LEVEL=1 /permissive-" +PM_CL="/MD /D_ITERATOR_DEBUG_LEVEL=0 /permissive- /Zc:char8_t- /Zc:preprocessor" +PM_CL="/MDd /D_ITERATOR_DEBUG_LEVEL=0 /permissive- /Zc:wchar_t-" +PM_CL="/MDd /D_ITERATOR_DEBUG_LEVEL=1 /permissive-" +PM_CL="/MDd /D_ITERATOR_DEBUG_LEVEL=2 /permissive- /fp:except /Zc:preprocessor" +PM_CL="/MT /D_ITERATOR_DEBUG_LEVEL=0 /permissive-" +PM_CL="/MT /D_ITERATOR_DEBUG_LEVEL=0 /permissive- /analyze:only /analyze:autolog-" +PM_CL="/MT /D_ITERATOR_DEBUG_LEVEL=1 /permissive-" +PM_CL="/MTd /D_ITERATOR_DEBUG_LEVEL=0 /permissive- /fp:strict" +PM_CL="/MTd /D_ITERATOR_DEBUG_LEVEL=1 /permissive-" +PM_CL="/MTd /D_ITERATOR_DEBUG_LEVEL=2 /permissive" +PM_CL="/MTd /D_ITERATOR_DEBUG_LEVEL=2 /permissive- /analyze:only /analyze:autolog-" +PM_CL="/permissive- /Za /MD" +PM_CL="/permissive- /Za /MDd" +# PM_CL="/permissive- /BE /c /MD" +# PM_CL="/permissive- /BE /c /MTd" diff --git a/tests/std/tests/P0645R10_text_formatting_legacy_text_encoding/test.cpp b/tests/std/tests/P0645R10_text_formatting_legacy_text_encoding/test.cpp new file mode 100644 index 00000000000..beb4a499c62 --- /dev/null +++ b/tests/std/tests/P0645R10_text_formatting_legacy_text_encoding/test.cpp @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include + +#include "test_format_support.hpp" + +using namespace std; + +void test_multibyte_format_strings() { + { + assert(setlocale(LC_ALL, ".932") != nullptr); + const auto s = + "\x93\xfa\x96{\x92\x6e\x90}"sv; // Note the use of `{` and `}` as continuation bytes (from GH-1576) + assert(format(s) == s); + + assert(format("{:.2}", s) == "\x93\xfa"sv); + assert(format("{:4.2}", s) == "\x93\xfa "sv); + + assert(format("{:<4.2}", s) == "\x93\xfa "sv); + assert(format("{:^4.2}", s) == " \x93\xfa "sv); + assert(format("{:>4.2}", s) == " \x93\xfa"sv); + + assert(format("{:\x90}<4.2}", s) == "\x93\xfa\x90}\x90}"sv); + assert(format("{:\x90}^4.2}", s) == "\x90}\x93\xfa\x90}"sv); + assert(format("{:\x90}>4.2}", s) == "\x90}\x90}\x93\xfa"sv); + + assert(format("{:.3}", s) == "\x93\xfa"sv); + assert(format("{:4.3}", s) == "\x93\xfa "sv); + + assert(format("{:<4.3}", s) == "\x93\xfa "sv); + assert(format("{:^4.3}", s) == " \x93\xfa "sv); + assert(format("{:>4.3}", s) == " \x93\xfa"sv); + + assert(format("{:\x90}<4.3}", s) == "\x93\xfa\x90}\x90}"sv); + assert(format("{:\x90}^4.3}", s) == "\x90}\x93\xfa\x90}"sv); + assert(format("{:\x90}>4.3}", s) == "\x90}\x90}\x93\xfa"sv); + } + + assert(setlocale(LC_ALL, "C") != nullptr); +} + +void test_parse_align() { + auto parse_align_fn = _Parse_align>; + + { + assert(setlocale(LC_ALL, ".932") != nullptr); + test_parse_helper(parse_align_fn, "\x93\xfaX"sv, false, 3, + {.expected_alignment = _Fmt_align::_Right, .expected_fill = "\x96\x7b"sv}); + test_parse_helper(parse_align_fn, "\x92\x6e^X"sv, false, 3, + {.expected_alignment = _Fmt_align::_Center, .expected_fill = "\x92\x6e"sv}); + } + + assert(setlocale(LC_ALL, "C") != nullptr); +} + +int main() { + test_multibyte_format_strings(); + test_parse_align(); +} diff --git a/tests/std/tests/P0645R10_text_formatting_parse_contexts/env.lst b/tests/std/tests/P0645R10_text_formatting_parse_contexts/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0645R10_text_formatting_parse_contexts/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0645R10_text_formatting_parse_contexts/test.cpp b/tests/std/tests/P0645R10_text_formatting_parse_contexts/test.cpp new file mode 100644 index 00000000000..1f62d3529d3 --- /dev/null +++ b/tests/std/tests/P0645R10_text_formatting_parse_contexts/test.cpp @@ -0,0 +1,109 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +template +constexpr auto get_input() { + if constexpr (same_as) { + return "First {} and second {} and third {}"sv; + } else { + return L"First {} and second {} and third {}"sv; + } +} + +template +constexpr bool ensure_is_constant_expression(const bool should_be_constant_expression) { + basic_format_parse_context context{get_input(), 1}; + if (should_be_constant_expression) { + context.check_arg_id(0); + } else { + context.check_arg_id(1); + } + + return is_constant_evaluated(); +} + +const bool check_arg_id_is_constexpr_char = ensure_is_constant_expression(true); +const bool check_arg_id_not_constexpr_char = !ensure_is_constant_expression(false); +const bool check_arg_id_is_constexpr_wchar = ensure_is_constant_expression(true); +const bool check_arg_id_not_constexpr_wchar = !ensure_is_constant_expression(false); + +template +constexpr bool test_basic_format_parse_context() { + static_assert(!is_copy_constructible_v>); + static_assert(!is_copy_assignable_v>); + + const auto format_string = get_input(); + { // iterator interface + basic_format_parse_context context{format_string}; + const same_as::const_iterator> auto b = context.begin(); + assert(b == format_string.begin()); + static_assert(noexcept(context.begin())); + + const same_as::const_iterator> auto e = context.end(); + assert(e == format_string.end()); + static_assert(noexcept(context.end())); + + const auto new_position = format_string.begin() + 5; + context.advance_to(new_position); + assert(to_address(context.begin()) == to_address(new_position)); + assert(to_address(context.end()) == to_address(e)); + } + + { // arg_id + basic_format_parse_context context{format_string}; + const same_as auto first_arg_id = context.next_arg_id(); + assert(first_arg_id == 0); + + const same_as auto second_arg_id = context.next_arg_id(); + assert(second_arg_id == 1); + + if (!is_constant_evaluated()) { + try { + context.check_arg_id(0); + } catch (const format_error& e) { + assert(e.what() == "Can not switch from automatic to manual indexing"sv); + } + } + } + + { // check_arg_id + basic_format_parse_context context{format_string, 3}; + context.check_arg_id(0); + context.check_arg_id(1); + context.check_arg_id(2); // intentional duplicates to check whether this is ok to call multiple times + context.check_arg_id(1); + context.check_arg_id(0); + + if (!is_constant_evaluated()) { + try { + (void) context.next_arg_id(); + } catch (const format_error& e) { + assert(e.what() == "Can not switch from manual to automatic indexing"sv); + } + } + } + + return true; +} + +int main() { + test_basic_format_parse_context(); + test_basic_format_parse_context(); + static_assert(test_basic_format_parse_context()); + static_assert(test_basic_format_parse_context()); + + assert(check_arg_id_is_constexpr_char); + assert(check_arg_id_not_constexpr_char); + assert(check_arg_id_is_constexpr_wchar); + assert(check_arg_id_not_constexpr_wchar); +} diff --git a/tests/std/tests/P0645R10_text_formatting_parsing/env.lst b/tests/std/tests/P0645R10_text_formatting_parsing/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0645R10_text_formatting_parsing/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0645R10_text_formatting_parsing/test.cpp b/tests/std/tests/P0645R10_text_formatting_parsing/test.cpp new file mode 100644 index 00000000000..ddf204c0db5 --- /dev/null +++ b/tests/std/tests/P0645R10_text_formatting_parsing/test.cpp @@ -0,0 +1,257 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test_format_support.hpp" + +using namespace std; + +template +bool test_parse_align() { + auto parse_align_fn = _Parse_align>; + using view_typ = basic_string_view; + + view_typ s1(TYPED_LITERAL(CharT, "*<")); + view_typ s2(TYPED_LITERAL(CharT, "*>")); + view_typ s3(TYPED_LITERAL(CharT, "*^")); + + test_parse_helper(parse_align_fn, s1, false, view_typ::npos, + {.expected_alignment = _Fmt_align::_Left, .expected_fill = view_typ(TYPED_LITERAL(CharT, "*"))}); + test_parse_helper(parse_align_fn, s2, false, view_typ::npos, + {.expected_alignment = _Fmt_align::_Right, .expected_fill = view_typ(TYPED_LITERAL(CharT, "*"))}); + test_parse_helper(parse_align_fn, s3, false, view_typ::npos, + {.expected_alignment = _Fmt_align::_Center, .expected_fill = view_typ(TYPED_LITERAL(CharT, "*"))}); + + if constexpr (same_as) { + // This is a CJK character where the least significant byte is the same as ASCII '>', + // libfmt and initial drafts of narrowed characters when parsing alignments, causing + // \x343E (which is from CJK unified ideographs extension A) and similar characters to parse as + // an alignment specifier. + auto s4 = L"*\x343E"sv; + test_parse_helper(parse_align_fn, s4, false, view_typ::npos, {.expected_fill = L"*"sv}); + + // test multi-code-unit fill characters + { + test_parse_helper(parse_align_fn, L"\U0001F3C8X"sv, false, 3, + {.expected_alignment = _Fmt_align::_Right, .expected_fill = L"\U0001F3C8"sv}); + test_parse_helper(parse_align_fn, L"\U0001F3C8^X"sv, false, 3, + {.expected_alignment = _Fmt_align::_Center, .expected_fill = L"\U0001F3C8"sv}); + } + } else { + // test multibyte fill characters +#ifndef MSVC_INTERNAL_TESTING // TRANSITION, the Windows version on Contest VMs doesn't always understand ".UTF-8" + { + assert(setlocale(LC_ALL, ".UTF-8") != nullptr); + // "\xf0\x9f\x8f\x88" is U+1F3C8 AMERICAN FOOTBALL + test_parse_helper(parse_align_fn, "\xf0\x9f\x8f\x88X"sv, false, 5, + {.expected_alignment = _Fmt_align::_Right, .expected_fill = "\xf0\x9f\x8f\x88"sv}); + test_parse_helper(parse_align_fn, "\xf0\x9f\x8f\x88^X"sv, false, 5, + {.expected_alignment = _Fmt_align::_Center, .expected_fill = "\xf0\x9f\x8f\x88"sv}); + } +#endif // MSVC_INTERNAL_TESTING + + assert(setlocale(LC_ALL, "C") != nullptr); + } + + return true; +} + +template +bool test_parse_width() { + auto parse_width_fn = _Parse_width>; + using view_typ = basic_string_view; + + view_typ s0(TYPED_LITERAL(CharT, "1")); + view_typ s1(TYPED_LITERAL(CharT, "{1}")); + view_typ s2(TYPED_LITERAL(CharT, "{0}")); + view_typ s3(TYPED_LITERAL(CharT, "{}")); + view_typ i0(TYPED_LITERAL(CharT, "0")); + view_typ i1(TYPED_LITERAL(CharT, "01")); + + test_parse_helper(parse_width_fn, s0, false, view_typ::npos, {.expected_width = 1}); + test_parse_helper(parse_width_fn, s1, false, view_typ::npos, {.expected_dynamic_width = 1}); + test_parse_helper(parse_width_fn, s2, false, view_typ::npos, {.expected_dynamic_width = 0}); + test_parse_helper(parse_width_fn, s3, false, view_typ::npos, {.expected_auto_dynamic_width = true}); + test_parse_helper(parse_width_fn, i0, false, false); + test_parse_helper(parse_width_fn, i1, false, false); + return true; +} + +template +bool test_parse_arg_id() { + auto parse_arg_id_fn = _Parse_arg_id; + using view_typ = basic_string_view; + // note that parse arg id starts with the arg id itself, not the { beginning of the + // format spec + view_typ s0(TYPED_LITERAL(CharT, "}")); + view_typ s1(TYPED_LITERAL(CharT, ":")); + view_typ s2(TYPED_LITERAL(CharT, ":}")); + view_typ s3(TYPED_LITERAL(CharT, "0:}")); + view_typ s4(TYPED_LITERAL(CharT, "0:")); + view_typ s5(TYPED_LITERAL(CharT, "1}")); + view_typ i0(TYPED_LITERAL(CharT, "01}")); + view_typ i1(TYPED_LITERAL(CharT, "0")); + + test_parse_helper(parse_arg_id_fn, s0, false, 0); + test_parse_helper(parse_arg_id_fn, s1, false, 0); + test_parse_helper(parse_arg_id_fn, s2, false, 0); + test_parse_helper(parse_arg_id_fn, s3, false, 1); + test_parse_helper(parse_arg_id_fn, s4, false, 1); + test_parse_helper(parse_arg_id_fn, s5, false, 1); + + // can't test the expected exceptions in a constexpr + // context + if (!is_constant_evaluated()) { + test_parse_helper(parse_arg_id_fn, i0, true); + test_parse_helper(parse_arg_id_fn, i1, true); + } + + return true; +} + +template +bool test_parse_precision() { + auto parse_pre_fn = _Parse_precision>; + using view_typ = basic_string_view; + + view_typ s0(TYPED_LITERAL(CharT, ".0")); + view_typ s1(TYPED_LITERAL(CharT, ".1")); + view_typ s2(TYPED_LITERAL(CharT, ".12")); + view_typ s3(TYPED_LITERAL(CharT, ".{1}")); + view_typ s4(TYPED_LITERAL(CharT, ".{}")); + + view_typ i0(TYPED_LITERAL(CharT, ".{ }")); + view_typ i1(TYPED_LITERAL(CharT, ".")); + view_typ i2(TYPED_LITERAL(CharT, ".{ |")); + view_typ i3(TYPED_LITERAL(CharT, ".{")); + + test_parse_helper(parse_pre_fn, s0, false, view_typ::npos, {.expected_precision = 0}); + test_parse_helper(parse_pre_fn, s1, false, view_typ::npos, {.expected_precision = 1}); + test_parse_helper(parse_pre_fn, s2, false, view_typ::npos, {.expected_precision = 12}); + test_parse_helper(parse_pre_fn, s3, false, view_typ::npos, {.expected_dynamic_precision = 1}); + test_parse_helper(parse_pre_fn, s4, false, view_typ::npos, {.expected_auto_dynamic_precision = true}); + + if (!is_constant_evaluated()) { + test_parse_helper(parse_pre_fn, i0, true); + test_parse_helper(parse_pre_fn, i1, true); + test_parse_helper(parse_pre_fn, i2, true); + test_parse_helper(parse_pre_fn, i3, true); + } + + return true; +} + +template +bool test_parse_format_specs() { + auto parse_format_specs_fn = _Parse_format_specs>; + using view_typ = basic_string_view; + + view_typ s0(TYPED_LITERAL(CharT, "6}")); + view_typ s1(TYPED_LITERAL(CharT, "*<6")); + view_typ s2(TYPED_LITERAL(CharT, "*>6}")); + view_typ s3(TYPED_LITERAL(CharT, "*^6}")); + view_typ s4(TYPED_LITERAL(CharT, "6d}")); + view_typ s5(TYPED_LITERAL(CharT, "*^+4.4a}")); + view_typ s6(TYPED_LITERAL(CharT, "*^+#04.4La}")); + test_parse_helper(parse_format_specs_fn, s0, false, s0.size() - 1, {.expected_width = 6}); + test_parse_helper(parse_format_specs_fn, s1, false, s1.size(), + {.expected_alignment = _Fmt_align::_Left, + .expected_fill = view_typ(TYPED_LITERAL(CharT, "*")), + .expected_width = 6}); + test_parse_helper(parse_format_specs_fn, s2, false, s2.size() - 1, + {.expected_alignment = _Fmt_align::_Right, + .expected_fill = view_typ(TYPED_LITERAL(CharT, "*")), + .expected_width = 6}); + test_parse_helper(parse_format_specs_fn, s3, false, s3.size() - 1, + {.expected_alignment = _Fmt_align::_Center, + .expected_fill = view_typ(TYPED_LITERAL(CharT, "*")), + .expected_width = 6}); + test_parse_helper(parse_format_specs_fn, s4, false, s4.size() - 1, {.expected_width = 6, .expected_type = 'd'}); + test_parse_helper(parse_format_specs_fn, s5, false, s5.size() - 1, + {.expected_alignment = _Fmt_align::_Center, + .expected_sign = _Fmt_sign::_Plus, + .expected_fill = view_typ(TYPED_LITERAL(CharT, "*")), + .expected_width = 4, + .expected_precision = 4, + .expected_type = 'a'}); + test_parse_helper(parse_format_specs_fn, s6, false, s6.size() - 1, + {.expected_alignment = _Fmt_align::_Center, + .expected_sign = _Fmt_sign::_Plus, + .expected_fill = view_typ(TYPED_LITERAL(CharT, "*")), + .expected_width = 4, + .expected_precision = 4, + .expected_hash = true, + .expected_zero = true, + .expected_localized = true, + .expected_type = 'a'}); + return true; +} + +template +constexpr bool test_specs_setter() { + // just instantiate for now. + _Basic_format_specs specs = {}; + _Specs_setter setter(specs); + + (void) setter; + return true; +} + +template +constexpr bool test_specs_checker() { + _Specs_checker> checker( + noop_testing_callbacks{}, _Basic_format_arg_type::_Float_type); + (void) checker; + return true; +} + +void test() { + test_parse_align(); + test_parse_align(); + + test_parse_arg_id(); + test_parse_arg_id(); + + test_parse_width(); + test_parse_width(); + + test_parse_precision(); + test_parse_precision(); + + test_parse_format_specs(); + test_parse_format_specs(); + + test_specs_setter(); + test_specs_setter(); + static_assert(test_specs_setter()); + static_assert(test_specs_setter()); + + test_specs_checker(); + test_specs_checker(); + static_assert(test_specs_checker()); + static_assert(test_specs_checker()); +} + +int main() { + try { + test(); + } catch (const format_error& e) { + printf("format_error: %s\n", e.what()); + assert(false); + } catch (const exception& e) { + printf("exception: %s\n", e.what()); + assert(false); + } +} diff --git a/tests/std/tests/P0645R10_text_formatting_utf8/env.lst b/tests/std/tests/P0645R10_text_formatting_utf8/env.lst new file mode 100644 index 00000000000..42da0946d2d --- /dev/null +++ b/tests/std/tests/P0645R10_text_formatting_utf8/env.lst @@ -0,0 +1,6 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst +RUNALL_CROSSLIST +PM_CL="/utf-8" diff --git a/tests/std/tests/P0645R10_text_formatting_utf8/test.cpp b/tests/std/tests/P0645R10_text_formatting_utf8/test.cpp new file mode 100644 index 00000000000..5b988794238 --- /dev/null +++ b/tests/std/tests/P0645R10_text_formatting_utf8/test.cpp @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include + +#include "test_format_support.hpp" + +using namespace std; + +void test_multibyte_format_strings() { + { + // Filling with footballs ("\xf0\x9f\x8f\x88" is U+1F3C8 AMERICAN FOOTBALL) + assert(format("{:\xf0\x9f\x8f\x88>4}"sv, 42) == "\xf0\x9f\x8f\x88\xf0\x9f\x8f\x88\x34\x32"); + + assert(format("{:\xf0\x9f\x8f\x88<4.2}", "1") == "\x31\xf0\x9f\x8f\x88\xf0\x9f\x8f\x88\xf0\x9f\x8f\x88"sv); + assert(format("{:\xf0\x9f\x8f\x88^4.2}", "1") == "\xf0\x9f\x8f\x88\x31\xf0\x9f\x8f\x88\xf0\x9f\x8f\x88"sv); + assert(format("{:\xf0\x9f\x8f\x88>4.2}", "1") == "\xf0\x9f\x8f\x88\xf0\x9f\x8f\x88\xf0\x9f\x8f\x88\x31"sv); + } + + { + try { + (void) format("{:\x9f\x8f\x88<10}"sv, 42); // Bad fill character encoding: missing lead byte before \x9f + assert(false); + } catch (const format_error&) { + } + } +} + +void test_parse_align() { + auto parse_align_fn = _Parse_align>; + + { + // "\xf0\x9f\x8f\x88" is U+1F3C8 AMERICAN FOOTBALL + test_parse_helper(parse_align_fn, "\xf0\x9f\x8f\x88X"sv, false, 5, + {.expected_alignment = _Fmt_align::_Right, .expected_fill = "\xf0\x9f\x8f\x88"sv}); + test_parse_helper(parse_align_fn, "\xf0\x9f\x8f\x88^X"sv, false, 5, + {.expected_alignment = _Fmt_align::_Center, .expected_fill = "\xf0\x9f\x8f\x88"sv}); + } +} + +void run_tests() { + test_multibyte_format_strings(); + test_parse_align(); +} + +int main() { + run_tests(); + + assert(setlocale(LC_ALL, ".1252") != nullptr); + run_tests(); + + assert(setlocale(LC_ALL, ".932") != nullptr); + run_tests(); + +#ifndef MSVC_INTERNAL_TESTING // TRANSITION, the Windows version on Contest VMs doesn't always understand ".UTF-8" + assert(setlocale(LC_ALL, ".UTF-8") != nullptr); + run_tests(); +#endif +} diff --git a/tests/std/tests/P1502R1_standard_library_header_units/custom_format.py b/tests/std/tests/P1502R1_standard_library_header_units/custom_format.py index d86678b8fd0..9b77575934a 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/custom_format.py +++ b/tests/std/tests/P1502R1_standard_library_header_units/custom_format.py @@ -29,7 +29,7 @@ def getBuildSteps(self, test, litConfig, shared): 'exception', 'execution', 'filesystem', - # 'format', + 'format', 'forward_list', 'fstream', 'functional', diff --git a/tests/std/tests/P1502R1_standard_library_header_units/custombuild.pl b/tests/std/tests/P1502R1_standard_library_header_units/custombuild.pl index 37dff666bd4..5d07acc08f2 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/custombuild.pl +++ b/tests/std/tests/P1502R1_standard_library_header_units/custombuild.pl @@ -28,7 +28,7 @@ () "exception", "execution", "filesystem", - # "format", + "format", "forward_list", "fstream", "functional", diff --git a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp index 5794c45566a..c34aa8835ab 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp +++ b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp @@ -29,7 +29,7 @@ import ; import ; import ; import ; -// import ; +import ; import ; import ; import ; @@ -126,7 +126,7 @@ int main() { { puts("Testing ."); -#ifdef MSVC_INTERNAL_TESTING // TRANSITION, VSO-1088552 (deduction guides) +#if 0 // TRANSITION, VSO-1088552 (deduction guides), VSO-1309454 (duplicated deduction guides) constexpr array arr{10, 20, 30, 40, 50}; #else // ^^^ no workaround / workaround vvv constexpr array arr{10, 20, 30, 40, 50}; @@ -305,7 +305,7 @@ int main() { { puts("Testing ."); - puts("(TRANSITION, not yet implemented.)"); + assert(format("{} {}", "testing", "format") == "testing format"); } { @@ -581,7 +581,7 @@ int main() { { puts("Testing ."); constexpr int arr[]{11, 0, 22, 0, 33, 0, 44, 0, 55}; -#ifdef MSVC_INTERNAL_TESTING // TRANSITION, VSO-1088552 (deduction guides) +#if 0 // TRANSITION, VSO-1088552 (deduction guides), VSO-1309454 (duplicated deduction guides) assert(ranges::distance(views::filter(arr, [](int x) { return x == 0; })) == 4); static_assert(ranges::distance(views::filter(arr, [](int x) { return x != 0; })) == 5); #else // ^^^ no workaround / workaround vvv diff --git a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp index 1b82f92ce4c..73bf5dfcf86 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp @@ -675,6 +675,20 @@ STATIC_ASSERT(__cpp_lib_filesystem == 201703L); #endif #endif +#if _HAS_CXX20 && !defined(__EDG__) +#ifndef __cpp_lib_format +#error __cpp_lib_format is not defined +#elif __cpp_lib_format != 201907L +#error __cpp_lib_format is not 201907L +#else +STATIC_ASSERT(__cpp_lib_format == 201907L); +#endif +#else +#ifdef __cpp_lib_format +#error __cpp_lib_format is defined +#endif +#endif + #if _HAS_CXX17 #ifndef __cpp_lib_gcd_lcm #error __cpp_lib_gcd_lcm is not defined diff --git a/tests/std/tests/concepts_matrix.lst b/tests/std/tests/concepts_matrix.lst index 5810e10f464..d35c3015ad0 100644 --- a/tests/std/tests/concepts_matrix.lst +++ b/tests/std/tests/concepts_matrix.lst @@ -1,6 +1,8 @@ # Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# When updating this file, also update tests\P0645R10_text_formatting_legacy_text_encoding\env.lst to match + RUNALL_INCLUDE .\prefix.lst RUNALL_CROSSLIST PM_CL="/w14640 /Zc:threadSafeInit- /EHsc /std:c++latest" diff --git a/tests/std/tests/include_each_header_alone_matrix.lst b/tests/std/tests/include_each_header_alone_matrix.lst index 504b759ef6d..1f68eacdd89 100644 --- a/tests/std/tests/include_each_header_alone_matrix.lst +++ b/tests/std/tests/include_each_header_alone_matrix.lst @@ -25,6 +25,7 @@ PM_CL="/DMEOW_HEADER=deque" PM_CL="/DMEOW_HEADER=exception" PM_CL="/DMEOW_HEADER=execution" PM_CL="/DMEOW_HEADER=filesystem" +PM_CL="/DMEOW_HEADER=format" PM_CL="/DMEOW_HEADER=forward_list" PM_CL="/DMEOW_HEADER=fstream" PM_CL="/DMEOW_HEADER=functional"