diff --git a/stl/inc/format b/stl/inc/format index cd441791ba..c7af4c36ca 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -2763,12 +2763,39 @@ struct _Arg_formatter { } }; +enum class _String_type : uint8_t { _Not_a_string, _Known_size_string, _Nullterminated_string }; + +template +inline constexpr _String_type _String_type_of = _String_type::_Not_a_string; + +template <_Format_supported_charT _CharT, class _Traits> +inline constexpr _String_type _String_type_of> = _String_type::_Known_size_string; + +template <_Format_supported_charT _CharT, class _Traits, class _Allocator> +inline constexpr _String_type _String_type_of> = + _String_type::_Known_size_string; + +template <_Format_supported_charT _CharT> +inline constexpr _String_type _String_type_of<_CharT*> = _String_type::_Nullterminated_string; + +template <_Format_supported_charT _CharT> +inline constexpr _String_type _String_type_of = _String_type::_Nullterminated_string; + +template <_Format_supported_charT _CharT, size_t _Nx> +inline constexpr _String_type _String_type_of = _String_type::_Nullterminated_string; + +template +concept _Derived_from_formatter_base = requires { + typename _Ty::_Is_formatter_base_specialization; +}; + // Special compile time version of _Parse_format_specs. This version is parameterized on // the type of the argument associated with the format specifier, since we don't really // care about avoiding code bloat for code that never runs at runtime, and we can't form // the erased basic_format_args structure at compile time. template -consteval typename _ParseContext::iterator _Compile_time_parse_format_specs(_ParseContext& _Pc) { +consteval pair> + _Compile_time_parse_format_specs(_ParseContext& _Pc) { using _CharT = typename _ParseContext::char_type; using _Context = basic_format_context>, _CharT>; using _ArgTraits = _Format_arg_traits<_Context>; @@ -2779,31 +2806,91 @@ consteval typename _ParseContext::iterator _Compile_time_parse_format_specs(_Par using _FormattedType = conditional_t::handle>, _Ty, _FormattedTypeMapping>; formatter<_FormattedType, _CharT> _Formatter{}; - return _Formatter.parse(_Pc); + auto _Iter = _Formatter.parse(_Pc); + if constexpr (_Derived_from_formatter_base>) { + return {_Iter, _Formatter._Specs}; + } else { + return {_Iter, {}}; + } } // set of format parsing actions that only checks for validity template struct _Format_checker { using _ParseContext = basic_format_parse_context<_CharT>; - using _ParseFunc = typename _ParseContext::iterator (*)(_ParseContext&); + using _ParseFunc = pair> (*)(_ParseContext&); - static constexpr size_t _Num_args = sizeof...(_Args); + static constexpr size_t _Num_args = sizeof...(_Args); + static constexpr size_t _Num_args_non_zero = _Num_args > 0 ? _Num_args : 1; _ParseContext _Parse_context; - _ParseFunc _Parse_funcs[_Num_args > 0 ? _Num_args : 1]; + _ParseFunc _Parse_funcs[_Num_args_non_zero]; + size_t _Arg_use_count[_Num_args_non_zero]{}; // only used for strings + _String_type _Arg_type[_Num_args_non_zero]; + size_t _Estimated_size = 0; + bool _Is_estimation_exact = true; consteval explicit _Format_checker(basic_string_view<_CharT> _Fmt) noexcept - : _Parse_context(_Fmt, _Num_args), _Parse_funcs{&_Compile_time_parse_format_specs<_Args, _ParseContext>...} {} - constexpr void _On_text(const _CharT*, const _CharT*) const noexcept {} - constexpr void _On_replacement_field(size_t, const _CharT*) const noexcept {} + : _Parse_context(_Fmt, _Num_args), _Parse_funcs{&_Compile_time_parse_format_specs<_Args, _ParseContext>...}, + _Arg_type{_String_type_of<_Args>...} {} + + constexpr void _On_text(const _CharT* _First, const _CharT* _Last) noexcept { + _Estimated_size += _Last - _First; + } + + constexpr void _On_replacement_field(const size_t _Id, const _CharT*) noexcept { + if (_Arg_type[_Id] == _String_type::_Known_size_string + || _Arg_type[_Id] == _String_type::_Nullterminated_string) { + // if type of the argument is a string we will add the length of it later and the size will remain exact + ++_Arg_use_count[_Id]; + } else { + _Estimated_size += 8; // estimate for length of all other arguments + _Is_estimation_exact = false; + } + } + constexpr const _CharT* _On_format_specs(const size_t _Id, const _CharT* _First, const _CharT*) { _Parse_context.advance_to(_Parse_context.begin() + (_First - _Parse_context.begin()._Unwrapped())); - if (_Id < _Num_args) { - auto _Iter = _Parse_funcs[_Id](_Parse_context); // TRANSITION, VSO-1451773 (workaround: named variable) - return _Iter._Unwrapped(); - } else { + if (_Id >= _Num_args) { return _First; } + + auto [_Iter, _Specs] = _Parse_funcs[_Id](_Parse_context); + if (_Arg_type[_Id] == _String_type::_Nullterminated_string) { + if (_Specs._Precision >= 0) { + // if precision (the maximum length) is specified as a constant, add it + _Estimated_size += _Specs._Precision; + _Is_estimation_exact = false; + } else if (_Specs._Width > 0) { + // otherwise, if width (the minimum length) is specified as a constant, add it + _Estimated_size += _Specs._Width; + _Is_estimation_exact = false; + } else if (_Specs._Dynamic_precision_index >= 0) { + // if precision is dynamic we can't really predict so let's estimate it to 32 + _Estimated_size += 32; + _Is_estimation_exact = false; + } else if (_Specs._Dynamic_width_index >= 0) { + // if precision is not specified and width is dynamic we will calculate the length of the + // argument and add it to estimation + ++_Arg_use_count[_Id]; + _Is_estimation_exact = false; + } else { + // if precision and width are not specified we will calculate the length of the argument and add + // it to estimation. The estimation will remain exact + ++_Arg_use_count[_Id]; + } + } else if (_Arg_type[_Id] == _String_type::_Known_size_string) { + // if the length of the string is known we will add it to estimation + ++_Arg_use_count[_Id]; + if (_Specs._Precision >= 0 || _Specs._Width > 0 || _Specs._Dynamic_precision_index >= 0 + || _Specs._Dynamic_width_index >= 0) { + _Is_estimation_exact = false; + } + } else { + // for all other arguments use the largest of precision, width, and 8 + _Estimated_size += (_STD max)((_STD max)(_Specs._Precision, _Specs._Width), 8); + _Is_estimation_exact = false; + } + return _Iter._Unwrapped(); } }; @@ -2869,6 +2956,8 @@ template struct _Formatter_base { using _Pc = basic_format_parse_context<_CharT>; + using _Is_formatter_base_specialization = void; + constexpr 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); @@ -2898,6 +2987,10 @@ struct _Formatter_base { private: _Dynamic_format_specs<_CharT> _Specs; + + template + friend consteval pair> + _Compile_time_parse_format_specs(_ParseContext& _Pc); }; #define _FORMAT_SPECIALIZE_FOR(_Type, _ArgType) \ @@ -2952,15 +3045,54 @@ struct formatter, _CharT> template struct _Basic_format_string { + static constexpr size_t _Num_args = sizeof...(_Args); + basic_string_view<_CharT> _Str; + size_t _Arg_use_count[_Num_args > 0 ? _Num_args : 1]{}; + size_t _Estimated_size = 0; + bool _Is_estimation_exact = false; template requires convertible_to> consteval _Basic_format_string(const _Ty& _Str_val) : _Str(_Str_val) { if (_Is_execution_charset_self_synchronizing()) { +#ifndef __clang__ // TRANSITION, clang consteval bug (likely LLVM-52648) + _Format_checker<_CharT, remove_cvref_t<_Args>...> _Handler{_Str}; + _Parse_format_string(_Str, _Handler); + _RANGES copy(_Handler._Arg_use_count, _Arg_use_count); + _Estimated_size = _Handler._Estimated_size; + _Is_estimation_exact = _Handler._Is_estimation_exact; +#else // ^^^ no workaround ^^^ / vvv workaround vvv _Parse_format_string(_Str, _Format_checker<_CharT, remove_cvref_t<_Args>...>{_Str}); + _Estimated_size = _Str.size() + _Num_args * 8; + _RANGES fill(_Arg_use_count, static_cast(1)); +#endif // ^^^ workaround ^^^ + } else { + // fallback to assumption that all arguments are used once + _Estimated_size = _Str.size() + _Num_args * 8; + _RANGES fill(_Arg_use_count, static_cast(1)); } } + + template + _NODISCARD constexpr size_t _Estimate_required_capacity_helper( + const _Args&... _Arg_values, integer_sequence) const noexcept { + const auto _Visitor = [this](const _ArgTy& _Arg, size_t _Id) noexcept { + if constexpr (_String_type_of<_ArgTy> == _String_type::_Known_size_string) { + return _Arg_use_count[_Id] * _Arg.size(); + } else if constexpr (_String_type_of<_ArgTy> == _String_type::_Nullterminated_string) { + // don't bother with calculating the length if we don't need to + return _Arg_use_count[_Id] > 0 ? _Arg_use_count[_Id] * char_traits<_CharT>::length(_Arg) : 0; + } else { + return 0; + } + }; + return (_Estimated_size + ... + _Visitor(_Arg_values, _Ids)); + } + + _NODISCARD constexpr size_t _Estimate_required_capacity(const _Args&... _Arg_values) const noexcept { + return _Estimate_required_capacity_helper(_Arg_values..., index_sequence_for<_Args...>{}); + } }; template @@ -3092,22 +3224,74 @@ _NODISCARD wstring vformat(const locale& _Loc, const wstring_view _Fmt, const wf template _NODISCARD string format(const _Fmt_string<_Types...> _Fmt, _Types&&... _Args) { - return _STD vformat(_Fmt._Str, _STD make_format_args(_Args...)); + const size_t _Estimated_size = _Fmt._Estimate_required_capacity(_Args...); + string _Str; + if (_Fmt._Is_estimation_exact) { + // The Standard does not allow op passed to resize_and_overwrite to throw an exception, but it's not a concern + // for our implementation + _Str._Resize_and_overwrite(_Estimated_size, [&](char* _Out, size_t _Size) { + _STD format_to(_Out, _Fmt, _STD forward<_Types>(_Args)...); + return _Size; + }); + } else { + _Str.reserve(_Estimated_size); + _STD format_to(back_insert_iterator{_Str}, _Fmt, _STD forward<_Types>(_Args)...); + } + return _Str; } template _NODISCARD wstring format(const _Fmt_wstring<_Types...> _Fmt, _Types&&... _Args) { - return _STD vformat(_Fmt._Str, _STD make_wformat_args(_Args...)); + const size_t _Estimated_size = _Fmt._Estimate_required_capacity(_Args...); + wstring _Str; + if (_Fmt._Is_estimation_exact) { + // The Standard does not allow op passed to resize_and_overwrite to throw an exception, but it's not a concern + // for our implementation + _Str._Resize_and_overwrite(_Estimated_size, [&](wchar_t* _Out, size_t _Size) { + _STD format_to(_Out, _Fmt, _STD forward<_Types>(_Args)...); + return _Size; + }); + } else { + _Str.reserve(_Estimated_size); + _STD format_to(back_insert_iterator{_Str}, _Fmt, _STD forward<_Types>(_Args)...); + } + return _Str; } template _NODISCARD string format(const locale& _Loc, const _Fmt_string<_Types...> _Fmt, _Types&&... _Args) { - return _STD vformat(_Loc, _Fmt._Str, _STD make_format_args(_Args...)); + const size_t _Estimated_size = _Fmt._Estimate_required_capacity(_Args...); + string _Str; + if (_Fmt._Is_estimation_exact) { + // The Standard does not allow op passed to resize_and_overwrite to throw an exception, but it's not a concern + // for our implementation + _Str._Resize_and_overwrite(_Estimated_size, [&](char* _Out, size_t _Size) { + _STD format_to(_Out, _Loc, _Fmt, _STD forward<_Types>(_Args)...); + return _Size; + }); + } else { + _Str.reserve(_Estimated_size); + _STD format_to(back_insert_iterator{_Str}, _Loc, _Fmt, _STD forward<_Types>(_Args)...); + } + return _Str; } template _NODISCARD wstring format(const locale& _Loc, const _Fmt_wstring<_Types...> _Fmt, _Types&&... _Args) { - return _STD vformat(_Loc, _Fmt._Str, _STD make_wformat_args(_Args...)); + const size_t _Estimated_size = _Fmt._Estimate_required_capacity(_Args...); + wstring _Str; + if (_Fmt._Is_estimation_exact) { + // The Standard does not allow op passed to resize_and_overwrite to throw an exception, but it's not a concern + // for our implementation + _Str._Resize_and_overwrite(_Estimated_size, [&](wchar_t* _Out, size_t _Size) { + _STD format_to(_Out, _Loc, _Fmt, _STD forward<_Types>(_Args)...); + return _Size; + }); + } else { + _Str.reserve(_Estimated_size); + _STD format_to(back_insert_iterator{_Str}, _Loc, _Fmt, _STD forward<_Types>(_Args)...); + } + return _Str; } template @@ -3150,30 +3334,46 @@ format_to_n_result<_OutputIt> format_to_n(_OutputIt _Out, const iter_difference_ template _NODISCARD size_t formatted_size(const _Fmt_string<_Types...> _Fmt, _Types&&... _Args) { - _Fmt_counting_buffer _Buf; - _STD vformat_to(_Fmt_it{_Buf}, _Fmt._Str, _STD make_format_args(_Args...)); - return _Buf._Count(); + if (_Fmt._Is_estimation_exact) { + return _Fmt._Estimate_required_capacity(_Args...); + } else { + _Fmt_counting_buffer _Buf; + _STD vformat_to(_Fmt_it{_Buf}, _Fmt._Str, _STD make_format_args(_Args...)); + return _Buf._Count(); + } } template _NODISCARD size_t formatted_size(const _Fmt_wstring<_Types...> _Fmt, _Types&&... _Args) { - _Fmt_counting_buffer _Buf; - _STD vformat_to(_Fmt_wit{_Buf}, _Fmt._Str, _STD make_wformat_args(_Args...)); - return _Buf._Count(); + if (_Fmt._Is_estimation_exact) { + return _Fmt._Estimate_required_capacity(_Args...); + } else { + _Fmt_counting_buffer _Buf; + _STD vformat_to(_Fmt_wit{_Buf}, _Fmt._Str, _STD make_wformat_args(_Args...)); + return _Buf._Count(); + } } template _NODISCARD size_t formatted_size(const locale& _Loc, const _Fmt_string<_Types...> _Fmt, _Types&&... _Args) { - _Fmt_counting_buffer _Buf; - _STD vformat_to(_Fmt_it{_Buf}, _Loc, _Fmt._Str, _STD make_format_args(_Args...)); - return _Buf._Count(); + if (_Fmt._Is_estimation_exact) { + return _Fmt._Estimate_required_capacity(_Args...); + } else { + _Fmt_counting_buffer _Buf; + _STD vformat_to(_Fmt_it{_Buf}, _Loc, _Fmt._Str, _STD make_format_args(_Args...)); + return _Buf._Count(); + } } template _NODISCARD size_t formatted_size(const locale& _Loc, const _Fmt_wstring<_Types...> _Fmt, _Types&&... _Args) { - _Fmt_counting_buffer _Buf; - _STD vformat_to(_Fmt_wit{_Buf}, _Loc, _Fmt._Str, _STD make_wformat_args(_Args...)); - return _Buf._Count(); + if (_Fmt._Is_estimation_exact) { + return _Fmt._Estimate_required_capacity(_Args...); + } else { + _Fmt_counting_buffer _Buf; + _STD vformat_to(_Fmt_wit{_Buf}, _Loc, _Fmt._Str, _STD make_wformat_args(_Args...)); + return _Buf._Count(); + } } _STD_END diff --git a/stl/inc/xstring b/stl/inc/xstring index 9398b7a7ab..a0c3af6f5a 100644 --- a/stl/inc/xstring +++ b/stl/inc/xstring @@ -3959,9 +3959,8 @@ public: } } -#if _HAS_CXX23 template - constexpr void resize_and_overwrite(_CRT_GUARDOVERFLOW const size_type _New_size, _Operation _Op) { + _CONSTEXPR20 void _Resize_and_overwrite(_CRT_GUARDOVERFLOW const size_type _New_size, _Operation _Op) { if (_Mypair._Myval2._Myres < _New_size) { _Reallocate_grow_by(_New_size - _Mypair._Myval2._Mysize, [](_Elem* const _New_ptr, const _Elem* const _Old_ptr, const size_type _Old_size) { @@ -3976,6 +3975,12 @@ public: #endif // _CONTAINER_DEBUG_LEVEL > 0 _Eos(_Result_size); } + +#if _HAS_CXX23 + template + constexpr void resize_and_overwrite(_CRT_GUARDOVERFLOW const size_type _New_size, _Operation _Op) { + _Resize_and_overwrite(_New_size, _STD move(_Op)); + } #endif // _HAS_CXX23 _NODISCARD _CONSTEXPR20 size_type capacity() const noexcept { diff --git a/tests/std/tests/P0645R10_text_formatting_formatting/env.lst b/tests/std/tests/P0645R10_text_formatting_formatting/env.lst index d6d824b587..24a1260eb7 100644 --- a/tests/std/tests/P0645R10_text_formatting_formatting/env.lst +++ b/tests/std/tests/P0645R10_text_formatting_formatting/env.lst @@ -2,3 +2,6 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception RUNALL_INCLUDE ..\concepts_20_matrix.lst +RUNALL_CROSSLIST +PM_CL="" +PM_CL="/utf-8" diff --git a/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp b/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp index 2fb6af1440..b7287c6f08 100644 --- a/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp +++ b/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp @@ -39,6 +39,7 @@ struct choose_literal { #define TYPED_LITERAL(CharT, Literal) (choose_literal::choose(Literal, L##Literal)) #define STR(Str) TYPED_LITERAL(charT, Str) +#define STR_VIEW(Str) basic_string_view(STR(Str)) // Test against IDL mismatch between the DLL which stores the locale and the code which uses it. #ifdef _DEBUG @@ -1016,11 +1017,49 @@ void test_spec_replacement_field() { test_pointer_specs(); test_string_specs(); } + +enum class my_integer { one, two, three, forty_two }; + +template +struct std::formatter : std::formatter, charT> { + template + auto format(const my_integer& val, FC& format_ctx) { + basic_string_view str; + switch (val) { + case my_integer::one: + str = STR("one"); + break; + case my_integer::two: + str = STR("two"); + break; + case my_integer::three: + str = STR("three"); + break; + case my_integer::forty_two: + str = STR("forty two"); + break; + } + return std::formatter, charT>::format(str, format_ctx); + } +}; + template -void test_size_helper_impl(const size_t expected_size, const _Basic_format_string fmt, Args&&... args) { +void test_size_helper_impl(const size_t expected_size, const bool expected_is_estimation_exact, + const _Basic_format_string fmt, Args&&... args) { assert(formatted_size(fmt, forward(args)...) == expected_size); assert(formatted_size(locale::classic(), fmt, forward(args)...) == expected_size); +#ifndef __clang__ // TRANSITION, clang consteval bug (likely LLVM-52648) + if (_Is_execution_charset_self_synchronizing()) { + assert(fmt._Is_estimation_exact == expected_is_estimation_exact); + if (expected_is_estimation_exact) { + assert(fmt._Estimate_required_capacity(args...) == expected_size); + } + } +#else // ^^^ no workaround ^^^ / vvv workaround vvv + (void) expected_is_estimation_exact; +#endif // ^^^ workaround ^^^ + const auto signed_size = static_cast(expected_size); basic_string str; { @@ -1050,20 +1089,214 @@ void test_size_helper_impl(const size_t expected_size, const _Basic_format_strin } template -void test_size_helper(const size_t expected_size, const _Fmt_string fmt, Args&&... args) { - test_size_helper_impl(expected_size, fmt, forward(args)...); +void test_size_helper(const size_t expected_size, const bool expected_is_estimation_exact, + const _Fmt_string fmt, Args&&... args) { + test_size_helper_impl(expected_size, expected_is_estimation_exact, fmt, forward(args)...); } template -void test_size_helper(const size_t expected_size, const _Fmt_wstring fmt, Args&&... args) { - test_size_helper_impl(expected_size, fmt, forward(args)...); +void test_size_helper(const size_t expected_size, const bool expected_is_estimation_exact, + const _Fmt_wstring fmt, Args&&... args) { + test_size_helper_impl(expected_size, expected_is_estimation_exact, fmt, forward(args)...); } 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")); + test_size_helper(7, true, STR("nothing")); + test_size_helper(13, true, STR("still nothing"), 123); + test_size_helper(13, true, STR("nothing again"), 3.1415); + test_size_helper(26, true, STR("not even one argument used"), STR("scully")); + test_size_helper(26, true, STR("not even one argument used"), STR_VIEW("scully")); + + test_size_helper(3, false, STR("{}"), 123); + test_size_helper(6, false, STR("{}"), 3.1415); + test_size_helper(10, false, STR("abcd{}efg"), 123); + test_size_helper(13, false, STR("abcd{}efg"), 3.1415); + + test_size_helper(3, false, STR("{:d}"), 123); + test_size_helper(6, false, STR("{:g}"), 3.1415); + test_size_helper(10, false, STR("abcd{:d}efg"), 123); + test_size_helper(13, false, STR("abcd{:g}efg"), 3.1415); + + test_size_helper(6, true, STR("{}"), STR("scully")); + test_size_helper(8, false, STR("{:8}"), STR("scully")); + test_size_helper(6, false, STR("{:3}"), STR("scully")); + test_size_helper(6, false, STR("{:.8}"), STR("scully")); + test_size_helper(3, false, STR("{:.3}"), STR("scully")); + test_size_helper(3, false, STR("{:2.3}"), STR("scully")); + test_size_helper(8, false, STR("{:8.10}"), STR("scully")); + + test_size_helper(6, true, STR("{}"), STR_VIEW("scully")); + test_size_helper(8, false, STR("{:8}"), STR_VIEW("scully")); + test_size_helper(6, false, STR("{:3}"), STR_VIEW("scully")); + test_size_helper(6, false, STR("{:.8}"), STR_VIEW("scully")); + test_size_helper(3, false, STR("{:.3}"), STR_VIEW("scully")); + test_size_helper(3, false, STR("{:2.3}"), STR_VIEW("scully")); + test_size_helper(8, false, STR("{:8.10}"), STR_VIEW("scully")); + + test_size_helper(13, true, STR("abcd{}efg"), STR("scully")); + test_size_helper(15, false, STR("abcd{:8}efg"), STR("scully")); + test_size_helper(13, false, STR("abcd{:3}efg"), STR("scully")); + test_size_helper(13, false, STR("abcd{:.8}efg"), STR("scully")); + test_size_helper(10, false, STR("abcd{:.3}efg"), STR("scully")); + test_size_helper(10, false, STR("abcd{:2.3}efg"), STR("scully")); + test_size_helper(15, false, STR("abcd{:8.10}efg"), STR("scully")); + + test_size_helper(13, true, STR("abcd{}efg"), STR_VIEW("scully")); + test_size_helper(15, false, STR("abcd{:8}efg"), STR_VIEW("scully")); + test_size_helper(13, false, STR("abcd{:3}efg"), STR_VIEW("scully")); + test_size_helper(13, false, STR("abcd{:.8}efg"), STR_VIEW("scully")); + test_size_helper(10, false, STR("abcd{:.3}efg"), STR_VIEW("scully")); + test_size_helper(10, false, STR("abcd{:2.3}efg"), STR_VIEW("scully")); + test_size_helper(15, false, STR("abcd{:8.10}efg"), STR_VIEW("scully")); + + test_size_helper(8, false, STR("{:{}}"), STR("scully"), 8); + test_size_helper(6, false, STR("{:{}}"), STR("scully"), 3); + test_size_helper(6, false, STR("{:.{}}"), STR("scully"), 8); + test_size_helper(3, false, STR("{:.{}}"), STR("scully"), 3); + test_size_helper(3, false, STR("{:{}.{}}"), STR("scully"), 2, 3); + test_size_helper(8, false, STR("{:{}.{}}"), STR("scully"), 8, 10); + + test_size_helper(8, false, STR("{:{}}"), STR_VIEW("scully"), 8); + test_size_helper(6, false, STR("{:{}}"), STR_VIEW("scully"), 3); + test_size_helper(6, false, STR("{:.{}}"), STR_VIEW("scully"), 8); + test_size_helper(3, false, STR("{:.{}}"), STR_VIEW("scully"), 3); + test_size_helper(3, false, STR("{:{}.{}}"), STR_VIEW("scully"), 2, 3); + test_size_helper(8, false, STR("{:{}.{}}"), STR_VIEW("scully"), 8, 10); + + test_size_helper(15, false, STR("abcd{:{}}efg"), STR("scully"), 8); + test_size_helper(13, false, STR("abcd{:{}}efg"), STR("scully"), 3); + test_size_helper(13, false, STR("abcd{:.{}}efg"), STR("scully"), 8); + test_size_helper(10, false, STR("abcd{:.{}}efg"), STR("scully"), 3); + test_size_helper(10, false, STR("abcd{:{}.{}}efg"), STR("scully"), 2, 3); + test_size_helper(15, false, STR("abcd{:{}.{}}efg"), STR("scully"), 8, 10); + + test_size_helper(15, false, STR("abcd{:{}}efg"), STR_VIEW("scully"), 8); + test_size_helper(13, false, STR("abcd{:{}}efg"), STR_VIEW("scully"), 3); + test_size_helper(13, false, STR("abcd{:.{}}efg"), STR_VIEW("scully"), 8); + test_size_helper(10, false, STR("abcd{:.{}}efg"), STR_VIEW("scully"), 3); + test_size_helper(10, false, STR("abcd{:{}.{}}efg"), STR_VIEW("scully"), 2, 3); + test_size_helper(15, false, STR("abcd{:{}.{}}efg"), STR_VIEW("scully"), 8, 10); + + test_size_helper(8, false, STR("{0:{1}}"), STR("scully"), 8); + test_size_helper(6, false, STR("{0:{1}}"), STR("scully"), 3); + test_size_helper(6, false, STR("{0:.{1}}"), STR("scully"), 8); + test_size_helper(3, false, STR("{0:.{1}}"), STR("scully"), 3); + test_size_helper(3, false, STR("{0:{1}.{2}}"), STR("scully"), 2, 3); + test_size_helper(8, false, STR("{0:{1}.{2}}"), STR("scully"), 8, 10); + + test_size_helper(8, false, STR("{0:{1}}"), STR_VIEW("scully"), 8); + test_size_helper(6, false, STR("{0:{1}}"), STR_VIEW("scully"), 3); + test_size_helper(6, false, STR("{0:.{1}}"), STR_VIEW("scully"), 8); + test_size_helper(3, false, STR("{0:.{1}}"), STR_VIEW("scully"), 3); + test_size_helper(3, false, STR("{0:{1}.{2}}"), STR_VIEW("scully"), 2, 3); + test_size_helper(8, false, STR("{0:{1}.{2}}"), STR_VIEW("scully"), 8, 10); + + test_size_helper(15, false, STR("abcd{0:{1}}efg"), STR("scully"), 8); + test_size_helper(13, false, STR("abcd{0:{1}}efg"), STR("scully"), 3); + test_size_helper(13, false, STR("abcd{0:.{1}}efg"), STR("scully"), 8); + test_size_helper(10, false, STR("abcd{0:.{1}}efg"), STR("scully"), 3); + test_size_helper(10, false, STR("abcd{0:{1}.{2}}efg"), STR("scully"), 2, 3); + test_size_helper(15, false, STR("abcd{0:{1}.{2}}efg"), STR("scully"), 8, 10); + + test_size_helper(15, false, STR("abcd{0:{1}}efg"), STR_VIEW("scully"), 8); + test_size_helper(13, false, STR("abcd{0:{1}}efg"), STR_VIEW("scully"), 3); + test_size_helper(13, false, STR("abcd{0:.{1}}efg"), STR_VIEW("scully"), 8); + test_size_helper(10, false, STR("abcd{0:.{1}}efg"), STR_VIEW("scully"), 3); + test_size_helper(10, false, STR("abcd{0:{1}.{2}}efg"), STR_VIEW("scully"), 2, 3); + test_size_helper(15, false, STR("abcd{0:{1}.{2}}efg"), STR_VIEW("scully"), 8, 10); + + test_size_helper(12, true, STR("{} {}!"), STR("Hello"), STR("World")); + test_size_helper(18, true, STR("{0} {1}"), STR("one two three"), STR("four")); + test_size_helper(27, true, STR("{0} {0}"), STR("one two three"), STR("four")); + test_size_helper(18, true, STR("{1} {0}"), STR("one two three"), STR("four")); + test_size_helper(9, true, STR("{1} {1}"), STR("one two three"), STR("four")); + + test_size_helper(12, true, STR("{} {}!"), STR_VIEW("Hello"), STR("World")); + test_size_helper(18, true, STR("{0} {1}"), STR_VIEW("one two three"), STR("four")); + test_size_helper(27, true, STR("{0} {0}"), STR_VIEW("one two three"), STR("four")); + test_size_helper(18, true, STR("{1} {0}"), STR_VIEW("one two three"), STR("four")); + test_size_helper(9, true, STR("{1} {1}"), STR_VIEW("one two three"), STR("four")); + + test_size_helper(12, true, STR("{} {}!"), STR("Hello"), STR_VIEW("World")); + test_size_helper(18, true, STR("{0} {1}"), STR("one two three"), STR_VIEW("four")); + test_size_helper(27, true, STR("{0} {0}"), STR("one two three"), STR_VIEW("four")); + test_size_helper(18, true, STR("{1} {0}"), STR("one two three"), STR_VIEW("four")); + test_size_helper(9, true, STR("{1} {1}"), STR("one two three"), STR_VIEW("four")); + + test_size_helper(12, true, STR("{} {}!"), STR_VIEW("Hello"), STR_VIEW("World")); + test_size_helper(18, true, STR("{0} {1}"), STR_VIEW("one two three"), STR_VIEW("four")); + test_size_helper(27, true, STR("{0} {0}"), STR_VIEW("one two three"), STR_VIEW("four")); + test_size_helper(18, true, STR("{1} {0}"), STR_VIEW("one two three"), STR_VIEW("four")); + test_size_helper(9, true, STR("{1} {1}"), STR_VIEW("one two three"), STR_VIEW("four")); + + test_size_helper(12, true, STR("{:s} {:s}!"), STR("Hello"), STR("World")); + test_size_helper(18, true, STR("{0:s} {1:s}"), STR("one two three"), STR("four")); + test_size_helper(27, true, STR("{0:s} {0:s}"), STR("one two three"), STR("four")); + test_size_helper(18, true, STR("{1:s} {0:s}"), STR("one two three"), STR("four")); + test_size_helper(9, true, STR("{1:s} {1:s}"), STR("one two three"), STR("four")); + + test_size_helper(12, true, STR("{:s} {:s}!"), STR_VIEW("Hello"), STR("World")); + test_size_helper(18, true, STR("{0:s} {1:s}"), STR_VIEW("one two three"), STR("four")); + test_size_helper(27, true, STR("{0:s} {0:s}"), STR_VIEW("one two three"), STR("four")); + test_size_helper(18, true, STR("{1:s} {0:s}"), STR_VIEW("one two three"), STR("four")); + test_size_helper(9, true, STR("{1:s} {1:s}"), STR_VIEW("one two three"), STR("four")); + + test_size_helper(12, true, STR("{:s} {:s}!"), STR("Hello"), STR_VIEW("World")); + test_size_helper(18, true, STR("{0:s} {1:s}"), STR("one two three"), STR_VIEW("four")); + test_size_helper(27, true, STR("{0:s} {0:s}"), STR("one two three"), STR_VIEW("four")); + test_size_helper(18, true, STR("{1:s} {0:s}"), STR("one two three"), STR_VIEW("four")); + test_size_helper(9, true, STR("{1:s} {1:s}"), STR("one two three"), STR_VIEW("four")); + + test_size_helper(12, true, STR("{:s} {:s}!"), STR_VIEW("Hello"), STR_VIEW("World")); + test_size_helper(18, true, STR("{0:s} {1:s}"), STR_VIEW("one two three"), STR_VIEW("four")); + test_size_helper(27, true, STR("{0:s} {0:s}"), STR_VIEW("one two three"), STR_VIEW("four")); + test_size_helper(18, true, STR("{1:s} {0:s}"), STR_VIEW("one two three"), STR_VIEW("four")); + test_size_helper(9, true, STR("{1:s} {1:s}"), STR_VIEW("one two three"), STR_VIEW("four")); + + test_size_helper(15, false, STR("{0} {1}"), STR("one two three"), 4); + test_size_helper(27, true, STR("{0} {0}"), STR("one two three"), 4); + test_size_helper(15, false, STR("{1} {0}"), STR("one two three"), 4); + test_size_helper(3, false, STR("{1} {1}"), STR("one two three"), 4); + test_size_helper(8, false, STR("{0} {1}"), 123, STR("four")); + test_size_helper(7, false, STR("{0} {0}"), 123, STR("four")); + test_size_helper(8, false, STR("{1} {0}"), 123, STR("four")); + test_size_helper(9, true, STR("{1} {1}"), 123, STR("four")); + + test_size_helper(15, false, STR("{0} {1}"), STR_VIEW("one two three"), 4); + test_size_helper(27, true, STR("{0} {0}"), STR_VIEW("one two three"), 4); + test_size_helper(15, false, STR("{1} {0}"), STR_VIEW("one two three"), 4); + test_size_helper(3, false, STR("{1} {1}"), STR_VIEW("one two three"), 4); + test_size_helper(8, false, STR("{0} {1}"), 123, STR_VIEW("four")); + test_size_helper(7, false, STR("{0} {0}"), 123, STR_VIEW("four")); + test_size_helper(8, false, STR("{1} {0}"), 123, STR_VIEW("four")); + test_size_helper(9, true, STR("{1} {1}"), 123, STR_VIEW("four")); + + test_size_helper(15, false, STR("{0:s} {1:d}"), STR("one two three"), 4); + test_size_helper(27, true, STR("{0:s} {0:s}"), STR("one two three"), 4); + test_size_helper(15, false, STR("{1:d} {0:s}"), STR("one two three"), 4); + test_size_helper(3, false, STR("{1:d} {1:d}"), STR("one two three"), 4); + test_size_helper(8, false, STR("{0:d} {1:s}"), 123, STR("four")); + test_size_helper(7, false, STR("{0:d} {0:d}"), 123, STR("four")); + test_size_helper(8, false, STR("{1:s} {0:d}"), 123, STR("four")); + test_size_helper(9, true, STR("{1:s} {1:s}"), 123, STR("four")); + + test_size_helper(15, false, STR("{0:s} {1:d}"), STR_VIEW("one two three"), 4); + test_size_helper(27, true, STR("{0:s} {0:s}"), STR_VIEW("one two three"), 4); + test_size_helper(15, false, STR("{1:d} {0:s}"), STR_VIEW("one two three"), 4); + test_size_helper(3, false, STR("{1:d} {1:d}"), STR_VIEW("one two three"), 4); + test_size_helper(8, false, STR("{0:d} {1:s}"), 123, STR_VIEW("four")); + test_size_helper(7, false, STR("{0:d} {0:d}"), 123, STR_VIEW("four")); + test_size_helper(8, false, STR("{1:s} {0:d}"), 123, STR_VIEW("four")); + test_size_helper(9, true, STR("{1:s} {1:s}"), 123, STR_VIEW("four")); + + test_size_helper(3, false, STR("{}"), my_integer::one); + test_size_helper(8, false, STR("{:8}"), my_integer::two); + test_size_helper(5, false, STR("{:3}"), my_integer::three); + test_size_helper(3, false, STR("{:.8}"), my_integer::one); + test_size_helper(3, false, STR("{:.3}"), my_integer::forty_two); + test_size_helper(3, false, STR("{:2.3}"), my_integer::three); + test_size_helper(8, false, STR("{:8.10}"), my_integer::two); } // The libfmt_ tests are derived from tests in