diff options
Diffstat (limited to 'libcxx/include/__format')
| -rw-r--r-- | libcxx/include/__format/format_arg.h | 256 | ||||
| -rw-r--r-- | libcxx/include/__format/format_args.h | 71 | ||||
| -rw-r--r-- | libcxx/include/__format/format_context.h | 168 | ||||
| -rw-r--r-- | libcxx/include/__format/format_error.h | 5 | ||||
| -rw-r--r-- | libcxx/include/__format/format_fwd.h | 56 | ||||
| -rw-r--r-- | libcxx/include/__format/format_parse_context.h | 12 | ||||
| -rw-r--r-- | libcxx/include/__format/format_string.h | 169 | ||||
| -rw-r--r-- | libcxx/include/__format/format_to_n_result.h | 41 | ||||
| -rw-r--r-- | libcxx/include/__format/formatter.h | 268 | ||||
| -rw-r--r-- | libcxx/include/__format/formatter_bool.h | 147 | ||||
| -rw-r--r-- | libcxx/include/__format/formatter_char.h | 104 | ||||
| -rw-r--r-- | libcxx/include/__format/formatter_integer.h | 170 | ||||
| -rw-r--r-- | libcxx/include/__format/formatter_integral.h | 463 | ||||
| -rw-r--r-- | libcxx/include/__format/formatter_string.h | 163 | ||||
| -rw-r--r-- | libcxx/include/__format/parser_std_format_spec.h | 1184 |
15 files changed, 3264 insertions, 13 deletions
diff --git a/libcxx/include/__format/format_arg.h b/libcxx/include/__format/format_arg.h new file mode 100644 index 000000000000..a9a8c1f0da03 --- /dev/null +++ b/libcxx/include/__format/format_arg.h @@ -0,0 +1,256 @@ +// -*- 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_FORMAT_ARG_H +#define _LIBCPP___FORMAT_FORMAT_ARG_H + +#include <__concepts/arithmetic.h> +#include <__config> +#include <__format/format_error.h> +#include <__format/format_fwd.h> +#include <__functional_base> +#include <__variant/monostate.h> +#include <string> +#include <string_view> + +#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 + +// TODO FMT Remove this once we require compilers with proper C++20 support. +// If the compiler has no concepts support, the format header will be disabled. +// Without concepts support enable_if needs to be used and that too much effort +// to support compilers with partial C++20 support. +#if !defined(_LIBCPP_HAS_NO_CONCEPTS) + +namespace __format { +/** The type stored in @ref basic_format_arg. */ +enum class _LIBCPP_ENUM_VIS __arg_t : uint8_t { + __none, + __boolean, + __char_type, + __int, + __long_long, +#ifndef _LIBCPP_HAS_NO_INT128 + __i128, +#endif + __unsigned, + __unsigned_long_long, +#ifndef _LIBCPP_HAS_NO_INT128 + __u128, +#endif + __float, + __double, + __long_double, + __const_char_type_ptr, + __string_view, + __ptr +}; +} // namespace __format + +template <class _Visitor, class _Context> +_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT decltype(auto) +visit_format_arg(_Visitor&& __vis, basic_format_arg<_Context> __arg) { + switch (__arg.__type_) { + case __format::__arg_t::__none: + return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), monostate{}); + case __format::__arg_t::__boolean: + return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__boolean); + case __format::__arg_t::__char_type: + return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__char_type); + case __format::__arg_t::__int: + return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__int); + case __format::__arg_t::__long_long: + return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__long_long); +#ifndef _LIBCPP_HAS_NO_INT128 + case __format::__arg_t::__i128: + return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__i128); +#endif + case __format::__arg_t::__unsigned: + return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__unsigned); + case __format::__arg_t::__unsigned_long_long: + return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), + __arg.__unsigned_long_long); +#ifndef _LIBCPP_HAS_NO_INT128 + case __format::__arg_t::__u128: + return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__u128); +#endif + case __format::__arg_t::__float: + return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__float); + case __format::__arg_t::__double: + return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__double); + case __format::__arg_t::__long_double: + return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__long_double); + case __format::__arg_t::__const_char_type_ptr: + return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), + __arg.__const_char_type_ptr); + case __format::__arg_t::__string_view: + return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__string_view); + case __format::__arg_t::__ptr: + return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__ptr); + } + _LIBCPP_UNREACHABLE(); +} + +template <class _Context> +class _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT basic_format_arg { +public: + // TODO FMT Define the handle class. + class handle; + + _LIBCPP_HIDE_FROM_ABI basic_format_arg() noexcept + : __type_{__format::__arg_t::__none} {} + + _LIBCPP_HIDE_FROM_ABI explicit operator bool() const noexcept { + return __type_ != __format::__arg_t::__none; + } + +private: + using char_type = typename _Context::char_type; + + // TODO FMT Implement constrain [format.arg]/4 + // Constraints: The template specialization + // typename Context::template formatter_type<T> + // meets the Formatter requirements ([formatter.requirements]). The extent + // to which an implementation determines that the specialization meets the + // Formatter requirements is unspecified, except that as a minimum the + // expression + // typename Context::template formatter_type<T>() + // .format(declval<const T&>(), declval<Context&>()) + // shall be well-formed when treated as an unevaluated operand. + + template <class _Ctx, class... _Args> + _LIBCPP_HIDE_FROM_ABI + _LIBCPP_AVAILABILITY_FORMAT friend __format_arg_store<_Ctx, _Args...> + _VSTD::make_format_args(const _Args&...); + + template <class _Visitor, class _Ctx> + _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT friend decltype(auto) + _VSTD::visit_format_arg(_Visitor&& __vis, basic_format_arg<_Ctx> __arg); + + union { + bool __boolean; + char_type __char_type; + int __int; + unsigned __unsigned; + long long __long_long; + unsigned long long __unsigned_long_long; +#ifndef _LIBCPP_HAS_NO_INT128 + __int128_t __i128; + __uint128_t __u128; +#endif + float __float; + double __double; + long double __long_double; + const char_type* __const_char_type_ptr; + basic_string_view<char_type> __string_view; + const void* __ptr; + // TODO FMT Add the handle. + }; + __format::__arg_t __type_; + + _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(bool __v) noexcept + : __boolean(__v), __type_(__format::__arg_t::__boolean) {} + + template <class _Tp> + _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(_Tp __v) noexcept + requires(same_as<_Tp, char_type> || + (same_as<_Tp, char> && same_as<char_type, wchar_t>)) + : __char_type(__v), __type_(__format::__arg_t::__char_type) {} + + template <__libcpp_signed_integer _Tp> + _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(_Tp __v) noexcept { + if constexpr (sizeof(_Tp) <= sizeof(int)) { + __int = static_cast<int>(__v); + __type_ = __format::__arg_t::__int; + } else if constexpr (sizeof(_Tp) <= sizeof(long long)) { + __long_long = static_cast<long long>(__v); + __type_ = __format::__arg_t::__long_long; + } +#ifndef _LIBCPP_HAS_NO_INT128 + else if constexpr (sizeof(_Tp) == sizeof(__int128_t)) { + __i128 = __v; + __type_ = __format::__arg_t::__i128; + } +#endif + else + static_assert(sizeof(_Tp) == 0, "An unsupported signed integer was used"); + } + + template <__libcpp_unsigned_integer _Tp> + _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(_Tp __v) noexcept { + if constexpr (sizeof(_Tp) <= sizeof(unsigned)) { + __unsigned = static_cast<unsigned>(__v); + __type_ = __format::__arg_t::__unsigned; + } else if constexpr (sizeof(_Tp) <= sizeof(unsigned long long)) { + __unsigned_long_long = static_cast<unsigned long long>(__v); + __type_ = __format::__arg_t::__unsigned_long_long; + } +#ifndef _LIBCPP_HAS_NO_INT128 + else if constexpr (sizeof(_Tp) == sizeof(__int128_t)) { + __u128 = __v; + __type_ = __format::__arg_t::__u128; + } +#endif + else + static_assert(sizeof(_Tp) == 0, + "An unsupported unsigned integer was used"); + } + + _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(float __v) noexcept + : __float(__v), __type_(__format::__arg_t::__float) {} + + _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(double __v) noexcept + : __double(__v), __type_(__format::__arg_t::__double) {} + + _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(long double __v) noexcept + : __long_double(__v), __type_(__format::__arg_t::__long_double) {} + + // Note not a 'noexcept' function. + _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(const char_type* __s) + : __const_char_type_ptr(__s), + __type_(__format::__arg_t::__const_char_type_ptr) { + _LIBCPP_ASSERT(__s, "Used a nullptr argument to initialize a C-string"); + } + + template <class _Traits> + _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg( + basic_string_view<char_type, _Traits> __s) noexcept + : __string_view{__s.data(), __s.size()}, + __type_(__format::__arg_t::__string_view) {} + + template <class _Traits, class _Allocator> + _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg( + const basic_string<char_type, _Traits, _Allocator>& __s) noexcept + : __string_view{__s.data(), __s.size()}, + __type_(__format::__arg_t::__string_view) {} + + _LIBCPP_HIDE_FROM_ABI + explicit basic_format_arg(nullptr_t) noexcept + : __ptr(nullptr), __type_(__format::__arg_t::__ptr) {} + + // TODO FMT Implement the _Tp* constructor. +}; + +#endif // !defined(_LIBCPP_HAS_NO_CONCEPTS) + +#endif //_LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___FORMAT_FORMAT_ARG_H diff --git a/libcxx/include/__format/format_args.h b/libcxx/include/__format/format_args.h new file mode 100644 index 000000000000..0a26b95d1b47 --- /dev/null +++ b/libcxx/include/__format/format_args.h @@ -0,0 +1,71 @@ +// -*- 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_FORMAT_ARGS_H +#define _LIBCPP___FORMAT_FORMAT_ARGS_H + +#include <__availability> +#include <__config> +#include <__format/format_fwd.h> +#include <cstddef> + +#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 + +// TODO FMT Remove this once we require compilers with proper C++20 support. +// If the compiler has no concepts support, the format header will be disabled. +// Without concepts support enable_if needs to be used and that too much effort +// to support compilers with partial C++20 support. +#if !defined(_LIBCPP_HAS_NO_CONCEPTS) + +template <class _Context> +class _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT basic_format_args { +public: + // TODO FMT Implement [format.args]/5 + // [Note 1: Implementations are encouraged to optimize the representation of + // basic_format_args for small number of formatting arguments by storing + // indices of type alternatives separately from values and packing the + // former. - end note] + // Note: Change __format_arg_store to use a built-in array. + _LIBCPP_HIDE_FROM_ABI basic_format_args() noexcept = default; + + template <class... _Args> + _LIBCPP_HIDE_FROM_ABI basic_format_args( + const __format_arg_store<_Context, _Args...>& __store) noexcept + : __size_(sizeof...(_Args)), __data_(__store.__args.data()) {} + + _LIBCPP_HIDE_FROM_ABI + basic_format_arg<_Context> get(size_t __id) const noexcept { + return __id < __size_ ? __data_[__id] : basic_format_arg<_Context>{}; + } + + _LIBCPP_HIDE_FROM_ABI size_t __size() const noexcept { return __size_; } + +private: + size_t __size_{0}; + const basic_format_arg<_Context>* __data_{nullptr}; +}; + +#endif // !defined(_LIBCPP_HAS_NO_CONCEPTS) + +#endif //_LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___FORMAT_FORMAT_ARGS_H diff --git a/libcxx/include/__format/format_context.h b/libcxx/include/__format/format_context.h new file mode 100644 index 000000000000..b4fe5cc7b12c --- /dev/null +++ b/libcxx/include/__format/format_context.h @@ -0,0 +1,168 @@ +// -*- 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_FORMAT_CONTEXT_H +#define _LIBCPP___FORMAT_FORMAT_CONTEXT_H + +#include <__availability> +#include <__config> +#include <__format/format_args.h> +#include <__format/format_fwd.h> +#include <__iterator/concepts.h> +#include <concepts> +#include <iterator> +#include <string> + +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +#include <locale> +#include <optional> +#endif + +#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 + +// TODO FMT Remove this once we require compilers with proper C++20 support. +// If the compiler has no concepts support, the format header will be disabled. +// Without concepts support enable_if needs to be used and that too much effort +// to support compilers with partial C++20 support. +#if !defined(_LIBCPP_HAS_NO_CONCEPTS) + +template <class _OutIt, class _CharT> +requires output_iterator<_OutIt, const _CharT&> +class _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT basic_format_context; + +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +/** + * Helper to create a basic_format_context. + * + * This is needed since the constructor is private. + */ +template <class _OutIt, class _CharT> +_LIBCPP_HIDE_FROM_ABI basic_format_context<_OutIt, _CharT> +__format_context_create( + _OutIt __out_it, + basic_format_args<basic_format_context<_OutIt, _CharT>> __args, + optional<_VSTD::locale>&& __loc = nullopt) { + return _VSTD::basic_format_context(_VSTD::move(__out_it), __args, + _VSTD::move(__loc)); +} +#else +template <class _OutIt, class _CharT> +_LIBCPP_HIDE_FROM_ABI basic_format_context<_OutIt, _CharT> +__format_context_create( + _OutIt __out_it, + basic_format_args<basic_format_context<_OutIt, _CharT>> __args) { + return _VSTD::basic_format_context(_VSTD::move(__out_it), __args); +} +#endif + +// TODO FMT Implement [format.context]/4 +// [Note 1: For a given type charT, implementations are encouraged to provide a +// single instantiation of basic_format_context for appending to +// basic_string<charT>, vector<charT>, or any other container with contiguous +// storage by wrapping those in temporary objects with a uniform interface +// (such as a span<charT>) and polymorphic reallocation. - end note] + +using format_context = basic_format_context<back_insert_iterator<string>, char>; +#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS +using wformat_context = basic_format_context<back_insert_iterator<wstring>, wchar_t>; +#endif + +template <class _OutIt, class _CharT> +requires output_iterator<_OutIt, const _CharT&> +class + // clang-format off + _LIBCPP_TEMPLATE_VIS + _LIBCPP_AVAILABILITY_FORMAT + _LIBCPP_PREFERRED_NAME(format_context) + _LIBCPP_IF_WIDE_CHARACTERS(_LIBCPP_PREFERRED_NAME(wformat_context)) + // clang-format on + basic_format_context { +public: + using iterator = _OutIt; + using char_type = _CharT; + template <class _Tp> + using formatter_type = formatter<_Tp, _CharT>; + + basic_format_context(const basic_format_context&) = delete; + basic_format_context& operator=(const basic_format_context&) = delete; + + _LIBCPP_HIDE_FROM_ABI basic_format_arg<basic_format_context> + arg(size_t __id) const { + return __args_.get(__id); + } +#ifndef _LIBCPP_HAS_NO_LOCALIZATION + _LIBCPP_HIDE_FROM_ABI _VSTD::locale locale() { + if (!__loc_) + __loc_ = _VSTD::locale{}; + return *__loc_; + } +#endif + _LIBCPP_HIDE_FROM_ABI iterator out() { return __out_it_; } + _LIBCPP_HIDE_FROM_ABI void advance_to(iterator __it) { __out_it_ = __it; } + +private: + iterator __out_it_; + basic_format_args<basic_format_context> __args_; +#ifndef _LIBCPP_HAS_NO_LOCALIZATION + + // The Standard doesn't specify how the locale is stored. + // [format.context]/6 + // std::locale locale(); + // Returns: The locale passed to the formatting function if the latter + // takes one, and std::locale() otherwise. + // This is done by storing the locale of the constructor in this optional. If + // locale() is called and the optional has no value the value will be created. + // This allows the implementation to lazily create the locale. + // TODO FMT Validate whether lazy creation is the best solution. + optional<_VSTD::locale> __loc_; + + template <class __OutIt, class __CharT> + friend _LIBCPP_HIDE_FROM_ABI basic_format_context<__OutIt, __CharT> + _VSTD::__format_context_create( + __OutIt, basic_format_args<basic_format_context<__OutIt, __CharT>>, + optional<_VSTD::locale>&&); + + // Note: the Standard doesn't specify the required constructors. + _LIBCPP_HIDE_FROM_ABI + explicit basic_format_context(_OutIt __out_it, + basic_format_args<basic_format_context> __args, + optional<_VSTD::locale>&& __loc) + : __out_it_(_VSTD::move(__out_it)), __args_(__args), + __loc_(_VSTD::move(__loc)) {} +#else + template <class __OutIt, class __CharT> + friend _LIBCPP_HIDE_FROM_ABI basic_format_context<__OutIt, __CharT> + _VSTD::__format_context_create( + __OutIt, basic_format_args<basic_format_context<__OutIt, __CharT>>); + + _LIBCPP_HIDE_FROM_ABI + explicit basic_format_context(_OutIt __out_it, + basic_format_args<basic_format_context> __args) + : __out_it_(_VSTD::move(__out_it)), __args_(__args) {} +#endif +}; + +#endif // !defined(_LIBCPP_HAS_NO_CONCEPTS) + +#endif //_LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___FORMAT_FORMAT_CONTEXT_H diff --git a/libcxx/include/__format/format_error.h b/libcxx/include/__format/format_error.h index f983d0ce4ac4..ac1d708030d6 100644 --- a/libcxx/include/__format/format_error.h +++ b/libcxx/include/__format/format_error.h @@ -21,9 +21,6 @@ #pragma GCC system_header #endif -_LIBCPP_PUSH_MACROS -#include <__undef_macros> - _LIBCPP_BEGIN_NAMESPACE_STD #if _LIBCPP_STD_VER > 17 @@ -51,6 +48,4 @@ __throw_format_error(const char* __s) { _LIBCPP_END_NAMESPACE_STD -_LIBCPP_POP_MACROS - #endif // _LIBCPP___FORMAT_FORMAT_ERROR_H diff --git a/libcxx/include/__format/format_fwd.h b/libcxx/include/__format/format_fwd.h new file mode 100644 index 000000000000..7da30aec5188 --- /dev/null +++ b/libcxx/include/__format/format_fwd.h @@ -0,0 +1,56 @@ +// -*- 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_FORMAT_FWD_H +#define _LIBCPP___FORMAT_FORMAT_FWD_H + +#include <__availability> +#include <__config> +#include <__iterator/concepts.h> +#include <__utility/forward.h> + +#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 + +// TODO FMT Remove this once we require compilers with proper C++20 support. +// If the compiler has no concepts support, the format header will be disabled. +// Without concepts support enable_if needs to be used and that too much effort +// to support compilers with partial C++20 support. +#if !defined(_LIBCPP_HAS_NO_CONCEPTS) + +template <class _Context> +class _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT basic_format_arg; + +template <class _Context, class... _Args> +struct _LIBCPP_TEMPLATE_VIS __format_arg_store; + +template <class _Ctx, class... _Args> +_LIBCPP_HIDE_FROM_ABI __format_arg_store<_Ctx, _Args...> +make_format_args(const _Args&...); + +template <class _Tp, class _CharT = char> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter; + +#endif // !defined(_LIBCPP_HAS_NO_CONCEPTS) + +#endif //_LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___FORMAT_FORMAT_FWD_H diff --git a/libcxx/include/__format/format_parse_context.h b/libcxx/include/__format/format_parse_context.h index db39c1b54830..289cab9f0ee4 100644 --- a/libcxx/include/__format/format_parse_context.h +++ b/libcxx/include/__format/format_parse_context.h @@ -18,9 +18,6 @@ #pragma GCC system_header #endif -_LIBCPP_PUSH_MACROS -#include <__undef_macros> - _LIBCPP_BEGIN_NAMESPACE_STD #if _LIBCPP_STD_VER > 17 @@ -29,8 +26,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD // If the compiler has no concepts support, the format header will be disabled. // Without concepts support enable_if needs to be used and that too much effort // to support compilers with partial C++20 support. -#if !defined(_LIBCPP_HAS_NO_CONCEPTS) && \ - !defined(_LIBCPP_HAS_NO_BUILTIN_IS_CONSTANT_EVALUATED) +#if !defined(_LIBCPP_HAS_NO_CONCEPTS) template <class _CharT> class _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT basic_format_parse_context { @@ -100,14 +96,14 @@ private: }; using format_parse_context = basic_format_parse_context<char>; +#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS using wformat_parse_context = basic_format_parse_context<wchar_t>; +#endif -#endif // !defined(_LIBCPP_HAS_NO_CONCEPTS) && !defined(_LIBCPP_HAS_NO_BUILTIN_IS_CONSTANT_EVALUATED) +#endif // !defined(_LIBCPP_HAS_NO_CONCEPTS) #endif //_LIBCPP_STD_VER > 17 _LIBCPP_END_NAMESPACE_STD -_LIBCPP_POP_MACROS - #endif // _LIBCPP___FORMAT_FORMAT_PARSE_CONTEXT_H diff --git a/libcxx/include/__format/format_string.h b/libcxx/include/__format/format_string.h new file mode 100644 index 000000000000..885e572fc068 --- /dev/null +++ b/libcxx/include/__format/format_string.h @@ -0,0 +1,169 @@ +// -*- 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_FORMAT_STRING_H +#define _LIBCPP___FORMAT_FORMAT_STRING_H + +#include <__config> +#include <__debug> +#include <__format/format_error.h> +#include <cstddef> +#include <cstdint> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 17 + +// TODO FMT Remove this once we require compilers with proper C++20 support. +// If the compiler has no concepts support, the format header will be disabled. +// Without concepts support enable_if needs to be used and that too much effort +// to support compilers with partial C++20 support. +#if !defined(_LIBCPP_HAS_NO_CONCEPTS) + +namespace __format { + +template <class _CharT> +struct _LIBCPP_TEMPLATE_VIS __parse_number_result { + const _CharT* __ptr; + uint32_t __value; +}; + +template <class _CharT> +_LIBCPP_HIDE_FROM_ABI constexpr __parse_number_result<_CharT> +__parse_number(const _CharT* __begin, const _CharT* __end); + +/** + * The maximum value of a numeric argument. + * + * This is used for: + * * arg-id + * * width as value or arg-id. + * * precision as value or arg-id. + * + * The value is compatible with the maximum formatting width and precision + * using the `%*` syntax on a 32-bit system. + */ +inline constexpr uint32_t __number_max = INT32_MAX; + +namespace __detail { +template <class _CharT> +_LIBCPP_HIDE_FROM_ABI constexpr __parse_number_result<_CharT> +__parse_zero(const _CharT* __begin, const _CharT*, auto& __parse_ctx) { + __parse_ctx.check_arg_id(0); + return {++__begin, 0}; // can never be larger than the maximum. +} + +template <class _CharT> +_LIBCPP_HIDE_FROM_ABI constexpr __parse_number_result<_CharT> +__parse_automatic(const _CharT* __begin, const _CharT*, auto& __parse_ctx) { + size_t __value = __parse_ctx.next_arg_id(); + _LIBCPP_ASSERT(__value <= __number_max, + "Compilers don't support this number of arguments"); + + return {__begin, uint32_t(__value)}; +} + +template <class _CharT> +_LIBCPP_HIDE_FROM_ABI constexpr __parse_number_result<_CharT> +__parse_manual(const _CharT* __begin, const _CharT* __end, auto& __parse_ctx) { + __parse_number_result<_CharT> __r = __parse_number(__begin, __end); + __parse_ctx.check_arg_id(__r.__value); + return __r; +} + +} // namespace __detail + +/** + * Parses a number. + * + * The number is used for the 31-bit values @em width and @em precision. This + * allows a maximum value of 2147483647. + */ +template <class _CharT> +_LIBCPP_HIDE_FROM_ABI constexpr __parse_number_result<_CharT> +__parse_number(const _CharT* __begin, const _CharT* __end_input) { + static_assert(__format::__number_max == INT32_MAX, + "The algorithm is implemented based on this value."); + /* + * Limit the input to 9 digits, otherwise we need two checks during every + * iteration: + * - Are we at the end of the input? + * - Does the value exceed width of an uint32_t? (Switching to uint64_t would + * have the same issue, but with a higher maximum.) + */ + const _CharT* __end = __end_input - __begin > 9 ? __begin + 9 : __end_input; + uint32_t __value = *__begin - _CharT('0'); + while (++__begin != __end) { + if (*__begin < _CharT('0') || *__begin > _CharT('9')) + return {__begin, __value}; + + __value = __value * 10 + *__begin - _CharT('0'); + } + + if (__begin != __end_input && *__begin >= _CharT('0') && + *__begin <= _CharT('9')) { + + /* + * There are more than 9 digits, do additional validations: + * - Does the 10th digit exceed the maximum allowed value? + * - Are there more than 10 digits? + * (More than 10 digits always overflows the maximum.) + */ + uint64_t __v = uint64_t(__value) * 10 + *__begin++ - _CharT('0'); + if (__v > __number_max || + (__begin != __end_input && *__begin >= _CharT('0') && + *__begin <= _CharT('9'))) + __throw_format_error("The numeric value of the format-spec is too large"); + + __value = __v; + } + + return {__begin, __value}; +} + +/** + * Multiplexer for all parse functions. + * + * The parser will return a pointer beyond the last consumed character. This + * should be the closing '}' of the arg-id. + */ +template <class _CharT> +_LIBCPP_HIDE_FROM_ABI constexpr __parse_number_result<_CharT> +__parse_arg_id(const _CharT* __begin, const _CharT* __end, auto& __parse_ctx) { + switch (*__begin) { + case _CharT('0'): + return __detail::__parse_zero(__begin, __end, __parse_ctx); + + case _CharT(':'): + // This case is conditionally valid. It's allowed in an arg-id in the + // replacement-field, but not in the std-format-spec. The caller can + // provide a better diagnostic, so accept it here unconditionally. + case _CharT('}'): + return __detail::__parse_automatic(__begin, __end, __parse_ctx); + } + if (*__begin < _CharT('0') || *__begin > _CharT('9')) + __throw_format_error( + "The arg-id of the format-spec starts with an invalid character"); + + return __detail::__parse_manual(__begin, __end, __parse_ctx); +} + +} // namespace __format + +#endif // !defined(_LIBCPP_HAS_NO_CONCEPTS) + +#endif //_LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___FORMAT_FORMAT_STRING_H diff --git a/libcxx/include/__format/format_to_n_result.h b/libcxx/include/__format/format_to_n_result.h new file mode 100644 index 000000000000..b973dc5c1dfe --- /dev/null +++ b/libcxx/include/__format/format_to_n_result.h @@ -0,0 +1,41 @@ +// -*- 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_FORMAT_TO_N_RESULT_H +#define _LIBCPP___FORMAT_FORMAT_TO_N_RESULT_H + +#include <__config> +#include <__iterator/incrementable_traits.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 17 + +// TODO FMT Remove this once we require compilers with proper C++20 support. +// If the compiler has no concepts support, the format header will be disabled. +// Without concepts support enable_if needs to be used and that too much effort +// to support compilers with partial C++20 support. +#if !defined(_LIBCPP_HAS_NO_CONCEPTS) + +template <class _OutIt> +struct _LIBCPP_TEMPLATE_VIS format_to_n_result { + _OutIt out; + iter_difference_t<_OutIt> size; +}; + +#endif // !defined(_LIBCPP_HAS_NO_CONCEPTS) +#endif //_LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___FORMAT_FORMAT_TO_N_RESULT_H diff --git a/libcxx/include/__format/formatter.h b/libcxx/include/__format/formatter.h new file mode 100644 index 000000000000..1adce75a8611 --- /dev/null +++ b/libcxx/include/__format/formatter.h @@ -0,0 +1,268 @@ +// -*- 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_H +#define _LIBCPP___FORMAT_FORMATTER_H + +#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/parser_std_format_spec.h> +#include <concepts> +#include <string_view> + +#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 + +// TODO FMT Remove this once we require compilers with proper C++20 support. +// If the compiler has no concepts support, the format header will be disabled. +// Without concepts support enable_if needs to be used and that too much effort +// to support compilers with partial C++20 support. +#if !defined(_LIBCPP_HAS_NO_CONCEPTS) + +// Currently not implemented specializations throw an exception when used. This +// does not conform to the Standard. However not all Standard defined formatters +// have been implemented yet. Until that time the current behavior is intended. +// TODO FMT Disable the default template. +template <class _Tp, class _CharT> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter { + _LIBCPP_NORETURN _LIBCPP_HIDE_FROM_ABI auto parse(auto& __parse_ctx) + -> decltype(__parse_ctx.begin()) { + __throw(); + } + + _LIBCPP_NORETURN _LIBCPP_HIDE_FROM_ABI auto format(_Tp, auto& __ctx) + -> decltype(__ctx.out()) { + __throw(); + } + +private: + _LIBCPP_NORETURN _LIBCPP_HIDE_FROM_ABI void __throw() { + __throw_format_error("Argument type not implemented yet"); + } +}; + +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 <class _CharT> +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 <class _CharT, class _Fill> +_LIBCPP_HIDE_FROM_ABI auto +__write(output_iterator<const _CharT&> 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 + * + * Uses a transformation operation before writing an element. + * + * TODO FMT Fill will probably change to support Unicode grapheme cluster. + */ +template <class _CharT, class _UnaryOperation, class _Fill> +_LIBCPP_HIDE_FROM_ABI auto +__write(output_iterator<const _CharT&> 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 <class _CharT, class _Fill> +_LIBCPP_HIDE_FROM_ABI auto +__write_unicode(output_iterator<const _CharT&> 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 // !defined(_LIBCPP_HAS_NO_CONCEPTS) + +#endif //_LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___FORMAT_FORMATTER_H diff --git a/libcxx/include/__format/formatter_bool.h b/libcxx/include/__format/formatter_bool.h new file mode 100644 index 000000000000..fdd1d75355d2 --- /dev/null +++ b/libcxx/include/__format/formatter_bool.h @@ -0,0 +1,147 @@ +// -*- 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_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 <string_view> + +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +#include <locale> +#endif + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 17 + +// TODO FMT Remove this once we require compilers with proper C++20 support. +// If the compiler has no concepts support, the format header will be disabled. +// Without concepts support enable_if needs to be used and that too much effort +// to support compilers with partial C++20 support. +#if !defined(_LIBCPP_HAS_NO_CONCEPTS) + +namespace __format_spec { + +template <class _CharT> +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::__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 bool argument"); + } + + return __it; + } +}; + +template <class _CharT> +struct _LIBCPP_TEMPLATE_VIS __bool_strings; + +template <> +struct _LIBCPP_TEMPLATE_VIS __bool_strings<char> { + 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<wchar_t> { + static constexpr wstring_view __true{L"true"}; + static constexpr wstring_view __false{L"false"}; +}; +#endif + +template <class _CharT> +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 <class _CharT> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter<bool, _CharT> + : 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<unsigned char>(__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<numpunct<_CharT>>(__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 // !defined(_LIBCPP_HAS_NO_CONCEPTS) + +#endif //_LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___FORMAT_FORMATTER_BOOL_H diff --git a/libcxx/include/__format/formatter_char.h b/libcxx/include/__format/formatter_char.h new file mode 100644 index 000000000000..2131de0778f2 --- /dev/null +++ b/libcxx/include/__format/formatter_char.h @@ -0,0 +1,104 @@ +// -*- 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_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 +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 17 + +// TODO FMT Remove this once we require compilers with proper C++20 support. +// If the compiler has no concepts support, the format header will be disabled. +// Without concepts support enable_if needs to be used and that too much effort +// to support compilers with partial C++20 support. +#if !defined(_LIBCPP_HAS_NO_CONCEPTS) + +namespace __format_spec { + +template <class _CharT> +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 <class _CharT> +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<char, char> + : public __format_spec::__formatter_char<char> {}; + +#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS +template <> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter<char, wchar_t> + : public __format_spec::__formatter_char<wchar_t> { + using _Base = __format_spec::__formatter_char<wchar_t>; + + _LIBCPP_HIDE_FROM_ABI auto format(char __value, auto& __ctx) + -> decltype(__ctx.out()) { + return _Base::format(static_cast<wchar_t>(__value), __ctx); + } +}; + +template <> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT + formatter<wchar_t, wchar_t> + : public __format_spec::__formatter_char<wchar_t> {}; +#endif // _LIBCPP_HAS_NO_WIDE_CHARACTERS +#endif // !defined(_LIBCPP_HAS_NO_CONCEPTS) + +#endif //_LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___FORMAT_FORMATTER_CHAR_H diff --git a/libcxx/include/__format/formatter_integer.h b/libcxx/include/__format/formatter_integer.h new file mode 100644 index 000000000000..767df36e61eb --- /dev/null +++ b/libcxx/include/__format/formatter_integer.h @@ -0,0 +1,170 @@ +// -*- 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_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 <limits> + +#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 + +// TODO FMT Remove this once we require compilers with proper C++20 support. +// If the compiler has no concepts support, the format header will be disabled. +// Without concepts support enable_if needs to be used and that too much effort +// to support compilers with partial C++20 support. +#if !defined(_LIBCPP_HAS_NO_CONCEPTS) + +namespace __format_spec { + +template <class _CharT> +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 <class _CharT> +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 <class _CharT> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT + formatter<signed char, _CharT> + : public __format_spec::__formatter_integer<_CharT> {}; +template <class _CharT> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter<short, _CharT> + : public __format_spec::__formatter_integer<_CharT> {}; +template <class _CharT> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter<int, _CharT> + : public __format_spec::__formatter_integer<_CharT> {}; +template <class _CharT> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter<long, _CharT> + : public __format_spec::__formatter_integer<_CharT> {}; +template <class _CharT> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT + formatter<long long, _CharT> + : public __format_spec::__formatter_integer<_CharT> {}; +#ifndef _LIBCPP_HAS_NO_INT128 +template <class _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 <class _CharT> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT + formatter<unsigned char, _CharT> + : public __format_spec::__formatter_integer<_CharT> {}; +template <class _CharT> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT + formatter<unsigned short, _CharT> + : public __format_spec::__formatter_integer<_CharT> {}; +template <class _CharT> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT + formatter<unsigned, _CharT> + : public __format_spec::__formatter_integer<_CharT> {}; +template <class _CharT> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT + formatter<unsigned long, _CharT> + : public __format_spec::__formatter_integer<_CharT> {}; +template <class _CharT> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT + formatter<unsigned long long, _CharT> + : public __format_spec::__formatter_integer<_CharT> {}; +#ifndef _LIBCPP_HAS_NO_INT128 +template <class _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 // !defined(_LIBCPP_HAS_NO_CONCEPTS) + +#endif //_LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___FORMAT_FORMATTER_INTEGER_H diff --git a/libcxx/include/__format/formatter_integral.h b/libcxx/include/__format/formatter_integral.h new file mode 100644 index 000000000000..5f1353effd77 --- /dev/null +++ b/libcxx/include/__format/formatter_integral.h @@ -0,0 +1,463 @@ +// -*- 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_INTEGRAL_H +#define _LIBCPP___FORMAT_FORMATTER_INTEGRAL_H + +#include <__config> +#include <__format/format_error.h> +#include <__format/format_fwd.h> +#include <__format/formatter.h> +#include <__format/parser_std_format_spec.h> +#include <__algorithm/copy.h> +#include <__algorithm/copy_n.h> +#include <__algorithm/fill_n.h> +#include <__algorithm/transform.h> +#include <array> +#include <charconv> +#include <concepts> +#include <limits> +#include <string> + +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +#include <locale> +#endif + +#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 + +// TODO FMT Remove this once we require compilers with proper C++20 support. +// If the compiler has no concepts support, the format header will be disabled. +// Without concepts support enable_if needs to be used and that too much effort +// to support compilers with partial C++20 support. +#if !defined(_LIBCPP_HAS_NO_CONCEPTS) + +/** + * 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 <class _Tp> +_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 <unsigned_integral _Tp, size_t _Base> +_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. +} + +template <unsigned_integral _Tp, size_t _Base> +_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. +} + +template <unsigned_integral _Tp, size_t _Base> +_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. +} + +template <unsigned_integral _Tp, size_t _Base> +_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. +} + +/** + * Determines the required grouping based on the size of the input. + * + * The grouping's last element will be repeated. For simplicity this repeating + * is unwrapped based on the length of the input. (When the input is short some + * groups are not processed.) + * + * @returns The size of the groups to write. This means the number of + * separator characters written is size() - 1. + * + * @note Since zero-sized groups cause issues they are silently ignored. + * + * @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_ASSERT(!__grouping.empty() && __size > __grouping[0], + "The slow grouping formatting is used while there will be no " + "separators written"); + string __r; + auto __end = __grouping.end() - 1; + auto __ptr = __grouping.begin(); + + while (true) { + __size -= *__ptr; + if (__size > 0) + __r.push_back(*__ptr); + else { + // __size <= 0 so the value pushed will be <= *__ptr. + __r.push_back(*__ptr + __size); + return __r; + } + + // Proceed to the next group. + if (__ptr != __end) { + do { + ++__ptr; + // Skip grouping with a width of 0. + } while (*__ptr == 0 && __ptr != __end); + } + } + + _LIBCPP_UNREACHABLE(); +} + +template <class _Parser> +requires __formatter::__char_type<typename _Parser::char_type> +class _LIBCPP_TEMPLATE_VIS __formatter_integral : public _Parser { +public: + using _CharT = typename _Parser::char_type; + + template <integral _Tp> + _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); + + 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); + } + } + +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<make_unsigned_t<_CharT>>(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<make_unsigned_t<_Tp>>(__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 __write(_VSTD::addressof(__c), _VSTD::addressof(__c) + 1, + __ctx.out()); + } + + /** + * 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<char, __buffer_size<decltype(__value), 2>()> __array; + return __format_unsigned_integral(__array.begin(), __array.end(), __value, + __negative, 2, __ctx, "0b"); + } + case _Flags::_Type::__binary_upper_case: { + array<char, __buffer_size<decltype(__value), 2>()> __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<char, __buffer_size<decltype(__value), 8>()> __array; + return __format_unsigned_integral(__array.begin(), __array.end(), __value, + __negative, 8, __ctx, + __value != 0 ? "0" : nullptr); + } + case _Flags::_Type::__decimal: { + array<char, __buffer_size<decltype(__value), 10>()> __array; + return __format_unsigned_integral(__array.begin(), __array.end(), __value, + __negative, 10, __ctx, nullptr); + } + case _Flags::_Type::__hexadecimal_lower_case: { + array<char, __buffer_size<decltype(__value), 16>()> __array; + return __format_unsigned_integral(__array.begin(), __array.end(), __value, + __negative, 16, __ctx, "0x"); + } + case _Flags::_Type::__hexadecimal_upper_case: { + array<char, __buffer_size<decltype(__value), 16>()> __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(); + } + } + + template <class _Tp> + requires(same_as<char, _Tp> || same_as<wchar_t, _Tp>) _LIBCPP_HIDE_FROM_ABI + auto __write(const _Tp* __first, const _Tp* __last, auto __out_it) + -> decltype(__out_it) { + + 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)); + + return __formatter::__write(_VSTD::move(__out_it), __first, __last, + __size, this->__width, this->__fill, + this->__alignment); + } + + // 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); + } + + _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<numpunct<_CharT>>(__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); + } + + return __write(__first, __last, _VSTD::move(__out_it)); + } + +#ifndef _LIBCPP_HAS_NO_LOCALIZATION + /** Format's the locale-specific form's groupings. */ + template <class _OutIt, class _CharT> + _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)); + } + + 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; + } + + return _VSTD::fill_n(_VSTD::move(__out_it), __padding.__after, + this->__fill); + } +#endif // _LIBCPP_HAS_NO_LOCALIZATION +}; + +} // namespace __format_spec + +#endif // !defined(_LIBCPP_HAS_NO_CONCEPTS) + +#endif //_LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___FORMAT_FORMATTER_INTEGRAL_H diff --git a/libcxx/include/__format/formatter_string.h b/libcxx/include/__format/formatter_string.h new file mode 100644 index 000000000000..2be36a1ba947 --- /dev/null +++ b/libcxx/include/__format/formatter_string.h @@ -0,0 +1,163 @@ +// -*- 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_STRING_H +#define _LIBCPP___FORMAT_FORMATTER_STRING_H + +#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 <algorithm> +#include <string_view> + +#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 + +// TODO FMT Remove this once we require compilers with proper C++20 support. +// If the compiler has no concepts support, the format header will be disabled. +// Without concepts support enable_if needs to be used and that too much effort +// to support compilers with partial C++20 support. +#if !defined(_LIBCPP_HAS_NO_CONCEPTS) + +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 <class _CharT> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT + formatter<const _CharT*, _CharT> + : 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 <class _CharT> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT + formatter<_CharT*, _CharT> : public formatter<const _CharT*, _CharT> { + using _Base = formatter<const _CharT*, _CharT>; + + _LIBCPP_HIDE_FROM_ABI auto format(_CharT* __str, auto& __ctx) + -> decltype(__ctx.out()) { + return _Base::format(__str, __ctx); + } +}; + +// Formatter const char[]. +template <class _CharT, size_t _Size> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT + formatter<const _CharT[_Size], _CharT> + : 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 <class _CharT, class _Traits, class _Allocator> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT + formatter<basic_string<_CharT, _Traits, _Allocator>, _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 <class _CharT, class _Traits> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter<basic_string_view<_CharT, _Traits>, _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 // !defined(_LIBCPP_HAS_NO_CONCEPTS) + +#endif //_LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___FORMAT_FORMATTER_STRING_H diff --git a/libcxx/include/__format/parser_std_format_spec.h b/libcxx/include/__format/parser_std_format_spec.h new file mode 100644 index 000000000000..9b713b811484 --- /dev/null +++ b/libcxx/include/__format/parser_std_format_spec.h @@ -0,0 +1,1184 @@ +// -*- 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_H +#define _LIBCPP___FORMAT_PARSER_STD_FORMAT_SPEC_H + +#include <__algorithm/find_if.h> +#include <__algorithm/min.h> +#include <__config> +#include <__debug> +#include <__format/format_arg.h> +#include <__format/format_error.h> +#include <__format/format_string.h> +#include <__variant/monostate.h> +#include <bit> +#include <concepts> +#include <cstdint> +#include <type_traits> + +#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 + +// TODO FMT Remove this once we require compilers with proper C++20 support. +// If the compiler has no concepts support, the format header will be disabled. +// Without concepts support enable_if needs to be used and that too much effort +// to support compilers with partial C++20 support. +# if !defined(_LIBCPP_HAS_NO_CONCEPTS) + +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. + */ +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}; +}; + +namespace __detail { +template <class _CharT> +_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 _CharT> +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 <class _CharT> +_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 <class _CharT> +_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 <class _CharT> +_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 <class _CharT> +_LIBCPP_HIDE_FROM_ABI constexpr __format::__parse_number_result< _CharT> +__parse_arg_id(const _CharT* __begin, const _CharT* __end, auto& __parse_ctx) { + // This function is a wrapper to call the real parser. But it does the + // validation for the pre-conditions and post-conditions. + if (__begin == __end) + __throw_format_error("End of input while parsing format-spec arg-id"); + + __format::__parse_number_result __r = + __format::__parse_arg_id(__begin, __end, __parse_ctx); + + if (__r.__ptr == __end || *__r.__ptr != _CharT('}')) + __throw_format_error("A format-spec arg-id should terminate at a '}'"); + + ++__r.__ptr; + return __r; +} + +template <class _Context> +_LIBCPP_HIDE_FROM_ABI constexpr uint32_t +__substitute_arg_id(basic_format_arg<_Context> __arg) { + return visit_format_arg( + [](auto __arg) -> uint32_t { + using _Type = decltype(__arg); + if constexpr (integral<_Type>) { + if constexpr (signed_integral<_Type>) { + if (__arg < 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)) + __throw_format_error("A format-spec arg-id replacement exceeds " + "the maximum supported value"); + + return __arg; + } 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); +} + +class _LIBCPP_TYPE_VIS __parser_width { +public: + /** Contains a width or an arg-id. */ + uint32_t __width : 31 {0}; + /** Determines whether the value stored is a width or an arg-id. */ + uint32_t __width_as_arg : 1 {0}; + +protected: + /** + * Does the supplied std-format-spec contain a width field? + * + * When the field isn't present there's no padding required. This can be used + * to optimize the formatting. + */ + constexpr bool __has_width_field() const noexcept { + return __width_as_arg || __width; + } + + /** + * Does the supplied width field contain an arg-id? + * + * If @c true the formatter needs to call @ref __substitute_width_arg_id. + */ + constexpr bool __width_needs_substitution() const noexcept { + return __width_as_arg; + } + + template <class _CharT> + _LIBCPP_HIDE_FROM_ABI constexpr const _CharT* + __parse(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 = + __parse_arg_id(++__begin, __end, __parse_ctx); + __width = __r.__value; + __width_as_arg = 1; + return __r.__ptr; + } + + if (*__begin < _CharT('0') || *__begin > _CharT('9')) + return __begin; + + __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"); + return __r.__ptr; + } + + _LIBCPP_HIDE_FROM_ABI constexpr void __substitute_width_arg_id(auto __arg) { + _LIBCPP_ASSERT(__width_as_arg == 1, + "Substitute width called when no substitution is required"); + + // The clearing of the flag isn't required but looks better when debugging + // the code. + __width_as_arg = 0; + __width = __substitute_arg_id(__arg); + if (__width == 0) + __throw_format_error( + "A format-spec width field replacement should have a positive value"); + } +}; + +class _LIBCPP_TYPE_VIS __parser_precision { +public: + /** Contains a precision or an arg-id. */ + uint32_t __precision : 31 {__format::__number_max}; + /** + * Determines whether the value stored is a precision or an arg-id. + * + * @note Since @ref __precision == @ref __format::__number_max is a valid + * value, the default value contains an arg-id of INT32_MAX. (This number of + * arguments isn't supported by compilers.) This is used to detect whether + * the std-format-spec contains a precision field. + */ + uint32_t __precision_as_arg : 1 {1}; + +protected: + /** + * Does the supplied std-format-spec contain a precision field? + * + * When the field isn't present there's no truncating required. This can be + * used to optimize the formatting. + */ + constexpr bool __has_precision_field() const noexcept { + + return __precision_as_arg == 0 || // Contains a value? + __precision != __format::__number_max; // The arg-id is valid? + } + + /** + * Does the supplied precision field contain an arg-id? + * + * If @c true the formatter needs to call @ref __substitute_precision_arg_id. + */ + constexpr bool __precision_needs_substitution() const noexcept { + return __precision_as_arg && __precision != __format::__number_max; + } + + template <class _CharT> + _LIBCPP_HIDE_FROM_ABI constexpr const _CharT* + __parse(const _CharT* __begin, const _CharT* __end, auto& __parse_ctx) { + if (*__begin != _CharT('.')) + return __begin; + + ++__begin; + if (__begin == __end) + __throw_format_error("End of input while parsing format-spec precision"); + + if (*__begin == _CharT('0')) { + ++__begin; + if (__begin != __end && *__begin >= '0' && *__begin <= '9') + __throw_format_error( + "A format-spec precision field shouldn't have a leading zero"); + + __precision = 0; + __precision_as_arg = 0; + return __begin; + } + + if (*__begin == _CharT('{')) { + __format::__parse_number_result __arg_id = + __parse_arg_id(++__begin, __end, __parse_ctx); + _LIBCPP_ASSERT(__arg_id.__value != __format::__number_max, + "Unsupported number of arguments, since this number of " + "arguments is used a special value"); + __precision = __arg_id.__value; + return __arg_id.__ptr; + } + + 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 = 0; + return __r.__ptr; + } + + _LIBCPP_HIDE_FROM_ABI constexpr void __substitute_precision_arg_id( + auto __arg) { + _LIBCPP_ASSERT( + __precision_as_arg == 1 && __precision != __format::__number_max, + "Substitute precision called when no substitution is required"); + + // The clearing of the flag isn't required but looks better when debugging + // the code. + __precision_as_arg = 0; + __precision = __substitute_arg_id(__arg); + } +}; + +template <class _CharT> +_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 <class _CharT> +_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; +} + +/** + * 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 _CharT> +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 _CharT> +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. + * + * Updates the zero-padding and alignment for integer 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. Update + * the alignment based on parsed format string. + */ + _LIBCPP_HIDE_FROM_ABI constexpr void __handle_integer() noexcept { + this->__zero_padding &= this->__alignment == _Flags::_Alignment::__default; + if (!this->__zero_padding && + this->__alignment == _Flags::_Alignment::__default) + this->__alignment = _Flags::_Alignment::__right; + } + + /** + * 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; + } +}; + +// TODO FMT Add a parser for floating-point values. +// TODO FMT Add a parser for pointer values. + +/** Helper struct returned from @ref __get_string_alignment. */ +template <class _CharT> +struct _LIBCPP_TEMPLATE_VIS __string_alignment { + /** Points beyond the last character to write to the output. */ + const _CharT* __last; + /** + * The estimated number of columns in the output or 0. + * + * Only when the output needs to be aligned it's required to know the exact + * number of columns in the output. So if the formatted output has only a + * minimum width the exact size isn't important. It's only important to know + * the minimum has been reached. The minimum width is the width specified in + * the format-spec. + * + * For example in this code @code std::format("{:10}", MyString); @endcode + * the width estimation can stop once the algorithm has determined the output + * width is 10 columns. + * + * So if: + * * @ref __align == @c true the @ref __size is the estimated number of + * columns required. + * * @ref __align == @c false the @ref __size is the estimated number of + * columns required or 0 when the estimation algorithm stopped prematurely. + */ + ptrdiff_t __size; + /** + * Does the output need to be aligned. + * + * When alignment is needed the output algorithm needs to add the proper + * padding. Else the output algorithm just needs to copy the input up to + * @ref __last. + */ + bool __align; +}; + +#ifndef _LIBCPP_HAS_NO_UNICODE +namespace __detail { + +/** + * Unicode column width estimates. + * + * Unicode can be stored in several formats: UTF-8, UTF-16, and UTF-32. + * Depending on format the relation between the number of code units stored and + * the number of output columns differs. The first relation is the number of + * code units forming a code point. (The text assumes the code units are + * unsigned.) + * - UTF-8 The number of code units is between one and four. The first 127 + * Unicode code points match the ASCII character set. When the highest bit is + * set it means the code point has more than one code unit. + * - UTF-16: The number of code units is between 1 and 2. When the first + * code unit is in the range [0xd800,0xdfff) it means the code point uses two + * code units. + * - UTF-32: The number of code units is always one. + * + * The code point to the number of columns isn't well defined. The code uses the + * estimations defined in [format.string.std]/11. This list might change in the + * future. + * + * The algorithm of @ref __get_string_alignment uses two different scanners: + * - The simple scanner @ref __estimate_column_width_fast. This scanner assumes + * 1 code unit is 1 column. This scanner stops when it can't be sure the + * assumption is valid: + * - UTF-8 when the code point is encoded in more than 1 code unit. + * - UTF-16 and UTF-32 when the first multi-column code point is encountered. + * (The code unit's value is lower than 0xd800 so the 2 code unit encoding + * is irrelevant for this scanner.) + * Due to these assumptions the scanner is faster than the full scanner. It + * can process all text only containing ASCII. For UTF-16/32 it can process + * most (all?) European languages. (Note the set it can process might be + * reduced in the future, due to updates in the scanning rules.) + * - The full scanner @ref __estimate_column_width. This scanner, if needed, + * converts multiple code units into one code point then converts the code + * point to a column width. + * + * See also: + * - [format.string.general]/11 + * - https://en.wikipedia.org/wiki/UTF-8#Encoding + * - https://en.wikipedia.org/wiki/UTF-16#U+D800_to_U+DFFF + */ + +/** + * The first 2 column code point. + * + * This is the point where the fast UTF-16/32 scanner needs to stop processing. + */ +inline constexpr uint32_t __two_column_code_point = 0x1100; + +/** Helper concept for an UTF-8 character type. */ +template <class _CharT> +concept __utf8_character = same_as<_CharT, char> || same_as<_CharT, char8_t>; + +/** Helper concept for an UTF-16 character type. */ +template <class _CharT> +concept __utf16_character = (same_as<_CharT, wchar_t> && sizeof(wchar_t) == 2) || same_as<_CharT, char16_t>; + +/** Helper concept for an UTF-32 character type. */ +template <class _CharT> +concept __utf32_character = (same_as<_CharT, wchar_t> && sizeof(wchar_t) == 4) || same_as<_CharT, char32_t>; + +/** Helper concept for an UTF-16 or UTF-32 character type. */ +template <class _CharT> +concept __utf16_or_32_character = __utf16_character<_CharT> || __utf32_character<_CharT>; + +/** + * Converts a code point to the column width. + * + * The estimations are conforming to [format.string.general]/11 + * + * This version expects a value less than 0x1'0000, which is a 3-byte UTF-8 + * character. + */ +_LIBCPP_HIDE_FROM_ABI inline constexpr int __column_width_3(uint32_t __c) noexcept { + _LIBCPP_ASSERT(__c < 0x1'0000, + "Use __column_width_4 or __column_width for larger values"); + + // clang-format off + return 1 + (__c >= 0x1100 && (__c <= 0x115f || + (__c >= 0x2329 && (__c <= 0x232a || + (__c >= 0x2e80 && (__c <= 0x303e || + (__c >= 0x3040 && (__c <= 0xa4cf || + (__c >= 0xac00 && (__c <= 0xd7a3 || + (__c >= 0xf900 && (__c <= 0xfaff || + (__c >= 0xfe10 && (__c <= 0xfe19 || + (__c >= 0xfe30 && (__c <= 0xfe6f || + (__c >= 0xff00 && (__c <= 0xff60 || + (__c >= 0xffe0 && (__c <= 0xffe6 + )))))))))))))))))))); + // clang-format on +} + +/** + * @overload + * + * This version expects a value greater than or equal to 0x1'0000, which is a + * 4-byte UTF-8 character. + */ +_LIBCPP_HIDE_FROM_ABI inline constexpr int __column_width_4(uint32_t __c) noexcept { + _LIBCPP_ASSERT(__c >= 0x1'0000, + "Use __column_width_3 or __column_width for smaller values"); + + // clang-format off + return 1 + (__c >= 0x1'f300 && (__c <= 0x1'f64f || + (__c >= 0x1'f900 && (__c <= 0x1'f9ff || + (__c >= 0x2'0000 && (__c <= 0x2'fffd || + (__c >= 0x3'0000 && (__c <= 0x3'fffd + )))))))); + // clang-format on +} + +/** + * @overload + * + * The general case, accepting all values. + */ +_LIBCPP_HIDE_FROM_ABI inline constexpr int __column_width(uint32_t __c) noexcept { + if (__c < 0x1'0000) + return __column_width_3(__c); + + return __column_width_4(__c); +} + +/** + * Estimate the column width for the UTF-8 sequence using the fast algorithm. + */ +template <__utf8_character _CharT> +_LIBCPP_HIDE_FROM_ABI constexpr const _CharT* +__estimate_column_width_fast(const _CharT* __first, + const _CharT* __last) noexcept { + return _VSTD::find_if(__first, __last, + [](unsigned char __c) { return __c & 0x80; }); +} + +/** + * @overload + * + * The implementation for UTF-16/32. + */ +template <__utf16_or_32_character _CharT> +_LIBCPP_HIDE_FROM_ABI constexpr const _CharT* +__estimate_column_width_fast(const _CharT* __first, + const _CharT* __last) noexcept { + return _VSTD::find_if(__first, __last, + [](uint32_t __c) { return __c >= 0x1100; }); +} + +template <class _CharT> +struct _LIBCPP_TEMPLATE_VIS __column_width_result { + /** The number of output columns. */ + size_t __width; + /** + * The last parsed element. + * + * This limits the original output to fit in the wanted number of columns. + */ + const _CharT* __ptr; +}; + +/** + * Small helper to determine the width of malformed Unicode. + * + * @note This function's only needed for UTF-8. During scanning UTF-8 there + * are multiple place where it can be detected that the Unicode is malformed. + * UTF-16 only requires 1 test and UTF-32 requires no testing. + */ +template <__utf8_character _CharT> +_LIBCPP_HIDE_FROM_ABI constexpr __column_width_result<_CharT> +__estimate_column_width_malformed(const _CharT* __first, const _CharT* __last, + size_t __maximum, size_t __result) noexcept { + size_t __size = __last - __first; + size_t __n = _VSTD::min(__size, __maximum); + return {__result + __n, __first + __n}; +} + +/** + * Determines the number of output columns needed to render the input. + * + * @note When the scanner encounters malformed Unicode it acts as-if every code + * unit at the end of the input is one output column. It's expected the output + * terminal will replace these malformed code units with a one column + * replacement characters. + * + * @param __first Points to the first element of the input range. + * @param __last Points beyond the last element of the input range. + * @param __maximum The maximum number of output columns. The returned number + * of estimated output columns will not exceed this value. + */ +template <__utf8_character _CharT> +_LIBCPP_HIDE_FROM_ABI constexpr __column_width_result<_CharT> +__estimate_column_width(const _CharT* __first, const _CharT* __last, + size_t __maximum) noexcept { + size_t __result = 0; + + while (__first != __last) { + // Based on the number of leading 1 bits the number of code units in the + // code point can be determined. See + // https://en.wikipedia.org/wiki/UTF-8#Encoding + switch (_VSTD::countl_one(static_cast<unsigned char>(*__first))) { + case 0: // 1-code unit encoding: all 1 column + ++__result; + ++__first; + break; + + case 2: // 2-code unit encoding: all 1 column + // Malformed Unicode. + if (__last - __first < 2) [[unlikely]] + return __estimate_column_width_malformed(__first, __last, __maximum, + __result); + __first += 2; + ++__result; + break; + + case 3: // 3-code unit encoding: either 1 or 2 columns + // Malformed Unicode. + if (__last - __first < 3) [[unlikely]] + return __estimate_column_width_malformed(__first, __last, __maximum, + __result); + { + uint32_t __c = static_cast<unsigned char>(*__first++) & 0x0f; + __c <<= 6; + __c |= static_cast<unsigned char>(*__first++) & 0x3f; + __c <<= 6; + __c |= static_cast<unsigned char>(*__first++) & 0x3f; + __result += __column_width_3(__c); + if (__result > __maximum) + return {__result - 2, __first - 3}; + } + break; + case 4: // 4-code unit encoding: either 1 or 2 columns + // Malformed Unicode. + if (__last - __first < 4) [[unlikely]] + return __estimate_column_width_malformed(__first, __last, __maximum, + __result); + { + uint32_t __c = static_cast<unsigned char>(*__first++) & 0x07; + __c <<= 6; + __c |= static_cast<unsigned char>(*__first++) & 0x3f; + __c <<= 6; + __c |= static_cast<unsigned char>(*__first++) & 0x3f; + __c <<= 6; + __c |= static_cast<unsigned char>(*__first++) & 0x3f; + __result += __column_width_4(__c); + if (__result > __maximum) + return {__result - 2, __first - 4}; + } + break; + default: + // Malformed Unicode. + return __estimate_column_width_malformed(__first, __last, __maximum, + __result); + } + + if (__result >= __maximum) + return {__result, __first}; + } + return {__result, __first}; +} + +template <__utf16_character _CharT> +_LIBCPP_HIDE_FROM_ABI constexpr __column_width_result<_CharT> +__estimate_column_width(const _CharT* __first, const _CharT* __last, + size_t __maximum) noexcept { + size_t __result = 0; + + while (__first != __last) { + uint32_t __c = *__first; + // Is the code unit part of a surrogate pair? See + // https://en.wikipedia.org/wiki/UTF-16#U+D800_to_U+DFFF + if (__c >= 0xd800 && __c <= 0xDfff) { + // Malformed Unicode. + if (__last - __first < 2) [[unlikely]] + return {__result + 1, __first + 1}; + + __c -= 0xd800; + __c <<= 10; + __c += (*(__first + 1) - 0xdc00); + __c += 0x10'000; + + __result += __column_width_4(__c); + if (__result > __maximum) + return {__result - 2, __first}; + __first += 2; + } else { + __result += __column_width_3(__c); + if (__result > __maximum) + return {__result - 2, __first}; + ++__first; + } + + if (__result >= __maximum) + return {__result, __first}; + } + + return {__result, __first}; +} + +template <__utf32_character _CharT> +_LIBCPP_HIDE_FROM_ABI constexpr __column_width_result<_CharT> +__estimate_column_width(const _CharT* __first, const _CharT* __last, + size_t __maximum) noexcept { + size_t __result = 0; + + while (__first != __last) { + wchar_t __c = *__first; + __result += __column_width(__c); + + if (__result > __maximum) + return {__result - 2, __first}; + + ++__first; + if (__result >= __maximum) + return {__result, __first}; + } + + return {__result, __first}; +} + +} // namespace __detail + +template <class _CharT> +_LIBCPP_HIDE_FROM_ABI constexpr __string_alignment<_CharT> +__get_string_alignment(const _CharT* __first, const _CharT* __last, + ptrdiff_t __width, ptrdiff_t __precision) noexcept { + _LIBCPP_ASSERT(__width != 0 || __precision != -1, + "The function has no effect and shouldn't be used"); + + // TODO FMT There might be more optimizations possible: + // If __precision == __format::__number_max and the encoding is: + // * UTF-8 : 4 * (__last - __first) >= __width + // * UTF-16 : 2 * (__last - __first) >= __width + // * UTF-32 : (__last - __first) >= __width + // In these cases it's certain the output is at least the requested width. + // It's unknown how often this happens in practice. For now the improvement + // isn't implemented. + + /* + * First assume there are no special Unicode code units in the input. + * - Apply the precision (this may reduce the size of the input). When + * __precison == -1 this step is omitted. + * - Scan for special code units in the input. + * If our assumption was correct the __pos will be at the end of the input. + */ + const ptrdiff_t __length = __last - __first; + const _CharT* __limit = + __first + + (__precision == -1 ? __length : _VSTD::min(__length, __precision)); + ptrdiff_t __size = __limit - __first; + const _CharT* __pos = + __detail::__estimate_column_width_fast(__first, __limit); + + if (__pos == __limit) + return {__limit, __size, __size < __width}; + + /* + * Our assumption was wrong, there are special Unicode code units. + * The range [__first, __pos) contains a set of code units with the + * following property: + * Every _CharT in the range will be rendered in 1 column. + * + * If there's no maximum width and the parsed size already exceeds the + * minimum required width. The real size isn't important. So bail out. + */ + if (__precision == -1 && (__pos - __first) >= __width) + return {__last, 0, false}; + + /* If there's a __precision, truncate the output to that width. */ + ptrdiff_t __prefix = __pos - __first; + if (__precision != -1) { + _LIBCPP_ASSERT(__precision > __prefix, "Logic error."); + auto __lengh_info = __detail::__estimate_column_width( + __pos, __last, __precision - __prefix); + __size = __lengh_info.__width + __prefix; + return {__lengh_info.__ptr, __size, __size < __width}; + } + + /* Else use __width to determine the number of required padding characters. */ + _LIBCPP_ASSERT(__width > __prefix, "Logic error."); + /* + * The column width is always one or two columns. For the precision the wanted + * column width is the maximum, for the width it's the minimum. Using the + * width estimation with its truncating behavior will result in the wrong + * result in the following case: + * - The last code unit processed requires two columns and exceeds the + * maximum column width. + * By increasing the __maximum by one avoids this issue. (It means it may + * pass one code point more than required to determine the proper result; + * that however isn't a problem for the algorithm.) + */ + size_t __maximum = 1 + __width - __prefix; + auto __lengh_info = + __detail::__estimate_column_width(__pos, __last, __maximum); + if (__lengh_info.__ptr != __last) { + // Consumed the width number of code units. The exact size of the string + // is unknown. We only know we don't need to align the output. + _LIBCPP_ASSERT(static_cast<ptrdiff_t>(__lengh_info.__width + __prefix) >= + __width, + "Logic error"); + return {__last, 0, false}; + } + + __size = __lengh_info.__width + __prefix; + return {__last, __size, __size < __width}; +} +#else // _LIBCPP_HAS_NO_UNICODE +template <class _CharT> +_LIBCPP_HIDE_FROM_ABI constexpr __string_alignment<_CharT> +__get_string_alignment(const _CharT* __first, const _CharT* __last, + ptrdiff_t __width, ptrdiff_t __precision) noexcept { + const ptrdiff_t __length = __last - __first; + const _CharT* __limit = + __first + + (__precision == -1 ? __length : _VSTD::min(__length, __precision)); + ptrdiff_t __size = __limit - __first; + return {__limit, __size, __size < __width}; +} +#endif // _LIBCPP_HAS_NO_UNICODE + +} // namespace __format_spec + +# endif // !defined(_LIBCPP_HAS_NO_CONCEPTS) + +#endif //_LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___FORMAT_PARSER_STD_FORMAT_SPEC_H |
