diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -221,9 +221,12 @@ __format/formatter_floating_point.h __format/formatter_integer.h __format/formatter_integral.h + __format/formatter_output.h __format/formatter_pointer.h __format/formatter_string.h + __format/formatter_v2.h __format/parser_std_format_spec.h + __format/parser_std_format_spec_v2.h __functional/binary_function.h __functional/binary_negate.h __functional/bind.h diff --git a/libcxx/include/__format/buffer.h b/libcxx/include/__format/buffer.h --- a/libcxx/include/__format/buffer.h +++ b/libcxx/include/__format/buffer.h @@ -14,7 +14,7 @@ #include <__algorithm/unwrap_iter.h> #include <__config> #include <__format/enable_insertable.h> -#include <__format/formatter.h> // for __char_type TODO FMT Move the concept? +#include <__format/format_fwd.h> // for __char_type TODO FMT Move the concept? #include <__iterator/back_insert_iterator.h> #include <__iterator/concepts.h> #include <__iterator/iterator_traits.h> diff --git a/libcxx/include/__format/format_arg.h b/libcxx/include/__format/format_arg.h --- a/libcxx/include/__format/format_arg.h +++ b/libcxx/include/__format/format_arg.h @@ -10,7 +10,6 @@ #ifndef _LIBCPP___FORMAT_FORMAT_ARG_H #define _LIBCPP___FORMAT_FORMAT_ARG_H -#include <__assert> #include <__concepts/arithmetic.h> #include <__config> #include <__format/format_error.h> @@ -84,7 +83,7 @@ #ifndef _LIBCPP_HAS_NO_INT128 return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__u128); #else - __libcpp_unreachable(); + __libcpp_unreachable(); #endif case __format::__arg_t::__float: return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__float); diff --git a/libcxx/include/__format/format_fwd.h b/libcxx/include/__format/format_fwd.h --- a/libcxx/include/__format/format_fwd.h +++ b/libcxx/include/__format/format_fwd.h @@ -11,6 +11,7 @@ #define _LIBCPP___FORMAT_FORMAT_FWD_H #include <__availability> +#include <__concepts/same_as.h> #include <__config> #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) @@ -30,6 +31,14 @@ template struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter; +namespace __formatter { + +/** The character types that formatters are specialized for. */ +template +concept __char_type = same_as<_CharT, char> || same_as<_CharT, wchar_t>; + +} // namespace __formatter + #endif //_LIBCPP_STD_VER > 17 _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__format/format_string.h b/libcxx/include/__format/format_string.h --- a/libcxx/include/__format/format_string.h +++ b/libcxx/include/__format/format_string.h @@ -10,8 +10,8 @@ #ifndef _LIBCPP___FORMAT_FORMAT_STRING_H #define _LIBCPP___FORMAT_FORMAT_STRING_H -#include <__assert> #include <__config> +#include <__debug> #include <__format/format_error.h> #include #include diff --git a/libcxx/include/__format/formatter.h b/libcxx/include/__format/formatter.h --- a/libcxx/include/__format/formatter.h +++ b/libcxx/include/__format/formatter.h @@ -6,23 +6,22 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// - +#if 0 #ifndef _LIBCPP___FORMAT_FORMATTER_H #define _LIBCPP___FORMAT_FORMATTER_H -#include <__algorithm/copy.h> -#include <__algorithm/fill_n.h> -#include <__algorithm/transform.h> -#include <__assert> -#include <__availability> -#include <__config> -#include <__format/format_error.h> -#include <__format/format_fwd.h> -#include <__format/format_string.h> -#include <__format/parser_std_format_spec.h> -#include <__utility/move.h> -#include <__utility/unreachable.h> -#include +//#include <__algorithm/copy.h> +//#include <__algorithm/fill_n.h> +//#include <__availability> +//#include <__config> +//#include <__format/format_error.h> +//#include <__format/format_fwd.h> +//#include <__format/format_string.h> +//#include <__format/formatter_integral.h> +//#include <__format/parser_std_format_spec.h> +//#include <__format/parser_std_format_spec_v2.h> +//#include +//#include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header @@ -32,249 +31,13 @@ #if _LIBCPP_STD_VER > 17 -/// The default formatter template. /// -/// [format.formatter.spec]/5 -/// If F is a disabled specialization of formatter, these values are false: -/// - is_default_constructible_v, -/// - is_copy_constructible_v, -/// - is_move_constructible_v, -/// - is_copy_assignable, and -/// - is_move_assignable. -template -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter { - formatter() = delete; - formatter(const formatter&) = delete; - formatter& operator=(const formatter&) = delete; -}; - -namespace __format_spec { - -_LIBCPP_HIDE_FROM_ABI inline char* __insert_sign(char* __buf, bool __negative, - _Flags::_Sign __sign) { - if (__negative) - *__buf++ = '-'; - else - switch (__sign) { - case _Flags::_Sign::__default: - case _Flags::_Sign::__minus: - // No sign added. - break; - case _Flags::_Sign::__plus: - *__buf++ = '+'; - break; - case _Flags::_Sign::__space: - *__buf++ = ' '; - break; - } - - return __buf; -} - -_LIBCPP_HIDE_FROM_ABI constexpr char __hex_to_upper(char c) { - switch (c) { - case 'a': - return 'A'; - case 'b': - return 'B'; - case 'c': - return 'C'; - case 'd': - return 'D'; - case 'e': - return 'E'; - case 'f': - return 'F'; - } - return c; -} - -} // namespace __format_spec - -namespace __formatter { - -/** The character types that formatters are specialized for. */ -template -concept __char_type = same_as<_CharT, char> || same_as<_CharT, wchar_t>; - -struct _LIBCPP_TEMPLATE_VIS __padding_size_result { - size_t __before; - size_t __after; -}; - -_LIBCPP_HIDE_FROM_ABI constexpr __padding_size_result -__padding_size(size_t __size, size_t __width, - __format_spec::_Flags::_Alignment __align) { - _LIBCPP_ASSERT(__width > __size, - "Don't call this function when no padding is required"); - _LIBCPP_ASSERT( - __align != __format_spec::_Flags::_Alignment::__default, - "Caller should adjust the default to the value required by the type"); - - size_t __fill = __width - __size; - switch (__align) { - case __format_spec::_Flags::_Alignment::__default: - __libcpp_unreachable(); - - case __format_spec::_Flags::_Alignment::__left: - return {0, __fill}; - - case __format_spec::_Flags::_Alignment::__center: { - // The extra padding is divided per [format.string.std]/3 - // __before = floor(__fill, 2); - // __after = ceil(__fill, 2); - size_t __before = __fill / 2; - size_t __after = __fill - __before; - return {__before, __after}; - } - case __format_spec::_Flags::_Alignment::__right: - return {__fill, 0}; - } - __libcpp_unreachable(); -} - -/** - * Writes the input to the output with the required padding. - * - * Since the output column width is specified the function can be used for - * ASCII and Unicode input. - * - * @pre [@a __first, @a __last) is a valid range. - * @pre @a __size <= @a __width. Using this function when this pre-condition - * doesn't hold incurs an unwanted overhead. - * - * @param __out_it The output iterator to write to. - * @param __first Pointer to the first element to write. - * @param __last Pointer beyond the last element to write. - * @param __size The (estimated) output column width. When the elements - * to be written are ASCII the following condition holds - * @a __size == @a __last - @a __first. - * @param __width The number of output columns to write. - * @param __fill The character used for the alignment of the output. - * TODO FMT Will probably change to support Unicode grapheme - * cluster. - * @param __alignment The requested alignment. - * - * @returns An iterator pointing beyond the last element written. - * - * @note The type of the elements in range [@a __first, @a __last) can differ - * from the type of @a __fill. Integer output uses @c std::to_chars for its - * conversion, which means the [@a __first, @a __last) always contains elements - * of the type @c char. - */ -template -_LIBCPP_HIDE_FROM_ABI auto -__write(output_iterator auto __out_it, const _CharT* __first, - const _CharT* __last, size_t __size, size_t __width, _Fill __fill, - __format_spec::_Flags::_Alignment __alignment) -> decltype(__out_it) { - - _LIBCPP_ASSERT(__first <= __last, "Not a valid range"); - _LIBCPP_ASSERT(__size < __width, "Precondition failure"); - - __padding_size_result __padding = - __padding_size(__size, __width, __alignment); - __out_it = _VSTD::fill_n(_VSTD::move(__out_it), __padding.__before, __fill); - __out_it = _VSTD::copy(__first, __last, _VSTD::move(__out_it)); - return _VSTD::fill_n(_VSTD::move(__out_it), __padding.__after, __fill); -} - -/** - * @overload - * - * Writes additional zero's for the precision before the exponent. - * This is used when the precision requested in the format string is larger - * than the maximum precision of the floating-point type. These precision - * digits are always 0. - * - * @param __exponent The location of the exponent character. - * @param __num_trailing_zeros The number of 0's to write before the exponent - * character. - */ -template -_LIBCPP_HIDE_FROM_ABI auto __write(output_iterator auto __out_it, const _CharT* __first, - const _CharT* __last, size_t __size, size_t __width, _Fill __fill, - __format_spec::_Flags::_Alignment __alignment, const _CharT* __exponent, - size_t __num_trailing_zeros) -> decltype(__out_it) { - _LIBCPP_ASSERT(__first <= __last, "Not a valid range"); - _LIBCPP_ASSERT(__num_trailing_zeros > 0, "The overload not writing trailing zeros should have been used"); - - __padding_size_result __padding = __padding_size(__size + __num_trailing_zeros, __width, __alignment); - __out_it = _VSTD::fill_n(_VSTD::move(__out_it), __padding.__before, __fill); - __out_it = _VSTD::copy(__first, __exponent, _VSTD::move(__out_it)); - __out_it = _VSTD::fill_n(_VSTD::move(__out_it), __num_trailing_zeros, _CharT('0')); - __out_it = _VSTD::copy(__exponent, __last, _VSTD::move(__out_it)); - return _VSTD::fill_n(_VSTD::move(__out_it), __padding.__after, __fill); -} - -/** - * @overload - * - * Uses a transformation operation before writing an element. - * - * TODO FMT Fill will probably change to support Unicode grapheme cluster. - */ -template -_LIBCPP_HIDE_FROM_ABI auto -__write(output_iterator auto __out_it, const _CharT* __first, - const _CharT* __last, size_t __size, _UnaryOperation __op, - size_t __width, _Fill __fill, - __format_spec::_Flags::_Alignment __alignment) -> decltype(__out_it) { - - _LIBCPP_ASSERT(__first <= __last, "Not a valid range"); - _LIBCPP_ASSERT(__size < __width, "Precondition failure"); - - __padding_size_result __padding = - __padding_size(__size, __width, __alignment); - __out_it = _VSTD::fill_n(_VSTD::move(__out_it), __padding.__before, __fill); - __out_it = _VSTD::transform(__first, __last, _VSTD::move(__out_it), __op); - return _VSTD::fill_n(_VSTD::move(__out_it), __padding.__after, __fill); -} - -/** - * Writes Unicode input to the output with the required padding. - * - * This function does almost the same as the @ref __write function, but handles - * the width estimation of the Unicode input. - * - * @param __str The range [@a __first, @a __last). - * @param __precision The width to truncate the input string to, use @c -1 for - * no limit. - */ -template -_LIBCPP_HIDE_FROM_ABI auto -__write_unicode(output_iterator auto __out_it, - basic_string_view<_CharT> __str, ptrdiff_t __width, - ptrdiff_t __precision, _Fill __fill, - __format_spec::_Flags::_Alignment __alignment) - -> decltype(__out_it) { - - // This value changes when there Unicode column width limits the output - // size. - auto __last = __str.end(); - if (__width != 0 || __precision != -1) { - __format_spec::__string_alignment<_CharT> __format_traits = - __format_spec::__get_string_alignment(__str.begin(), __str.end(), - __width, __precision); - - if (__format_traits.__align) - return __write(_VSTD::move(__out_it), __str.begin(), - __format_traits.__last, __format_traits.__size, __width, - __fill, __alignment); - - // No alignment required update the output based on the precision. - // This might be the same as __str.end(). - __last = __format_traits.__last; - } - - // Copy the input to the output. The output size might be limited by the - // precision. - return _VSTD::copy(__str.begin(), __last, _VSTD::move(__out_it)); -} - -} // namespace __formatter +// +// #endif //_LIBCPP_STD_VER > 17 _LIBCPP_END_NAMESPACE_STD #endif // _LIBCPP___FORMAT_FORMATTER_H +#endif 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 @@ -10,18 +10,7 @@ #ifndef _LIBCPP___FORMAT_FORMATTER_BOOL_H #define _LIBCPP___FORMAT_FORMATTER_BOOL_H -#include <__availability> #include <__config> -#include <__format/format_error.h> -#include <__format/format_fwd.h> -#include <__format/formatter.h> -#include <__format/formatter_integral.h> -#include <__format/parser_std_format_spec.h> -#include - -#ifndef _LIBCPP_HAS_NO_LOCALIZATION -#include -#endif #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header @@ -31,103 +20,6 @@ #if _LIBCPP_STD_VER > 17 -namespace __format_spec { - -template -class _LIBCPP_TEMPLATE_VIS __parser_bool : 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::__string; - [[fallthrough]]; - case _Flags::_Type::__string: - this->__handle_bool(); - 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 bool argument"); - } - - 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); - } -}; - #endif //_LIBCPP_STD_VER > 17 _LIBCPP_END_NAMESPACE_STD 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 @@ -10,13 +10,7 @@ #ifndef _LIBCPP___FORMAT_FORMATTER_CHAR_H #define _LIBCPP___FORMAT_FORMATTER_CHAR_H -#include <__availability> #include <__config> -#include <__format/format_error.h> -#include <__format/format_fwd.h> -#include <__format/formatter.h> -#include <__format/formatter_integral.h> -#include <__format/parser_std_format_spec.h> #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header @@ -26,70 +20,6 @@ #if _LIBCPP_STD_VER > 17 -namespace __format_spec { - -template -class _LIBCPP_TEMPLATE_VIS __parser_char : 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::__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; - } -}; - -template -using __formatter_char = __formatter_integral<__parser_char<_CharT>>; - -} // namespace __format_spec - -// [format.formatter.spec]/2.1 The specializations - -template <> -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter - : public __format_spec::__formatter_char {}; - -#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; - - _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 __format_spec::__formatter_char {}; -#endif // _LIBCPP_HAS_NO_WIDE_CHARACTERS - #endif //_LIBCPP_STD_VER > 17 _LIBCPP_END_NAMESPACE_STD 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 @@ -10,26 +10,18 @@ #ifndef _LIBCPP___FORMAT_FORMATTER_FLOATING_POINT_H #define _LIBCPP___FORMAT_FORMATTER_FLOATING_POINT_H -#include <__algorithm/copy.h> -#include <__algorithm/copy_n.h> -#include <__algorithm/fill_n.h> #include <__algorithm/find.h> #include <__algorithm/min.h> #include <__algorithm/rotate.h> #include <__algorithm/transform.h> -#include <__assert> -#include <__concepts/arithmetic.h> #include <__config> -#include <__format/format_error.h> -#include <__format/format_fwd.h> -#include <__format/format_string.h> -#include <__format/formatter.h> #include <__format/formatter_integral.h> -#include <__format/parser_std_format_spec.h> -#include <__utility/move.h> +#include <__format/formatter_output.h> +#include <__format/parser_std_format_spec_v2.h> +#include <__memory/allocator.h> #include <__utility/unreachable.h> #include -#include +#include #ifndef _LIBCPP_HAS_NO_LOCALIZATION # include @@ -46,7 +38,7 @@ #if _LIBCPP_STD_VER > 17 -namespace __format_spec { +namespace __formatter { template _LIBCPP_HIDE_FROM_ABI char* __to_buffer(char* __first, char* __last, _Tp __value) { @@ -162,7 +154,7 @@ __precision_ = _Traits::__max_fractional; } - __size_ = __format_spec::__float_buffer_size<_Fp>(__precision_); + __size_ = __formatter::__float_buffer_size<_Fp>(__precision_); if (__size_ > _Traits::__stack_buffer_size) // The allocated buffer's contents don't need initialization. __begin_ = allocator{}.allocate(__size_); @@ -231,9 +223,9 @@ char* __integral) { __float_result __result; __result.__integral = __integral; - __result.__last = __format_spec::__to_buffer(__integral, __buffer.end(), __value); + __result.__last = __formatter::__to_buffer(__integral, __buffer.end(), __value); - __result.__exponent = __format_spec::__find_exponent(__result.__integral, __result.__last); + __result.__exponent = __formatter::__find_exponent(__result.__integral, __result.__last); // Constrains: // - There's at least one decimal digit before the radix point. @@ -262,9 +254,9 @@ __float_result __result; __result.__integral = __integral; if (__precision == -1) - __result.__last = __format_spec::__to_buffer(__integral, __buffer.end(), __value, chars_format::hex); + __result.__last = __formatter::__to_buffer(__integral, __buffer.end(), __value, chars_format::hex); else - __result.__last = __format_spec::__to_buffer(__integral, __buffer.end(), __value, chars_format::hex, __precision); + __result.__last = __formatter::__to_buffer(__integral, __buffer.end(), __value, chars_format::hex, __precision); // H = one or more hex-digits // S = sign @@ -313,7 +305,7 @@ _Tp __value, int __precision, char* __integral) { __float_result __result = - __format_spec::__format_buffer_hexadecimal_lower_case(__buffer, __value, __precision, __integral); + __formatter::__format_buffer_hexadecimal_lower_case(__buffer, __value, __precision, __integral); _VSTD::transform(__result.__integral, __result.__exponent, __result.__integral, __hex_to_upper); *__result.__exponent = 'P'; return __result; @@ -326,13 +318,13 @@ __float_result __result; __result.__integral = __integral; __result.__last = - __format_spec::__to_buffer(__integral, __buffer.end(), __value, chars_format::scientific, __precision); + __formatter::__to_buffer(__integral, __buffer.end(), __value, chars_format::scientific, __precision); char* __first = __integral + 1; _LIBCPP_ASSERT(__first != __result.__last, "No exponent present"); if (*__first == '.') { __result.__radix_point = __first; - __result.__exponent = __format_spec::__find_exponent(__first + 1, __result.__last); + __result.__exponent = __formatter::__find_exponent(__first + 1, __result.__last); } else { __result.__radix_point = __result.__last; __result.__exponent = __first; @@ -352,7 +344,7 @@ _Tp __value, int __precision, char* __integral) { __float_result __result = - __format_spec::__format_buffer_scientific_lower_case(__buffer, __value, __precision, __integral); + __formatter::__format_buffer_scientific_lower_case(__buffer, __value, __precision, __integral); *__result.__exponent = 'E'; return __result; } @@ -362,7 +354,7 @@ int __precision, char* __integral) { __float_result __result; __result.__integral = __integral; - __result.__last = __format_spec::__to_buffer(__integral, __buffer.end(), __value, chars_format::fixed, __precision); + __result.__last = __formatter::__to_buffer(__integral, __buffer.end(), __value, chars_format::fixed, __precision); // When there's no precision there's no radix point. // Else the radix point is placed at __precision + 1 from the end. @@ -388,14 +380,14 @@ __float_result __result; __result.__integral = __integral; - __result.__last = __format_spec::__to_buffer(__integral, __buffer.end(), __value, chars_format::general, __precision); + __result.__last = __formatter::__to_buffer(__integral, __buffer.end(), __value, chars_format::general, __precision); char* __first = __integral + 1; if (__first == __result.__last) { __result.__radix_point = __result.__last; __result.__exponent = __result.__last; } else { - __result.__exponent = __format_spec::__find_exponent(__first, __result.__last); + __result.__exponent = __formatter::__find_exponent(__first, __result.__last); if (__result.__exponent != __result.__last) // In scientific mode if there's a radix point it will always be after // the first digit. (This is the position __first points at). @@ -421,19 +413,72 @@ template _LIBCPP_HIDE_FROM_ABI __float_result __format_buffer_general_upper_case(__float_buffer<_Fp>& __buffer, _Tp __value, int __precision, char* __integral) { - __float_result __result = - __format_spec::__format_buffer_general_lower_case(__buffer, __value, __precision, __integral); + __float_result __result = __formatter::__format_buffer_general_lower_case(__buffer, __value, __precision, __integral); if (__result.__exponent != __result.__last) *__result.__exponent = 'E'; return __result; } +/// Fills the buffer with the data based on the requested formatting. +/// +/// This function, when needed, turns the characters to upper case and +/// determines the "interesting" locations which are returned to the caller. +/// +/// This means the caller never has to convert the contents of the buffer to +/// upper case or search for radix points and the location of the exponent. +/// This gives a bit of overhead. The original code didn't do that, but due +/// to the number of possible additional work needed to turn this number to +/// the proper output the code was littered with tests for upper cases and +/// searches for radix points and exponents. +/// - When a precision larger than the type's precision is selected +/// additional zero characters need to be written before the exponent. +/// - alternate form needs to add a radix point when not present. +/// - localization needs to do grouping in the integral part. +template +// TODO FMT _Fp should just be _Tp when to_chars has proper long double support. +_LIBCPP_HIDE_FROM_ABI __float_result __format_buffer(__float_buffer<_Fp>& __buffer, _Tp __value, bool __negative, + bool __has_precision, __format_spec::__sign __sign, + __format_spec::__type __type) { + char* __first = __insert_sign(__buffer.begin(), __negative, __sign); + switch (__type) { + case __format_spec::__type::__default: + return __formatter::__format_buffer_default(__buffer, __value, __first); + + case __format_spec::__type::__float_hexadecimal_lower_case: + return __formatter::__format_buffer_hexadecimal_lower_case(__buffer, __value, + __has_precision ? __buffer.__precision() : -1, __first); + + case __format_spec::__type::__float_hexadecimal_upper_case: + return __formatter::__format_buffer_hexadecimal_upper_case(__buffer, __value, + __has_precision ? __buffer.__precision() : -1, __first); + + case __format_spec::__type::__scientific_lower_case: + return __formatter::__format_buffer_scientific_lower_case(__buffer, __value, __buffer.__precision(), __first); + + case __format_spec::__type::__scientific_upper_case: + return __formatter::__format_buffer_scientific_upper_case(__buffer, __value, __buffer.__precision(), __first); + + case __format_spec::__type::__fixed_lower_case: + case __format_spec::__type::__fixed_upper_case: + return __formatter::__format_buffer_fixed(__buffer, __value, __buffer.__precision(), __first); + + case __format_spec::__type::__general_lower_case: + return __formatter::__format_buffer_general_lower_case(__buffer, __value, __buffer.__precision(), __first); + + case __format_spec::__type::__general_upper_case: + return __formatter::__format_buffer_general_upper_case(__buffer, __value, __buffer.__precision(), __first); + + default: + _LIBCPP_ASSERT(false, "The parser should have validated the type"); + __libcpp_unreachable(); + } +} + # ifndef _LIBCPP_HAS_NO_LOCALIZATION template _LIBCPP_HIDE_FROM_ABI _OutIt __format_locale_specific_form(_OutIt __out_it, const __float_buffer<_Fp>& __buffer, const __float_result& __result, _VSTD::locale __loc, - size_t __width, _Flags::_Alignment __alignment, - _CharT __fill) { + __format_spec::__parser<_CharT> __parser) { const auto& __np = use_facet>(__loc); string __grouping = __np.grouping(); char* __first = __result.__integral; @@ -445,29 +490,29 @@ 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 - __buffer.__num_trailing_zeros() + // Not yet rendered zeros - __grouping.size() - // Grouping contains one - !__grouping.empty(); // additional character + ptrdiff_t __size = __result.__last - __buffer.begin() + // Formatted string + __buffer.__num_trailing_zeros() + // Not yet rendered zeros + __grouping.size() - // Grouping contains one + !__grouping.empty(); // additional character __formatter::__padding_size_result __padding = {0, 0}; - bool __zero_padding = __alignment == _Flags::_Alignment::__default; - if (__size < __width) { + bool __zero_padding = __parser.__alignment_ == __format_spec::__alignment::__default; + if (__size < __parser.__width_) { if (__zero_padding) { - __alignment = _Flags::_Alignment::__right; - __fill = _CharT('0'); + __parser.__alignment_ = __format_spec::__alignment::__right; + __parser.__fill_ = _CharT('0'); } - __padding = __formatter::__padding_size(__size, __width, __alignment); + __padding = __padding_size(__size, __parser.__width_, __parser.__alignment_); } // sign and (zero padding or alignment) if (__zero_padding && __first != __buffer.begin()) *__out_it++ = *__buffer.begin(); - __out_it = _VSTD::fill_n(_VSTD::move(__out_it), __padding.__before, __fill); + __out_it = _VSTD::fill_n(_VSTD::move(__out_it), __padding.__before_, __parser.__fill_); if (!__zero_padding && __first != __buffer.begin()) *__out_it++ = *__buffer.begin(); @@ -508,198 +553,120 @@ __out_it = _VSTD::copy(__result.__exponent, __result.__last, _VSTD::move(__out_it)); // alignment - return _VSTD::fill_n(_VSTD::move(__out_it), __padding.__after, __fill); + return _VSTD::fill_n(_VSTD::move(__out_it), __padding.__after_, __parser.__fill_); } - # endif // _LIBCPP_HAS_NO_LOCALIZATION -template <__formatter::__char_type _CharT> -class _LIBCPP_TEMPLATE_VIS __formatter_floating_point : public __parser_floating_point<_CharT> { -public: - 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)); - - bool __negative = _VSTD::signbit(__value); - - if (!_VSTD::isfinite(__value)) [[unlikely]] - return __format_non_finite(__ctx.out(), __negative, _VSTD::isnan(__value)); - - bool __has_precision = this->__has_precision_field(); - if (this->__precision_needs_substitution()) - this->__substitute_precision_arg_id(__ctx.arg(this->__precision)); - - // Depending on the std-format-spec string the sign and the value - // might not be outputted together: - // - zero-padding may insert additional '0' characters. - // Therefore the value is processed as a non negative value. - // The function @ref __insert_sign will insert a '-' when the value was - // negative. - - if (__negative) - __value = _VSTD::copysign(__value, +1.0); - - // TODO FMT _Fp should just be _Tp when to_chars has proper long double support. - using _Fp = conditional_t, double, _Tp>; - // Force the type of the precision to avoid -1 to become an unsigned value. - __float_buffer<_Fp> __buffer(__has_precision ? int(this->__precision) : -1); - __float_result __result = __format_buffer(__buffer, __value, __negative, __has_precision); - - if (this->__alternate_form && __result.__radix_point == __result.__last) { - *__result.__last++ = '.'; - - // When there is an exponent the point needs to be moved before the - // exponent. When there's no exponent the rotate does nothing. Since - // rotate tests whether the operation is a nop, call it unconditionally. - _VSTD::rotate(__result.__exponent, __result.__last - 1, __result.__last); - __result.__radix_point = __result.__exponent; - - // The radix point is always placed before the exponent. - // - No exponent needs to point to the new last. - // - An exponent needs to move one position to the right. - // So it's safe to increment the value unconditionally. - ++__result.__exponent; - } +template +_LIBCPP_HIDE_FROM_ABI _OutIt __format_floating_point_non_finite(_OutIt __out_it, + __format_spec::__parser<_CharT> __parser, + bool __negative, bool __isnan) { + char __buffer[4]; + char* __last = __formatter::__insert_sign(__buffer, __negative, __parser.__sign_); + + // to_char can return inf, infinity, nan, and nan(n-char-sequence). + // The format library requires inf and nan. + // All in one expression to avoid dangling references. + __last = + _VSTD::copy_n(&("infnanINFNAN"[6 * (__parser.__type_ == __format_spec::__type::__float_hexadecimal_upper_case || + __parser.__type_ == __format_spec::__type::__scientific_upper_case || + __parser.__type_ == __format_spec::__type::__fixed_upper_case || + __parser.__type_ == __format_spec::__type::__general_upper_case) + + 3 * __isnan]), + 3, __last); + + // [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. + if (__parser.__alignment_ == __format_spec::__alignment::__default) + __parser.__alignment_ = __format_spec::__alignment::__right; + + return __formatter::__write(__buffer, __last, _VSTD::move(__out_it), __parser); +} -# ifndef _LIBCPP_HAS_NO_LOCALIZATION - if (this->__locale_specific_form) - return __format_spec::__format_locale_specific_form(__ctx.out(), __buffer, __result, __ctx.locale(), - this->__width, this->__alignment, this->__fill); -# endif +template +_LIBCPP_HIDE_FROM_ABI auto __format_floating_point(_Tp __value, auto& __ctx, __format_spec::__parser<_CharT> __parser) + -> decltype(__ctx.out()) { - ptrdiff_t __size = __result.__last - __buffer.begin(); - int __num_trailing_zeros = __buffer.__num_trailing_zeros(); - if (__size + __num_trailing_zeros >= this->__width) { - if (__num_trailing_zeros && __result.__exponent != __result.__last) - // Insert trailing zeros before exponent character. - return _VSTD::copy(__result.__exponent, __result.__last, - _VSTD::fill_n(_VSTD::copy(__buffer.begin(), __result.__exponent, __ctx.out()), - __num_trailing_zeros, _CharT('0'))); - - return _VSTD::fill_n(_VSTD::copy(__buffer.begin(), __result.__last, __ctx.out()), __num_trailing_zeros, - _CharT('0')); - } + bool __negative = _VSTD::signbit(__value); - auto __out_it = __ctx.out(); - char* __first = __buffer.begin(); - if (this->__alignment == _Flags::_Alignment::__default) { - // When there is a sign output it before the padding. Note the __size - // doesn't need any adjustment, regardless whether the sign is written - // here or in __formatter::__write. - if (__first != __result.__integral) - *__out_it++ = *__first++; - // After the sign is written, zero padding is the same a right alignment - // with '0'. - this->__alignment = _Flags::_Alignment::__right; - this->__fill = _CharT('0'); - } + if (!_VSTD::isfinite(__value)) [[unlikely]] + return __formatter::__format_floating_point_non_finite(__ctx.out(), __parser, __negative, _VSTD::isnan(__value)); - if (__num_trailing_zeros) - return __formatter::__write(_VSTD::move(__out_it), __first, __result.__last, __size, this->__width, this->__fill, - this->__alignment, __result.__exponent, __num_trailing_zeros); + // Depending on the std-format-spec string the sign and the value + // might not be outputted together: + // - zero-padding may insert additional '0' characters. + // Therefore the value is processed as a non negative value. + // The function @ref __insert_sign will insert a '-' when the value was + // negative. - return __formatter::__write(_VSTD::move(__out_it), __first, __result.__last, __size, this->__width, this->__fill, - this->__alignment); - } + if (__negative) + __value = -__value; -private: - template - _LIBCPP_HIDE_FROM_ABI _OutIt __format_non_finite(_OutIt __out_it, bool __negative, bool __isnan) { - char __buffer[4]; - char* __last = __insert_sign(__buffer, __negative, this->__sign); - - // to_char can return inf, infinity, nan, and nan(n-char-sequence). - // The format library requires inf and nan. - // All in one expression to avoid dangling references. - __last = _VSTD::copy_n(&("infnanINFNAN"[6 * (this->__type == _Flags::_Type::__float_hexadecimal_upper_case || - this->__type == _Flags::_Type::__scientific_upper_case || - this->__type == _Flags::_Type::__fixed_upper_case || - this->__type == _Flags::_Type::__general_upper_case) + - 3 * __isnan]), - 3, __last); - - // [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. - if (this->__alignment == _Flags::_Alignment::__default) - this->__alignment = _Flags::_Alignment::__right; - - ptrdiff_t __size = __last - __buffer; - if (__size >= this->__width) - return _VSTD::copy_n(__buffer, __size, _VSTD::move(__out_it)); - - return __formatter::__write(_VSTD::move(__out_it), __buffer, __last, __size, this->__width, this->__fill, - this->__alignment); - } - - /// Fills the buffer with the data based on the requested formatting. - /// - /// This function, when needed, turns the characters to upper case and - /// determines the "interesting" locations which are returned to the caller. - /// - /// This means the caller never has to convert the contents of the buffer to - /// upper case or search for radix points and the location of the exponent. - /// This gives a bit of overhead. The original code didn't do that, but due - /// to the number of possible additional work needed to turn this number to - /// the proper output the code was littered with tests for upper cases and - /// searches for radix points and exponents. - /// - When a precision larger than the type's precision is selected - /// additional zero characters need to be written before the exponent. - /// - alternate form needs to add a radix point when not present. - /// - localization needs to do grouping in the integral part. - template // TODO FMT _Fp should just be _Tp when to_chars has proper long double support. - _LIBCPP_HIDE_FROM_ABI __float_result __format_buffer(__float_buffer<_Fp>& __buffer, _Tp __value, bool __negative, - bool __has_precision) { - char* __first = __insert_sign(__buffer.begin(), __negative, this->__sign); - switch (this->__type) { - case _Flags::_Type::__default: - return __format_spec::__format_buffer_default(__buffer, __value, __first); - - case _Flags::_Type::__float_hexadecimal_lower_case: - return __format_spec::__format_buffer_hexadecimal_lower_case( - __buffer, __value, __has_precision ? __buffer.__precision() : -1, __first); - - case _Flags::_Type::__float_hexadecimal_upper_case: - return __format_spec::__format_buffer_hexadecimal_upper_case( - __buffer, __value, __has_precision ? __buffer.__precision() : -1, __first); - - case _Flags::_Type::__scientific_lower_case: - return __format_spec::__format_buffer_scientific_lower_case(__buffer, __value, __buffer.__precision(), __first); + using _Fp = conditional_t, double, _Tp>; + // Force the type of the precision to avoid -1 to become an unsigned value. + __float_buffer<_Fp> __buffer(__parser.__precision_); + __float_result __result = __formatter::__format_buffer(__buffer, __value, __negative, (__parser.__has_precision()), + __parser.__sign_, __parser.__type_); + + if (__parser.__alternate_form_ && __result.__radix_point == __result.__last) { + *__result.__last++ = '.'; + + // When there is an exponent the point needs to be moved before the + // exponent. When there's no exponent the rotate does nothing. Since + // rotate tests whether the operation is a nop, call it unconditionally. + _VSTD::rotate(__result.__exponent, __result.__last - 1, __result.__last); + __result.__radix_point = __result.__exponent; + + // The radix point is always placed before the exponent. + // - No exponent needs to point to the new last. + // - An exponent needs to move one position to the right. + // So it's safe to increment the value unconditionally. + ++__result.__exponent; + } - case _Flags::_Type::__scientific_upper_case: - return __format_spec::__format_buffer_scientific_upper_case(__buffer, __value, __buffer.__precision(), __first); +# ifndef _LIBCPP_HAS_NO_LOCALIZATION + if (__parser.__locale_specific_form_) + return __formatter::__format_locale_specific_form(__ctx.out(), __buffer, __result, __ctx.locale(), __parser); +# endif - case _Flags::_Type::__fixed_lower_case: - case _Flags::_Type::__fixed_upper_case: - return __format_spec::__format_buffer_fixed(__buffer, __value, __buffer.__precision(), __first); + ptrdiff_t __size = __result.__last - __buffer.begin(); + int __num_trailing_zeros = __buffer.__num_trailing_zeros(); + if (__size + __num_trailing_zeros >= __parser.__width_) { + if (__num_trailing_zeros && __result.__exponent != __result.__last) + // Insert trailing zeros before exponent character. + return _VSTD::copy(__result.__exponent, __result.__last, + _VSTD::fill_n(_VSTD::copy(__buffer.begin(), __result.__exponent, __ctx.out()), + __num_trailing_zeros, _CharT('0'))); + + return _VSTD::fill_n(_VSTD::copy(__buffer.begin(), __result.__last, __ctx.out()), __num_trailing_zeros, + _CharT('0')); + } - case _Flags::_Type::__general_lower_case: - return __format_spec::__format_buffer_general_lower_case(__buffer, __value, __buffer.__precision(), __first); + auto __out_it = __ctx.out(); + char* __first = __buffer.begin(); + if (__parser.__alignment_ == __format_spec::__alignment ::__default) { + // When there is a sign output it before the padding. Note the __size + // doesn't need any adjustment, regardless whether the sign is written + // here or in __formatter::__write. + if (__first != __result.__integral) + *__out_it++ = *__first++; + // After the sign is written, zero padding is the same a right alignment + // with '0'. + __parser.__alignment_ = __format_spec::__alignment::__right; + __parser.__fill_ = _CharT('0'); + } - case _Flags::_Type::__general_upper_case: - return __format_spec::__format_buffer_general_upper_case(__buffer, __value, __buffer.__precision(), __first); + if (__num_trailing_zeros) + return __formatter::__write_using_trailing_zeros(__first, __result.__last, _VSTD::move(__out_it), __parser, __size, + __result.__exponent, __num_trailing_zeros); - default: - _LIBCPP_ASSERT(false, "The parser should have validated the type"); - __libcpp_unreachable(); - } - } -}; + return __formatter::__write(__first, __result.__last, _VSTD::move(__out_it), __parser, __size); +} -} //namespace __format_spec - -template <__formatter::__char_type _CharT> -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter - : public __format_spec::__formatter_floating_point<_CharT> {}; -template <__formatter::__char_type _CharT> -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter - : public __format_spec::__formatter_floating_point<_CharT> {}; -template <__formatter::__char_type _CharT> -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter - : public __format_spec::__formatter_floating_point<_CharT> {}; +} // namespace __formatter #endif //_LIBCPP_STD_VER > 17 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 @@ -10,14 +10,7 @@ #ifndef _LIBCPP___FORMAT_FORMATTER_INTEGER_H #define _LIBCPP___FORMAT_FORMATTER_INTEGER_H -#include <__availability> #include <__config> -#include <__format/format_error.h> -#include <__format/format_fwd.h> -#include <__format/formatter.h> -#include <__format/formatter_integral.h> -#include <__format/parser_std_format_spec.h> -#include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header @@ -30,129 +23,6 @@ #if _LIBCPP_STD_VER > 17 -namespace __format_spec { - -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; - } -}; - -template -using __formatter_integer = __formatter_integral<__parser_integer<_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 - -// Signed integral types. -template <__formatter::__char_type _CharT> -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT - formatter - : public __format_spec::__formatter_integer<_CharT> {}; -template <__formatter::__char_type _CharT> -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter - : public __format_spec::__formatter_integer<_CharT> {}; -template <__formatter::__char_type _CharT> -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter - : public __format_spec::__formatter_integer<_CharT> {}; -template <__formatter::__char_type _CharT> -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter - : public __format_spec::__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 -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>; - - _LIBCPP_HIDE_FROM_ABI auto format(__int128_t __value, auto& __ctx) - -> 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"); - - return _Base::format(static_cast<_To>(__value), __ctx); - } -}; -#endif - -// Unsigned integral types. -template <__formatter::__char_type _CharT> -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT - formatter - : public __format_spec::__formatter_integer<_CharT> {}; -template <__formatter::__char_type _CharT> -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT - formatter - : public __format_spec::__formatter_integer<_CharT> {}; -template <__formatter::__char_type _CharT> -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT - formatter - : public __format_spec::__formatter_integer<_CharT> {}; -template <__formatter::__char_type _CharT> -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT - formatter - : public __format_spec::__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 -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>; - - _LIBCPP_HIDE_FROM_ABI auto format(__uint128_t __value, auto& __ctx) - -> 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"); - - return _Base::format(static_cast<_To>(__value), __ctx); - } -}; -#endif - #endif //_LIBCPP_STD_VER > 17 _LIBCPP_END_NAMESPACE_STD 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,17 @@ #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 <__config> #include <__format/format_error.h> -#include <__format/format_fwd.h> -#include <__format/formatter.h> -#include <__format/parser_std_format_spec.h> +#include <__format/format_fwd.h> // for __char_type TODO FMT Move the concept? +#include <__format/formatter_output.h> +#include <__format/parser_std_format_spec_v2.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 +34,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 +75,8 @@ * @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) { /// XXX integral or arithmetic _LIBCPP_ASSERT(!__grouping.empty() && __size > __grouping[0], "The slow grouping formatting is used while there will be no " "separators written"); @@ -175,278 +106,236 @@ __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::__parser<_CharT> __parser) -> 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()) + __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"); } } -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), __parser); +} - 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 +_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. +} - 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 +_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. +} - return __formatter::__write(_VSTD::move(__out_it), __first, __last, - __size, this->__width, this->__fill, - this->__alignment); - } +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. +} - // 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 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. +} + +template +_LIBCPP_HIDE_FROM_ABI auto __format_integer(_Tp __value, auto& __ctx, __format_spec::__parser<_CharT> __parser, + bool __negative, char* __begin, char* __end, const char* __prefix, + int __base) -> decltype(__ctx.out()) { + char* __first = __formatter::__insert_sign(__begin, __negative, __parser.__sign_); + if (__parser.__alternate_form_ && __prefix) + while (*__prefix) + *__first++ = *__prefix++; + + char* __last = __formatter::__to_buffer(__first, __end, __value, __base); + +# ifndef _LIBCPP_HAS_NO_LOCALIZATION + if (__parser.__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::__format_grouping(__ctx.out(), __begin, __first, __last, + __formatter::__determine_grouping(__size, __grouping), __np.thousands_sep(), + __parser); + } +# endif + auto __out_it = __ctx.out(); + if (__parser.__alignment_ != __format_spec::__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)); + __parser.__alignment_ = __format_spec::__alignment::__right; + __parser.__fill_ = _CharT('0'); + int32_t __size = __first - __begin; + + __parser.__width_ -= _VSTD::min(__size, __parser.__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 (__parser.__type_ != __format_spec::__type::__hexadecimal_upper_case) [[likely]] + return __formatter::__write(__first, __last, __ctx.out(), __parser); + + return __formatter::__write_transformed(__first, __last, __ctx.out(), __parser, __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::__parser<_CharT> __parser, + bool __negative = false) -> decltype(__ctx.out()) { + + switch (__parser.__type_) { + case __format_spec::__type::__binary_lower_case: { + array()> __array; + return __formatter::__format_integer(__value, __ctx, __parser, __negative, __array.begin(), __array.end(), "0b", 2); + } + case __format_spec::__type::__binary_upper_case: { + array()> __array; + return __formatter::__format_integer(__value, __ctx, __parser, __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, __parser, __negative, __array.begin(), __array.end(), + __value != 0 ? "0" : nullptr, 8); + } + case __format_spec::__type::__decimal: { + array()> __array; + return __formatter::__format_integer(__value, __ctx, __parser, __negative, __array.begin(), __array.end(), nullptr, + 10); + } + case __format_spec::__type::__hexadecimal_lower_case: { + array()> __array; + return __formatter::__format_integer(__value, __ctx, __parser, __negative, __array.begin(), __array.end(), "0x", + 16); + } + case __format_spec::__type::__hexadecimal_upper_case: { + array()> __array; + return __formatter::__format_integer(__value, __ctx, __parser, __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::__parser<_CharT> __parser) + -> 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 = __to_unsigned_like(__value); + bool __negative = __value < 0; + if (__negative) + __r = __complement(__r); + + return __formatter::__format_integer(__r, __ctx, __parser, __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::__parser<_CharT> __parser) { + +# ifndef _LIBCPP_HAS_NO_LOCALIZATION + if (__parser.__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(), __parser); + } +# 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(), __parser); +} -} // namespace __format_spec +} // namespace __formatter #endif //_LIBCPP_STD_VER > 17 diff --git a/libcxx/include/__format/formatter_output.h b/libcxx/include/__format/formatter_output.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__format/formatter_output.h @@ -0,0 +1,362 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___FORMAT_FORMATTER_OUTPUT_H +#define _LIBCPP___FORMAT_FORMATTER_OUTPUT_H + +#include <__algorithm/copy.h> +#include <__algorithm/copy_n.h> +#include <__algorithm/fill_n.h> +#include <__algorithm/transform.h> +#include <__concepts/same_as.h> +#include <__config> +#include <__format/format_string.h> +#include <__format/parser_std_format_spec.h> +#include <__format/parser_std_format_spec_v2.h> +#include <__utility/unreachable.h> +#include +#include +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 17 + +namespace __formatter { + +_LIBCPP_HIDE_FROM_ABI constexpr char __hex_to_upper(char c) { + switch (c) { + case 'a': + return 'A'; + case 'b': + return 'B'; + case 'c': + return 'C'; + case 'd': + return 'D'; + case 'e': + return 'E'; + case 'f': + return 'F'; + } + return c; +} + +struct _LIBCPP_TYPE_VIS __padding_size_result { + size_t __before_; + size_t __after_; +}; + +_LIBCPP_HIDE_FROM_ABI constexpr __padding_size_result __padding_size(size_t __size, size_t __width, + __format_spec::__alignment __align) { + _LIBCPP_ASSERT(__width > __size, "Don't call this function when no padding is required"); + _LIBCPP_ASSERT(__align != __format_spec::__alignment::__default, + "Caller should adjust the default to the value required by the type"); + + size_t __fill = __width - __size; + switch (__align) { + case __format_spec::__alignment::__default: + __libcpp_unreachable(); + + case __format_spec::__alignment::__left: + return {0, __fill}; + + case __format_spec::__alignment::__center: { + // The extra padding is divided per [format.string.std]/3 + // __before = floor(__fill, 2); + // __after = ceil(__fill, 2); + size_t __before = __fill / 2; + size_t __after = __fill - __before; + return {__before, __after}; + } + case __format_spec::__alignment::__right: + return {__fill, 0}; + } + __libcpp_unreachable(); +} + +// XXX write groupping +template +_LIBCPP_HIDE_FROM_ABI _OutIt __format_grouping(_OutIt __out_it, const char* __begin, const char* __first, + const char* __last, string&& __grouping, _CharT __sep, + __format_spec::__parser<_CharT> __parser) { + + // TODO FMT This function duplicates some functionality of the normal + // output routines. Evaluate whether these parts can be efficiently + // combined with the existing routines. + + int __size = (__first - __begin) + // [sign][prefix] + (__last - __first) + // data + (__grouping.size() - 1); // number of separator characters + + __padding_size_result __padding = {0, 0}; + if (__parser.__alignment_ == __format_spec::__alignment::__default) { + // Write [sign][prefix]. + __out_it = _VSTD::copy(__begin, __first, _VSTD::move(__out_it)); + + if (__parser.__width_ > __size) { + // Write zero padding. + __padding.__before_ = __parser.__width_ - __size; + __out_it = _VSTD::fill_n(_VSTD::move(__out_it), __parser.__width_ - __size, _CharT('0')); + } + } else { + if (__parser.__width_ > __size) { + // Determine padding and write padding. + __padding = __padding_size(__size, __parser.__width_, __parser.__alignment_); + + __out_it = _VSTD::fill_n(_VSTD::move(__out_it), __padding.__before_, __parser.__fill_); + } + // Write [sign][prefix]. + __out_it = _VSTD::copy(__begin, __first, _VSTD::move(__out_it)); + } + + 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 `__parser.__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 (__parser.__type_ == __format_spec::__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; + } + + return _VSTD::fill_n(_VSTD::move(__out_it), __padding.__after_, __parser.__fill_); +} + +/** + * Writes the input to the output with the required padding. + * + * Since the output column width is specified the function can be used for + * ASCII and Unicode input. + * + * @pre [@a __first, @a __last) is a valid range. + * @pre @a __size <= @a __width. Using this function when this pre-condition + * doesn't hold incurs an unwanted overhead. + * + * @param __out_it The output iterator to write to. + * @param __first Pointer to the first element to write. + * @param __last Pointer beyond the last element to write. + * @param __size The (estimated) output column width. When the elements + * to be written are ASCII the following condition holds + * @a __size == @a __last - @a __first. + * @param __width The number of output columns to write. + * @param __fill The character used for the alignment of the output. + * TODO FMT Will probably change to support Unicode grapheme + * cluster. + * @param __alignment The requested alignment. + * + * @returns An iterator pointing beyond the last element written. + * + * @note The type of the elements in range [@a __first, @a __last) can differ + * from the type of @a __fill. Integer output uses @c std::to_chars for its + * conversion, which means the [@a __first, @a __last) always contains elements + * of the type @c char. + */ +template +_LIBCPP_HIDE_FROM_ABI auto __write(const _CharT* __first, const _CharT* __last, + output_iterator auto __out_it, + __format_spec::__parser<_ParserCharT> __parser, ptrdiff_t __size) { + _LIBCPP_ASSERT(__first <= __last, "Not a valid range"); + + if (__size >= __parser.__width_) + return _VSTD::copy(__first, __last, _VSTD::move(__out_it)); + + __padding_size_result __padding = __formatter::__padding_size(__size, __parser.__width_, __parser.__alignment_); + __out_it = _VSTD::fill_n(_VSTD::move(__out_it), __padding.__before_, __parser.__fill_); + __out_it = _VSTD::copy(__first, __last, _VSTD::move(__out_it)); + return _VSTD::fill_n(_VSTD::move(__out_it), __padding.__after_, __parser.__fill_); +} + +/** XXX update comment + * Writes the input to the output with the required padding. + * + * Since the output column width is specified the function can be used for + * ASCII and Unicode input. + * + * @pre [@a __first, @a __last) is a valid range. + * @pre @a __size <= @a __width. Using this function when this pre-condition + * doesn't hold incurs an unwanted overhead. + * + * @param __out_it The output iterator to write to. + * @param __first Pointer to the first element to write. + * @param __last Pointer beyond the last element to write. + * @param __size The (estimated) output column width. When the elements + * to be written are ASCII the following condition holds + * @a __size == @a __last - @a __first. + * @param __width The number of output columns to write. + * @param __fill The character used for the alignment of the output. + * TODO FMT Will probably change to support Unicode grapheme + * cluster. + * @param __alignment The requested alignment. + * + * @returns An iterator pointing beyond the last element written. + * + * @note The type of the elements in range [@a __first, @a __last) can differ + * from the type of @a __fill. Integer output uses @c std::to_chars for its + * conversion, which means the [@a __first, @a __last) always contains elements + * of the type @c char. + */ +// Ignores precision +template +_LIBCPP_HIDE_FROM_ABI auto __write(const _CharT* __first, const _CharT* __last, + output_iterator auto __out_it, + __format_spec::__parser<_ParserCharT> __parser) { + return __write(__first, __last, _VSTD::move(__out_it), __parser, __last - __first); +} + +// Ignores precision +template +_LIBCPP_HIDE_FROM_ABI auto __write_transformed(const _CharT* __first, const _CharT* __last, + output_iterator auto __out_it, + __format_spec::__parser<_ParserCharT> __parser, _UnaryOperation __op) { + _LIBCPP_ASSERT(__first <= __last, "Not a valid range"); + _LIBCPP_ASSERT(!__parser.__has_precision(), "Use XXXX"); + + ptrdiff_t __size = __last - __first; + if (__size >= __parser.__width_) + return _VSTD::transform(__first, __last, _VSTD::move(__out_it), __op); + + __padding_size_result __padding = __padding_size(__size, __parser.__width_, __parser.__alignment_); + __out_it = _VSTD::fill_n(_VSTD::move(__out_it), __padding.__before_, __parser.__fill_); + __out_it = _VSTD::transform(__first, __last, _VSTD::move(__out_it), __op); + return _VSTD::fill_n(_VSTD::move(__out_it), __padding.__after_, __parser.__fill_); +} + +/** + * @overload + * + * Writes additional zero's for the precision before the exponent. + * This is used when the precision requested in the format string is larger + * than the maximum precision of the floating-point type. These precision + * digits are always 0. + * + * @param __exponent The location of the exponent character. + * @param __num_trailing_zeros The number of 0's to write before the exponent + * character. + */ +template +_LIBCPP_HIDE_FROM_ABI auto __write_using_trailing_zeros(const _CharT* __first, const _CharT* __last, + output_iterator auto __out_it, + __format_spec::__parser<_ParserCharT> __parser, size_t __size, + const _CharT* __exponent, size_t __num_trailing_zeros) { + _LIBCPP_ASSERT(__first <= __last, "Not a valid range"); + _LIBCPP_ASSERT(__num_trailing_zeros > 0, "The overload not writing trailing zeros should have been used"); + + __padding_size_result __padding = + __padding_size(__size + __num_trailing_zeros, __parser.__width_, __parser.__alignment_); + __out_it = _VSTD::fill_n(_VSTD::move(__out_it), __padding.__before_, __parser.__fill_); + __out_it = _VSTD::copy(__first, __exponent, _VSTD::move(__out_it)); + __out_it = _VSTD::fill_n(_VSTD::move(__out_it), __num_trailing_zeros, _CharT('0')); + __out_it = _VSTD::copy(__exponent, __last, _VSTD::move(__out_it)); + return _VSTD::fill_n(_VSTD::move(__out_it), __padding.__after_, __parser.__fill_); +} + +# ifndef _LIBCPP_HAS_NO_UNICODE +template +_LIBCPP_HIDE_FROM_ABI auto __write_unicode_no_precision(basic_string_view<_CharT> __str, + output_iterator auto __out_it, + __format_spec::__parser<_CharT> __parser) { + _LIBCPP_ASSERT(!__parser.__has_precision(), "use __write_unicode"); + // No padding -> copy the string + if (!__parser.__has_width()) + return _VSTD::copy(__str.begin(), __str.end(), _VSTD::move(__out_it)); + + // Non Unicode part larger than width -> copy the string + auto __last = __format_spec::__detail::__estimate_column_width_fast(__str.begin(), __str.end()); + ptrdiff_t __size = __last - __str.begin(); + if (__size >= __parser.__width_) + return _VSTD::copy(__str.begin(), __str.end(), _VSTD::move(__out_it)); + + // Is there a non Unicode part? + if (__last != __str.end()) { + // Non Unicode and Unicode part larger than width -> copy the string + __format_spec::__detail::__column_width_result __column_width = + __format_spec::__detail::__estimate_column_width(__last, __str.end(), __parser.__width_); + __size += __column_width.__width; // Note this new size is used when __size < __parser.__width_ + if (__size >= __parser.__width_) + return _VSTD::copy(__str.begin(), __str.end(), _VSTD::move(__out_it)); + } + + return __formatter::__write(__str.begin(), __str.end(), _VSTD::move(__out_it), __parser, __size); +} +# endif + +template +_LIBCPP_HIDE_FROM_ABI auto __write_unicode(basic_string_view<_CharT> __str, + output_iterator auto __out_it, + __format_spec::__parser<_CharT> __parser) { +# ifndef _LIBCPP_HAS_NO_UNICODE + if (!__parser.__has_precision()) + return __formatter::__write_unicode_no_precision(__str, _VSTD::move(__out_it), __parser); + + // Non unicode part larger than precision -> truncate the output and use the normal write operation. + auto __last = __format_spec::__detail::__estimate_column_width_fast(__str.begin(), __str.end()); + ptrdiff_t __size = __last - __str.begin(); + if (__size >= __parser.__precision_) + return __formatter::__write(__str.begin(), __str.begin() + __parser.__precision_, _VSTD::move(__out_it), __parser, + __parser.__precision_); + + // No non Unicode part, implies __size < __parser.__precision_ -> use normal write operation + if (__last == __str.end()) + return __formatter::__write(__str.begin(), __str.end(), _VSTD::move(__out_it), __parser, __str.size()); + + __format_spec::__detail::__column_width_result __column_width = + __format_spec::__detail::__estimate_column_width(__last, __str.end(), __parser.__precision_ - __size); + __size += __column_width.__width; + // Truncate the output + if (__column_width.__ptr != __str.end()) + __str.remove_suffix(__str.end() - __column_width.__ptr); + + return __formatter::__write(__str.begin(), __str.end(), _VSTD::move(__out_it), __parser, __size); + +# else + if (__parser.__has_precision()) { + ptrdiff_t __size = __str.size(); + if (__size > __parser.__precision_) + return __formatter::__write(__str.begin(), __str.begin() + __parser.__precision_, _VSTD::move(__out_it), __parser, + __parser.__precision_); + } + return __formatter::__write(__str.begin(), __str.end(), _VSTD::move(__out_it), __parser, __str.size()); + +# endif +} + +} // namespace __formatter + +#endif //_LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___FORMAT_FORMATTER_V2_H 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 @@ -10,18 +10,7 @@ #ifndef _LIBCPP___FORMAT_FORMATTER_POINTER_H #define _LIBCPP___FORMAT_FORMATTER_POINTER_H -#include <__algorithm/copy.h> -#include <__assert> -#include <__availability> #include <__config> -#include <__format/format_error.h> -#include <__format/format_fwd.h> -#include <__format/formatter.h> -#include <__format/formatter_integral.h> -#include <__format/parser_std_format_spec.h> -#include <__iterator/access.h> -#include -#include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header @@ -30,52 +19,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD #if _LIBCPP_STD_VER > 17 - -namespace __format_spec { - -template <__formatter::__char_type _CharT> -class _LIBCPP_TEMPLATE_VIS __formatter_pointer : public __parser_pointer<_CharT> { -public: - _LIBCPP_HIDE_FROM_ABI auto format(const void* __ptr, auto& __ctx) -> decltype(__ctx.out()) { - _LIBCPP_ASSERT(this->__alignment != _Flags::_Alignment::__default, - "The call to parse should have updated the alignment"); - if (this->__width_needs_substitution()) - this->__substitute_width_arg_id(__ctx.arg(this->__width)); - - // This code looks a lot like the code to format a hexadecimal integral, - // but that code isn't public. Making that code public requires some - // refactoring. - // TODO FMT Remove code duplication. - 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); - - unsigned __size = __last - __buffer; - if (__size >= this->__width) - return _VSTD::copy(__buffer, __last, __ctx.out()); - - return __formatter::__write(__ctx.out(), __buffer, __last, __size, this->__width, this->__fill, this->__alignment); - } -}; - -} // namespace __format_spec - -// [format.formatter.spec]/2.4 -// For each charT, the pointer type specializations template<> -// - struct formatter; -// - template<> struct formatter; -// - template<> struct formatter; -template <__formatter::__char_type _CharT> -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter - : public __format_spec::__formatter_pointer<_CharT> {}; -template <__formatter::__char_type _CharT> -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter - : public __format_spec::__formatter_pointer<_CharT> {}; -template <__formatter::__char_type _CharT> -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter - : public __format_spec::__formatter_pointer<_CharT> {}; - #endif //_LIBCPP_STD_VER > 17 _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__format/formatter_string.h b/libcxx/include/__format/formatter_string.h --- a/libcxx/include/__format/formatter_string.h +++ b/libcxx/include/__format/formatter_string.h @@ -10,13 +10,13 @@ #ifndef _LIBCPP___FORMAT_FORMATTER_STRING_H #define _LIBCPP___FORMAT_FORMATTER_STRING_H -#include <__assert> #include <__config> #include <__format/format_error.h> #include <__format/format_fwd.h> #include <__format/format_string.h> #include <__format/formatter.h> #include <__format/parser_std_format_spec.h> +#include <__format/parser_std_format_spec_v2.h> #include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) @@ -26,123 +26,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD #if _LIBCPP_STD_VER > 17 - -namespace __format_spec { - -template <__formatter::__char_type _CharT> -class _LIBCPP_TEMPLATE_VIS __formatter_string : public __parser_string<_CharT> { -public: - _LIBCPP_HIDE_FROM_ABI auto format(basic_string_view<_CharT> __str, - auto& __ctx) -> decltype(__ctx.out()) { - - _LIBCPP_ASSERT(this->__alignment != _Flags::_Alignment::__default, - "The parser should not use these defaults"); - - if (this->__width_needs_substitution()) - this->__substitute_width_arg_id(__ctx.arg(this->__width)); - - if (this->__precision_needs_substitution()) - this->__substitute_precision_arg_id(__ctx.arg(this->__precision)); - - return __formatter::__write_unicode( - __ctx.out(), __str, this->__width, - this->__has_precision_field() ? this->__precision : -1, this->__fill, - this->__alignment); - } -}; - -} //namespace __format_spec - -// [format.formatter.spec]/2.2 For each charT, the string type specializations - -// Formatter const char*. -template <__formatter::__char_type _CharT> -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT - formatter - : public __format_spec::__formatter_string<_CharT> { - using _Base = __format_spec::__formatter_string<_CharT>; - - _LIBCPP_HIDE_FROM_ABI auto format(const _CharT* __str, auto& __ctx) - -> decltype(__ctx.out()) { - _LIBCPP_ASSERT(__str, "The basic_format_arg constructor should have " - "prevented an invalid pointer."); - - // When using a center or right alignment and the width option the length - // of __str must be known to add the padding upfront. This case is handled - // by the base class by converting the argument to a basic_string_view. - // - // When using left alignment and the width option the padding is added - // after outputting __str so the length can be determined while outputting - // __str. The same holds true for the precision, during outputting __str it - // can be validated whether the precision threshold has been reached. For - // now these optimizations aren't implemented. Instead the base class - // handles these options. - // TODO FMT Implement these improvements. - if (this->__has_width_field() || this->__has_precision_field()) - return _Base::format(__str, __ctx); - - // No formatting required, copy the string to the output. - auto __out_it = __ctx.out(); - while (*__str) - *__out_it++ = *__str++; - return __out_it; - } -}; - -// Formatter char*. -template <__formatter::__char_type _CharT> -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT - formatter<_CharT*, _CharT> : public formatter { - using _Base = formatter; - - _LIBCPP_HIDE_FROM_ABI auto format(_CharT* __str, auto& __ctx) - -> decltype(__ctx.out()) { - return _Base::format(__str, __ctx); - } -}; - -// Formatter const char[]. -template <__formatter::__char_type _CharT, size_t _Size> -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT - formatter - : public __format_spec::__formatter_string<_CharT> { - using _Base = __format_spec::__formatter_string<_CharT>; - - _LIBCPP_HIDE_FROM_ABI auto format(const _CharT __str[_Size], auto& __ctx) - -> decltype(__ctx.out()) { - return _Base::format(basic_string_view<_CharT>(__str, _Size), __ctx); - } -}; - -// Formatter std::string. -template <__formatter::__char_type _CharT, class _Traits, class _Allocator> -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT - formatter, _CharT> - : public __format_spec::__formatter_string<_CharT> { - using _Base = __format_spec::__formatter_string<_CharT>; - - _LIBCPP_HIDE_FROM_ABI auto - format(const basic_string<_CharT, _Traits, _Allocator>& __str, auto& __ctx) - -> decltype(__ctx.out()) { - // drop _Traits and _Allocator - return _Base::format(basic_string_view<_CharT>(__str.data(), __str.size()), __ctx); - } -}; - -// Formatter std::string_view. -template <__formatter::__char_type _CharT, class _Traits> -struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter, _CharT> - : public __format_spec::__formatter_string<_CharT> { - using _Base = __format_spec::__formatter_string<_CharT>; - - _LIBCPP_HIDE_FROM_ABI auto - format(basic_string_view<_CharT, _Traits> __str, auto& __ctx) - -> decltype(__ctx.out()) { - // drop _Traits - return _Base::format(basic_string_view<_CharT>(__str.data(), __str.size()), __ctx); - } -}; - #endif //_LIBCPP_STD_VER > 17 _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__format/formatter_v2.h b/libcxx/include/__format/formatter_v2.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__format/formatter_v2.h @@ -0,0 +1,475 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___FORMAT_FORMATTER_V2_H +#define _LIBCPP___FORMAT_FORMATTER_V2_H + +#include <__algorithm/copy.h> +#include <__algorithm/fill_n.h> +#include <__config> +#include <__format/format_error.h> +#include <__format/format_fwd.h> // for __char_type TODO FMT Move the concept? +#include <__format/format_parse_context.h> +#include <__format/formatter_floating_point.h> +#include <__format/formatter_integral.h> +#include <__format/formatter_output.h> +#include <__format/parser_std_format_spec.h> +#include <__format/parser_std_format_spec_v2.h> +#include <__utility/move.h> +#include <__utility/unreachable.h> +#include +#include +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 17 + +namespace __formatter { + +// +// Formatter string +// + +template +_LIBCPP_HIDE_FROM_ABI auto __format_string(basic_string_view<_CharT> __str, + output_iterator auto __out_it, + __format_spec::__parser<_CharT> __parser) { + return __formatter::__write_unicode(__str, _VSTD::move(__out_it), __parser); +} +// +// Formatter pointer +// + +template +_LIBCPP_HIDE_FROM_ABI auto __format_pointer(const void* __ptr, auto& __ctx, __format_spec::__parser<_CharT> __parser) { + _LIBCPP_ASSERT(__parser.__alignment_ != __format_spec::__alignment::__default, + "The call to parse should have updated the alignment"); + + __parser.__alternate_form_ = true; + __parser.__type_ = __format_spec::__type::__hexadecimal_lower_case; + return __format_integer(reinterpret_cast(__ptr), __ctx, __parser); +} + +} // namespace __formatter + +/// The default formatter template. +/// +/// [format.formatter.spec]/5 +/// If F is a disabled specialization of formatter, these values are false: +/// - is_default_constructible_v, +/// - is_copy_constructible_v, +/// - is_move_constructible_v, +/// - is_copy_assignable, and +/// - is_move_assignable. +template +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter { + formatter() = delete; + formatter(const formatter&) = delete; + formatter& operator=(const formatter&) = delete; +}; + +// +// Formatter char +// + +template <__formatter::__char_type _CharT> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT __formatter_char { +public: + _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; + } + + _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_.__resolve_dynamic_sizes(__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. + if constexpr (is_signed_v<_CharT>) + return __formatter::__format_integer(static_cast(__value), __ctx, + __parser_.__resolve_dynamic_sizes(__ctx)); + else + return __formatter::__format_integer(static_cast(__value), __ctx, + __parser_.__resolve_dynamic_sizes(__ctx)); + } else + return __formatter::__format_integer(__value, __ctx, __parser_.__resolve_dynamic_sizes(__ctx)); + } + + _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_spec::__parser<_CharT> __parser_; +}; + +template <> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter : public __formatter_char {}; + +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS +template <> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter : public __formatter_char {}; + +template <> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter : public __formatter_char { +}; + +# endif // _LIBCPP_HAS_NO_WIDE_CHARACTERS + +// +// Formatter arithmetic (bool) +// + +template <__formatter::__char_type _CharT> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter { +public: + _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; + } + + _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_.__resolve_dynamic_sizes(__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: + // Casting this to a formatter more likely being used reduces the binary size. + return __formatter::__format_integer(static_cast(__value), __ctx, + __parser_.__resolve_dynamic_sizes(__ctx)); + + default: + _LIBCPP_ASSERT(false, "The parse function should have validated the type"); + __libcpp_unreachable(); + } + } + + __format_spec::__parser<_CharT> __parser_; +}; + +// +// Formatter arithmetic (integer) +// + +template <__formatter::__char_type _CharT> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT __formatter_integer { +public: + _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; + } + + // TODO might want to reduce the number of instantiations + // but need to measure the size of them (eg 64 bit and 128 bit version) + // One formatter integral and char + template + _LIBCPP_HIDE_FROM_ABI auto format(_Tp __value, auto& __ctx) const -> decltype(__ctx.out()) { + return format(__value, __ctx, __parser_.__resolve_dynamic_sizes(__ctx)); + } + + template + static _LIBCPP_HIDE_FROM_ABI auto format(_Tp __value, auto& __ctx, __format_spec::__parser<_CharT> __parser) + -> decltype(__ctx.out()) { + if (__parser.__type_ == __format_spec::__type::__char) + return __formatter::__format_char(__value, __ctx.out(), __parser); + + // Reduce the number of instantiation of the integer formatter + if constexpr (is_signed_v<_Tp>) { + if constexpr (sizeof(_Tp) <= sizeof(int)) + return __formatter::__format_integer(static_cast(__value), __ctx, __parser); + else if constexpr (sizeof(_Tp) <= sizeof(long long)) + return __formatter::__format_integer(static_cast(__value), __ctx, __parser); +# ifndef _LIBCPP_HAS_NO_INT128 + else if constexpr (sizeof(_Tp) <= sizeof(__int128_t)) + return __formatter::__format_integer(static_cast<__int128_t>(__value), __ctx, __parser); +# endif + else + static_assert(sizeof(_Tp) == 0, "Unsupported type"); + } else { + if constexpr (sizeof(_Tp) <= sizeof(unsigned)) + return __formatter::__format_integer(static_cast(__value), __ctx, __parser); + else if constexpr (sizeof(_Tp) <= sizeof(unsigned long long)) + return __formatter::__format_integer(static_cast(__value), __ctx, __parser); +# ifndef _LIBCPP_HAS_NO_INT128 + else if constexpr (sizeof(_Tp) <= sizeof(__uint128_t)) + return __formatter::__format_integer(static_cast<__uint128_t>(__value), __ctx, __parser); +# endif + else + static_assert(sizeof(_Tp) == 0, "Unsupported type"); + } + } + + __format_spec::__parser<_CharT> __parser_; +}; + +// Signed integral types. +template <__formatter::__char_type _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 __formatter_integer<_CharT> { +}; +template <__formatter::__char_type _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 __formatter_integer<_CharT> {}; +template <__formatter::__char_type _CharT> +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 __formatter_integer<_CharT> { + using _Base = __formatter_integer<_CharT>; + + _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"); + + return _Base::format(static_cast<_To>(__value), __ctx); + } +}; +# endif + +// Unsigned integral types. +template <__formatter::__char_type _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 __formatter_integer<_CharT> {}; +template <__formatter::__char_type _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 __formatter_integer<_CharT> {}; +template <__formatter::__char_type _CharT> +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 __formatter_integer<_CharT> { + using _Base = __formatter_integer<_CharT>; + + _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"); + + return _Base::format(static_cast<_To>(__value), __ctx); + } +}; +# endif + +// +// Formatter arithmetic (floating-point) +// + +template <__formatter::__char_type _CharT> +struct _LIBCPP_TEMPLATE_VIS __formatter_floating_point { +public: + _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_floating_point); + __format_spec::__process_parsed_floating_point(__parser_); + return __result; + } + + template + _LIBCPP_HIDE_FROM_ABI auto format(_Tp __value, auto& __ctx) const -> decltype(__ctx.out()) { + return __formatter::__format_floating_point(__value, __ctx, __parser_.__resolve_dynamic_sizes(__ctx)); + } + + __format_spec::__parser<_CharT> __parser_; +}; + +template <__formatter::__char_type _CharT> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter + : public __formatter_floating_point<_CharT> {}; +template <__formatter::__char_type _CharT> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter + : public __formatter_floating_point<_CharT> {}; +template <__formatter::__char_type _CharT> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter + : public __formatter_floating_point<_CharT> {}; + +// +// Formatter string +// + +template <__formatter::__char_type _CharT> +struct _LIBCPP_TEMPLATE_VIS __formatter_string { +public: + _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_string); + __format_spec::__process_display_type_string(__parser_.__type_); + return __result; + } + + _LIBCPP_HIDE_FROM_ABI auto format(basic_string_view<_CharT> __str, auto& __ctx) const -> decltype(__ctx.out()) { + return __formatter::__format_string(__str, __ctx.out(), __parser_.__resolve_dynamic_sizes(__ctx)); + } + + __format_spec::__parser<_CharT> __parser_; +}; + +template <__formatter::__char_type _CharT> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter + : public __formatter_string<_CharT> { + using _Base = __formatter_string<_CharT>; + + // XXX const qualify + _LIBCPP_HIDE_FROM_ABI auto format(const _CharT* __str, auto& __ctx) const -> decltype(__ctx.out()) { + _LIBCPP_ASSERT(__str, "The basic_format_arg constructor should have " + "prevented an invalid pointer."); + + // When using a center or right alignment and the width option the length + // of __str must be known to add the padding upfront. This case is handled + // by the base class by converting the argument to a basic_string_view. + // + // When using left alignment and the width option the padding is added + // after outputting __str so the length can be determined while outputting + // __str. The same holds true for the precision, during outputting __str it + // can be validated whether the precision threshold has been reached. For + // now these optimizations aren't implemented. Instead the base class + // handles these options. + // TODO FMT Implement these improvements. + if (_Base::__parser_.__width_as_arg_ || _Base::__parser_.__precision_as_arg_ || // + _Base::__parser_.__has_width() || _Base::__parser_.__has_precision()) + return _Base::format(basic_string_view<_CharT>(__str), __ctx); + + // No formatting required, copy the string to the output. + auto __out_it = __ctx.out(); + while (*__str) + *__out_it++ = *__str++; + return __out_it; + } +}; + +// Formatter char*. +template <__formatter::__char_type _CharT> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter<_CharT*, _CharT> + : public formatter { + using _Base = formatter; + + _LIBCPP_HIDE_FROM_ABI auto format(_CharT* __str, auto& __ctx) const -> decltype(__ctx.out()) { + return _Base::format(__str, __ctx); + } +}; + +// Formatter const char[]. +template <__formatter::__char_type _CharT, size_t _Size> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter + : public __formatter_string<_CharT> { + using _Base = __formatter_string<_CharT>; + + _LIBCPP_HIDE_FROM_ABI auto format(const _CharT __str[_Size], auto& __ctx) const -> decltype(__ctx.out()) { + return _Base::format(basic_string_view<_CharT>(__str, _Size), __ctx); + } +}; + +// Formatter std::string. +template <__formatter::__char_type _CharT, class _Traits, class _Allocator> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter, _CharT> + : public __formatter_string<_CharT> { + using _Base = __formatter_string<_CharT>; + + _LIBCPP_HIDE_FROM_ABI auto format(const basic_string<_CharT, _Traits, _Allocator>& __str, auto& __ctx) const + -> decltype(__ctx.out()) { + // drop _Traits and _Allocator + return _Base::format(basic_string_view<_CharT>(__str.data(), __str.size()), __ctx); + } +}; + +// Formatter std::string_view. +template <__formatter::__char_type _CharT, class _Traits> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter, _CharT> + : public __formatter_string<_CharT> { + using _Base = __formatter_string<_CharT>; + + _LIBCPP_HIDE_FROM_ABI auto format(basic_string_view<_CharT, _Traits> __str, auto& __ctx) const + -> decltype(__ctx.out()) { + // drop _Traits + return _Base::format(basic_string_view<_CharT>(__str.data(), __str.size()), __ctx); + } +}; + +// +// Formatter pointer +// + +template <__formatter::__char_type _CharT> +struct _LIBCPP_TEMPLATE_VIS __formatter_pointer { +public: + __formatter_pointer() { __parser_.__alignment_ = __format_spec::__alignment::__right; } + + _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_pointer); + __format_spec::__process_display_type_pointer(__parser_.__type_); + return __result; + } + + _LIBCPP_HIDE_FROM_ABI auto format(const void* __ptr, auto& __ctx) const -> decltype(__ctx.out()) { + return __formatter::__format_pointer(__ptr, __ctx, __parser_.__resolve_dynamic_sizes(__ctx)); + } + + __format_spec::__parser<_CharT> __parser_; +}; + +// [format.formatter.spec]/2.4 +// For each charT, the pointer type specializations template<> +// - struct formatter; +// - template<> struct formatter; +// - template<> struct formatter; +template <__formatter::__char_type _CharT> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter + : public __formatter_pointer<_CharT> {}; +template <__formatter::__char_type _CharT> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter : public __formatter_pointer<_CharT> { +}; +template <__formatter::__char_type _CharT> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter + : public __formatter_pointer<_CharT> {}; + +#endif //_LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___FORMAT_FORMATTER_V2_H 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 @@ -12,8 +12,8 @@ #include <__algorithm/find_if.h> #include <__algorithm/min.h> -#include <__assert> #include <__config> +#include <__debug> #include <__format/format_arg.h> #include <__format/format_error.h> #include <__format/format_string.h> @@ -36,168 +36,6 @@ namespace __format_spec { -/** - * Contains the flags for the std-format-spec. - * - * Some format-options can only be used for specific C++ types and may depend on - * the selected format-type. - * * The C++type filtering can be done using the proper policies for - * @ref __parser_std. - * * The format-type filtering needs to be done post parsing in the parser - * derived from @ref __parser_std. - */ -_LIBCPP_PACKED_BYTE_FOR_AIX -class _LIBCPP_TYPE_VIS _Flags { -public: - enum class _LIBCPP_ENUM_VIS _Alignment : uint8_t { - /** - * No alignment is set in the format string. - * - * Zero-padding is ignored when an alignment is selected. - * The default alignment depends on the selected format-type. - */ - __default, - __left, - __center, - __right - }; - enum class _LIBCPP_ENUM_VIS _Sign : uint8_t { - /** - * No sign is set in the format string. - * - * The sign isn't allowed for certain format-types. By using this value - * it's possible to detect whether or not the user explicitly set the sign - * flag. For formatting purposes it behaves the same as @ref __minus. - */ - __default, - __minus, - __plus, - __space - }; - - _Alignment __alignment : 2 {_Alignment::__default}; - _Sign __sign : 2 {_Sign::__default}; - uint8_t __alternate_form : 1 {false}; - uint8_t __zero_padding : 1 {false}; - uint8_t __locale_specific_form : 1 {false}; - - enum class _LIBCPP_ENUM_VIS _Type : uint8_t { - __default, - __string, - __binary_lower_case, - __binary_upper_case, - __octal, - __decimal, - __hexadecimal_lower_case, - __hexadecimal_upper_case, - __pointer, - __char, - __float_hexadecimal_lower_case, - __float_hexadecimal_upper_case, - __scientific_lower_case, - __scientific_upper_case, - __fixed_lower_case, - __fixed_upper_case, - __general_lower_case, - __general_upper_case - }; - - _Type __type{_Type::__default}; -}; -_LIBCPP_PACKED_BYTE_FOR_AIX_END - -namespace __detail { -template -_LIBCPP_HIDE_FROM_ABI constexpr bool -__parse_alignment(_CharT __c, _Flags& __flags) noexcept { - switch (__c) { - case _CharT('<'): - __flags.__alignment = _Flags::_Alignment::__left; - return true; - - case _CharT('^'): - __flags.__alignment = _Flags::_Alignment::__center; - return true; - - case _CharT('>'): - __flags.__alignment = _Flags::_Alignment::__right; - return true; - } - return false; -} -} // namespace __detail - -template -class _LIBCPP_TEMPLATE_VIS __parser_fill_align { -public: - // TODO FMT The standard doesn't specify this character is a Unicode - // character. Validate what fmt and MSVC have implemented. - _CharT __fill{_CharT(' ')}; - -protected: - _LIBCPP_HIDE_FROM_ABI constexpr const _CharT* - __parse(const _CharT* __begin, const _CharT* __end, _Flags& __flags) { - _LIBCPP_ASSERT(__begin != __end, - "When called with an empty input the function will cause " - "undefined behavior by evaluating data not in the input"); - if (__begin + 1 != __end) { - if (__detail::__parse_alignment(*(__begin + 1), __flags)) { - if (*__begin == _CharT('{') || *__begin == _CharT('}')) - __throw_format_error( - "The format-spec fill field contains an invalid character"); - __fill = *__begin; - return __begin + 2; - } - } - - if (__detail::__parse_alignment(*__begin, __flags)) - return __begin + 1; - - return __begin; - } -}; - -template -_LIBCPP_HIDE_FROM_ABI constexpr const _CharT* -__parse_sign(const _CharT* __begin, _Flags& __flags) noexcept { - switch (*__begin) { - case _CharT('-'): - __flags.__sign = _Flags::_Sign::__minus; - break; - case _CharT('+'): - __flags.__sign = _Flags::_Sign::__plus; - break; - case _CharT(' '): - __flags.__sign = _Flags::_Sign::__space; - break; - default: - return __begin; - } - return __begin + 1; -} - -template -_LIBCPP_HIDE_FROM_ABI constexpr const _CharT* -__parse_alternate_form(const _CharT* __begin, _Flags& __flags) noexcept { - if (*__begin == _CharT('#')) { - __flags.__alternate_form = true; - ++__begin; - } - - return __begin; -} - -template -_LIBCPP_HIDE_FROM_ABI constexpr const _CharT* -__parse_zero_padding(const _CharT* __begin, _Flags& __flags) noexcept { - if (*__begin == _CharT('0')) { - __flags.__zero_padding = true; - ++__begin; - } - - return __begin; -} - template _LIBCPP_HIDE_FROM_ABI constexpr __format::__parse_number_result< _CharT> __parse_arg_id(const _CharT* __begin, const _CharT* __end, auto& __parse_ctx) { @@ -218,33 +56,32 @@ template _LIBCPP_HIDE_FROM_ABI constexpr uint32_t -__substitute_arg_id(basic_format_arg<_Context> _Arg) { +__substitute_arg_id(basic_format_arg<_Context> __arg) { return visit_format_arg( - [](auto __arg) -> uint32_t { - using _Type = decltype(__arg); + [](auto __value) -> uint32_t { + using _Type = decltype(__value); if constexpr (integral<_Type>) { if constexpr (signed_integral<_Type>) { - if (__arg < 0) + if (__value < 0) __throw_format_error("A format-spec arg-id replacement shouldn't " "have a negative value"); } using _CT = common_type_t<_Type, decltype(__format::__number_max)>; - if (static_cast<_CT>(__arg) > - static_cast<_CT>(__format::__number_max)) + if (static_cast<_CT>(__value) > static_cast<_CT>(__format::__number_max)) __throw_format_error("A format-spec arg-id replacement exceeds " "the maximum supported value"); - return __arg; + return __value; } else if constexpr (same_as<_Type, monostate>) __throw_format_error("Argument index out of bounds"); else __throw_format_error("A format-spec arg-id replacement argument " "isn't an integral type"); }, - _Arg); + __arg); } - +#if 0 class _LIBCPP_TYPE_VIS __parser_width { public: /** Contains a width or an arg-id. */ @@ -393,538 +230,6 @@ } }; -template -_LIBCPP_HIDE_FROM_ABI constexpr const _CharT* -__parse_locale_specific_form(const _CharT* __begin, _Flags& __flags) noexcept { - if (*__begin == _CharT('L')) { - __flags.__locale_specific_form = true; - ++__begin; - } - - return __begin; -} - -template -_LIBCPP_HIDE_FROM_ABI constexpr const _CharT* -__parse_type(const _CharT* __begin, _Flags& __flags) { - - // Determines the type. It does not validate whether the selected type is - // valid. Most formatters have optional fields that are only allowed for - // certain types. These parsers need to do validation after the type has - // been parsed. So its easier to implement the validation for all types in - // the specific parse function. - switch (*__begin) { - case 'A': - __flags.__type = _Flags::_Type::__float_hexadecimal_upper_case; - break; - case 'B': - __flags.__type = _Flags::_Type::__binary_upper_case; - break; - case 'E': - __flags.__type = _Flags::_Type::__scientific_upper_case; - break; - case 'F': - __flags.__type = _Flags::_Type::__fixed_upper_case; - break; - case 'G': - __flags.__type = _Flags::_Type::__general_upper_case; - break; - case 'X': - __flags.__type = _Flags::_Type::__hexadecimal_upper_case; - break; - case 'a': - __flags.__type = _Flags::_Type::__float_hexadecimal_lower_case; - break; - case 'b': - __flags.__type = _Flags::_Type::__binary_lower_case; - break; - case 'c': - __flags.__type = _Flags::_Type::__char; - break; - case 'd': - __flags.__type = _Flags::_Type::__decimal; - break; - case 'e': - __flags.__type = _Flags::_Type::__scientific_lower_case; - break; - case 'f': - __flags.__type = _Flags::_Type::__fixed_lower_case; - break; - case 'g': - __flags.__type = _Flags::_Type::__general_lower_case; - break; - case 'o': - __flags.__type = _Flags::_Type::__octal; - break; - case 'p': - __flags.__type = _Flags::_Type::__pointer; - break; - case 's': - __flags.__type = _Flags::_Type::__string; - break; - case 'x': - __flags.__type = _Flags::_Type::__hexadecimal_lower_case; - break; - default: - return __begin; - } - return ++__begin; -} - -/** - * Process the parsed alignment and zero-padding state of arithmetic types. - * - * [format.string.std]/13 - * If the 0 character and an align option both appear, the 0 character is - * ignored. - * - * For the formatter a @ref __default alignment means zero-padding. - */ -_LIBCPP_HIDE_FROM_ABI constexpr void __process_arithmetic_alignment(_Flags& __flags) { - __flags.__zero_padding &= __flags.__alignment == _Flags::_Alignment::__default; - if (!__flags.__zero_padding && __flags.__alignment == _Flags::_Alignment::__default) - __flags.__alignment = _Flags::_Alignment::__right; -} - -/** - * The parser for the std-format-spec. - * - * [format.string.std]/1 specifies the std-format-spec: - * fill-and-align sign # 0 width precision L type - * - * All these fields are optional. Whether these fields can be used depend on: - * - The type supplied to the format string. - * E.g. A string never uses the sign field so the field may not be set. - * This constrain is validated by the parsers in this file. - * - The supplied value for the optional type field. - * E.g. A int formatted as decimal uses the sign field. - * When formatted as a char the sign field may no longer be set. - * This constrain isn't validated by the parsers in this file. - * - * The base classes are ordered to minimize the amount of padding. - * - * This implements the parser for the string types. - */ -template -class _LIBCPP_TEMPLATE_VIS __parser_string - : public __parser_width, // provides __width(|as_arg) - public __parser_precision, // provides __precision(|as_arg) - public __parser_fill_align<_CharT>, // provides __fill and uses __flags - public _Flags // provides __flags -{ -public: - using char_type = _CharT; - - _LIBCPP_HIDE_FROM_ABI constexpr __parser_string() { - this->__alignment = _Flags::_Alignment::__left; - } - - /** - * The low-level std-format-spec parse function. - * - * @pre __begin points at the beginning of the std-format-spec. This means - * directly after the ':'. - * @pre The std-format-spec parses the entire input, or the first unmatched - * character is a '}'. - * - * @returns The iterator pointing at the last parsed character. - */ - _LIBCPP_HIDE_FROM_ABI constexpr auto parse(auto& __parse_ctx) - -> decltype(__parse_ctx.begin()) { - auto __it = __parse(__parse_ctx); - __process_display_type(); - return __it; - } - -private: - /** - * Parses the std-format-spec. - * - * @throws __throw_format_error When @a __parse_ctx contains an ill-formed - * std-format-spec. - * - * @returns An iterator to the end of input or point at the closing '}'. - */ - _LIBCPP_HIDE_FROM_ABI constexpr auto __parse(auto& __parse_ctx) - -> decltype(__parse_ctx.begin()) { - - auto __begin = __parse_ctx.begin(); - auto __end = __parse_ctx.end(); - if (__begin == __end) - return __begin; - - __begin = __parser_fill_align<_CharT>::__parse(__begin, __end, - static_cast<_Flags&>(*this)); - if (__begin == __end) - return __begin; - - __begin = __parser_width::__parse(__begin, __end, __parse_ctx); - if (__begin == __end) - return __begin; - - __begin = __parser_precision::__parse(__begin, __end, __parse_ctx); - if (__begin == __end) - return __begin; - - __begin = __parse_type(__begin, static_cast<_Flags&>(*this)); - - if (__begin != __end && *__begin != _CharT('}')) - __throw_format_error( - "The format-spec should consume the input or end with a '}'"); - - return __begin; - } - - /** Processes the parsed std-format-spec based on the parsed display type. */ - _LIBCPP_HIDE_FROM_ABI constexpr void __process_display_type() { - switch (this->__type) { - case _Flags::_Type::__default: - case _Flags::_Type::__string: - break; - - default: - __throw_format_error("The format-spec type has a type not supported for " - "a string argument"); - } - } -}; - -/** - * The parser for the std-format-spec. - * - * This implements the parser for the integral types. This includes the - * character type and boolean type. - * - * See @ref __parser_string. - */ -template -class _LIBCPP_TEMPLATE_VIS __parser_integral - : public __parser_width, // provides __width(|as_arg) - public __parser_fill_align<_CharT>, // provides __fill and uses __flags - public _Flags // provides __flags -{ -public: - using char_type = _CharT; - -protected: - /** - * The low-level std-format-spec parse function. - * - * @pre __begin points at the beginning of the std-format-spec. This means - * directly after the ':'. - * @pre The std-format-spec parses the entire input, or the first unmatched - * character is a '}'. - * - * @returns The iterator pointing at the last parsed character. - */ - _LIBCPP_HIDE_FROM_ABI constexpr auto __parse(auto& __parse_ctx) - -> decltype(__parse_ctx.begin()) { - auto __begin = __parse_ctx.begin(); - auto __end = __parse_ctx.end(); - if (__begin == __end) - return __begin; - - __begin = __parser_fill_align<_CharT>::__parse(__begin, __end, - static_cast<_Flags&>(*this)); - if (__begin == __end) - return __begin; - - __begin = __parse_sign(__begin, static_cast<_Flags&>(*this)); - if (__begin == __end) - return __begin; - - __begin = __parse_alternate_form(__begin, static_cast<_Flags&>(*this)); - if (__begin == __end) - return __begin; - - __begin = __parse_zero_padding(__begin, static_cast<_Flags&>(*this)); - if (__begin == __end) - return __begin; - - __begin = __parser_width::__parse(__begin, __end, __parse_ctx); - if (__begin == __end) - return __begin; - - __begin = - __parse_locale_specific_form(__begin, static_cast<_Flags&>(*this)); - if (__begin == __end) - return __begin; - - __begin = __parse_type(__begin, static_cast<_Flags&>(*this)); - - if (__begin != __end && *__begin != _CharT('}')) - __throw_format_error( - "The format-spec should consume the input or end with a '}'"); - - return __begin; - } - - /** Handles the post-parsing updates for the integer types. */ - _LIBCPP_HIDE_FROM_ABI constexpr void __handle_integer() noexcept { - __process_arithmetic_alignment(static_cast<_Flags&>(*this)); - } - - /** - * Handles the post-parsing updates for the character types. - * - * Sets the alignment and validates the format flags set for a character type. - * - * At the moment the validation for a character and a Boolean behave the - * same, but this may change in the future. - * Specifically at the moment the locale-specific form is allowed for the - * char output type, but it has no effect on the output. - */ - _LIBCPP_HIDE_FROM_ABI constexpr void __handle_char() { __handle_bool(); } - - /** - * Handles the post-parsing updates for the Boolean types. - * - * Sets the alignment and validates the format flags set for a Boolean type. - */ - _LIBCPP_HIDE_FROM_ABI constexpr void __handle_bool() { - if (this->__sign != _Flags::_Sign::__default) - __throw_format_error("A sign field isn't allowed in this format-spec"); - - if (this->__alternate_form) - __throw_format_error( - "An alternate form field isn't allowed in this format-spec"); - - if (this->__zero_padding) - __throw_format_error( - "A zero-padding field isn't allowed in this format-spec"); - - if (this->__alignment == _Flags::_Alignment::__default) - this->__alignment = _Flags::_Alignment::__left; - } -}; - -/** - * The parser for the std-format-spec. - * - * This implements the parser for the floating-point types. - * - * See @ref __parser_string. - */ -template -class _LIBCPP_TEMPLATE_VIS __parser_floating_point - : public __parser_width, // provides __width(|as_arg) - public __parser_precision, // provides __precision(|as_arg) - public __parser_fill_align<_CharT>, // provides __fill and uses __flags - public _Flags // provides __flags -{ -public: - using char_type = _CharT; - - /** - * The low-level std-format-spec parse function. - * - * @pre __begin points at the beginning of the std-format-spec. This means - * directly after the ':'. - * @pre The std-format-spec parses the entire input, or the first unmatched - * character is a '}'. - * - * @returns The iterator pointing at the last parsed character. - */ - _LIBCPP_HIDE_FROM_ABI constexpr auto parse(auto& __parse_ctx) - -> decltype(__parse_ctx.begin()) { - auto __it = __parse(__parse_ctx); - __process_arithmetic_alignment(static_cast<_Flags&>(*this)); - __process_display_type(); - return __it; - } -protected: - /** - * The low-level std-format-spec parse function. - * - * @pre __begin points at the beginning of the std-format-spec. This means - * directly after the ':'. - * @pre The std-format-spec parses the entire input, or the first unmatched - * character is a '}'. - * - * @returns The iterator pointing at the last parsed character. - */ - _LIBCPP_HIDE_FROM_ABI constexpr auto __parse(auto& __parse_ctx) - -> decltype(__parse_ctx.begin()) { - auto __begin = __parse_ctx.begin(); - auto __end = __parse_ctx.end(); - if (__begin == __end) - return __begin; - - __begin = __parser_fill_align<_CharT>::__parse(__begin, __end, - static_cast<_Flags&>(*this)); - if (__begin == __end) - return __begin; - - __begin = __parse_sign(__begin, static_cast<_Flags&>(*this)); - if (__begin == __end) - return __begin; - - __begin = __parse_alternate_form(__begin, static_cast<_Flags&>(*this)); - if (__begin == __end) - return __begin; - - __begin = __parse_zero_padding(__begin, static_cast<_Flags&>(*this)); - if (__begin == __end) - return __begin; - - __begin = __parser_width::__parse(__begin, __end, __parse_ctx); - if (__begin == __end) - return __begin; - - __begin = __parser_precision::__parse(__begin, __end, __parse_ctx); - if (__begin == __end) - return __begin; - - __begin = - __parse_locale_specific_form(__begin, static_cast<_Flags&>(*this)); - if (__begin == __end) - return __begin; - - __begin = __parse_type(__begin, static_cast<_Flags&>(*this)); - - if (__begin != __end && *__begin != _CharT('}')) - __throw_format_error( - "The format-spec should consume the input or end with a '}'"); - - return __begin; - } - - /** Processes the parsed std-format-spec based on the parsed display type. */ - _LIBCPP_HIDE_FROM_ABI constexpr void __process_display_type() { - switch (this->__type) { - case _Flags::_Type::__default: - // When no precision specified then it keeps default since that - // formatting differs from the other types. - if (this->__has_precision_field()) - this->__type = _Flags::_Type::__general_lower_case; - break; - case _Flags::_Type::__float_hexadecimal_lower_case: - case _Flags::_Type::__float_hexadecimal_upper_case: - // Precision specific behavior will be handled later. - break; - case _Flags::_Type::__scientific_lower_case: - case _Flags::_Type::__scientific_upper_case: - case _Flags::_Type::__fixed_lower_case: - case _Flags::_Type::__fixed_upper_case: - case _Flags::_Type::__general_lower_case: - case _Flags::_Type::__general_upper_case: - if (!this->__has_precision_field()) { - // Set the default precision for the call to to_chars. - this->__precision = 6; - this->__precision_as_arg = false; - } - break; - - default: - __throw_format_error("The format-spec type has a type not supported for " - "a floating-point argument"); - } - } -}; - -/** - * The parser for the std-format-spec. - * - * This implements the parser for the pointer types. - * - * See @ref __parser_string. - */ -template -class _LIBCPP_TEMPLATE_VIS __parser_pointer : public __parser_width, // provides __width(|as_arg) - public __parser_fill_align<_CharT>, // provides __fill and uses __flags - public _Flags // provides __flags -{ -public: - using char_type = _CharT; - - _LIBCPP_HIDE_FROM_ABI constexpr __parser_pointer() { - // Implements LWG3612 Inconsistent pointer alignment in std::format. - // The issue's current status is "Tentatively Ready" and libc++ status is - // still experimental. - // - // TODO FMT Validate this with the final resolution of LWG3612. - this->__alignment = _Flags::_Alignment::__right; - } - - /** - * The low-level std-format-spec parse function. - * - * @pre __begin points at the beginning of the std-format-spec. This means - * directly after the ':'. - * @pre The std-format-spec parses the entire input, or the first unmatched - * character is a '}'. - * - * @returns The iterator pointing at the last parsed character. - */ - _LIBCPP_HIDE_FROM_ABI constexpr auto parse(auto& __parse_ctx) -> decltype(__parse_ctx.begin()) { - auto __it = __parse(__parse_ctx); - __process_display_type(); - return __it; - } - -protected: - /** - * The low-level std-format-spec parse function. - * - * @pre __begin points at the beginning of the std-format-spec. This means - * directly after the ':'. - * @pre The std-format-spec parses the entire input, or the first unmatched - * character is a '}'. - * - * @returns The iterator pointing at the last parsed character. - */ - _LIBCPP_HIDE_FROM_ABI constexpr auto __parse(auto& __parse_ctx) -> decltype(__parse_ctx.begin()) { - auto __begin = __parse_ctx.begin(); - auto __end = __parse_ctx.end(); - if (__begin == __end) - return __begin; - - __begin = __parser_fill_align<_CharT>::__parse(__begin, __end, static_cast<_Flags&>(*this)); - if (__begin == __end) - return __begin; - - // An integer presentation type isn't defined in the Standard. - // Since a pointer is formatted as an integer it can be argued it's an - // integer presentation type. However there are two LWG-issues asserting it - // isn't an integer presentation type: - // - LWG3612 Inconsistent pointer alignment in std::format - // - LWG3644 std::format does not define "integer presentation type" - // - // There's a paper to make additional clarifications on the status of - // formatting pointers and proposes additional fields to be valid. That - // paper hasn't been reviewed by the Committee yet. - // - P2510 Formatting pointers - // - // The current implementation assumes formatting pointers isn't covered by - // "integer presentation type". - // TODO FMT Apply the LWG-issues/papers after approval/rejection by the Committee. - - __begin = __parser_width::__parse(__begin, __end, __parse_ctx); - if (__begin == __end) - return __begin; - - __begin = __parse_type(__begin, static_cast<_Flags&>(*this)); - - if (__begin != __end && *__begin != _CharT('}')) - __throw_format_error("The format-spec should consume the input or end with a '}'"); - - return __begin; - } - - /** Processes the parsed std-format-spec based on the parsed display type. */ - _LIBCPP_HIDE_FROM_ABI constexpr void __process_display_type() { - switch (this->__type) { - case _Flags::_Type::__default: - this->__type = _Flags::_Type::__pointer; - break; - case _Flags::_Type::__pointer: - break; - default: - __throw_format_error("The format-spec type has a type not supported for a pointer argument"); - } - } -}; - /** Helper struct returned from @ref __get_string_alignment. */ template struct _LIBCPP_TEMPLATE_VIS __string_alignment { @@ -959,6 +264,7 @@ */ bool __align; }; +#endif #ifndef _LIBCPP_HAS_NO_UNICODE namespace __detail { @@ -1267,7 +573,7 @@ size_t __result = 0; while (__first != __last) { - uint32_t __c = *__first; + wchar_t __c = *__first; __result += __column_width(__c); if (__result > __maximum) @@ -1282,7 +588,7 @@ } } // namespace __detail - +#if 0 template _LIBCPP_HIDE_FROM_ABI constexpr __string_alignment<_CharT> __get_string_alignment(const _CharT* __first, const _CharT* __last, @@ -1368,6 +674,7 @@ return {__last, __size, __size < __width}; } #else // _LIBCPP_HAS_NO_UNICODE +#if 0 template _LIBCPP_HIDE_FROM_ABI constexpr __string_alignment<_CharT> __get_string_alignment(const _CharT* __first, const _CharT* __last, @@ -1379,7 +686,9 @@ ptrdiff_t __size = __limit - __first; return {__limit, __size, __size < __width}; } +#endif #endif // _LIBCPP_HAS_NO_UNICODE +#endif } // namespace __format_spec diff --git a/libcxx/include/__format/parser_std_format_spec_v2.h b/libcxx/include/__format/parser_std_format_spec_v2.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__format/parser_std_format_spec_v2.h @@ -0,0 +1,594 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___FORMAT_PARSER_STD_FORMAT_SPEC_V2_H +#define _LIBCPP___FORMAT_PARSER_STD_FORMAT_SPEC_V2_H + +#include <__algorithm/min.h> +#include <__config> +#include <__format/format_error.h> +#include <__format/format_fwd.h> +#include <__format/format_parse_context.h> +#include <__format/format_string.h> +#include <__format/parser_std_format_spec.h> +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 17 + +/// \file Contains the std-format-spec parser. +/// +/// Most of the code can be reused in the chrono-format-spec. +/// This header has some support for the chrono-format-spec since it doesn't +/// affect the std-format-spec. + +namespace __format_spec { + +/// These fields are a filter for which elements to parse. +/// +/// They default to false so when a new field is added it needs to be opted in +/// explicitly. That way the behaviour of user-defined formatters won't change. +/// +/// @note Since this stuct is part of the ABI it might be good to reserver some +/// unreserved space. +/// +/// @note Type bool or unspecified allowing bit-fields? +struct __fields { + uint8_t __sign_ : 1 {false}; + uint8_t __alternate_form_ : 1 {false}; + uint8_t __zero_padding_ : 1 {false}; + uint8_t __precision_ : 1 {false}; + uint8_t __locale_specific_form_ : 1 {false}; + // This field is special it's always true for the std-format-spec. + // Since the chrono-format-spec doesn't use this field it can be disabled. + uint8_t __type_ : 1 {true}; +}; + +// 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}; +inline constexpr __fields __fields_floating_point{ + .__sign_ = true, + .__alternate_form_ = true, + .__zero_padding_ = true, + .__precision_ = true, + .__locale_specific_form_ = true, +}; +inline constexpr __fields __fields_string{ + .__precision_ = true, +}; +inline constexpr __fields __fields_pointer{}; + +enum class _LIBCPP_ENUM_VIS __alignment : uint8_t { + /// No alignment is set in the format string. + /// + /// Zero-padding is ignored when an alignment is selected. + /// The default alignment depends on the selected format-type. + __default, + __left, + __center, + __right +}; +enum class _LIBCPP_ENUM_VIS __sign : uint8_t { + /// No sign is set in the format string. + /// + /// The sign isn't allowed for certain format-types. By using this value + /// it's possible to detect whether or not the user explicitly set the sign + /// flag. For formatting purposes it behaves the same as @ref __minus. + __default, + __minus, + __plus, + __space +}; + +enum class _LIBCPP_ENUM_VIS __type : uint8_t { + __default, + __string, + __binary_lower_case, + __binary_upper_case, + __octal, + __decimal, + __hexadecimal_lower_case, + __hexadecimal_upper_case, + __pointer, + __char, + __float_hexadecimal_lower_case, + __float_hexadecimal_upper_case, + __scientific_lower_case, + __scientific_upper_case, + __fixed_lower_case, + __fixed_upper_case, + __general_lower_case, + __general_upper_case +}; + +template +class _LIBCPP_TEMPLATE_VIS __parser { +public: + _LIBCPP_HIDE_FROM_ABI constexpr auto __parse(basic_format_parse_context<_CharT>& __parse_ctx, __fields __fields) + -> decltype(__parse_ctx.begin()) { + + const _CharT* __begin = __parse_ctx.begin(); + const _CharT* __end = __parse_ctx.end(); + if (__begin == __end) + return __begin; + + if (__parse_fill_align(__begin, __end) && __begin == __end) + return __begin; + + if (__fields.__sign_ && __parse_sign(__begin) && __begin == __end) + return __begin; + + if (__fields.__alternate_form_ && __parse_alternate_form(__begin) && __begin == __end) + return __begin; + + if (__fields.__zero_padding_ && __parse_zero_padding(__begin) && __begin == __end) + return __begin; + + if (__parse_width(__begin, __end, __parse_ctx) && __begin == __end) + return __begin; + + if (__fields.__precision_ && __parse_precision(__begin, __end, __parse_ctx) && __begin == __end) + return __begin; + + if (__fields.__locale_specific_form_ && __parse_locale_specific_form(__begin) && __begin == __end) + return __begin; + + if (__fields.__type_) { + __parse_type(__begin); + + if (__begin != __end && *__begin != _CharT('}')) + __throw_format_error("The format-spec should consume the input or end with a '}'"); + } + + return __begin; + } + + _LIBCPP_HIDE_FROM_ABI constexpr bool __has_width() const { + _LIBCPP_ASSERT(!__width_as_arg_, "the function should only be used to test __width_"); + return __width_ > 0; + } + + _LIBCPP_HIDE_FROM_ABI constexpr bool __has_precision() const { + _LIBCPP_ASSERT(!__precision_as_arg_, "the function should only be used to test __precision_"); + return __precision_ >= 0; + } + + /// \returns a copy of the parser with the resolved dynamic sizes. + /// + /// Returning a copy makes it possible to create a const qualified format + /// member function. The parser is cheap to copy. + _LIBCPP_HIDE_FROM_ABI + __parser __resolve_dynamic_sizes(auto& __ctx) const { + __parser __result{*this}; + + if (__result.__width_as_arg_) + __result.__substitute_width_arg_id(__ctx.arg(__result.__width_)); + + if (__result.__precision_as_arg_) + __result.__substitute_precision_arg_id(__ctx.arg(__result.__precision_)); + + return __result; + } + + __alignment __alignment_ : 2 {__alignment::__default}; + __sign __sign_ : 2 {__sign::__default}; + uint8_t __alternate_form_ : 1 {false}; + uint8_t __zero_padding_ : 1 {false}; + uint8_t __locale_specific_form_ : 1 {false}; + __type __type_ : 5 {__type::__default}; + + uint8_t __width_as_arg_ : 1 {false}; + uint8_t __precision_as_arg_ : 1 {false}; + + // These two flags are used for formatting chrono. Since the struct has + // padding space left it's added to this structure. + uint8_t __weekday_name_ : 1 {false}; + uint8_t __month_name_ : 1 {false}; + // LWG 3576 will probably change this to always accept a Unicode code point + // To avoid changing the size with that change align the field so when it + // becomes 32-bit its alignment will remain the same. That also means the + // size will remain the same. (D2572 addresses the solution for LWG 3576.) + alignas(4) _CharT __fill_{_CharT(' ')}; + int32_t __width_{0}; + int32_t __precision_{-1}; + +private: + _LIBCPP_HIDE_FROM_ABI constexpr bool __parse_alignment(_CharT __c) { + switch (__c) { + case _CharT('<'): + __alignment_ = __alignment::__left; + return true; + + case _CharT('^'): + __alignment_ = __alignment::__center; + return true; + + case _CharT('>'): + __alignment_ = __alignment::__right; + return true; + } + return false; + } + + _LIBCPP_HIDE_FROM_ABI constexpr bool __parse_fill_align(const _CharT*& __begin, const _CharT* __end) { + _LIBCPP_ASSERT(__begin != __end, "when called with an empty input the function will cause " + "undefined behavior by evaluating data not in the input"); + if (__begin + 1 != __end) { + if (__parse_alignment(*(__begin + 1))) { + if (*__begin == _CharT('{') || *__begin == _CharT('}')) + __throw_format_error("The format-spec fill field contains an invalid character"); + + __fill_ = *__begin; + __begin += 2; + return true; + } + } + + if (!__parse_alignment(*__begin)) + return false; + + ++__begin; + return true; + } + + _LIBCPP_HIDE_FROM_ABI constexpr bool __parse_sign(const _CharT*& __begin) { + switch (*__begin) { + case _CharT('-'): + __sign_ = __sign::__minus; + break; + case _CharT('+'): + __sign_ = __sign::__plus; + break; + case _CharT(' '): + __sign_ = __sign::__space; + break; + default: + return false; + } + ++__begin; + return true; + } + + _LIBCPP_HIDE_FROM_ABI constexpr bool __parse_alternate_form(const _CharT*& __begin) { + if (*__begin != _CharT('#')) + return false; + + __alternate_form_ = true; + ++__begin; + return true; + } + + _LIBCPP_HIDE_FROM_ABI constexpr bool __parse_zero_padding(const _CharT*& __begin) { + if (*__begin != _CharT('0')) + return false; + + __zero_padding_ = true; + ++__begin; + return true; + } + + _LIBCPP_HIDE_FROM_ABI constexpr bool __parse_width(const _CharT*& __begin, const _CharT* __end, auto& __parse_ctx) { + if (*__begin == _CharT('0')) + __throw_format_error("A format-spec width field shouldn't have a leading zero"); + + if (*__begin == _CharT('{')) { + __format::__parse_number_result __r = __format_spec::__parse_arg_id(++__begin, __end, __parse_ctx); + __width_as_arg_ = true; + __width_ = __r.__value; + __begin = __r.__ptr; + return true; + } + + if (*__begin < _CharT('0') || *__begin > _CharT('9')) + return false; + + __format::__parse_number_result __r = __format::__parse_number(__begin, __end); + __width_ = __r.__value; + _LIBCPP_ASSERT(__width_ != 0, "A zero value isn't allowed and should be impossible, " + "due to validations in this function"); + __begin = __r.__ptr; + return true; + } + + _LIBCPP_HIDE_FROM_ABI constexpr bool __parse_precision(const _CharT*& __begin, const _CharT* __end, + auto& __parse_ctx) { + if (*__begin != _CharT('.')) + return false; + + ++__begin; + if (__begin == __end) + __throw_format_error("End of input while parsing format-spec precision"); + + if (*__begin == _CharT('{')) { + __format::__parse_number_result __arg_id = __format_spec::__parse_arg_id(++__begin, __end, __parse_ctx); + __precision_as_arg_ = true; + __precision_ = __arg_id.__value; + __begin = __arg_id.__ptr; + return true; + } + + if (*__begin < _CharT('0') || *__begin > _CharT('9')) + __throw_format_error("The format-spec precision field doesn't contain a value or arg-id"); + + __format::__parse_number_result __r = __format::__parse_number(__begin, __end); + __precision_ = __r.__value; + __precision_as_arg_ = false; + __begin = __r.__ptr; + return true; + } + + _LIBCPP_HIDE_FROM_ABI constexpr bool __parse_locale_specific_form(const _CharT*& __begin) { + if (*__begin != _CharT('L')) + return false; + + __locale_specific_form_ = true; + ++__begin; + return true; + } + + _LIBCPP_HIDE_FROM_ABI constexpr void __parse_type(const _CharT*& __begin) { + // Determines the type. It does not validate whether the selected type is + // valid. Most formatters have optional fields that are only allowed for + // certain types. These parsers need to do validation after the type has + // been parsed. So its easier to implement the validation for all types in + // the specific parse function. + switch (*__begin) { + case 'A': + __type_ = __type::__float_hexadecimal_upper_case; + break; + case 'B': + __type_ = __type::__binary_upper_case; + break; + case 'E': + __type_ = __type::__scientific_upper_case; + break; + case 'F': + __type_ = __type::__fixed_upper_case; + break; + case 'G': + __type_ = __type::__general_upper_case; + break; + case 'X': + __type_ = __type::__hexadecimal_upper_case; + break; + case 'a': + __type_ = __type::__float_hexadecimal_lower_case; + break; + case 'b': + __type_ = __type::__binary_lower_case; + break; + case 'c': + __type_ = __type::__char; + break; + case 'd': + __type_ = __type::__decimal; + break; + case 'e': + __type_ = __type::__scientific_lower_case; + break; + case 'f': + __type_ = __type::__fixed_lower_case; + break; + case 'g': + __type_ = __type::__general_lower_case; + break; + case 'o': + __type_ = __type::__octal; + break; + case 'p': + __type_ = __type::__pointer; + break; + case 's': + __type_ = __type::__string; + break; + case 'x': + __type_ = __type::__hexadecimal_lower_case; + break; + default: + return; + } + ++__begin; + } + + _LIBCPP_HIDE_FROM_ABI constexpr void __substitute_width_arg_id(auto __arg) { + _LIBCPP_ASSERT(__width_as_arg_ == true, "Substitute width called when no substitution is required"); + + __width_as_arg_ = 0; + __width_ = __format_spec::__substitute_arg_id(__arg); + if (__width_ == 0) + __throw_format_error("A format-spec width field replacement should have a positive value"); + } + + _LIBCPP_HIDE_FROM_ABI constexpr void __substitute_precision_arg_id(auto __arg) { + _LIBCPP_ASSERT(__precision_as_arg_ == true, "Substitute width called when no substitution is required"); + + __precision_as_arg_ = false; + __precision_ = __format_spec::__substitute_arg_id(__arg); + } +}; + +_LIBCPP_HIDE_FROM_ABI constexpr void __process_display_type_string(__format_spec::__type __type) { + switch (__type) { + case __format_spec::__type::__default: + case __format_spec::__type::__string: + break; + + default: + __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) + __throw_format_error("A sign field isn't allowed in this format-spec"); + + if (__parser.__alternate_form_) + __throw_format_error("An alternate form field isn't allowed in this format-spec"); + + if (__parser.__zero_padding_) + __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) { + __process_display_type_bool_string(__parser); +} + +template +_LIBCPP_HIDE_FROM_ABI constexpr void __resolve_zero_padding(__parser<_CharT>& __parser) { + __parser.__zero_padding_ &= __parser.__alignment_ == __alignment::__default; + if (!__parser.__zero_padding_ && __parser.__alignment_ == __alignment::__default) + __parser.__alignment_ = __alignment::__right; +} + +template +_LIBCPP_HIDE_FROM_ABI constexpr void __process_display_type_integer(__parser<_CharT>& __parser) { + __resolve_zero_padding(__parser); +} + +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: + __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: + __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: + __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: + __process_display_type_integer(__parser); + break; + + default: + __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: + __process_display_type_integer(__parser); + break; + + case __format_spec::__type::__char: + __process_display_type_char(__parser); + break; + + default: + __throw_format_error("The format-spec type has a type not supported for " + "an integer argument"); + } +} + +template +_LIBCPP_HIDE_FROM_ABI constexpr void __process_parsed_floating_point(__parser<_CharT>& __parser) { + __resolve_zero_padding(__parser); + + switch (__parser.__type_) { + case __format_spec::__type::__default: + // When no precision specified then it keeps default since that + // formatting differs from the other types. + if (__parser.__precision_as_arg_ || __parser.__has_precision()) + __parser.__type_ = __format_spec::__type::__general_lower_case; + break; + case __format_spec::__type::__float_hexadecimal_lower_case: + case __format_spec::__type::__float_hexadecimal_upper_case: + // Precision specific behavior will be handled later. + break; + case __format_spec::__type::__scientific_lower_case: + case __format_spec::__type::__scientific_upper_case: + case __format_spec::__type::__fixed_lower_case: + case __format_spec::__type::__fixed_upper_case: + case __format_spec::__type::__general_lower_case: + case __format_spec::__type::__general_upper_case: + if (!__parser.__precision_as_arg_ && !__parser.__has_precision()) + // Set the default precision for the call to to_chars. + __parser.__precision_ = 6; + break; + + default: + __throw_format_error("The format-spec type has a type not supported for " + "a floating-point argument"); + } +} + +_LIBCPP_HIDE_FROM_ABI constexpr void __process_display_type_pointer(__format_spec::__type __type) { + switch (__type) { + case __format_spec::__type::__default: + case __format_spec::__type::__pointer: + break; + + default: + __throw_format_error("The format-spec type has a type not supported for " + "a pointer argument"); + } +} + +} // namespace __format_spec + +#endif //_LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___FORMAT_PARSER_STD_FORMAT_SPEC_V2_H diff --git a/libcxx/include/format b/libcxx/include/format --- a/libcxx/include/format +++ b/libcxx/include/format @@ -141,9 +141,12 @@ #include <__format/formatter_char.h> #include <__format/formatter_floating_point.h> #include <__format/formatter_integer.h> +#include <__format/formatter_output.h> #include <__format/formatter_pointer.h> #include <__format/formatter_string.h> +#include <__format/formatter_v2.h> #include <__format/parser_std_format_spec.h> +#include <__format/parser_std_format_spec_v2.h> #include <__variant/monostate.h> #include #include diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -553,9 +553,12 @@ module formatter_floating_point { private header "__format/formatter_floating_point.h" } module formatter_integer { private header "__format/formatter_integer.h" } module formatter_integral { private header "__format/formatter_integral.h" } + module formatter_output { private header "__format/formatter_output.h" } module formatter_pointer { private header "__format/formatter_pointer.h" } module formatter_string { private header "__format/formatter_string.h" } + module formatter_v2 { private header "__format/formatter_v2.h" } module parser_std_format_spec { private header "__format/parser_std_format_spec.h" } + module parser_std_format_spec_v2 { private header "__format/parser_std_format_spec_v2.h" } } } module forward_list { diff --git a/libcxx/test/libcxx/clang_tidy.sh.cpp b/libcxx/test/libcxx/clang_tidy.sh.cpp --- a/libcxx/test/libcxx/clang_tidy.sh.cpp +++ b/libcxx/test/libcxx/clang_tidy.sh.cpp @@ -9,6 +9,8 @@ // REQUIRES: has-clang-tidy // XFAIL: modules-build +// Display the clang-tidy version for debugging purposes. +// RUN: clang-tidy --version // RUN: clang-tidy %s --warnings-as-errors=* -header-filter=.* -- -Wno-unknown-warning-option %{compile_flags} // -Wno-unknown-warning-option tells clang-tidy to ignore '-W' command-line arguments that it doesn't know. // There are some GCC-specific ones where clang-tidy would warn otherwise. diff --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp --- a/libcxx/test/libcxx/private_headers.verify.cpp +++ b/libcxx/test/libcxx/private_headers.verify.cpp @@ -253,9 +253,12 @@ #include <__format/formatter_floating_point.h> // expected-error@*:* {{use of private header from outside its module: '__format/formatter_floating_point.h'}} #include <__format/formatter_integer.h> // expected-error@*:* {{use of private header from outside its module: '__format/formatter_integer.h'}} #include <__format/formatter_integral.h> // expected-error@*:* {{use of private header from outside its module: '__format/formatter_integral.h'}} +#include <__format/formatter_output.h> // expected-error@*:* {{use of private header from outside its module: '__format/formatter_output.h'}} #include <__format/formatter_pointer.h> // expected-error@*:* {{use of private header from outside its module: '__format/formatter_pointer.h'}} #include <__format/formatter_string.h> // expected-error@*:* {{use of private header from outside its module: '__format/formatter_string.h'}} +#include <__format/formatter_v2.h> // expected-error@*:* {{use of private header from outside its module: '__format/formatter_v2.h'}} #include <__format/parser_std_format_spec.h> // expected-error@*:* {{use of private header from outside its module: '__format/parser_std_format_spec.h'}} +#include <__format/parser_std_format_spec_v2.h> // expected-error@*:* {{use of private header from outside its module: '__format/parser_std_format_spec_v2.h'}} #include <__functional/binary_function.h> // expected-error@*:* {{use of private header from outside its module: '__functional/binary_function.h'}} #include <__functional/binary_negate.h> // expected-error@*:* {{use of private header from outside its module: '__functional/binary_negate.h'}} #include <__functional/bind.h> // expected-error@*:* {{use of private header from outside its module: '__functional/bind.h'}} diff --git a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/concepts_precision.h b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/concepts_precision.h deleted file mode 100644 --- a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/concepts_precision.h +++ /dev/null @@ -1,21 +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 -// -//===----------------------------------------------------------------------===// - -#ifndef LIBCXX_TEST_STD_UTILITIES_FORMAT_FORMAT_STRING_FORMAT_STRING_STD_CONCEPTS_PRECISION_H -#define LIBCXX_TEST_STD_UTILITIES_FORMAT_FORMAT_STRING_FORMAT_STRING_STD_CONCEPTS_PRECISION_H - -template -concept has_precision = requires(T parser) { - parser.__precision; -}; - -template -concept has_precision_as_arg = requires(T parser) { - parser.__precision_as_arg; -}; - -#endif // LIBCXX_TEST_STD_UTILITIES_FORMAT_FORMAT_STRING_FORMAT_STRING_STD_CONCEPTS_PRECISION_H 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_floating_point.pass.cpp b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/std_format_spec_floating_point.pass.cpp deleted file mode 100644 --- a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/std_format_spec_floating_point.pass.cpp +++ /dev/null @@ -1,352 +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 floating-point 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_floating_point; - -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; - uint32_t precision = std::__format::__number_max; - bool precision_as_arg = true; - bool locale_specific_form = false; - _Flags::_Type type = _Flags::_Type::__default; -}; - -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.__precision == expected.precision); - assert(parser.__precision_as_arg == expected.precision_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); - assert(parser.__precision == std::__format::__number_max); - assert(parser.__precision_as_arg == true); - assert(parser.__locale_specific_form == false); - assert(parser.__type == _Flags::_Type::__default); - - // Depending on whether or not a precision is specified the results differ. - // Table 65: Meaning of type options for floating-point types [tab:format.type.float] - - test({}, 0, CSTR("}")); - test({.precision = 0, .precision_as_arg = false, .type = _Flags::_Type::__general_lower_case}, 2, CSTR(".0}")); - test({.precision = 1, .precision_as_arg = true, .type = _Flags::_Type::__general_lower_case}, 4, CSTR(".{1}}")); - - test({.type = _Flags::_Type::__float_hexadecimal_lower_case}, 1, CSTR("a}")); - test({.type = _Flags::_Type::__float_hexadecimal_upper_case}, 1, CSTR("A}")); - - test({.precision = 6, .precision_as_arg = false, .type = _Flags::_Type::__scientific_lower_case}, 1, CSTR("e}")); - test({.precision = 0, .precision_as_arg = false, .type = _Flags::_Type::__scientific_lower_case}, 3, CSTR(".0e}")); - test({.precision = 1, .precision_as_arg = true, .type = _Flags::_Type::__scientific_lower_case}, 5, CSTR(".{1}e}")); - test({.precision = 6, .precision_as_arg = false, .type = _Flags::_Type::__scientific_upper_case}, 1, CSTR("E}")); - test({.precision = 0, .precision_as_arg = false, .type = _Flags::_Type::__scientific_upper_case}, 3, CSTR(".0E}")); - test({.precision = 1, .precision_as_arg = true, .type = _Flags::_Type::__scientific_upper_case}, 5, CSTR(".{1}E}")); - - test({.precision = 6, .precision_as_arg = false, .type = _Flags::_Type::__fixed_lower_case}, 1, CSTR("f}")); - test({.precision = 0, .precision_as_arg = false, .type = _Flags::_Type::__fixed_lower_case}, 3, CSTR(".0f}")); - test({.precision = 1, .precision_as_arg = true, .type = _Flags::_Type::__fixed_lower_case}, 5, CSTR(".{1}f}")); - test({.precision = 6, .precision_as_arg = false, .type = _Flags::_Type::__fixed_upper_case}, 1, CSTR("F}")); - test({.precision = 0, .precision_as_arg = false, .type = _Flags::_Type::__fixed_upper_case}, 3, CSTR(".0F}")); - test({.precision = 1, .precision_as_arg = true, .type = _Flags::_Type::__fixed_upper_case}, 5, CSTR(".{1}F}")); - - test({.precision = 6, .precision_as_arg = false, .type = _Flags::_Type::__general_lower_case}, 1, CSTR("g}")); - test({.precision = 0, .precision_as_arg = false, .type = _Flags::_Type::__general_lower_case}, 3, CSTR(".0g}")); - test({.precision = 1, .precision_as_arg = true, .type = _Flags::_Type::__general_lower_case}, 5, CSTR(".{1}g}")); - test({.precision = 6, .precision_as_arg = false, .type = _Flags::_Type::__general_upper_case}, 1, CSTR("G}")); - test({.precision = 0, .precision_as_arg = false, .type = _Flags::_Type::__general_upper_case}, 3, CSTR(".0G}")); - test({.precision = 1, .precision_as_arg = true, .type = _Flags::_Type::__general_upper_case}, 5, CSTR(".{1}G}")); - - // *** 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(" }")); - - // *** Alternate form *** - test({.alternate_form = true}, 1, CSTR("#}")); - - // *** 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 - // error? - 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}")); - - // *** 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({.precision = 0, .precision_as_arg = false, .type = _Flags::_Type::__general_lower_case}, 2, CSTR(".0}")); - test({.precision = 1, .precision_as_arg = false, .type = _Flags::_Type::__general_lower_case}, 2, CSTR(".1}")); - test({.precision = 10, .precision_as_arg = false, .type = _Flags::_Type::__general_lower_case}, 3, CSTR(".10}")); - test({.precision = 1000, .precision_as_arg = false, .type = _Flags::_Type::__general_lower_case}, 5, CSTR(".1000}")); - test({.precision = 1000000, .precision_as_arg = false, .type = _Flags::_Type::__general_lower_case}, 8, - CSTR(".1000000}")); - - test({.precision = 0, .precision_as_arg = true, .type = _Flags::_Type::__general_lower_case}, 3, CSTR(".{}}")); - test({.precision = 0, .precision_as_arg = true, .type = _Flags::_Type::__general_lower_case}, 4, CSTR(".{0}}")); - test({.precision = 1, .precision_as_arg = true, .type = _Flags::_Type::__general_lower_case}, 4, CSTR(".{1}}")); - - test_exception>("The format-spec precision field doesn't contain a value or arg-id", CSTR(".a")); - test_exception>("The format-spec precision field doesn't contain a value or arg-id", CSTR(".:")); - - static_assert(std::__format::__number_max == 2'147'483'647, "Update the assert and the test."); - test({.precision = 2'147'483'647, .precision_as_arg = false, .type = _Flags::_Type::__general_lower_case}, 11, - 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({.precision = 2'147'483'646, .precision_as_arg = true, .type = _Flags::_Type::__general_lower_case}, 13, - 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}")); - - // *** Width & Precision *** - test({.width = 1, - .width_as_arg = false, - .precision = 0, - .precision_as_arg = false, - .type = _Flags::_Type::__general_lower_case}, - 3, CSTR("1.0}")); - test({.width = 0, - .width_as_arg = true, - .precision = 1, - .precision_as_arg = true, - .type = _Flags::_Type::__general_lower_case}, - 5, CSTR("{}.{}}")); - test({.width = 10, - .width_as_arg = true, - .precision = 9, - .precision_as_arg = true, - .type = _Flags::_Type::__general_lower_case}, - 8, CSTR("{10}.{9}}")); - - // *** Locale-specific form *** - test({.locale_specific_form = true}, 1, CSTR("L}")); - - // *** Type *** - { - const char* unsuported_type = "The format-spec type has a type not supported for a floating-point argument"; - const char* not_a_type = "The format-spec should consume the input or end with a '}'"; - - test({.type = _Flags::_Type::__float_hexadecimal_upper_case}, 1, CSTR("A}")); - test_exception>(unsuported_type, CSTR("B}")); - test_exception>(not_a_type, CSTR("C}")); - test_exception>(not_a_type, CSTR("D}")); - test({.precision = 6, .precision_as_arg = false, .type = _Flags::_Type::__scientific_upper_case}, 1, CSTR("E}")); - test({.precision = 6, .precision_as_arg = false, .type = _Flags::_Type::__fixed_upper_case}, 1, CSTR("F}")); - test({.precision = 6, .precision_as_arg = false, .type = _Flags::_Type::__general_upper_case}, 1, 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_exception>(unsuported_type, CSTR("X}")); - test_exception>(not_a_type, CSTR("Y}")); - test_exception>(not_a_type, CSTR("Z}")); - - test({.type = _Flags::_Type::__float_hexadecimal_lower_case}, 1, CSTR("a}")); - test_exception>(unsuported_type, CSTR("b}")); - test_exception>(unsuported_type, CSTR("c}")); - test_exception>(unsuported_type, CSTR("d}")); - test({.precision = 6, .precision_as_arg = false, .type = _Flags::_Type::__scientific_lower_case}, 1, CSTR("e}")); - test({.precision = 6, .precision_as_arg = false, .type = _Flags::_Type::__fixed_lower_case}, 1, CSTR("f}")); - test({.precision = 6, .precision_as_arg = false, .type = _Flags::_Type::__general_lower_case}, 1, 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_exception>(unsuported_type, 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_exception>(unsuported_type, 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**) { -#if !defined(_WIN32) && !defined(_AIX) - // Make sure the parsers match the expectations. The layout of the - // subobjects is chosen to minimize the size required. - static_assert(sizeof(Parser) == 3 * sizeof(uint32_t)); -# ifndef TEST_HAS_NO_WIDE_CHARACTERS - static_assert(sizeof(Parser) == (sizeof(wchar_t) <= 2 ? 3 * sizeof(uint32_t) : 4 * sizeof(uint32_t))); -# endif -#endif - - 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; -} diff --git a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/std_format_spec_parser.pass.cpp b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/std_format_spec_parser.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/std_format_spec_parser.pass.cpp @@ -0,0 +1,533 @@ +//===----------------------------------------------------------------------===// +// 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 + +// + +#include +#include +#include + +#include "test_macros.h" +#include "make_string.h" + +#define SV(S) MAKE_STRING_VIEW(CharT, S) + +template +constexpr void check_exception(F&& f) { + if (std::is_constant_evaluated()) + return; + +#ifndef TEST_HAS_NO_EXCEPTIONS + try { + f(); + assert(false); + } catch (std::format_error&) { + return; + } + assert(false); +#else + (void)f; +#endif +} + +// Valiate the values of the default constructed fields and the provided named field selections. +static_assert(std::__format_spec::__fields{}.__sign_ == false && + std::__format_spec::__fields{}.__alternate_form_ == false && + std::__format_spec::__fields{}.__zero_padding_ == false && + std::__format_spec::__fields{}.__precision_ == false && + std::__format_spec::__fields{}.__locale_specific_form_ == false); + +static_assert(std::__format_spec::__fields_integral.__sign_ == true && + std::__format_spec::__fields_integral.__alternate_form_ == true && + std::__format_spec::__fields_integral.__zero_padding_ == true && + std::__format_spec::__fields_integral.__precision_ == false && + std::__format_spec::__fields_integral.__locale_specific_form_ == true); + +static_assert(std::__format_spec::__fields_floating_point.__sign_ == true && + std::__format_spec::__fields_floating_point.__alternate_form_ == true && + std::__format_spec::__fields_floating_point.__zero_padding_ == true && + std::__format_spec::__fields_floating_point.__precision_ == true && + std::__format_spec::__fields_floating_point.__locale_specific_form_ == true); + +static_assert(std::__format_spec::__fields_pointer.__sign_ == false && + std::__format_spec::__fields_pointer.__alternate_form_ == false && + std::__format_spec::__fields_pointer.__zero_padding_ == false && + std::__format_spec::__fields_pointer.__precision_ == false && + std::__format_spec::__fields_pointer.__locale_specific_form_ == false); + +static_assert(std::__format_spec::__fields_string.__sign_ == false && + std::__format_spec::__fields_string.__alternate_form_ == false && + std::__format_spec::__fields_string.__zero_padding_ == false && + std::__format_spec::__fields_string.__precision_ == true && + std::__format_spec::__fields_string.__locale_specific_form_ == false); + +inline constexpr std::__format_spec::__fields all_fields{true, true, true, true, true}; + +// A std::basic_format_parse_context needs a number of arguments. +// This number is large enough for the parser not to fail on a lack of arguments. +constexpr size_t num_args = 100; + +template +constexpr void test_align_fill(std::__format_spec::__fields fields) { + { + std::__format_spec::__parser parser; + assert(parser.__fill_ == CharT(' ')); + + std::basic_format_parse_context parse_ctx{SV(""), num_args}; + parser.__parse(parse_ctx, fields); + + assert(parser.__fill_ == CharT(' ')); + } + { + std::__format_spec::__parser parser; + assert(parser.__fill_ == CharT(' ')); + + std::basic_format_parse_context parse_ctx{SV("a<"), num_args}; + parser.__parse(parse_ctx, fields); + + assert(parser.__fill_ == CharT('a')); + } +} + +template +constexpr void test_align_fill() { + test_align_fill(std::__format_spec::__fields{}); + test_align_fill(all_fields); +} + +template +constexpr void test_sign(std::__format_spec::__fields fields) { + { + std::__format_spec::__parser parser; + assert(parser.__sign_ == std::__format_spec::__sign::__default); + + std::basic_format_parse_context parse_ctx{SV(""), num_args}; + parser.__parse(parse_ctx, fields); + assert(parser.__sign_ == std::__format_spec::__sign::__default); + } + { + std::__format_spec::__parser parser; + assert(parser.__sign_ == std::__format_spec::__sign::__default); + { + std::basic_format_parse_context parse_ctx{SV("-"), num_args}; + parser.__parse(parse_ctx, fields); + assert(parser.__sign_ == std::__format_spec::__sign::__minus); + } + { + std::basic_format_parse_context parse_ctx{SV("+"), num_args}; + parser.__parse(parse_ctx, fields); + assert(parser.__sign_ == std::__format_spec::__sign::__plus); + } + { + std::basic_format_parse_context parse_ctx{SV(" "), num_args}; + parser.__parse(parse_ctx, fields); + assert(parser.__sign_ == std::__format_spec::__sign::__space); + } + } +} + +template +constexpr void test_sign() { + test_sign(std::__format_spec::__fields{.__sign_ = true}); + test_sign(all_fields); + + { + std::__format_spec::__parser parser; + std::basic_format_parse_context parse_ctx{SV(""), num_args}; + parser.__parse(parse_ctx, std::__format_spec::__fields{.__sign_ = false}); + } + check_exception([] { + std::__format_spec::__parser parser; + std::basic_format_parse_context parse_ctx{SV("-"), num_args}; + parser.__parse(parse_ctx, std::__format_spec::__fields{.__sign_ = false}); + }); +} + +template +constexpr void test_alternate_form(std::__format_spec::__fields fields) { + { + std::__format_spec::__parser parser; + assert(parser.__alternate_form_ == false); + + std::basic_format_parse_context parse_ctx{SV(""), num_args}; + parser.__parse(parse_ctx, fields); + assert(parser.__alternate_form_ == false); + } + { + std::__format_spec::__parser parser; + + std::basic_format_parse_context parse_ctx{SV("#"), num_args}; + parser.__parse(parse_ctx, fields); + assert(parser.__alternate_form_ == true); + } +} + +template +constexpr void test_alternate_form() { + test_alternate_form(std::__format_spec::__fields{.__alternate_form_ = true}); + test_alternate_form(all_fields); + + { + std::__format_spec::__parser parser; + std::basic_format_parse_context parse_ctx{SV(""), num_args}; + parser.__parse(parse_ctx, std::__format_spec::__fields{.__alternate_form_ = false}); + } + check_exception([] { + std::__format_spec::__parser parser; + std::basic_format_parse_context parse_ctx{SV("#"), num_args}; + parser.__parse(parse_ctx, std::__format_spec::__fields{.__alternate_form_ = false}); + }); +} + +template +constexpr void test_zero_padding(std::__format_spec::__fields fields) { + { + std::__format_spec::__parser parser; + assert(parser.__zero_padding_ == false); + + std::basic_format_parse_context parse_ctx{SV(""), num_args}; + parser.__parse(parse_ctx, fields); + assert(parser.__zero_padding_ == false); + } + { + std::__format_spec::__parser parser; + + std::basic_format_parse_context parse_ctx{SV("0"), num_args}; + parser.__parse(parse_ctx, fields); + assert(parser.__zero_padding_ == true); + } +} + +template +constexpr void test_zero_padding() { + test_zero_padding(std::__format_spec::__fields{.__zero_padding_ = true}); + test_zero_padding(all_fields); + + { + std::__format_spec::__parser parser; + std::basic_format_parse_context parse_ctx{SV(""), num_args}; + parser.__parse(parse_ctx, std::__format_spec::__fields{.__zero_padding_ = false}); + } + check_exception([] { + std::__format_spec::__parser parser; + std::basic_format_parse_context parse_ctx{SV("0"), num_args}; + parser.__parse(parse_ctx, std::__format_spec::__fields{.__zero_padding_ = false}); + }); +} + +template +constexpr void test_width(std::__format_spec::__fields fields) { + // XXX test limits int32_max + { + std::__format_spec::__parser parser; + assert(!parser.__has_width()); + assert(!parser.__width_as_arg_); + assert(parser.__width_ == 0); + } + { + std::__format_spec::__parser parser; + std::basic_format_parse_context parse_ctx{SV("1"), num_args}; + parser.__parse(parse_ctx, fields); + + assert(parser.__zero_padding_ == false); + assert(parser.__has_width()); + assert(!parser.__width_as_arg_); + assert(parser.__width_ == 1); + } + if (fields.__zero_padding_) { + std::__format_spec::__parser parser; + std::basic_format_parse_context parse_ctx{SV("09"), num_args}; + parser.__parse(parse_ctx, fields); + + assert(parser.__zero_padding_ == true); + assert(parser.__has_width()); + assert(!parser.__width_as_arg_); + assert(parser.__width_ == 9); + } + if (fields.__zero_padding_) + check_exception([fields] { + std::__format_spec::__parser parser; + std::basic_format_parse_context parse_ctx{SV("00"), num_args}; + parser.__parse(parse_ctx, fields); + }); + else + check_exception([fields] { + std::__format_spec::__parser parser; + std::basic_format_parse_context parse_ctx{SV("0"), num_args}; + parser.__parse(parse_ctx, fields); + }); + { + std::__format_spec::__parser parser; + std::basic_format_parse_context parse_ctx{SV("{}"), num_args}; + parser.__parse(parse_ctx, fields); + + assert(parser.__width_as_arg_); + assert(parser.__width_ == 0); + } + { + std::__format_spec::__parser parser; + std::basic_format_parse_context parse_ctx{SV("{42}"), 43}; + parser.__parse(parse_ctx, fields); + + assert(parser.__width_as_arg_); + assert(parser.__width_ == 42); + } +} + +template +constexpr void test_width() { + test_width(std::__format_spec::__fields{}); + test_width(all_fields); +} + +template +constexpr void test_precision(std::__format_spec::__fields fields) { + // XXX test limits int32_max + { + std::__format_spec::__parser parser; + assert(!parser.__has_precision()); + assert(!parser.__precision_as_arg_); + LIBCPP_ASSERT(parser.__precision_ == -1 /*std::__format::__number_max*/); + } + { + std::__format_spec::__parser parser; + std::basic_format_parse_context parse_ctx{SV(".0"), num_args}; + parser.__parse(parse_ctx, fields); + + assert(parser.__has_precision()); + assert(!parser.__precision_as_arg_); + assert(parser.__precision_ == 0); + } + { + std::__format_spec::__parser parser; + std::basic_format_parse_context parse_ctx{SV(".{}"), num_args}; + parser.__parse(parse_ctx, fields); + + assert(parser.__precision_as_arg_); + assert(parser.__precision_ == 0); + /* + parser.__set_precision(42); + assert(parser.__has_precision()); + assert(!parser.__precision_as_arg_); + assert(parser.__precision_ == 42);*/ + } + { + std::__format_spec::__parser parser; + std::basic_format_parse_context parse_ctx{SV("{}.{}"), num_args}; + parser.__parse(parse_ctx, fields); + + assert(parser.__width_as_arg_); + assert(parser.__width_ == 0); + + assert(parser.__precision_as_arg_); + assert(parser.__precision_ == 1); + } + { + std::__format_spec::__parser parser; + std::basic_format_parse_context parse_ctx{SV(".{42}"), num_args}; + parser.__parse(parse_ctx, fields); + + assert(parser.__precision_as_arg_); + assert(parser.__precision_ == 42); + } +} + +template +constexpr void test_precision() { + test_precision(std::__format_spec::__fields{.__precision_ = true}); + test_precision(all_fields); + + { + std::__format_spec::__parser parser; + std::basic_format_parse_context parse_ctx{SV(""), num_args}; + parser.__parse(parse_ctx, std::__format_spec::__fields{.__precision_ = false}); + } + check_exception([] { + std::__format_spec::__parser parser; + std::basic_format_parse_context parse_ctx{SV(".1"), num_args}; + parser.__parse(parse_ctx, std::__format_spec::__fields{.__precision_ = false}); + }); +} + +template +constexpr void test_locale_specific_form(std::__format_spec::__fields fields) { + { + std::__format_spec::__parser parser; + std::basic_format_parse_context parse_ctx{SV(""), num_args}; + parser.__parse(parse_ctx, fields); + + assert(parser.__locale_specific_form_ == false); + } + { + std::__format_spec::__parser parser; + std::basic_format_parse_context parse_ctx{SV("L"), num_args}; + parser.__parse(parse_ctx, fields); + + assert(parser.__locale_specific_form_ == true); + } +} + +template +constexpr void test_locale_specific_form() { + test_locale_specific_form(std::__format_spec::__fields{.__locale_specific_form_ = true}); + test_locale_specific_form(all_fields); + + { + std::__format_spec::__parser parser; + std::basic_format_parse_context parse_ctx{SV(""), num_args}; + parser.__parse(parse_ctx, std::__format_spec::__fields{.__locale_specific_form_ = false}); + } + check_exception([] { + std::__format_spec::__parser parser; + std::basic_format_parse_context parse_ctx{SV("L"), num_args}; + parser.__parse(parse_ctx, std::__format_spec::__fields{.__locale_specific_form_ = false}); + }); +} + +template +constexpr void test_type(std::__format_spec::__fields fields) { + { + std::__format_spec::__parser parser; + assert(parser.__type_ == std::__format_spec::__type::__default); + + std::basic_format_parse_context parse_ctx{SV(""), num_args}; + parser.__parse(parse_ctx, fields); + assert(parser.__type_ == std::__format_spec::__type::__default); + } + { + std::__format_spec::__parser parser; + assert(parser.__type_ == std::__format_spec::__type::__default); + { + std::basic_format_parse_context parse_ctx{SV("a"), num_args}; + parser.__parse(parse_ctx, fields); + assert(parser.__type_ == std::__format_spec::__type::__float_hexadecimal_lower_case); + } + { + std::basic_format_parse_context parse_ctx{SV("A"), num_args}; + parser.__parse(parse_ctx, fields); + assert(parser.__type_ == std::__format_spec::__type::__float_hexadecimal_upper_case); + } + { + std::basic_format_parse_context parse_ctx{SV("b"), num_args}; + parser.__parse(parse_ctx, fields); + assert(parser.__type_ == std::__format_spec::__type::__binary_lower_case); + } + { + std::basic_format_parse_context parse_ctx{SV("B"), num_args}; + parser.__parse(parse_ctx, fields); + assert(parser.__type_ == std::__format_spec::__type::__binary_upper_case); + } + { + std::basic_format_parse_context parse_ctx{SV("c"), num_args}; + parser.__parse(parse_ctx, fields); + assert(parser.__type_ == std::__format_spec::__type::__char); + } + { + std::basic_format_parse_context parse_ctx{SV("d"), num_args}; + parser.__parse(parse_ctx, fields); + assert(parser.__type_ == std::__format_spec::__type::__decimal); + } + { + std::basic_format_parse_context parse_ctx{SV("e"), num_args}; + parser.__parse(parse_ctx, fields); + assert(parser.__type_ == std::__format_spec::__type::__scientific_lower_case); + } + { + std::basic_format_parse_context parse_ctx{SV("E"), num_args}; + parser.__parse(parse_ctx, fields); + assert(parser.__type_ == std::__format_spec::__type::__scientific_upper_case); + } + { + std::basic_format_parse_context parse_ctx{SV("f"), num_args}; + parser.__parse(parse_ctx, fields); + assert(parser.__type_ == std::__format_spec::__type::__fixed_lower_case); + } + { + std::basic_format_parse_context parse_ctx{SV("F"), num_args}; + parser.__parse(parse_ctx, fields); + assert(parser.__type_ == std::__format_spec::__type::__fixed_upper_case); + } + { + std::basic_format_parse_context parse_ctx{SV("g"), num_args}; + parser.__parse(parse_ctx, fields); + assert(parser.__type_ == std::__format_spec::__type::__general_lower_case); + } + { + std::basic_format_parse_context parse_ctx{SV("G"), num_args}; + parser.__parse(parse_ctx, fields); + assert(parser.__type_ == std::__format_spec::__type::__general_upper_case); + } + { + std::basic_format_parse_context parse_ctx{SV("o"), num_args}; + parser.__parse(parse_ctx, fields); + assert(parser.__type_ == std::__format_spec::__type::__octal); + } + { + std::basic_format_parse_context parse_ctx{SV("p"), num_args}; + parser.__parse(parse_ctx, fields); + assert(parser.__type_ == std::__format_spec::__type::__pointer); + } + { + std::basic_format_parse_context parse_ctx{SV("s"), num_args}; + parser.__parse(parse_ctx, fields); + assert(parser.__type_ == std::__format_spec::__type::__string); + } + { + std::basic_format_parse_context parse_ctx{SV("x"), num_args}; + parser.__parse(parse_ctx, fields); + assert(parser.__type_ == std::__format_spec::__type::__hexadecimal_lower_case); + } + { + std::basic_format_parse_context parse_ctx{SV("X"), num_args}; + parser.__parse(parse_ctx, fields); + assert(parser.__type_ == std::__format_spec::__type::__hexadecimal_upper_case); + } + } +} + +template +constexpr void test_type() { + test_type(std::__format_spec::__fields{}); + test_type(all_fields); +} + +template +constexpr void test() { + + test_align_fill(); + test_sign(); + test_alternate_form(); + test_zero_padding(); + + test_width(); + test_precision(); + test_locale_specific_form(); + test_type(); + + // The parser should fit in 128-bit allowing it to be passed in the registers on 64-bit platforms. + static_assert(sizeof(std::__format_spec::__parser) == 2 * sizeof(int64_t)); + static_assert(std::is_trivially_copyable_v>); +} + +constexpr bool test() { + test(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test(); +#endif + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/std_format_spec_pointer.pass.cpp b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/std_format_spec_pointer.pass.cpp deleted file mode 100644 --- a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/std_format_spec_pointer.pass.cpp +++ /dev/null @@ -1,253 +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 pointer 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_pointer; - -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::__pointer; -}; - -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::__right); - 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); - assert(parser.__locale_specific_form == false); - assert(parser.__type == _Flags::_Type::__default); - - test({}, 0, CSTR("}")); - - // *** 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_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("-")); - test_exception>("The format-spec should consume the input or end with a '}'", CSTR(" ")); - - // *** Alternate form *** - test_exception>("The format-spec should consume the input or end with a '}'", CSTR("#")); - - // *** Zero padding *** - test_exception>("A format-spec width field shouldn't have a leading zero", CSTR("0")); - - // *** 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_exception>("The format-spec should consume the input or end with a '}'", CSTR("L")); - - // *** Type *** - { - const char* unsuported_type = "The format-spec type has a type not supported for a pointer argument"; - const char* not_a_type = "The format-spec should consume the input or end with a '}'"; - - test_exception>(unsuported_type, CSTR("A}")); - test_exception>(unsuported_type, 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_exception>("The format-spec should consume the input or end with a '}'", 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_exception>(unsuported_type, CSTR("X}")); - test_exception>(not_a_type, CSTR("Y}")); - test_exception>(not_a_type, CSTR("Z}")); - - test_exception>(unsuported_type, CSTR("a}")); - test_exception>(unsuported_type, CSTR("b}")); - test_exception>(unsuported_type, CSTR("c}")); - test_exception>(unsuported_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_exception>(not_a_type, CSTR("l}")); - test_exception>(not_a_type, CSTR("m}")); - test_exception>(not_a_type, CSTR("n}")); - test_exception>(unsuported_type, CSTR("o}")); - test({.type = _Flags::_Type::__pointer}, 1, 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_exception>(unsuported_type, 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**) { -#if !defined(_WIN32) && !defined(_AIX) - // 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 - - test(); - static_assert(test()); - - return 0; -} diff --git a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/std_format_spec_string.pass.cpp b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/std_format_spec_string.pass.cpp deleted file mode 100644 --- a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/std_format_spec_string.pass.cpp +++ /dev/null @@ -1,367 +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 string type. - -#include -#include -#ifndef _LIBCPP_HAS_NO_LOCALIZATION -# include -#endif - -#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_string; - -template -struct Expected { - CharT fill = CharT(' '); - _Flags::_Alignment alignment = _Flags::_Alignment::__left; - uint32_t width = 0; - bool width_as_arg = false; - uint32_t precision = std::__format::__number_max; - bool precision_as_arg = true; - _Flags::_Type type = _Flags::_Type::__default; -}; - -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 == _Flags::_Sign::__default); - assert(parser.__alternate_form == false); - assert(parser.__zero_padding == false); - assert(parser.__width == expected.width); - assert(parser.__width_as_arg == expected.width_as_arg); - assert(parser.__precision == expected.precision); - assert(parser.__precision_as_arg == expected.precision_as_arg); - assert(parser.__locale_specific_form == false); - 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::__left); - 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); - assert(parser.__precision == std::__format::__number_max); - assert(parser.__precision_as_arg == true); - assert(parser.__locale_specific_form == false); - assert(parser.__type == _Flags::_Type::__default); - - test({}, 0, CSTR("}")); - - // *** 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_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("-")); - test_exception>( - "The format-spec should consume the input or end with a '}'", CSTR(" ")); - - // *** Alternate form *** - test_exception>( - "The format-spec should consume the input or end with a '}'", CSTR("#")); - - // *** Zero padding *** - test_exception>( - "A format-spec width field shouldn't have a leading zero", CSTR("0")); - - // *** 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({.precision = 0, .precision_as_arg = false}, 2, CSTR(".0}")); - test({.precision = 1, .precision_as_arg = false}, 2, CSTR(".1}")); - test({.precision = 10, .precision_as_arg = false}, 3, CSTR(".10}")); - test({.precision = 1000, .precision_as_arg = false}, 5, CSTR(".1000}")); - test({.precision = 1000000, .precision_as_arg = false}, 8, CSTR(".1000000}")); - - test({.precision = 0, .precision_as_arg = true}, 3, CSTR(".{}}")); - test({.precision = 0, .precision_as_arg = true}, 4, CSTR(".{0}}")); - test({.precision = 1, .precision_as_arg = true}, 4, CSTR(".{1}}")); - - test_exception>( - "The format-spec precision field doesn't contain a value or arg-id", - CSTR(".a")); - test_exception>( - "The format-spec precision field doesn't contain a value or arg-id", - CSTR(".:")); - - static_assert(std::__format::__number_max == 2'147'483'647, - "Update the assert and the test."); - test({.precision = 2'147'483'647, .precision_as_arg = false}, 11, - 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({.precision = 2'147'483'646, .precision_as_arg = true}, 13, - 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}")); - - // *** Width & Precision *** - test({.width = 1, - .width_as_arg = false, - .precision = 0, - .precision_as_arg = false}, - 3, CSTR("1.0}")); - test({.width = 0, - .width_as_arg = true, - .precision = 1, - .precision_as_arg = true}, - 5, CSTR("{}.{}}")); - test({.width = 10, - .width_as_arg = true, - .precision = 9, - .precision_as_arg = true}, - 8, CSTR("{10}.{9}}")); - - // *** Locale-specific form *** - test_exception>( - "The format-spec should consume the input or end with a '}'", CSTR("L")); - - // *** Type *** - - { - const char* unsuported_type = - "The format-spec type has a type not supported for a string argument"; - const char* not_a_type = - "The format-spec should consume the input or end with a '}'"; - - test_exception>(unsuported_type, CSTR("A}")); - test_exception>(unsuported_type, 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_exception>(not_a_type, 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_exception>(unsuported_type, CSTR("X}")); - test_exception>(not_a_type, CSTR("Y}")); - test_exception>(not_a_type, CSTR("Z}")); - - test_exception>(unsuported_type, CSTR("a}")); - test_exception>(unsuported_type, CSTR("b}")); - test_exception>(unsuported_type, CSTR("c}")); - test_exception>(unsuported_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_exception>(not_a_type, CSTR("l}")); - test_exception>(not_a_type, CSTR("m}")); - test_exception>(not_a_type, CSTR("n}")); - test_exception>(unsuported_type, CSTR("o}")); - test_exception>(unsuported_type, CSTR("p}")); - test_exception>(not_a_type, CSTR("q}")); - test_exception>(not_a_type, CSTR("r}")); - test({.type = _Flags::_Type::__string}, 1, 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_exception>(unsuported_type, 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 -#ifndef _LIBCPP_HAS_NO_CHAR8_T - test(); -#endif -#ifndef TEST_HAS_NO_UNICODE_CHARS - test(); - 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. - LIBCPP_STATIC_ASSERT(sizeof(Parser) == 3 * sizeof(uint32_t)); -#ifndef TEST_HAS_NO_WIDE_CHARACTERS - LIBCPP_STATIC_ASSERT( - sizeof(Parser) == - (sizeof(wchar_t) <= 2 ? 3 * sizeof(uint32_t) : 4 * 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_string_non_unicode.pass.cpp b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/std_format_spec_string_non_unicode.pass.cpp --- a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/std_format_spec_string_non_unicode.pass.cpp +++ b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/std_format_spec_string_non_unicode.pass.cpp @@ -26,8 +26,8 @@ #include "test_macros.h" #include "make_string.h" - -#define CSTR(S) MAKE_CSTRING(CharT, S) +#if 0 +# define CSTR(S) MAKE_CSTRING(CharT, S) using namespace std::__format_spec; @@ -90,16 +90,16 @@ constexpr bool test() { test(); -#ifndef TEST_HAS_NO_WIDE_CHARACTERS +# ifndef TEST_HAS_NO_WIDE_CHARACTERS test(); -#endif -#ifndef _LIBCPP_HAS_NO_CHAR8_T +# endif +# ifndef _LIBCPP_HAS_NO_CHAR8_T test(); -#endif -#ifndef TEST_HAS_NO_UNICODE_CHARS +# endif +# ifndef TEST_HAS_NO_UNICODE_CHARS test(); test(); -#endif +# endif return true; } @@ -109,3 +109,4 @@ return 0; } +#endif diff --git a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/std_format_spec_string_unicode.pass.cpp b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/std_format_spec_string_unicode.pass.cpp --- a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/std_format_spec_string_unicode.pass.cpp +++ b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/std_format_spec_string_unicode.pass.cpp @@ -27,7 +27,7 @@ #define CSTR(S) MAKE_CSTRING(CharT, S) using namespace std::__format_spec; - +#if 0 template constexpr bool operator==(const __string_alignment& lhs, const __string_alignment& rhs) noexcept { @@ -45,7 +45,7 @@ __get_string_alignment(sv.begin(), sv.end(), width, precision); assert(traits == expected); } - +#endif template constexpr void estimate_column_width_fast(size_t expected, const CharT* str) { std::basic_string_view sv{str}; @@ -166,7 +166,7 @@ 2, CSTR("\U00030000")); // estimate_column_width(2, CSTR("\U0003fffd")); // Undefined Character } - +#if 0 template constexpr void get_string_alignment() { // Truncate the input. @@ -237,12 +237,13 @@ } // UTF-32 doesn't combine characters, thus no corruption tests. } +#endif template constexpr void test() { estimate_column_width_fast(); estimate_column_width(); - get_string_alignment(); + // get_string_alignment(); } constexpr bool test() { diff --git a/libcxx/test/std/utilities/format/format.formatter/format.context/types.compile.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.context/types.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.context/types.compile.pass.cpp @@ -0,0 +1,131 @@ +//===----------------------------------------------------------------------===// +// 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 + +// + +// Class typedefs: +// template +// class basic_format_context { +// public: +// using iterator = Out +// using char_type = charT; +// template using formatter_type = formatter; +// } +// +// Namespace std typedefs: +// using format_context = basic_format_context; +// using wformat_context = basic_format_context; + +#include +#include +#include +#include + +#include "test_macros.h" + +template +constexpr void test() { + static_assert( + std::is_same_v::iterator, + OutIt>); + static_assert( + std::is_same_v< + typename std::basic_format_context::char_type, CharT>); + static_assert(std::is_same_v::template formatter_type, + std::formatter>); + static_assert( + std::is_same_v::template formatter_type, + std::formatter>); + static_assert(std::is_same_v::template formatter_type, + std::formatter>); + static_assert( + std::is_same_v::template formatter_type, + std::formatter>); + static_assert( + std::is_same_v::template formatter_type, + std::formatter>); + static_assert( + std::is_same_v:: + template formatter_type, + std::formatter>); +#ifndef TEST_HAS_NO_INT128 + static_assert( + std::is_same_v::template formatter_type<__int128_t>, + std::formatter<__int128_t, CharT>>); + static_assert( + std::is_same_v::template formatter_type<__uint128_t>, + std::formatter<__uint128_t, CharT>>); +#endif + static_assert( + std::is_same_v::template formatter_type, + std::formatter>); + static_assert( + std::is_same_v::template formatter_type, + std::formatter>); + static_assert( + std::is_same_v::template formatter_type, + std::formatter>); + static_assert( + std::is_same_v::template formatter_type, + std::formatter>); + static_assert( + std::is_same_v:: + template formatter_type>, + std::formatter, CharT>>); + static_assert( + std::is_same_v::template formatter_type, + std::formatter>); +} + +constexpr void test() { + test>, char>(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test>, wchar_t>(); +#endif + test>, char8_t>(); + test>, char16_t>(); + test>, char32_t>(); +} + +template +constexpr bool is_basic_format_context_specialization = false; +template +constexpr bool is_basic_format_context_specialization, CharT> = true; + +static_assert(is_basic_format_context_specialization); +LIBCPP_STATIC_ASSERT( + std::is_same_v< + std::format_context, + std::basic_format_context< + std::back_insert_iterator>, char>>); + +#ifndef TEST_HAS_NO_WIDE_CHARACTERS +static_assert(is_basic_format_context_specialization); +LIBCPP_STATIC_ASSERT( + std::is_same_v< + std::wformat_context, + std::basic_format_context< + std::back_insert_iterator>, wchar_t>>); +#endif + +// Required for MSVC internal test runner compatibility. +int main(int, char**) { return 0; } diff --git a/libcxx/test/std/utilities/format/format.functions/format_tests.h b/libcxx/test/std/utilities/format/format.functions/format_tests.h --- a/libcxx/test/std/utilities/format/format.functions/format_tests.h +++ b/libcxx/test/std/utilities/format/format.functions/format_tests.h @@ -2455,6 +2455,7 @@ template void format_tests(TestFunction check, ExceptionTest check_exception) { + // *** Test escaping *** check.template operator()<"{{">(SV("{"));