diff --git a/libcxx/include/__format/formatter_bool.h b/libcxx/include/__format/formatter_bool.h --- a/libcxx/include/__format/formatter_bool.h +++ b/libcxx/include/__format/formatter_bool.h @@ -13,15 +13,18 @@ #include <__algorithm/copy.h> #include <__availability> #include <__config> +#include <__debug> #include <__format/format_error.h> #include <__format/format_fwd.h> +#include <__format/format_parse_context.h> #include <__format/formatter.h> #include <__format/formatter_integral.h> #include <__format/parser_std_format_spec.h> +#include <__utility/unreachable.h> #include #ifndef _LIBCPP_HAS_NO_LOCALIZATION -#include +# include #endif #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) @@ -32,101 +35,39 @@ #if _LIBCPP_STD_VER > 17 -namespace __format_spec { - -template -class _LIBCPP_TEMPLATE_VIS __parser_bool : public __parser_integral<_CharT> { +template <__formatter::__char_type _CharT> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter { public: - _LIBCPP_HIDE_FROM_ABI constexpr auto parse(auto& __parse_ctx) - -> decltype(__parse_ctx.begin()) { - auto __it = __parser_integral<_CharT>::__parse(__parse_ctx); - - switch (this->__type) { - case _Flags::_Type::__default: - this->__type = _Flags::_Type::__string; - [[fallthrough]]; - case _Flags::_Type::__string: - this->__handle_bool(); - break; + _LIBCPP_HIDE_FROM_ABI constexpr auto + parse(basic_format_parse_context<_CharT>& __parse_ctx) -> decltype(__parse_ctx.begin()) { + auto __result = __parser_.__parse(__parse_ctx, __format_spec::__fields_integral); + __format_spec::__process_parsed_bool(__parser_); + return __result; + } - case _Flags::_Type::__binary_lower_case: - case _Flags::_Type::__binary_upper_case: - case _Flags::_Type::__octal: - case _Flags::_Type::__decimal: - case _Flags::_Type::__hexadecimal_lower_case: - case _Flags::_Type::__hexadecimal_upper_case: - this->__handle_integer(); - break; + _LIBCPP_HIDE_FROM_ABI auto format(bool __value, auto& __ctx) const -> decltype(__ctx.out()) { + switch (__parser_.__type_) { + case __format_spec::__type::__string: + return __formatter::__format_bool(__value, __ctx, __parser_.__get_parsed_std_specifications(__ctx)); + + case __format_spec::__type::__binary_lower_case: + case __format_spec::__type::__binary_upper_case: + case __format_spec::__type::__octal: + case __format_spec::__type::__decimal: + case __format_spec::__type::__hexadecimal_lower_case: + case __format_spec::__type::__hexadecimal_upper_case: + // Promotes bool to an integral type. This reduces the number of + // instantiations of __format_integer reducing code size. + return __formatter::__format_integer( + static_cast(__value), __ctx, __parser_.__get_parsed_std_specifications(__ctx)); default: - __throw_format_error( - "The format-spec type has a type not supported for a bool argument"); + _LIBCPP_ASSERT(false, "The parse function should have validated the type"); + __libcpp_unreachable(); } - - return __it; } -}; - -template -struct _LIBCPP_TEMPLATE_VIS __bool_strings; - -template <> -struct _LIBCPP_TEMPLATE_VIS __bool_strings { - static constexpr string_view __true{"true"}; - static constexpr string_view __false{"false"}; -}; - -#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS -template <> -struct _LIBCPP_TEMPLATE_VIS __bool_strings { - static constexpr wstring_view __true{L"true"}; - static constexpr wstring_view __false{L"false"}; -}; -#endif - -template -using __formatter_bool = __formatter_integral<__parser_bool<_CharT>>; -} //namespace __format_spec - -// [format.formatter.spec]/2.3 -// For each charT, for each cv-unqualified arithmetic type ArithmeticT other -// than char, wchar_t, char8_t, char16_t, or char32_t, a specialization - -template <__formatter::__char_type _CharT> -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter - : public __format_spec::__formatter_bool<_CharT> { - using _Base = __format_spec::__formatter_bool<_CharT>; - - _LIBCPP_HIDE_FROM_ABI auto format(bool __value, auto& __ctx) - -> decltype(__ctx.out()) { - if (this->__type != __format_spec::_Flags::_Type::__string) - return _Base::format(static_cast(__value), __ctx); - - if (this->__width_needs_substitution()) - this->__substitute_width_arg_id(__ctx.arg(this->__width)); - -#ifndef _LIBCPP_HAS_NO_LOCALIZATION - if (this->__locale_specific_form) { - const auto& __np = use_facet>(__ctx.locale()); - basic_string<_CharT> __str = __value ? __np.truename() : __np.falsename(); - return __formatter::__write_unicode( - __ctx.out(), basic_string_view<_CharT>{__str}, this->__width, -1, - this->__fill, this->__alignment); - } -#endif - basic_string_view<_CharT> __str = - __value ? __format_spec::__bool_strings<_CharT>::__true - : __format_spec::__bool_strings<_CharT>::__false; - - // The output only uses ASCII so every character is one column. - unsigned __size = __str.size(); - if (__size >= this->__width) - return _VSTD::copy(__str.begin(), __str.end(), __ctx.out()); - - return __formatter::__write(__ctx.out(), __str.begin(), __str.end(), __size, - this->__width, this->__fill, this->__alignment); - } + __format_spec::__parser<_CharT> __parser_; }; #endif //_LIBCPP_STD_VER > 17 diff --git a/libcxx/include/__format/formatter_char.h b/libcxx/include/__format/formatter_char.h --- a/libcxx/include/__format/formatter_char.h +++ b/libcxx/include/__format/formatter_char.h @@ -11,12 +11,16 @@ #define _LIBCPP___FORMAT_FORMATTER_CHAR_H #include <__availability> +#include <__concepts/same_as.h> #include <__config> -#include <__format/format_error.h> #include <__format/format_fwd.h> +#include <__format/format_parse_context.h> #include <__format/formatter.h> #include <__format/formatter_integral.h> +#include <__format/formatter_output.h> #include <__format/parser_std_format_spec.h> +#include <__type_traits/conditional.h> +#include <__type_traits/is_signed.h> #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header @@ -26,69 +30,52 @@ #if _LIBCPP_STD_VER > 17 -namespace __format_spec { - -template -class _LIBCPP_TEMPLATE_VIS __parser_char : public __parser_integral<_CharT> { +template <__formatter::__char_type _CharT> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT __formatter_char { public: - _LIBCPP_HIDE_FROM_ABI constexpr auto parse(auto& __parse_ctx) - -> decltype(__parse_ctx.begin()) { - auto __it = __parser_integral<_CharT>::__parse(__parse_ctx); - - switch (this->__type) { - case _Flags::_Type::__default: - this->__type = _Flags::_Type::__char; - [[fallthrough]]; - case _Flags::_Type::__char: - this->__handle_char(); - break; - - case _Flags::_Type::__binary_lower_case: - case _Flags::_Type::__binary_upper_case: - case _Flags::_Type::__octal: - case _Flags::_Type::__decimal: - case _Flags::_Type::__hexadecimal_lower_case: - case _Flags::_Type::__hexadecimal_upper_case: - this->__handle_integer(); - break; - - default: - __throw_format_error( - "The format-spec type has a type not supported for a char argument"); - } - - return __it; + _LIBCPP_HIDE_FROM_ABI constexpr auto + parse(basic_format_parse_context<_CharT>& __parse_ctx) -> decltype(__parse_ctx.begin()) { + auto __result = __parser_.__parse(__parse_ctx, __format_spec::__fields_integral); + __format_spec::__process_parsed_char(__parser_); + return __result; } -}; -template -using __formatter_char = __formatter_integral<__parser_char<_CharT>>; + _LIBCPP_HIDE_FROM_ABI auto format(_CharT __value, auto& __ctx) const -> decltype(__ctx.out()) { + if (__parser_.__type_ == __format_spec::__type::__char) + return __formatter::__format_char(__value, __ctx.out(), __parser_.__get_parsed_std_specifications(__ctx)); + + if constexpr (sizeof(_CharT) <= sizeof(int)) + // Promotes _CharT to an integral type. This reduces the number of + // instantiations of __format_integer reducing code size. + return __formatter::__format_integer( + static_cast, int, unsigned>>(__value), + __ctx, + __parser_.__get_parsed_std_specifications(__ctx)); + else + return __formatter::__format_integer(__value, __ctx, __parser_.__get_parsed_std_specifications(__ctx)); + } -} // namespace __format_spec + _LIBCPP_HIDE_FROM_ABI auto format(char __value, auto& __ctx) const -> decltype(__ctx.out()) + requires(same_as<_CharT, wchar_t>) + { + return format(static_cast(__value), __ctx); + } -// [format.formatter.spec]/2.1 The specializations + __format_spec::__parser<_CharT> __parser_; +}; template <> -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter - : public __format_spec::__formatter_char {}; +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter : public __formatter_char {}; -#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS template <> -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter - : public __format_spec::__formatter_char { - using _Base = __format_spec::__formatter_char; +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter : public __formatter_char {}; - _LIBCPP_HIDE_FROM_ABI auto format(char __value, auto& __ctx) - -> decltype(__ctx.out()) { - return _Base::format(static_cast(__value), __ctx); - } +template <> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter : public __formatter_char { }; -template <> -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT - formatter - : public __format_spec::__formatter_char {}; -#endif // _LIBCPP_HAS_NO_WIDE_CHARACTERS +# endif // _LIBCPP_HAS_NO_WIDE_CHARACTERS #endif //_LIBCPP_STD_VER > 17 diff --git a/libcxx/include/__format/formatter_floating_point.h b/libcxx/include/__format/formatter_floating_point.h --- a/libcxx/include/__format/formatter_floating_point.h +++ b/libcxx/include/__format/formatter_floating_point.h @@ -447,7 +447,7 @@ if (__digits <= __grouping[0]) __grouping.clear(); else - __grouping = __determine_grouping(__digits, __grouping); + __grouping = __formatter::__determine_grouping(__digits, __grouping); } size_t __size = __result.__last - __buffer.begin() + // Formatted string diff --git a/libcxx/include/__format/formatter_integer.h b/libcxx/include/__format/formatter_integer.h --- a/libcxx/include/__format/formatter_integer.h +++ b/libcxx/include/__format/formatter_integer.h @@ -11,147 +11,120 @@ #define _LIBCPP___FORMAT_FORMATTER_INTEGER_H #include <__availability> +#include <__concepts/arithmetic.h> #include <__config> -#include <__format/format_error.h> +#include <__format/format_error.h> // TODO FMT Remove after adding 128-bit support #include <__format/format_fwd.h> +#include <__format/format_parse_context.h> #include <__format/formatter.h> #include <__format/formatter_integral.h> +#include <__format/formatter_output.h> #include <__format/parser_std_format_spec.h> -#include +#include // TODO FMT Remove after adding 128-bit support +#include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header #endif -_LIBCPP_PUSH_MACROS +_LIBCPP_PUSH_MACROS // TODO FMT Remove after adding 128-bit support #include <__undef_macros> -_LIBCPP_BEGIN_NAMESPACE_STD + _LIBCPP_BEGIN_NAMESPACE_STD #if _LIBCPP_STD_VER > 17 -namespace __format_spec { + template <__formatter::__char_type _CharT> + struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT __formatter_integer { -template -class _LIBCPP_TEMPLATE_VIS __parser_integer : public __parser_integral<_CharT> { public: - _LIBCPP_HIDE_FROM_ABI constexpr auto parse(auto& __parse_ctx) - -> decltype(__parse_ctx.begin()) { - auto __it = __parser_integral<_CharT>::__parse(__parse_ctx); - - switch (this->__type) { - case _Flags::_Type::__default: - this->__type = _Flags::_Type::__decimal; - [[fallthrough]]; - - case _Flags::_Type::__binary_lower_case: - case _Flags::_Type::__binary_upper_case: - case _Flags::_Type::__octal: - case _Flags::_Type::__decimal: - case _Flags::_Type::__hexadecimal_lower_case: - case _Flags::_Type::__hexadecimal_upper_case: - this->__handle_integer(); - break; - - case _Flags::_Type::__char: - this->__handle_char(); - break; - - default: - __throw_format_error("The format-spec type has a type not supported for " - "an integer argument"); - } - return __it; + _LIBCPP_HIDE_FROM_ABI constexpr auto + parse(basic_format_parse_context<_CharT>& __parse_ctx) -> decltype(__parse_ctx.begin()) { + auto __result = __parser_.__parse(__parse_ctx, __format_spec::__fields_integral); + __format_spec::__process_parsed_integer(__parser_); + return __result; } -}; -template -using __formatter_integer = __formatter_integral<__parser_integer<_CharT>>; + template + _LIBCPP_HIDE_FROM_ABI auto format(_Tp __value, auto& __ctx) const -> decltype(__ctx.out()) { + __format_spec::__parsed_specifications<_CharT> __specs = __parser_.__get_parsed_std_specifications(__ctx); + + if (__specs.__std_.__type_ == __format_spec::__type::__char) + return __formatter::__format_char(__value, __ctx.out(), __specs); -} // namespace __format_spec + using _Type = __make_32_64_or_128_bit_t<_Tp>; + static_assert(!is_same<_Type, void>::value, "unsupported integral type used in __formatter_integer::__format"); + + // Reduce the number of instantiation of the integer formatter + return __formatter::__format_integer(static_cast<_Type>(__value), __ctx, __specs); + } -// [format.formatter.spec]/2.3 -// For each charT, for each cv-unqualified arithmetic type ArithmeticT other -// than char, wchar_t, char8_t, char16_t, or char32_t, a specialization + __format_spec::__parser<_CharT> __parser_; +}; // Signed integral types. template <__formatter::__char_type _CharT> -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT - formatter - : public __format_spec::__formatter_integer<_CharT> {}; +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter + : public __formatter_integer<_CharT> {}; template <__formatter::__char_type _CharT> -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter - : public __format_spec::__formatter_integer<_CharT> {}; +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter : public __formatter_integer<_CharT> { +}; template <__formatter::__char_type _CharT> -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter - : public __format_spec::__formatter_integer<_CharT> {}; +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter : public __formatter_integer<_CharT> {}; template <__formatter::__char_type _CharT> -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter - : public __format_spec::__formatter_integer<_CharT> {}; +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter : public __formatter_integer<_CharT> {}; template <__formatter::__char_type _CharT> -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT - formatter - : public __format_spec::__formatter_integer<_CharT> {}; -#ifndef _LIBCPP_HAS_NO_INT128 +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter + : public __formatter_integer<_CharT> {}; +# ifndef _LIBCPP_HAS_NO_INT128 template <__formatter::__char_type _CharT> -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT - formatter<__int128_t, _CharT> - : public __format_spec::__formatter_integer<_CharT> { - using _Base = __format_spec::__formatter_integer<_CharT>; +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter<__int128_t, _CharT> + : public __formatter_integer<_CharT> { + using _Base = __formatter_integer<_CharT>; - _LIBCPP_HIDE_FROM_ABI auto format(__int128_t __value, auto& __ctx) - -> decltype(__ctx.out()) { + _LIBCPP_HIDE_FROM_ABI auto format(__int128_t __value, auto& __ctx) const -> decltype(__ctx.out()) { // TODO FMT Implement full 128 bit support. using _To = long long; - if (__value < numeric_limits<_To>::min() || - __value > numeric_limits<_To>::max()) - __throw_format_error("128-bit value is outside of implemented range"); + if (__value < numeric_limits<_To>::min() || __value > numeric_limits<_To>::max()) + std::__throw_format_error("128-bit value is outside of implemented range"); return _Base::format(static_cast<_To>(__value), __ctx); } }; -#endif +# endif // Unsigned integral types. template <__formatter::__char_type _CharT> -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT - formatter - : public __format_spec::__formatter_integer<_CharT> {}; +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter + : public __formatter_integer<_CharT> {}; template <__formatter::__char_type _CharT> -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT - formatter - : public __format_spec::__formatter_integer<_CharT> {}; +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter + : public __formatter_integer<_CharT> {}; template <__formatter::__char_type _CharT> -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT - formatter - : public __format_spec::__formatter_integer<_CharT> {}; +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter + : public __formatter_integer<_CharT> {}; template <__formatter::__char_type _CharT> -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT - formatter - : public __format_spec::__formatter_integer<_CharT> {}; +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter + : public __formatter_integer<_CharT> {}; template <__formatter::__char_type _CharT> -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT - formatter - : public __format_spec::__formatter_integer<_CharT> {}; -#ifndef _LIBCPP_HAS_NO_INT128 +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter + : public __formatter_integer<_CharT> {}; +# ifndef _LIBCPP_HAS_NO_INT128 template <__formatter::__char_type _CharT> -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT - formatter<__uint128_t, _CharT> - : public __format_spec::__formatter_integer<_CharT> { - using _Base = __format_spec::__formatter_integer<_CharT>; +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter<__uint128_t, _CharT> + : public __formatter_integer<_CharT> { + using _Base = __formatter_integer<_CharT>; - _LIBCPP_HIDE_FROM_ABI auto format(__uint128_t __value, auto& __ctx) - -> decltype(__ctx.out()) { + _LIBCPP_HIDE_FROM_ABI auto format(__uint128_t __value, auto& __ctx) const -> decltype(__ctx.out()) { // TODO FMT Implement full 128 bit support. using _To = unsigned long long; - if (__value < numeric_limits<_To>::min() || - __value > numeric_limits<_To>::max()) - __throw_format_error("128-bit value is outside of implemented range"); + if (__value < numeric_limits<_To>::min() || __value > numeric_limits<_To>::max()) + std::__throw_format_error("128-bit value is outside of implemented range"); return _Base::format(static_cast<_To>(__value), __ctx); } }; -#endif +# endif #endif //_LIBCPP_STD_VER > 17 diff --git a/libcxx/include/__format/formatter_integral.h b/libcxx/include/__format/formatter_integral.h --- a/libcxx/include/__format/formatter_integral.h +++ b/libcxx/include/__format/formatter_integral.h @@ -10,25 +10,20 @@ #ifndef _LIBCPP___FORMAT_FORMATTER_INTEGRAL_H #define _LIBCPP___FORMAT_FORMATTER_INTEGRAL_H -#include <__algorithm/copy.h> -#include <__algorithm/copy_n.h> -#include <__algorithm/fill_n.h> -#include <__algorithm/transform.h> -#include <__assert> +#include <__concepts/arithmetic.h> +#include <__concepts/same_as.h> #include <__config> #include <__format/format_error.h> -#include <__format/format_fwd.h> -#include <__format/formatter.h> +#include <__format/formatter.h> // for __char_type TODO FMT Move the concept? +#include <__format/formatter_output.h> #include <__format/parser_std_format_spec.h> #include <__utility/unreachable.h> -#include #include -#include #include #include #ifndef _LIBCPP_HAS_NO_LOCALIZATION -#include +# include #endif #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) @@ -42,91 +37,30 @@ #if _LIBCPP_STD_VER > 17 -/** - * Integral formatting classes. - * - * There are two types used here: - * * C++-type, the type as used in C++. - * * format-type, the output type specified in the std-format-spec. - * - * Design of the integral formatters consists of several layers. - * * @ref __parser_integral The basic std-format-spec parser for all integral - * classes. This parser does the basic sanity checks. It also contains some - * helper functions that are nice to have available for all parsers. - * * A C++-type specific parser. These parsers must derive from - * @ref __parser_integral. Their task is to validate whether the parsed - * std-format-spec is valid for the C++-type and selected format-type. After - * validation they need to make sure all members are properly set. For - * example, when the alignment hasn't changed it needs to set the proper - * default alignment for the format-type. The following parsers are available: - * - @ref __parser_integer - * - @ref __parser_char - * - @ref __parser_bool - * * A general formatter for all integral types @ref __formatter_integral. This - * formatter can handle all formatting of integers and characters. The class - * derives from the proper formatter. - * Note the boolean string format-type isn't supported in this class. - * * A typedef C++-type group combining the @ref __formatter_integral with a - * parser: - * * @ref __formatter_integer - * * @ref __formatter_char - * * @ref __formatter_bool - * * Then every C++-type has its own formatter specializations. They inherit - * from the C++-type group typedef. Most specializations need nothing else. - * Others need some additional specializations in this class. - */ -namespace __format_spec { - -/** Wrapper around @ref to_chars, returning the output pointer. */ -template -_LIBCPP_HIDE_FROM_ABI char* __to_buffer(char* __first, char* __last, - _Tp __value, int __base) { - // TODO FMT Evaluate code overhead due to not calling the internal function - // directly. (Should be zero overhead.) - to_chars_result __r = _VSTD::to_chars(__first, __last, __value, __base); - _LIBCPP_ASSERT(__r.ec == errc(0), "Internal buffer too small"); - return __r.ptr; -} - -/** - * Helper to determine the buffer size to output a integer in Base @em x. - * - * There are several overloads for the supported bases. The function uses the - * base as template argument so it can be used in a constant expression. - */ -template -_LIBCPP_HIDE_FROM_ABI constexpr size_t __buffer_size() noexcept - requires(_Base == 2) { - return numeric_limits<_Tp>::digits // The number of binary digits. - + 2 // Reserve space for the '0[Bb]' prefix. - + 1; // Reserve space for the sign. -} +namespace __formatter { -template -_LIBCPP_HIDE_FROM_ABI constexpr size_t __buffer_size() noexcept - requires(_Base == 8) { - return numeric_limits<_Tp>::digits // The number of binary digits. - / 3 // Adjust to octal. - + 1 // Turn floor to ceil. - + 1 // Reserve space for the '0' prefix. - + 1; // Reserve space for the sign. -} +// +// Generic +// -template -_LIBCPP_HIDE_FROM_ABI constexpr size_t __buffer_size() noexcept - requires(_Base == 10) { - return numeric_limits<_Tp>::digits10 // The floored value. - + 1 // Turn floor to ceil. - + 1; // Reserve space for the sign. -} +_LIBCPP_HIDE_FROM_ABI inline char* __insert_sign(char* __buf, bool __negative, __format_spec::__sign __sign) { + if (__negative) + *__buf++ = '-'; + else + switch (__sign) { + case __format_spec::__sign::__default: + case __format_spec::__sign::__minus: + // No sign added. + break; + case __format_spec::__sign::__plus: + *__buf++ = '+'; + break; + case __format_spec::__sign::__space: + *__buf++ = ' '; + break; + } -template -_LIBCPP_HIDE_FROM_ABI constexpr size_t __buffer_size() noexcept - requires(_Base == 16) { - return numeric_limits<_Tp>::digits // The number of binary digits. - / 4 // Adjust to hexadecimal. - + 2 // Reserve space for the '0[Xx]' prefix. - + 1; // Reserve space for the sign. + return __buf; } /** @@ -144,8 +78,7 @@ * @note The grouping field of the locale is always a @c std::string, * regardless whether the @c std::numpunct's type is @c char or @c wchar_t. */ -_LIBCPP_HIDE_FROM_ABI inline string -__determine_grouping(ptrdiff_t __size, const string& __grouping) { +_LIBCPP_HIDE_FROM_ABI inline string __determine_grouping(ptrdiff_t __size, const string& __grouping) { _LIBCPP_ASSERT(!__grouping.empty() && __size > __grouping[0], "The slow grouping formatting is used while there will be no " "separators written"); @@ -175,278 +108,253 @@ __libcpp_unreachable(); } -template -requires __formatter::__char_type -class _LIBCPP_TEMPLATE_VIS __formatter_integral : public _Parser { -public: - using _CharT = typename _Parser::char_type; - - template - _LIBCPP_HIDE_FROM_ABI auto format(_Tp __value, auto& __ctx) - -> decltype(__ctx.out()) { - if (this->__width_needs_substitution()) - this->__substitute_width_arg_id(__ctx.arg(this->__width)); - - if (this->__type == _Flags::_Type::__char) - return __format_as_char(__value, __ctx); +// +// Char +// - if constexpr (unsigned_integral<_Tp>) - return __format_unsigned_integral(__value, false, __ctx); - else { - // Depending on the std-format-spec string the sign and the value - // might not be outputted together: - // - alternate form may insert a prefix string. - // - zero-padding may insert additional '0' characters. - // Therefore the value is processed as a positive unsigned value. - // The function @ref __insert_sign will a '-' when the value was negative. - auto __r = __to_unsigned_like(__value); - bool __negative = __value < 0; - if (__negative) - __r = __complement(__r); - - return __format_unsigned_integral(__r, __negative, __ctx); +template <__formatter::__char_type _CharT> +_LIBCPP_HIDE_FROM_ABI auto __format_char( + integral auto __value, + output_iterator auto __out_it, + __format_spec::__parsed_specifications<_CharT> __specs) -> decltype(__out_it) { + using _Tp = decltype(__value); + if constexpr (!same_as<_CharT, _Tp>) { + // cmp_less and cmp_greater can't be used for character types. + if constexpr (signed_integral<_CharT> == signed_integral<_Tp>) { + if (__value < numeric_limits<_CharT>::min() || __value > numeric_limits<_CharT>::max()) + std::__throw_format_error("Integral value outside the range of the char type"); + } else if constexpr (signed_integral<_CharT>) { + // _CharT is signed _Tp is unsigned + if (__value > static_cast>(numeric_limits<_CharT>::max())) + std::__throw_format_error("Integral value outside the range of the char type"); + } else { + // _CharT is unsigned _Tp is signed + if (__value < 0 || static_cast>(__value) > numeric_limits<_CharT>::max()) + std::__throw_format_error("Integral value outside the range of the char type"); } } -private: - /** Generic formatting for format-type c. */ - _LIBCPP_HIDE_FROM_ABI auto __format_as_char(integral auto __value, - auto& __ctx) - -> decltype(__ctx.out()) { - if (this->__alignment == _Flags::_Alignment::__default) - this->__alignment = _Flags::_Alignment::__right; - - using _Tp = decltype(__value); - if constexpr (!same_as<_CharT, _Tp>) { - // cmp_less and cmp_greater can't be used for character types. - if constexpr (signed_integral<_CharT> == signed_integral<_Tp>) { - if (__value < numeric_limits<_CharT>::min() || - __value > numeric_limits<_CharT>::max()) - __throw_format_error( - "Integral value outside the range of the char type"); - } else if constexpr (signed_integral<_CharT>) { - // _CharT is signed _Tp is unsigned - if (__value > - static_cast>(numeric_limits<_CharT>::max())) - __throw_format_error( - "Integral value outside the range of the char type"); - } else { - // _CharT is unsigned _Tp is signed - if (__value < 0 || static_cast>(__value) > - numeric_limits<_CharT>::max()) - __throw_format_error( - "Integral value outside the range of the char type"); - } - } + const auto __c = static_cast<_CharT>(__value); + return __formatter::__write(_VSTD::addressof(__c), _VSTD::addressof(__c) + 1, _VSTD::move(__out_it), __specs); +} - const auto __c = static_cast<_CharT>(__value); - return __write(_VSTD::addressof(__c), _VSTD::addressof(__c) + 1, - __ctx.out()); - } +// +// Integer +// - /** - * Generic formatting for format-type bBdoxX. - * - * This small wrapper allocates a buffer with the required size. Then calls - * the real formatter with the buffer and the prefix for the base. - */ - _LIBCPP_HIDE_FROM_ABI auto - __format_unsigned_integral(unsigned_integral auto __value, bool __negative, - auto& __ctx) -> decltype(__ctx.out()) { - switch (this->__type) { - case _Flags::_Type::__binary_lower_case: { - array()> __array; - return __format_unsigned_integral(__array.begin(), __array.end(), __value, - __negative, 2, __ctx, "0b"); - } - case _Flags::_Type::__binary_upper_case: { - array()> __array; - return __format_unsigned_integral(__array.begin(), __array.end(), __value, - __negative, 2, __ctx, "0B"); - } - case _Flags::_Type::__octal: { - // Octal is special; if __value == 0 there's no prefix. - array()> __array; - return __format_unsigned_integral(__array.begin(), __array.end(), __value, - __negative, 8, __ctx, - __value != 0 ? "0" : nullptr); - } - case _Flags::_Type::__decimal: { - array()> __array; - return __format_unsigned_integral(__array.begin(), __array.end(), __value, - __negative, 10, __ctx, nullptr); - } - case _Flags::_Type::__hexadecimal_lower_case: { - array()> __array; - return __format_unsigned_integral(__array.begin(), __array.end(), __value, - __negative, 16, __ctx, "0x"); - } - case _Flags::_Type::__hexadecimal_upper_case: { - array()> __array; - return __format_unsigned_integral(__array.begin(), __array.end(), __value, - __negative, 16, __ctx, "0X"); - } - default: - _LIBCPP_ASSERT(false, "The parser should have validated the type"); - __libcpp_unreachable(); - } - } +/** Wrapper around @ref to_chars, returning the output pointer. */ +template +_LIBCPP_HIDE_FROM_ABI char* __to_buffer(char* __first, char* __last, _Tp __value, int __base) { + // TODO FMT Evaluate code overhead due to not calling the internal function + // directly. (Should be zero overhead.) + to_chars_result __r = _VSTD::to_chars(__first, __last, __value, __base); + _LIBCPP_ASSERT(__r.ec == errc(0), "Internal buffer too small"); + return __r.ptr; +} - template - requires(same_as || same_as) _LIBCPP_HIDE_FROM_ABI - auto __write(const _Tp* __first, const _Tp* __last, auto __out_it) - -> decltype(__out_it) { +/** + * Helper to determine the buffer size to output a integer in Base @em x. + * + * There are several overloads for the supported bases. The function uses the + * base as template argument so it can be used in a constant expression. + */ +template +consteval size_t __buffer_size() noexcept + requires(_Base == 2) +{ + return numeric_limits<_Tp>::digits // The number of binary digits. + + 2 // Reserve space for the '0[Bb]' prefix. + + 1; // Reserve space for the sign. +} - unsigned __size = __last - __first; - if (this->__type != _Flags::_Type::__hexadecimal_upper_case) [[likely]] { - if (__size >= this->__width) - return _VSTD::copy(__first, __last, _VSTD::move(__out_it)); +template +consteval size_t __buffer_size() noexcept + requires(_Base == 8) +{ + return numeric_limits<_Tp>::digits // The number of binary digits. + / 3 // Adjust to octal. + + 1 // Turn floor to ceil. + + 1 // Reserve space for the '0' prefix. + + 1; // Reserve space for the sign. +} - return __formatter::__write(_VSTD::move(__out_it), __first, __last, - __size, this->__width, this->__fill, - this->__alignment); - } +template +consteval size_t __buffer_size() noexcept + requires(_Base == 10) +{ + return numeric_limits<_Tp>::digits10 // The floored value. + + 1 // Turn floor to ceil. + + 1; // Reserve space for the sign. +} + +template +consteval size_t __buffer_size() noexcept + requires(_Base == 16) +{ + return numeric_limits<_Tp>::digits // The number of binary digits. + / 4 // Adjust to hexadecimal. + + 2 // Reserve space for the '0[Xx]' prefix. + + 1; // Reserve space for the sign. +} - // this->__type == _Flags::_Type::__hexadecimal_upper_case - // This means all characters in the range [a-f] need to be changed to their - // uppercase representation. The transformation is done as transformation - // in the output routine instead of before. This avoids another pass over - // the data. - // TODO FMT See whether it's possible to do this transformation during the - // conversion. (This probably requires changing std::to_chars' alphabet.) - if (__size >= this->__width) - return _VSTD::transform(__first, __last, _VSTD::move(__out_it), - __hex_to_upper); - - return __formatter::__write(_VSTD::move(__out_it), __first, __last, __size, - __hex_to_upper, this->__width, this->__fill, - this->__alignment); +template +_LIBCPP_HIDE_FROM_ABI auto __format_integer( + _Tp __value, + auto& __ctx, + __format_spec::__parsed_specifications<_CharT> __specs, + bool __negative, + char* __begin, + char* __end, + const char* __prefix, + int __base) -> decltype(__ctx.out()) { + _LIBCPP_ASSERT( + __specs.__alignment_ != __format_spec::__alignment::__default, + "the caller should adjust the default to the value required by the type"); + + char* __first = __formatter::__insert_sign(__begin, __negative, __specs.__std_.__sign_); + if (__specs.__std_.__alternate_form_ && __prefix) + while (*__prefix) + *__first++ = *__prefix++; + + char* __last = __formatter::__to_buffer(__first, __end, __value, __base); + +# ifndef _LIBCPP_HAS_NO_LOCALIZATION + if (__specs.__std_.__locale_specific_form_) { + const auto& __np = use_facet>(__ctx.locale()); + string __grouping = __np.grouping(); + ptrdiff_t __size = __last - __first; + // Writing the grouped form has more overhead than the normal output + // routines. If there will be no separators written the locale-specific + // form is identical to the normal routine. Test whether to grouped form + // is required. + if (!__grouping.empty() && __size > __grouping[0]) + return __formatter::__write_using_decimal_separators( + __ctx.out(), + __begin, + __first, + __last, + __formatter::__determine_grouping(__size, __grouping), + __np.thousands_sep(), + __specs); + } +# endif + auto __out_it = __ctx.out(); + if (__specs.__alignment_ != __format_spec::__alignment::__zero_padding) + __first = __begin; + else { + // __buf contains [sign][prefix]data + // ^ location of __first + // The zero padding is done like: + // - Write [sign][prefix] + // - Write data right aligned with '0' as fill character. + __out_it = _VSTD::copy(__begin, __first, _VSTD::move(__out_it)); + __specs.__alignment_ = __format_spec::__alignment::__right; + __specs.__fill_ = _CharT('0'); + int32_t __size = __first - __begin; + + __specs.__width_ -= _VSTD::min(__size, __specs.__width_); } - _LIBCPP_HIDE_FROM_ABI auto - __format_unsigned_integral(char* __begin, char* __end, - unsigned_integral auto __value, bool __negative, - int __base, auto& __ctx, const char* __prefix) - -> decltype(__ctx.out()) { - char* __first = __insert_sign(__begin, __negative, this->__sign); - if (this->__alternate_form && __prefix) - while (*__prefix) - *__first++ = *__prefix++; - - char* __last = __to_buffer(__first, __end, __value, __base); -#ifndef _LIBCPP_HAS_NO_LOCALIZATION - if (this->__locale_specific_form) { - const auto& __np = use_facet>(__ctx.locale()); - string __grouping = __np.grouping(); - ptrdiff_t __size = __last - __first; - // Writing the grouped form has more overhead than the normal output - // routines. If there will be no separators written the locale-specific - // form is identical to the normal routine. Test whether to grouped form - // is required. - if (!__grouping.empty() && __size > __grouping[0]) - return __format_grouping(__ctx.out(), __begin, __first, __last, - __determine_grouping(__size, __grouping), - __np.thousands_sep()); - } -#endif - auto __out_it = __ctx.out(); - if (this->__alignment != _Flags::_Alignment::__default) - __first = __begin; - else { - // __buf contains [sign][prefix]data - // ^ location of __first - // The zero padding is done like: - // - Write [sign][prefix] - // - Write data right aligned with '0' as fill character. - __out_it = _VSTD::copy(__begin, __first, _VSTD::move(__out_it)); - this->__alignment = _Flags::_Alignment::__right; - this->__fill = _CharT('0'); - uint32_t __size = __first - __begin; - this->__width -= _VSTD::min(__size, this->__width); - } + if (__specs.__std_.__type_ != __format_spec::__type::__hexadecimal_upper_case) [[likely]] + return __formatter::__write(__first, __last, __ctx.out(), __specs); + + return __formatter::__write_transformed(__first, __last, __ctx.out(), __specs, __formatter::__hex_to_upper); +} - return __write(__first, __last, _VSTD::move(__out_it)); +template +_LIBCPP_HIDE_FROM_ABI auto __format_integer( + _Tp __value, auto& __ctx, __format_spec::__parsed_specifications<_CharT> __specs, bool __negative = false) + -> decltype(__ctx.out()) { + switch (__specs.__std_.__type_) { + case __format_spec::__type::__binary_lower_case: { + array()> __array; + return __formatter::__format_integer(__value, __ctx, __specs, __negative, __array.begin(), __array.end(), "0b", 2); } + case __format_spec::__type::__binary_upper_case: { + array()> __array; + return __formatter::__format_integer(__value, __ctx, __specs, __negative, __array.begin(), __array.end(), "0B", 2); + } + case __format_spec::__type::__octal: { + // Octal is special; if __value == 0 there's no prefix. + array()> __array; + return __formatter::__format_integer( + __value, __ctx, __specs, __negative, __array.begin(), __array.end(), __value != 0 ? "0" : nullptr, 8); + } + case __format_spec::__type::__decimal: { + array()> __array; + return __formatter::__format_integer( + __value, __ctx, __specs, __negative, __array.begin(), __array.end(), nullptr, 10); + } + case __format_spec::__type::__hexadecimal_lower_case: { + array()> __array; + return __formatter::__format_integer(__value, __ctx, __specs, __negative, __array.begin(), __array.end(), "0x", 16); + } + case __format_spec::__type::__hexadecimal_upper_case: { + array()> __array; + return __formatter::__format_integer(__value, __ctx, __specs, __negative, __array.begin(), __array.end(), "0X", 16); + } + default: + _LIBCPP_ASSERT(false, "The parse function should have validated the type"); + __libcpp_unreachable(); + } +} -#ifndef _LIBCPP_HAS_NO_LOCALIZATION - /** Format's the locale-specific form's groupings. */ - template - _LIBCPP_HIDE_FROM_ABI _OutIt - __format_grouping(_OutIt __out_it, const char* __begin, const char* __first, - const char* __last, string&& __grouping, _CharT __sep) { - - // TODO FMT This function duplicates some functionality of the normal - // output routines. Evaluate whether these parts can be efficiently - // combined with the existing routines. - - unsigned __size = (__first - __begin) + // [sign][prefix] - (__last - __first) + // data - (__grouping.size() - 1); // number of separator characters - - __formatter::__padding_size_result __padding = {0, 0}; - if (this->__alignment == _Flags::_Alignment::__default) { - // Write [sign][prefix]. - __out_it = _VSTD::copy(__begin, __first, _VSTD::move(__out_it)); - - if (this->__width > __size) { - // Write zero padding. - __padding.__before = this->__width - __size; - __out_it = _VSTD::fill_n(_VSTD::move(__out_it), this->__width - __size, - _CharT('0')); - } - } else { - if (this->__width > __size) { - // Determine padding and write padding. - __padding = __formatter::__padding_size(__size, this->__width, - this->__alignment); - - __out_it = _VSTD::fill_n(_VSTD::move(__out_it), __padding.__before, - this->__fill); - } - // Write [sign][prefix]. - __out_it = _VSTD::copy(__begin, __first, _VSTD::move(__out_it)); - } +template +_LIBCPP_HIDE_FROM_ABI auto +__format_integer(_Tp __value, auto& __ctx, __format_spec::__parsed_specifications<_CharT> __specs) + -> decltype(__ctx.out()) { + // Depending on the std-format-spec string the sign and the value + // might not be outputted together: + // - alternate form may insert a prefix string. + // - zero-padding may insert additional '0' characters. + // Therefore the value is processed as a positive unsigned value. + // The function @ref __insert_sign will a '-' when the value was negative. + auto __r = std::__to_unsigned_like(__value); + bool __negative = __value < 0; + if (__negative) + __r = __complement(__r); + + return __formatter::__format_integer(__r, __ctx, __specs, __negative); +} - auto __r = __grouping.rbegin(); - auto __e = __grouping.rend() - 1; - _LIBCPP_ASSERT(__r != __e, "The slow grouping formatting is used while " - "there will be no separators written."); - // The output is divided in small groups of numbers to write: - // - A group before the first separator. - // - A separator and a group, repeated for the number of separators. - // - A group after the last separator. - // This loop achieves that process by testing the termination condition - // midway in the loop. - // - // TODO FMT This loop evaluates the loop invariant `this->__type != - // _Flags::_Type::__hexadecimal_upper_case` for every iteration. (This test - // happens in the __write call.) Benchmark whether making two loops and - // hoisting the invariant is worth the effort. - while (true) { - if (this->__type == _Flags::_Type::__hexadecimal_upper_case) { - __last = __first + *__r; - __out_it = _VSTD::transform(__first, __last, _VSTD::move(__out_it), - __hex_to_upper); - __first = __last; - } else { - __out_it = _VSTD::copy_n(__first, *__r, _VSTD::move(__out_it)); - __first += *__r; - } - - if (__r == __e) - break; - - ++__r; - *__out_it++ = __sep; - } +// +// Formatter arithmetic (bool) +// - return _VSTD::fill_n(_VSTD::move(__out_it), __padding.__after, - this->__fill); - } -#endif // _LIBCPP_HAS_NO_LOCALIZATION +template +struct _LIBCPP_TEMPLATE_VIS __bool_strings; + +template <> +struct _LIBCPP_TEMPLATE_VIS __bool_strings { + static constexpr string_view __true{"true"}; + static constexpr string_view __false{"false"}; +}; + +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS +template <> +struct _LIBCPP_TEMPLATE_VIS __bool_strings { + static constexpr wstring_view __true{L"true"}; + static constexpr wstring_view __false{L"false"}; }; +# endif + +template +_LIBCPP_HIDE_FROM_ABI auto +__format_bool(bool __value, auto& __ctx, __format_spec::__parsed_specifications<_CharT> __specs) + -> decltype(__ctx.out()) { +# ifndef _LIBCPP_HAS_NO_LOCALIZATION + if (__specs.__std_.__locale_specific_form_) { + const auto& __np = use_facet>(__ctx.locale()); + basic_string<_CharT> __str = __value ? __np.truename() : __np.falsename(); + return __formatter::__write_unicode_no_precision(basic_string_view<_CharT>{__str}, __ctx.out(), __specs); + } +# endif + basic_string_view<_CharT> __str = + __value ? __formatter::__bool_strings<_CharT>::__true : __formatter::__bool_strings<_CharT>::__false; + return __formatter::__write(__str.begin(), __str.end(), __ctx.out(), __specs); +} -} // namespace __format_spec +} // namespace __formatter #endif //_LIBCPP_STD_VER > 17 diff --git a/libcxx/include/__format/formatter_pointer.h b/libcxx/include/__format/formatter_pointer.h --- a/libcxx/include/__format/formatter_pointer.h +++ b/libcxx/include/__format/formatter_pointer.h @@ -49,7 +49,7 @@ char __buffer[2 + 2 * sizeof(uintptr_t)]; __buffer[0] = '0'; __buffer[1] = 'x'; - char* __last = __to_buffer(__buffer + 2, _VSTD::end(__buffer), reinterpret_cast(__ptr), 16); + char* __last = __formatter::__to_buffer(__buffer + 2, _VSTD::end(__buffer), reinterpret_cast(__ptr), 16); unsigned __size = __last - __buffer; if (__size >= this->__width) diff --git a/libcxx/include/__format/parser_std_format_spec.h b/libcxx/include/__format/parser_std_format_spec.h --- a/libcxx/include/__format/parser_std_format_spec.h +++ b/libcxx/include/__format/parser_std_format_spec.h @@ -1400,6 +1400,12 @@ // By not placing this constant in the formatter class it's not duplicated for // char and wchar_t. +inline constexpr __fields __fields_integral{ + .__sign_ = true, + .__alternate_form_ = true, + .__zero_padding_ = true, + .__locale_specific_form_ = true, + .__type_ = true}; inline constexpr __fields __fields_string{.__precision_ = true, .__type_ = true}; enum class _LIBCPP_ENUM_VIS __alignment : uint8_t { @@ -1840,8 +1846,105 @@ break; default: - __throw_format_error("The format-spec type has a type not supported for " - "a string argument"); + std::__throw_format_error("The format-spec type has a type not supported for a string argument"); + } +} + +template +_LIBCPP_HIDE_FROM_ABI constexpr void __process_display_type_bool_string(__parser<_CharT>& __parser) { + if (__parser.__sign_ != __sign::__default) + std::__throw_format_error("A sign field isn't allowed in this format-spec"); + + if (__parser.__alternate_form_) + std::__throw_format_error("An alternate form field isn't allowed in this format-spec"); + + if (__parser.__alignment_ == __alignment::__zero_padding) + std::__throw_format_error("A zero-padding field isn't allowed in this format-spec"); + + if (__parser.__alignment_ == __alignment::__default) + __parser.__alignment_ = __alignment::__left; +} + +template +_LIBCPP_HIDE_FROM_ABI constexpr void __process_display_type_char(__parser<_CharT>& __parser) { + __format_spec::__process_display_type_bool_string(__parser); +} + +template +_LIBCPP_HIDE_FROM_ABI constexpr void __process_display_type_integer(__parser<_CharT>& __parser) { + if (__parser.__alignment_ == __alignment::__default) + __parser.__alignment_ = __alignment::__right; +} + +template +_LIBCPP_HIDE_FROM_ABI constexpr void __process_parsed_bool(__parser<_CharT>& __parser) { + switch (__parser.__type_) { + case __format_spec::__type::__default: + __parser.__type_ = __format_spec::__type::__string; + [[fallthrough]]; + case __format_spec::__type::__string: + __format_spec::__process_display_type_bool_string(__parser); + break; + + case __format_spec::__type::__binary_lower_case: + case __format_spec::__type::__binary_upper_case: + case __format_spec::__type::__octal: + case __format_spec::__type::__decimal: + case __format_spec::__type::__hexadecimal_lower_case: + case __format_spec::__type::__hexadecimal_upper_case: + __process_display_type_integer(__parser); + break; + + default: + std::__throw_format_error("The format-spec type has a type not supported for a bool argument"); + } +} + +template +_LIBCPP_HIDE_FROM_ABI constexpr void __process_parsed_char(__parser<_CharT>& __parser) { + switch (__parser.__type_) { + case __format_spec::__type::__default: + __parser.__type_ = __format_spec::__type::__char; + [[fallthrough]]; + case __format_spec::__type::__char: + __format_spec::__process_display_type_char(__parser); + break; + + case __format_spec::__type::__binary_lower_case: + case __format_spec::__type::__binary_upper_case: + case __format_spec::__type::__octal: + case __format_spec::__type::__decimal: + case __format_spec::__type::__hexadecimal_lower_case: + case __format_spec::__type::__hexadecimal_upper_case: + __format_spec::__process_display_type_integer(__parser); + break; + + default: + std::__throw_format_error("The format-spec type has a type not supported for a char argument"); + } +} + +template +_LIBCPP_HIDE_FROM_ABI constexpr void __process_parsed_integer(__parser<_CharT>& __parser) { + switch (__parser.__type_) { + case __format_spec::__type::__default: + __parser.__type_ = __format_spec::__type::__decimal; + [[fallthrough]]; + case __format_spec::__type::__binary_lower_case: + case __format_spec::__type::__binary_upper_case: + case __format_spec::__type::__octal: + case __format_spec::__type::__decimal: + case __format_spec::__type::__hexadecimal_lower_case: + case __format_spec::__type::__hexadecimal_upper_case: + __format_spec::__process_display_type_integer(__parser); + break; + + case __format_spec::__type::__char: + __format_spec::__process_display_type_char(__parser); + break; + + default: + std::__throw_format_error("The format-spec type has a type not supported for an integer argument"); } } diff --git a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/std_format_spec_bool.pass.cpp b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/std_format_spec_bool.pass.cpp deleted file mode 100644 --- a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/std_format_spec_bool.pass.cpp +++ /dev/null @@ -1,380 +0,0 @@ -//===----------------------------------------------------------------------===// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -// UNSUPPORTED: c++03, c++11, c++14, c++17 -// UNSUPPORTED: libcpp-has-no-incomplete-format - -// - -// Tests the parsing of the format string as specified in [format.string.std]. -// It validates whether the std-format-spec is valid for a boolean type. - -#include -#include -#ifndef _LIBCPP_HAS_NO_LOCALIZATION -# include -#endif - -#include "concepts_precision.h" -#include "test_macros.h" -#include "make_string.h" -#include "test_exception.h" - -#define CSTR(S) MAKE_CSTRING(CharT, S) - -using namespace std::__format_spec; - -template -using Parser = __parser_bool; - -template -struct Expected { - CharT fill = CharT(' '); - _Flags::_Alignment alignment = _Flags::_Alignment::__left; - _Flags::_Sign sign = _Flags::_Sign::__default; - bool alternate_form = false; - bool zero_padding = false; - uint32_t width = 0; - bool width_as_arg = false; - bool locale_specific_form = false; - _Flags::_Type type = _Flags::_Type::__string; -}; - -template -constexpr void test(Expected expected, size_t size, - std::basic_string_view fmt) { - // Initialize parser with sufficient arguments to avoid the parsing to fail - // due to insufficient arguments. - std::basic_format_parse_context parse_ctx(fmt, - std::__format::__number_max); - auto begin = parse_ctx.begin(); - auto end = parse_ctx.end(); - Parser parser; - auto it = parser.parse(parse_ctx); - - assert(begin == parse_ctx.begin()); - assert(end == parse_ctx.end()); - - assert(begin + size == it); - assert(parser.__fill == expected.fill); - assert(parser.__alignment == expected.alignment); - assert(parser.__sign == expected.sign); - assert(parser.__alternate_form == expected.alternate_form); - assert(parser.__zero_padding == expected.zero_padding); - assert(parser.__width == expected.width); - assert(parser.__width_as_arg == expected.width_as_arg); - assert(parser.__locale_specific_form == expected.locale_specific_form); - assert(parser.__type == expected.type); -} - -template -constexpr void test(Expected expected, size_t size, const CharT* f) { - // The format-spec is valid if completely consumed or terminates at a '}'. - // The valid inputs all end with a '}'. The test is executed twice: - // - first with the terminating '}', - // - second consuming the entire input. - std::basic_string_view fmt{f}; - assert(fmt.back() == CharT('}') && "Pre-condition failure"); - - test(expected, size, fmt); - fmt.remove_suffix(1); - test(expected, size, fmt); -} - -template -constexpr void test_as_string() { - - test({}, 1, CSTR("s}")); - - // *** Align-fill *** - test({.alignment = _Flags::_Alignment::__left}, 1, CSTR("<}")); - test({.alignment = _Flags::_Alignment::__center}, 1, "^}"); - test({.alignment = _Flags::_Alignment::__right}, 1, ">}"); - - test({.alignment = _Flags::_Alignment::__left}, 2, CSTR("s}"); - - test({.fill = CharT('L'), .alignment = _Flags::_Alignment::__left}, 2, - CSTR("L<}")); - test({.fill = CharT('#'), .alignment = _Flags::_Alignment::__center}, 2, - CSTR("#^}")); - test({.fill = CharT('0'), .alignment = _Flags::_Alignment::__right}, 2, - CSTR("0>}")); - - test({.fill = CharT('L'), .alignment = _Flags::_Alignment::__left}, 3, - CSTR("Ls}")); - - // *** Sign *** - test_exception>( - "A sign field isn't allowed in this format-spec", CSTR("-}")); - test_exception>( - "A sign field isn't allowed in this format-spec", CSTR("-s}")); - - // *** Alternate form *** - test_exception>( - "An alternate form field isn't allowed in this format-spec", CSTR("#}")); - test_exception>( - "An alternate form field isn't allowed in this format-spec", CSTR("#s}")); - - // *** Zero padding *** - test_exception>( - "A zero-padding field isn't allowed in this format-spec", CSTR("0}")); - test_exception>( - "A zero-padding field isn't allowed in this format-spec", CSTR("0s}")); - - // *** Width *** - test({.width = 0, .width_as_arg = false}, 0, CSTR("}")); - test({.width = 1, .width_as_arg = false}, 1, CSTR("1}")); - test({.width = 10, .width_as_arg = false}, 2, CSTR("10}")); - test({.width = 1000, .width_as_arg = false}, 4, CSTR("1000}")); - test({.width = 1000000, .width_as_arg = false}, 7, CSTR("1000000}")); - - test({.width = 0, .width_as_arg = true}, 2, CSTR("{}}")); - test({.width = 0, .width_as_arg = true}, 3, CSTR("{0}}")); - test({.width = 1, .width_as_arg = true}, 3, CSTR("{1}}")); - - test_exception>( - "A format-spec width field shouldn't have a leading zero", CSTR("00")); - - static_assert(std::__format::__number_max == 2'147'483'647, - "Update the assert and the test."); - test({.width = 2'147'483'647, .width_as_arg = false}, 10, - CSTR("2147483647}")); - test_exception>( - "The numeric value of the format-spec is too large", CSTR("2147483648")); - test_exception>( - "The numeric value of the format-spec is too large", CSTR("5000000000")); - test_exception>( - "The numeric value of the format-spec is too large", CSTR("10000000000")); - - test_exception>("End of input while parsing format-spec arg-id", - CSTR("{")); - test_exception>("Invalid arg-id", CSTR("{0")); - test_exception>( - "The arg-id of the format-spec starts with an invalid character", - CSTR("{a")); - test_exception>("Invalid arg-id", CSTR("{1")); - test_exception>("Invalid arg-id", CSTR("{9")); - test_exception>("Invalid arg-id", CSTR("{9:")); - test_exception>("Invalid arg-id", CSTR("{9a")); - - static_assert(std::__format::__number_max == 2'147'483'647, - "Update the assert and the test."); - // Note the static_assert tests whether the arg-id is valid. - // Therefore the following should be true arg-id < __format::__number_max. - test({.width = 2'147'483'646, .width_as_arg = true}, 12, - CSTR("{2147483646}}")); - test_exception>( - "The numeric value of the format-spec is too large", - CSTR("{2147483648}")); - test_exception>( - "The numeric value of the format-spec is too large", - CSTR("{5000000000}")); - test_exception>( - "The numeric value of the format-spec is too large", - CSTR("{10000000000}")); - - // *** Precision *** - test_exception>( - "The format-spec should consume the input or end with a '}'", CSTR(".")); - test_exception>( - "The format-spec should consume the input or end with a '}'", CSTR(".1")); - - // *** Locale-specific form *** - test({.locale_specific_form = true}, 1, CSTR("L}")); - test({.locale_specific_form = true}, 2, CSTR("Ls}")); -} - -template -constexpr void test_as_integer() { - - test({.alignment = _Flags::_Alignment::__right, - .type = _Flags::_Type::__decimal}, - 1, CSTR("d}")); - - // *** Align-fill *** - test({.alignment = _Flags::_Alignment::__left, - .type = _Flags::_Type::__decimal}, - 2, CSTR("d}"); - - test({.fill = CharT('L'), - .alignment = _Flags::_Alignment::__left, - .type = _Flags::_Type::__decimal}, - 3, CSTR("Ld}")); - - // *** Sign *** - test({.alignment = _Flags::_Alignment::__right, - .sign = _Flags::_Sign::__minus, - .type = _Flags::_Type::__decimal}, - 2, CSTR("-d}")); - test({.alignment = _Flags::_Alignment::__right, - .sign = _Flags::_Sign::__plus, - .type = _Flags::_Type::__decimal}, - 2, CSTR("+d}")); - test({.alignment = _Flags::_Alignment::__right, - .sign = _Flags::_Sign::__space, - .type = _Flags::_Type::__decimal}, - 2, CSTR(" d}")); - - // *** Alternate form *** - test({.alignment = _Flags::_Alignment::__right, - .alternate_form = true, - .type = _Flags::_Type::__decimal}, - 2, CSTR("#d}")); - - // *** Zero padding *** - test({.alignment = _Flags::_Alignment::__default, - .zero_padding = true, - .type = _Flags::_Type::__decimal}, - 2, CSTR("0d}")); - test({.alignment = _Flags::_Alignment::__center, - .type = _Flags::_Type::__decimal}, - 3, CSTR("^0d}")); - - // *** Width *** - test({.alignment = _Flags::_Alignment::__right, - .width = 0, - .width_as_arg = false, - .type = _Flags::_Type::__decimal}, - 1, CSTR("d}")); - test({.alignment = _Flags::_Alignment::__right, - .width = 1, - .width_as_arg = false, - .type = _Flags::_Type::__decimal}, - 2, CSTR("1d}")); - test({.alignment = _Flags::_Alignment::__right, - .width = 10, - .width_as_arg = false, - .type = _Flags::_Type::__decimal}, - 3, CSTR("10d}")); - test({.alignment = _Flags::_Alignment::__right, - .width = 1000, - .width_as_arg = false, - .type = _Flags::_Type::__decimal}, - 5, CSTR("1000d}")); - test({.alignment = _Flags::_Alignment::__right, - .width = 1000000, - .width_as_arg = false, - .type = _Flags::_Type::__decimal}, - 8, CSTR("1000000d}")); - - test({.alignment = _Flags::_Alignment::__right, - .width = 0, - .width_as_arg = true, - .type = _Flags::_Type::__decimal}, - 3, CSTR("{}d}")); - test({.alignment = _Flags::_Alignment::__right, - .width = 0, - .width_as_arg = true, - .type = _Flags::_Type::__decimal}, - 4, CSTR("{0}d}")); - test({.alignment = _Flags::_Alignment::__right, - .width = 1, - .width_as_arg = true, - .type = _Flags::_Type::__decimal}, - 4, CSTR("{1}d}")); - - // *** Precision *** - test_exception>( - "The format-spec should consume the input or end with a '}'", CSTR(".")); - test_exception>( - "The format-spec should consume the input or end with a '}'", CSTR(".1")); - - // *** Locale-specific form *** - test({.alignment = _Flags::_Alignment::__right, - .locale_specific_form = true, - .type = _Flags::_Type::__decimal}, - 2, CSTR("Ld}")); -} - -template -constexpr void test() { - Parser parser; - - assert(parser.__fill == CharT(' ')); - assert(parser.__alignment == _Flags::_Alignment::__default); - assert(parser.__sign == _Flags::_Sign::__default); - assert(parser.__alternate_form == false); - assert(parser.__zero_padding == false); - assert(parser.__width == 0); - assert(parser.__width_as_arg == false); - static_assert(!has_precision); - static_assert(!has_precision_as_arg); - assert(parser.__locale_specific_form == false); - assert(parser.__type == _Flags::_Type::__default); - - test({}, 0, CSTR("}")); - - test_as_string(); - test_as_integer(); - - // *** Type *** - { - const char* expected = - "The format-spec type has a type not supported for a bool argument"; - test_exception>(expected, CSTR("A}")); - test_exception>(expected, CSTR("E}")); - test_exception>(expected, CSTR("F}")); - test_exception>(expected, CSTR("G}")); - test_exception>(expected, CSTR("a}")); - test_exception>(expected, CSTR("c}")); - test_exception>(expected, CSTR("e}")); - test_exception>(expected, CSTR("f}")); - test_exception>(expected, CSTR("g}")); - test_exception>(expected, CSTR("p}")); - } - - // **** General *** - test_exception>( - "The format-spec should consume the input or end with a '}'", CSTR("ss")); -} - -constexpr bool test() { - test(); -#ifndef TEST_HAS_NO_WIDE_CHARACTERS - test(); -#endif - - return true; -} - -int main(int, char**) { -#ifndef _WIN32 - // Make sure the parsers match the expectations. The layout of the - // subobjects is chosen to minimize the size required. - static_assert(sizeof(Parser) == 2 * sizeof(uint32_t)); -#ifndef TEST_HAS_NO_WIDE_CHARACTERS - static_assert( - sizeof(Parser) == - (sizeof(wchar_t) <= 2 ? 2 * sizeof(uint32_t) : 3 * sizeof(uint32_t))); -#endif -#endif // _WIN32 - - test(); - static_assert(test()); - - return 0; -} diff --git a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/std_format_spec_char.pass.cpp b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/std_format_spec_char.pass.cpp deleted file mode 100644 --- a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/std_format_spec_char.pass.cpp +++ /dev/null @@ -1,450 +0,0 @@ -//===----------------------------------------------------------------------===// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -// UNSUPPORTED: c++03, c++11, c++14, c++17 -// UNSUPPORTED: libcpp-has-no-incomplete-format - -// - -// Tests the parsing of the format string as specified in [format.string.std]. -// It validates whether the std-format-spec is valid for a char type. - -#include -#include -#ifndef _LIBCPP_HAS_NO_LOCALIZATION -# include -#endif - -#include "concepts_precision.h" -#include "test_macros.h" -#include "make_string.h" -#include "test_exception.h" - -#define CSTR(S) MAKE_CSTRING(CharT, S) - -using namespace std::__format_spec; - -template -using Parser = __parser_char; - -template -struct Expected { - CharT fill = CharT(' '); - _Flags::_Alignment alignment = _Flags::_Alignment::__left; - _Flags::_Sign sign = _Flags::_Sign::__default; - bool alternate_form = false; - bool zero_padding = false; - uint32_t width = 0; - bool width_as_arg = false; - bool locale_specific_form = false; - _Flags::_Type type = _Flags::_Type::__char; -}; - -template -constexpr void test(Expected expected, size_t size, - std::basic_string_view fmt) { - // Initialize parser with sufficient arguments to avoid the parsing to fail - // due to insufficient arguments. - std::basic_format_parse_context parse_ctx(fmt, - std::__format::__number_max); - auto begin = parse_ctx.begin(); - auto end = parse_ctx.end(); - Parser parser; - auto it = parser.parse(parse_ctx); - - assert(begin == parse_ctx.begin()); - assert(end == parse_ctx.end()); - - assert(begin + size == it); - assert(parser.__fill == expected.fill); - assert(parser.__alignment == expected.alignment); - assert(parser.__sign == expected.sign); - assert(parser.__alternate_form == expected.alternate_form); - assert(parser.__zero_padding == expected.zero_padding); - assert(parser.__width == expected.width); - assert(parser.__width_as_arg == expected.width_as_arg); - assert(parser.__locale_specific_form == expected.locale_specific_form); - assert(parser.__type == expected.type); -} - -template -constexpr void test(Expected expected, size_t size, const CharT* f) { - // The format-spec is valid if completely consumed or terminates at a '}'. - // The valid inputs all end with a '}'. The test is executed twice: - // - first with the terminating '}', - // - second consuming the entire input. - std::basic_string_view fmt{f}; - assert(fmt.back() == CharT('}') && "Pre-condition failure"); - - test(expected, size, fmt); - fmt.remove_suffix(1); - test(expected, size, fmt); -} - -template -constexpr void test_as_char() { - - test({}, 1, CSTR("c}")); - - // *** Align-fill *** - test({.alignment = _Flags::_Alignment::__left}, 1, CSTR("<}")); - test({.alignment = _Flags::_Alignment::__center}, 1, "^}"); - test({.alignment = _Flags::_Alignment::__right}, 1, ">}"); - - test({.alignment = _Flags::_Alignment::__left}, 2, CSTR("c}"); - - test({.fill = CharT('L'), .alignment = _Flags::_Alignment::__left}, 2, - CSTR("L<}")); - test({.fill = CharT('#'), .alignment = _Flags::_Alignment::__center}, 2, - CSTR("#^}")); - test({.fill = CharT('0'), .alignment = _Flags::_Alignment::__right}, 2, - CSTR("0>}")); - - test({.fill = CharT('L'), .alignment = _Flags::_Alignment::__left}, 3, - CSTR("Lc}")); - - // *** Sign *** - test_exception>( - "A sign field isn't allowed in this format-spec", CSTR("-")); - test_exception>( - "A sign field isn't allowed in this format-spec", CSTR("+")); - test_exception>( - "A sign field isn't allowed in this format-spec", CSTR(" ")); - - test_exception>( - "A sign field isn't allowed in this format-spec", CSTR("-c")); - test_exception>( - "A sign field isn't allowed in this format-spec", CSTR("+c")); - test_exception>( - "A sign field isn't allowed in this format-spec", CSTR(" c")); - - // *** Alternate form *** - test_exception>( - "An alternate form field isn't allowed in this format-spec", CSTR("#}")); - test_exception>( - "An alternate form field isn't allowed in this format-spec", CSTR("#c}")); - - // *** Zero padding *** - test_exception>( - "A zero-padding field isn't allowed in this format-spec", CSTR("0}")); - test_exception>( - "A zero-padding field isn't allowed in this format-spec", CSTR("0c}")); - - // *** Width *** - test({.width = 0, .width_as_arg = false}, 0, CSTR("}")); - test({.width = 1, .width_as_arg = false}, 1, CSTR("1}")); - test({.width = 10, .width_as_arg = false}, 2, CSTR("10}")); - test({.width = 1000, .width_as_arg = false}, 4, CSTR("1000}")); - test({.width = 1000000, .width_as_arg = false}, 7, CSTR("1000000}")); - - test({.width = 0, .width_as_arg = true}, 2, CSTR("{}}")); - test({.width = 0, .width_as_arg = true}, 3, CSTR("{0}}")); - test({.width = 1, .width_as_arg = true}, 3, CSTR("{1}}")); - - test_exception>( - "A format-spec width field shouldn't have a leading zero", CSTR("00")); - - static_assert(std::__format::__number_max == 2'147'483'647, - "Update the assert and the test."); - test({.width = 2'147'483'647, .width_as_arg = false}, 10, - CSTR("2147483647}")); - test_exception>( - "The numeric value of the format-spec is too large", CSTR("2147483648")); - test_exception>( - "The numeric value of the format-spec is too large", CSTR("5000000000")); - test_exception>( - "The numeric value of the format-spec is too large", CSTR("10000000000")); - - test_exception>("End of input while parsing format-spec arg-id", - CSTR("{")); - test_exception>("Invalid arg-id", CSTR("{0")); - test_exception>( - "The arg-id of the format-spec starts with an invalid character", - CSTR("{a")); - test_exception>("Invalid arg-id", CSTR("{1")); - test_exception>("Invalid arg-id", CSTR("{9")); - test_exception>("Invalid arg-id", CSTR("{9:")); - test_exception>("Invalid arg-id", CSTR("{9a")); - - static_assert(std::__format::__number_max == 2'147'483'647, - "Update the assert and the test."); - // Note the static_assert tests whether the arg-id is valid. - // Therefore the following should be true arg-id < __format::__number_max. - test({.width = 2'147'483'646, .width_as_arg = true}, 12, - CSTR("{2147483646}}")); - test_exception>( - "The numeric value of the format-spec is too large", - CSTR("{2147483648}")); - test_exception>( - "The numeric value of the format-spec is too large", - CSTR("{5000000000}")); - test_exception>( - "The numeric value of the format-spec is too large", - CSTR("{10000000000}")); - - // *** Precision *** - test_exception>( - "The format-spec should consume the input or end with a '}'", CSTR(".")); - test_exception>( - "The format-spec should consume the input or end with a '}'", CSTR(".1")); - - // *** Locale-specific form *** - // Note the flag is allowed, but has no effect. - test({.locale_specific_form = true}, 1, CSTR("L}")); - test({.locale_specific_form = true}, 2, CSTR("Lc}")); -} - -template -constexpr void test_as_integer() { - - test({.alignment = _Flags::_Alignment::__right, - .type = _Flags::_Type::__decimal}, - 1, CSTR("d}")); - - // *** Align-fill *** - test({.alignment = _Flags::_Alignment::__left, - .type = _Flags::_Type::__decimal}, - 2, CSTR("d}"); - - test({.fill = CharT('L'), - .alignment = _Flags::_Alignment::__left, - .type = _Flags::_Type::__decimal}, - 3, CSTR("Ld}")); - - // *** Sign *** - test({.alignment = _Flags::_Alignment::__right, - .sign = _Flags::_Sign::__minus, - .type = _Flags::_Type::__decimal}, - 2, CSTR("-d}")); - test({.alignment = _Flags::_Alignment::__right, - .sign = _Flags::_Sign::__plus, - .type = _Flags::_Type::__decimal}, - 2, CSTR("+d}")); - test({.alignment = _Flags::_Alignment::__right, - .sign = _Flags::_Sign::__space, - .type = _Flags::_Type::__decimal}, - 2, CSTR(" d}")); - - // *** Alternate form *** - test({.alignment = _Flags::_Alignment::__right, - .alternate_form = true, - .type = _Flags::_Type::__decimal}, - 2, CSTR("#d}")); - - // *** Zero padding *** - test({.alignment = _Flags::_Alignment::__default, - .zero_padding = true, - .type = _Flags::_Type::__decimal}, - 2, CSTR("0d}")); - test({.alignment = _Flags::_Alignment::__center, - .type = _Flags::_Type::__decimal}, - 3, CSTR("^0d}")); - - // *** Width *** - test({.alignment = _Flags::_Alignment::__right, - .width = 0, - .width_as_arg = false, - .type = _Flags::_Type::__decimal}, - 1, CSTR("d}")); - test({.alignment = _Flags::_Alignment::__right, - .width = 1, - .width_as_arg = false, - .type = _Flags::_Type::__decimal}, - 2, CSTR("1d}")); - test({.alignment = _Flags::_Alignment::__right, - .width = 10, - .width_as_arg = false, - .type = _Flags::_Type::__decimal}, - 3, CSTR("10d}")); - test({.alignment = _Flags::_Alignment::__right, - .width = 1000, - .width_as_arg = false, - .type = _Flags::_Type::__decimal}, - 5, CSTR("1000d}")); - test({.alignment = _Flags::_Alignment::__right, - .width = 1000000, - .width_as_arg = false, - .type = _Flags::_Type::__decimal}, - 8, CSTR("1000000d}")); - - test({.alignment = _Flags::_Alignment::__right, - .width = 0, - .width_as_arg = true, - .type = _Flags::_Type::__decimal}, - 3, CSTR("{}d}")); - test({.alignment = _Flags::_Alignment::__right, - .width = 0, - .width_as_arg = true, - .type = _Flags::_Type::__decimal}, - 4, CSTR("{0}d}")); - test({.alignment = _Flags::_Alignment::__right, - .width = 1, - .width_as_arg = true, - .type = _Flags::_Type::__decimal}, - 4, CSTR("{1}d}")); - - // *** Precision *** - test_exception>( - "The format-spec should consume the input or end with a '}'", CSTR(".")); - test_exception>( - "The format-spec should consume the input or end with a '}'", CSTR(".1")); - - // *** Locale-specific form *** - test({.alignment = _Flags::_Alignment::__right, - .locale_specific_form = true, - .type = _Flags::_Type::__decimal}, - 2, CSTR("Ld}")); -} - -template -constexpr void test() { - Parser parser; - - assert(parser.__fill == CharT(' ')); - assert(parser.__alignment == _Flags::_Alignment::__default); - assert(parser.__sign == _Flags::_Sign::__default); - assert(parser.__alternate_form == false); - assert(parser.__zero_padding == false); - assert(parser.__width == 0); - assert(parser.__width_as_arg == false); - static_assert(!has_precision); - static_assert(!has_precision_as_arg); - assert(parser.__locale_specific_form == false); - assert(parser.__type == _Flags::_Type::__default); - - test({}, 0, CSTR("}")); - - test_as_char(); - test_as_integer(); - - // *** Type *** - { - const char* unsuported_type = - "The format-spec type has a type not supported for a char argument"; - const char* not_a_type = - "The format-spec should consume the input or end with a '}'"; - - test_exception>(unsuported_type, CSTR("A}")); - test({.alignment = _Flags::_Alignment::__right, - .type = _Flags::_Type::__binary_upper_case}, - 1, CSTR("B}")); - test_exception>(not_a_type, CSTR("C}")); - test_exception>(not_a_type, CSTR("D}")); - test_exception>(unsuported_type, CSTR("E}")); - test_exception>(unsuported_type, CSTR("F}")); - test_exception>(unsuported_type, CSTR("G}")); - test_exception>(not_a_type, CSTR("H}")); - test_exception>(not_a_type, CSTR("I}")); - test_exception>(not_a_type, CSTR("J}")); - test_exception>(not_a_type, CSTR("K}")); - test({.locale_specific_form = true}, 1, CSTR("L}")); - test_exception>(not_a_type, CSTR("M}")); - test_exception>(not_a_type, CSTR("N}")); - test_exception>(not_a_type, CSTR("O}")); - test_exception>(not_a_type, CSTR("P}")); - test_exception>(not_a_type, CSTR("Q}")); - test_exception>(not_a_type, CSTR("R}")); - test_exception>(not_a_type, CSTR("S}")); - test_exception>(not_a_type, CSTR("T}")); - test_exception>(not_a_type, CSTR("U}")); - test_exception>(not_a_type, CSTR("V}")); - test_exception>(not_a_type, CSTR("W}")); - test({.alignment = _Flags::_Alignment::__right, - .type = _Flags::_Type::__hexadecimal_upper_case}, - 1, CSTR("X}")); - test_exception>(not_a_type, CSTR("Y}")); - test_exception>(not_a_type, CSTR("Z}")); - - test_exception>(unsuported_type, CSTR("a}")); - test({.alignment = _Flags::_Alignment::__right, - .type = _Flags::_Type::__binary_lower_case}, - 1, CSTR("b}")); - test({.type = _Flags::_Type::__char}, 1, CSTR("c}")); - test({.alignment = _Flags::_Alignment::__right, - .type = _Flags::_Type::__decimal}, - 1, CSTR("d}")); - test_exception>(unsuported_type, CSTR("e}")); - test_exception>(unsuported_type, CSTR("f}")); - test_exception>(unsuported_type, CSTR("g}")); - test_exception>(not_a_type, CSTR("h}")); - test_exception>(not_a_type, CSTR("i}")); - test_exception>(not_a_type, CSTR("j}")); - test_exception>(not_a_type, CSTR("k}")); - test_exception>(not_a_type, CSTR("l}")); - test_exception>(not_a_type, CSTR("m}")); - test_exception>(not_a_type, CSTR("n}")); - test({.alignment = _Flags::_Alignment::__right, - .type = _Flags::_Type::__octal}, - 1, CSTR("o}")); - test_exception>(unsuported_type, CSTR("p}")); - test_exception>(not_a_type, CSTR("q}")); - test_exception>(not_a_type, CSTR("r}")); - test_exception>(unsuported_type, CSTR("s}")); - test_exception>(not_a_type, CSTR("t}")); - test_exception>(not_a_type, CSTR("u}")); - test_exception>(not_a_type, CSTR("v}")); - test_exception>(not_a_type, CSTR("w}")); - test({.alignment = _Flags::_Alignment::__right, - .type = _Flags::_Type::__hexadecimal_lower_case}, - 1, CSTR("x}")); - test_exception>(not_a_type, CSTR("y}")); - test_exception>(not_a_type, CSTR("z}")); - } - - // **** General *** - test_exception>( - "The format-spec should consume the input or end with a '}'", CSTR("ss")); -} - -constexpr bool test() { - test(); -#ifndef TEST_HAS_NO_WIDE_CHARACTERS - test(); -#endif - - return true; -} - -int main(int, char**) { -#ifndef _WIN32 - // TODO FMT Investigate why this doesn't work. - // (Wait until LWG-3576 has been resolved.) - // Make sure the parsers match the expectations. The layout of the - // subobjects is chosen to minimize the size required. - static_assert(sizeof(Parser) == 2 * sizeof(uint32_t)); -#ifndef TEST_HAS_NO_WIDE_CHARACTERS - static_assert( - sizeof(Parser) == - (sizeof(wchar_t) <= 2 ? 2 * sizeof(uint32_t) : 3 * sizeof(uint32_t))); -#endif -#endif // _WIN32 - - test(); - static_assert(test()); - - return 0; -} diff --git a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/std_format_spec_integer.pass.cpp b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/std_format_spec_integer.pass.cpp deleted file mode 100644 --- a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/std_format_spec_integer.pass.cpp +++ /dev/null @@ -1,343 +0,0 @@ -//===----------------------------------------------------------------------===// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -// UNSUPPORTED: c++03, c++11, c++14, c++17 -// UNSUPPORTED: libcpp-has-no-incomplete-format - -// - -// Tests the parsing of the format string as specified in [format.string.std]. -// It validates whether the std-format-spec is valid for an integer type. - -#include -#include -#ifndef _LIBCPP_HAS_NO_LOCALIZATION -# include -#endif - -#include "concepts_precision.h" -#include "test_macros.h" -#include "make_string.h" -#include "test_exception.h" - -#define CSTR(S) MAKE_CSTRING(CharT, S) - -using namespace std::__format_spec; - -template -using Parser = __parser_integer; - -template -struct Expected { - CharT fill = CharT(' '); - _Flags::_Alignment alignment = _Flags::_Alignment::__right; - _Flags::_Sign sign = _Flags::_Sign::__default; - bool alternate_form = false; - bool zero_padding = false; - uint32_t width = 0; - bool width_as_arg = false; - bool locale_specific_form = false; - _Flags::_Type type = _Flags::_Type::__decimal; -}; - -template -constexpr void test(Expected expected, size_t size, - std::basic_string_view fmt) { - // Initialize parser with sufficient arguments to avoid the parsing to fail - // due to insufficient arguments. - std::basic_format_parse_context parse_ctx(fmt, - std::__format::__number_max); - auto begin = parse_ctx.begin(); - auto end = parse_ctx.end(); - Parser parser; - auto it = parser.parse(parse_ctx); - - assert(begin == parse_ctx.begin()); - assert(end == parse_ctx.end()); - - assert(begin + size == it); - assert(parser.__fill == expected.fill); - assert(parser.__alignment == expected.alignment); - assert(parser.__sign == expected.sign); - assert(parser.__alternate_form == expected.alternate_form); - assert(parser.__zero_padding == expected.zero_padding); - assert(parser.__width == expected.width); - assert(parser.__width_as_arg == expected.width_as_arg); - assert(parser.__locale_specific_form == expected.locale_specific_form); - assert(parser.__type == expected.type); -} - -template -constexpr void test(Expected expected, size_t size, const CharT* f) { - // The format-spec is valid if completely consumed or terminates at a '}'. - // The valid inputs all end with a '}'. The test is executed twice: - // - first with the terminating '}', - // - second consuming the entire input. - std::basic_string_view fmt{f}; - assert(fmt.back() == CharT('}') && "Pre-condition failure"); - - test(expected, size, fmt); - fmt.remove_suffix(1); - test(expected, size, fmt); -} - -template -constexpr void test() { - Parser parser; - - assert(parser.__fill == CharT(' ')); - assert(parser.__alignment == _Flags::_Alignment::__default); - assert(parser.__sign == _Flags::_Sign::__default); - assert(parser.__alternate_form == false); - assert(parser.__zero_padding == false); - assert(parser.__width == 0); - assert(parser.__width_as_arg == false); - static_assert(!has_precision); - static_assert(!has_precision_as_arg); - assert(parser.__locale_specific_form == false); - assert(parser.__type == _Flags::_Type::__default); - - test({}, 0, CSTR("}")); - test({}, 1, CSTR("d}")); - test({.alignment = _Flags::_Alignment::__left, .type = _Flags::_Type::__char}, - 1, CSTR("c}")); - - // *** Align-fill *** - test({.alignment = _Flags::_Alignment::__left}, 1, CSTR("<}")); - test({.alignment = _Flags::_Alignment::__center}, 1, "^}"); - test({.alignment = _Flags::_Alignment::__right}, 1, ">}"); - - test({.fill = CharT('L'), .alignment = _Flags::_Alignment::__left}, 2, - CSTR("L<}")); - test({.fill = CharT('#'), .alignment = _Flags::_Alignment::__center}, 2, - CSTR("#^}")); - test({.fill = CharT('0'), .alignment = _Flags::_Alignment::__right}, 2, - CSTR("0>}")); - - test_exception>( - "The format-spec fill field contains an invalid character", CSTR("{<")); - test_exception>( - "The format-spec fill field contains an invalid character", CSTR("}<")); - - // *** Sign *** - test({.sign = _Flags::_Sign::__minus}, 1, CSTR("-}")); - test({.sign = _Flags::_Sign::__plus}, 1, CSTR("+}")); - test({.sign = _Flags::_Sign::__space}, 1, CSTR(" }")); - - test({.sign = _Flags::_Sign::__minus}, 2, CSTR("-d}")); - test({.sign = _Flags::_Sign::__plus}, 2, CSTR("+d}")); - test({.sign = _Flags::_Sign::__space}, 2, CSTR(" d}")); - - test_exception>( - "A sign field isn't allowed in this format-spec", CSTR("-c")); - test_exception>( - "A sign field isn't allowed in this format-spec", CSTR("+c")); - test_exception>( - "A sign field isn't allowed in this format-spec", CSTR(" c")); - - // *** Alternate form *** - test({.alternate_form = true}, 1, CSTR("#}")); - test({.alternate_form = true}, 2, CSTR("#d}")); - test_exception>( - "An alternate form field isn't allowed in this format-spec", CSTR("#c")); - - // *** Zero padding *** - // TODO FMT What to do with zero-padding without a width? - // [format.string.std]/13 - // A zero (0) character preceding the width field pads the field with - // leading zeros (following any indication of sign or base) to the field - // width, except when applied to an infinity or NaN. - // Obviously it makes no sense, but should it be allowed or is it a format - // errror? - test({.alignment = _Flags::_Alignment::__default, .zero_padding = true}, 1, - CSTR("0}")); - test({.alignment = _Flags::_Alignment::__left, .zero_padding = false}, 2, - CSTR("<0}")); - test({.alignment = _Flags::_Alignment::__center, .zero_padding = false}, 2, - CSTR("^0}")); - test({.alignment = _Flags::_Alignment::__right, .zero_padding = false}, 2, - CSTR(">0}")); - - test({.alignment = _Flags::_Alignment::__default, .zero_padding = true}, 2, - CSTR("0d}")); - test({.alignment = _Flags::_Alignment::__left, .zero_padding = false}, 3, - CSTR("<0d}")); - test({.alignment = _Flags::_Alignment::__center, .zero_padding = false}, 3, - CSTR("^0d}")); - test({.alignment = _Flags::_Alignment::__right, .zero_padding = false}, 3, - CSTR(">0d}")); - - test_exception>( - "A zero-padding field isn't allowed in this format-spec", CSTR("0c")); - test_exception>( - "A zero-padding field isn't allowed in this format-spec", CSTR("<0c")); - test_exception>( - "A zero-padding field isn't allowed in this format-spec", CSTR("^0c")); - test_exception>( - "A zero-padding field isn't allowed in this format-spec", CSTR(">0c")); - - // *** Width *** - test({.width = 0, .width_as_arg = false}, 0, CSTR("}")); - test({.width = 1, .width_as_arg = false}, 1, CSTR("1}")); - test({.width = 10, .width_as_arg = false}, 2, CSTR("10}")); - test({.width = 1000, .width_as_arg = false}, 4, CSTR("1000}")); - test({.width = 1000000, .width_as_arg = false}, 7, CSTR("1000000}")); - - test({.width = 0, .width_as_arg = true}, 2, CSTR("{}}")); - test({.width = 0, .width_as_arg = true}, 3, CSTR("{0}}")); - test({.width = 1, .width_as_arg = true}, 3, CSTR("{1}}")); - - test_exception>( - "A format-spec width field shouldn't have a leading zero", CSTR("00")); - - static_assert(std::__format::__number_max == 2'147'483'647, - "Update the assert and the test."); - test({.width = 2'147'483'647, .width_as_arg = false}, 10, - CSTR("2147483647}")); - test_exception>( - "The numeric value of the format-spec is too large", CSTR("2147483648")); - test_exception>( - "The numeric value of the format-spec is too large", CSTR("5000000000")); - test_exception>( - "The numeric value of the format-spec is too large", CSTR("10000000000")); - - test_exception>("End of input while parsing format-spec arg-id", - CSTR("{")); - test_exception>("Invalid arg-id", CSTR("{0")); - test_exception>( - "The arg-id of the format-spec starts with an invalid character", - CSTR("{a")); - test_exception>("Invalid arg-id", CSTR("{1")); - test_exception>("Invalid arg-id", CSTR("{9")); - test_exception>("Invalid arg-id", CSTR("{9:")); - test_exception>("Invalid arg-id", CSTR("{9a")); - - static_assert(std::__format::__number_max == 2'147'483'647, - "Update the assert and the test."); - // Note the static_assert tests whether the arg-id is valid. - // Therefore the following should be true arg-id < __format::__number_max. - test({.width = 2'147'483'646, .width_as_arg = true}, 12, - CSTR("{2147483646}}")); - test_exception>( - "The numeric value of the format-spec is too large", - CSTR("{2147483648}")); - test_exception>( - "The numeric value of the format-spec is too large", - CSTR("{5000000000}")); - test_exception>( - "The numeric value of the format-spec is too large", - CSTR("{10000000000}")); - - // *** Precision *** - test_exception>( - "The format-spec should consume the input or end with a '}'", CSTR(".")); - test_exception>( - "The format-spec should consume the input or end with a '}'", CSTR(".1")); - - // *** Locale-specific form *** - test({.locale_specific_form = true}, 1, CSTR("L}")); - test({.locale_specific_form = true}, 2, CSTR("Ld}")); - // Note the flag is allowed, but has no effect. - test({.alignment = _Flags::_Alignment::__left, - .locale_specific_form = true, - .type = _Flags::_Type::__char}, - 2, CSTR("Lc}")); - - // *** Type *** - { - const char* unsuported_type = - "The format-spec type has a type not supported for an integer argument"; - const char* not_a_type = - "The format-spec should consume the input or end with a '}'"; - - test_exception>(unsuported_type, CSTR("A}")); - test({.type = _Flags::_Type::__binary_upper_case}, 1, CSTR("B}")); - test_exception>(not_a_type, CSTR("C}")); - test_exception>(not_a_type, CSTR("D}")); - test_exception>(unsuported_type, CSTR("E}")); - test_exception>(unsuported_type, CSTR("F}")); - test_exception>(unsuported_type, CSTR("G}")); - test_exception>(not_a_type, CSTR("H}")); - test_exception>(not_a_type, CSTR("I}")); - test_exception>(not_a_type, CSTR("J}")); - test_exception>(not_a_type, CSTR("K}")); - test({.locale_specific_form = true}, 1, CSTR("L}")); - test_exception>(not_a_type, CSTR("M}")); - test_exception>(not_a_type, CSTR("N}")); - test_exception>(not_a_type, CSTR("O}")); - test_exception>(not_a_type, CSTR("P}")); - test_exception>(not_a_type, CSTR("Q}")); - test_exception>(not_a_type, CSTR("R}")); - test_exception>(not_a_type, CSTR("S}")); - test_exception>(not_a_type, CSTR("T}")); - test_exception>(not_a_type, CSTR("U}")); - test_exception>(not_a_type, CSTR("V}")); - test_exception>(not_a_type, CSTR("W}")); - test({.type = _Flags::_Type::__hexadecimal_upper_case}, 1, CSTR("X}")); - test_exception>(not_a_type, CSTR("Y}")); - test_exception>(not_a_type, CSTR("Z}")); - - test_exception>(unsuported_type, CSTR("a}")); - test({.type = _Flags::_Type::__binary_lower_case}, 1, CSTR("b}")); - test({.alignment = _Flags::_Alignment::__left, - .type = _Flags::_Type::__char}, - 1, CSTR("c}")); - test({.type = _Flags::_Type::__decimal}, 1, CSTR("d}")); - test_exception>(unsuported_type, CSTR("e}")); - test_exception>(unsuported_type, CSTR("f}")); - test_exception>(unsuported_type, CSTR("g}")); - test_exception>(not_a_type, CSTR("h}")); - test_exception>(not_a_type, CSTR("i}")); - test_exception>(not_a_type, CSTR("j}")); - test_exception>(not_a_type, CSTR("k}")); - test_exception>(not_a_type, CSTR("l}")); - test_exception>(not_a_type, CSTR("m}")); - test_exception>(not_a_type, CSTR("n}")); - test({.type = _Flags::_Type::__octal}, 1, CSTR("o}")); - test_exception>(unsuported_type, CSTR("p}")); - test_exception>(not_a_type, CSTR("q}")); - test_exception>(not_a_type, CSTR("r}")); - test_exception>(unsuported_type, CSTR("s}")); - test_exception>(not_a_type, CSTR("t}")); - test_exception>(not_a_type, CSTR("u}")); - test_exception>(not_a_type, CSTR("v}")); - test_exception>(not_a_type, CSTR("w}")); - test({.type = _Flags::_Type::__hexadecimal_lower_case}, 1, CSTR("x}")); - test_exception>(not_a_type, CSTR("y}")); - test_exception>(not_a_type, CSTR("z}")); - } - // **** General *** - test_exception>( - "The format-spec should consume the input or end with a '}'", CSTR("ss")); -} - -constexpr bool test() { - test(); -#ifndef TEST_HAS_NO_WIDE_CHARACTERS - test(); -#endif - - return true; -} - -int main(int, char**) { -#ifndef _WIN32 - // Make sure the parsers match the expectations. The layout of the - // subobjects is chosen to minimize the size required. - static_assert(sizeof(Parser) == 2 * sizeof(uint32_t)); -#ifndef TEST_HAS_NO_WIDE_CHARACTERS - static_assert( - sizeof(Parser) == - (sizeof(wchar_t) <= 2 ? 2 * sizeof(uint32_t) : 3 * sizeof(uint32_t))); -#endif -#endif // _WIN32 - - test(); - static_assert(test()); - - return 0; -}