| // -*- 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 |