return {0, nullptr};
}
- enum _Pres_type {
+ enum class _Pres_type : unsigned char {
_Pres_none = 0, // Default type (not valid for integer presentation types).
+ _Pres_s = 1, // For strings, bool, ranges
// Presentation types for integral types (including bool and charT).
- _Pres_d = 1, _Pres_b, _Pres_B, _Pres_o, _Pres_x, _Pres_X, _Pres_c,
- // Presentation types for floating-point types.
- _Pres_a = 1, _Pres_A, _Pres_e, _Pres_E, _Pres_f, _Pres_F, _Pres_g, _Pres_G,
- _Pres_p = 0, _Pres_P, // For pointers.
- _Pres_s = 0, // For strings, bool
- _Pres_seq = 0, _Pres_str, // For ranges
- _Pres_esc = 0xf, // For strings, charT and ranges
+ _Pres_c = 2, _Pres_x, _Pres_X, _Pres_d, _Pres_o, _Pres_b, _Pres_B,
+ // Presentation types for floating-point types
+ _Pres_g = 1, _Pres_G, _Pres_a, _Pres_A, _Pres_e, _Pres_E, _Pres_f, _Pres_F,
+ // For pointers, the value are same as hexadecimal presentations for integers
+ _Pres_p = _Pres_x, _Pres_P = _Pres_X,
+ _Pres_max = 0xf,
};
+ using enum _Pres_type;
- enum _Sign {
+ enum class _Sign : unsigned char {
_Sign_default,
_Sign_plus,
_Sign_minus, // XXX does this need to be distinct from _Sign_default?
_Sign_space,
};
+ using enum _Sign;
- enum _WidthPrec {
+ enum _WidthPrec : unsigned char {
_WP_none, // No width/prec specified.
_WP_value, // Fixed width/prec specified.
_WP_from_arg // Use a formatting argument for width/prec.
};
+ using enum _WidthPrec;
template<typename _Context>
size_t
constexpr bool __is_xdigit(char __c)
{ return std::__detail::__from_chars_alnum_to_val(__c) < 16; }
+ // Used to make _Spec a non-C++98 POD, so the tail-padding is used.
+ // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#pod
+ struct _SpecBase
+ { };
+
template<typename _CharT>
- struct _Spec
+ struct _Spec : _SpecBase
{
+ unsigned short _M_width;
+ unsigned short _M_prec;
+ char32_t _M_fill = ' ';
_Align _M_align : 2;
_Sign _M_sign : 2;
unsigned _M_alt : 1;
unsigned _M_zero_fill : 1;
_WidthPrec _M_width_kind : 2;
_WidthPrec _M_prec_kind : 2;
+ unsigned _M_debug : 1;
_Pres_type _M_type : 4;
- unsigned _M_reserved : 1;
- unsigned _M_reserved2 : 16;
- unsigned short _M_width;
- unsigned short _M_prec;
- char32_t _M_fill = ' ';
+ unsigned _M_reserved : 8;
+ // This class has 8 bits of tail padding, that can be used by
+ // derived classes.
using iterator = typename basic_string_view<_CharT>::iterator;
char32_t __c = *__beg++;
if (__is_scalar_value(__c))
if (auto __next = __beg.base(); __next != __last)
- if (_Align __align = _S_align(*__next))
+ if (_Align __align = _S_align(*__next); __align != _Align_default)
{
_M_fill = __c;
_M_align = __align;
}
}
else if (__last - __first >= 2)
- if (_Align __align = _S_align(__first[1]))
+ if (_Align __align = _S_align(__first[1]); __align != _Align_default)
{
_M_fill = *__first;
_M_align = __align;
return __first + 2;
}
- if (_Align __align = _S_align(__first[0]))
+ if (_Align __align = _S_align(__first[0]); __align != _Align_default)
{
_M_fill = ' ';
_M_align = __align;
constexpr iterator
_M_parse_sign(iterator __first, iterator) noexcept
{
- if (_Sign __sign = _S_sign(*__first))
+ if (_Sign __sign = _S_sign(*__first); __sign != _Sign_default)
{
_M_sign = __sign;
return __first + 1;
const size_t __nfill = __width - __estimated_width;
- if (__spec._M_align)
+ if (__spec._M_align != _Align_default)
__align = __spec._M_align;
return __format::__write_padded(__fc.out(), __str, __align, __nfill,
}
}
-
- // Values are indices into _Escapes::all.
enum class _Term_char : unsigned char {
- _Tc_quote = 12,
- _Tc_apos = 15
+ _Term_none,
+ _Term_quote,
+ _Term_apos,
};
+ using enum _Term_char;
template<typename _CharT>
struct _Escapes
_Str_view _S_all()
{ return _GLIBCXX_WIDEN("\t\\t\n\\n\r\\r\\\\\\\"\\\"'\\'\\u\\x"); }
- static constexpr
- _CharT _S_term(_Term_char __term)
- { return _S_all()[static_cast<unsigned char>(__term)]; }
-
static consteval
_Str_view _S_tab()
{ return _S_all().substr(0, 3); }
static consteval
_Str_view _S_x()
{ return _S_all().substr(20, 2); }
+
+ static constexpr
+ _Str_view _S_term(_Term_char __term)
+ {
+ switch (__term)
+ {
+ case _Term_none:
+ return _Str_view();
+ case _Term_quote:
+ return _S_quote().substr(0, 1);
+ case _Term_apos:
+ return _S_apos().substr(0, 1);
+ }
+ __builtin_unreachable();
+ }
};
template<typename _CharT>
case _Esc::_S_bslash()[0]:
return true;
case _Esc::_S_quote()[0]:
- return __term == _Term_char::_Tc_quote;
+ return __term == _Term_quote;
case _Esc::_S_apos()[0]:
- return __term == _Term_char::_Tc_apos;
+ return __term == _Term_apos;
default:
return (__c >= 0 && __c < 0x20) || __c == 0x7f;
};
case _Esc::_S_apos()[0]:
return __format::__write(__out, _Esc::_S_apos().substr(1, 2));
default:
- return __format::__write_escape_seq(__out,
- static_cast<_UChar>(__c),
- _Esc::_S_u());
+ return __format::__write_escape_seq(
+ __out, static_cast<_UChar>(__c), _Esc::_S_u());
}
}
_Out
__write_escaped(_Out __out, basic_string_view<_CharT> __str, _Term_char __term)
{
- *__out = _Escapes<_CharT>::_S_term(__term);
- ++__out;
+ __out = __format::__write(__out, _Escapes<_CharT>::_S_term(__term));
if constexpr (__unicode::__literal_encoding_is_unicode<_CharT>())
__out = __format::__write_escaped_unicode(__out, __str, __term);
// TODO Handle non-ascii extended encoding
__out = __format::__write_escaped_ascii(__out, __str, __term);
- *__out = _Escapes<_CharT>::_S_term(__term);
- return ++__out;
+ return __format::__write(__out, _Escapes<_CharT>::_S_term(__term));
}
// A lightweight optional<locale>.
return __first;
if (*__first == 's')
- ++__first;
+ {
+ __spec._M_type = _Pres_s;
+ ++__first;
+ }
#if __glibcxx_format_ranges // C++ >= 23 && HOSTED
else if (*__first == '?')
{
- __spec._M_type = _Pres_esc;
+ __spec._M_debug = true;
++__first;
}
#endif
format(basic_string_view<_CharT> __s,
basic_format_context<_Out, _CharT>& __fc) const
{
- if (_M_spec._M_type == _Pres_esc)
+ if (_M_spec._M_debug)
return _M_format_escaped(__s, __fc);
if (_M_spec._M_width_kind == _WP_none
_M_format_escaped(basic_string_view<_CharT> __s,
basic_format_context<_Out, _CharT>& __fc) const
{
- constexpr auto __term = __format::_Term_char::_Tc_quote;
const size_t __padwidth = _M_spec._M_get_width(__fc);
if (__padwidth == 0 && _M_spec._M_prec_kind == _WP_none)
- return __format::__write_escaped(__fc.out(), __s, __term);
+ return __format::__write_escaped(__fc.out(), __s, _Term_quote);
const size_t __maxwidth = _M_spec._M_get_precision(__fc);
const size_t __width = __truncate(__s, __maxwidth);
// N.B. Escaping only increases width
if (__padwidth <= __width && _M_spec._M_prec_kind == _WP_none)
- return __format::__write_escaped(__fc.out(), __s, __term);
+ return __format::__write_escaped(__fc.out(), __s, _Term_quote);
// N.B. [tab:format.type.string] defines '?' as
// Copies the escaped string ([format.string.escaped]) to the output,
// so precision seem to appy to escaped string.
_Padding_sink<_Out, _CharT> __sink(__fc.out(), __padwidth, __maxwidth);
- __format::__write_escaped(__sink.out(), __s, __term);
+ __format::__write_escaped(__sink.out(), __s, _Term_quote);
return __sink._M_finish(_M_spec._M_align, _M_spec._M_fill);
}
size_t(ranges::distance(__rg)));
return format(__str, __fc);
}
- else if (_M_spec._M_type != _Pres_esc)
+ else if (!_M_spec._M_debug)
{
const size_t __padwidth = _M_spec._M_get_width(__fc);
if (__padwidth == 0 && _M_spec._M_prec_kind == _WP_none)
constexpr void
set_debug_format() noexcept
- { _M_spec._M_type = _Pres_esc; }
+ { _M_spec._M_debug = true; }
#endif
private:
constexpr
__formatter_int(_Spec<_CharT> __spec) noexcept
: _M_spec(__spec)
- { }
+ {
+ if (_M_spec._M_type == _Pres_none)
+ _M_spec._M_type = _Pres_d;
+ }
constexpr typename basic_format_parse_context<_CharT>::iterator
_M_do_parse(basic_format_parse_context<_CharT>& __pc, _Pres_type __type)
case 's':
if (__type == _AsBool)
{
- __spec._M_type = _Pres_s; // same value (and meaning) as "none"
+ __spec._M_type = _Pres_s; // same meaning as "none" for bool
++__first;
}
break;
case '?':
if (__type == _AsChar)
{
- __spec._M_type = _Pres_esc;
+ __spec._M_debug = true;
++__first;
}
#endif
{
auto __end = _M_do_parse(__pc, _AsBool);
if (_M_spec._M_type == _Pres_s)
- if (_M_spec._M_sign || _M_spec._M_alt || _M_spec._M_zero_fill)
+ if (_M_spec._M_sign != _Sign_default || _M_spec._M_alt
+ || _M_spec._M_zero_fill)
__throw_format_error("format error: format-spec contains "
"invalid formatting options for "
"'bool'");
else if constexpr (__char<_Tp>)
{
auto __end = _M_do_parse(__pc, _AsChar);
- if (_M_spec._M_type == _Pres_c || _M_spec._M_type == _Pres_esc)
- if (_M_spec._M_sign || _M_spec._M_alt || _M_spec._M_zero_fill
+ if (_M_spec._M_type == _Pres_c)
+ if (_M_spec._M_sign != _Sign_default || _M_spec._M_alt
+ || _M_spec._M_zero_fill
/* XXX should be invalid? || _M_spec._M_localized */)
__throw_format_error("format error: format-spec contains "
"invalid formatting options for "
_M_spec);
}
- [[__gnu__::__always_inline__]]
- static size_t
- _S_character_width(_CharT __c)
- {
- // N.B. single byte cannot encode charcter of width greater than 1
- if constexpr (sizeof(_CharT) > 1u &&
- __unicode::__literal_encoding_is_unicode<_CharT>())
- return __unicode::__field_width(__c);
- else
- return 1u;
- }
-
template<typename _Out>
typename basic_format_context<_Out, _CharT>::iterator
_M_format_character(_CharT __c,
- basic_format_context<_Out, _CharT>& __fc) const
+ basic_format_context<_Out, _CharT>& __fc) const
{
- return __format::__write_padded_as_spec({&__c, 1u},
- _S_character_width(__c),
- __fc, _M_spec);
- }
+ basic_string_view<_CharT> __in(&__c, 1u);
+ size_t __width = 1u;
+ // N.B. single byte cannot encode character of width greater than 1
+ if constexpr (sizeof(_CharT) > 1u &&
+ __unicode::__literal_encoding_is_unicode<_CharT>())
+ __width = __unicode::__field_width(__c);
- template<typename _Out>
- typename basic_format_context<_Out, _CharT>::iterator
- _M_format_character_escaped(_CharT __c,
- basic_format_context<_Out, _CharT>& __fc) const
- {
- using _Esc = _Escapes<_CharT>;
- constexpr auto __term = __format::_Term_char::_Tc_apos;
- const basic_string_view<_CharT> __in(&__c, 1u);
- if (_M_spec._M_get_width(__fc) <= 3u)
- return __format::__write_escaped(__fc.out(), __in, __term);
+ if (!_M_spec._M_debug)
+ return __format::__write_padded_as_spec(__in, __width,
+ __fc, _M_spec);
+
+ __width += 2;
+ if (_M_spec._M_get_width(__fc) <= __width)
+ return __format::__write_escaped(__fc.out(), __in, _Term_apos);
_CharT __buf[12];
- __format::_Fixedbuf_sink<_CharT> __sink(__buf);
- __format::__write_escaped(__sink.out(), __in, __term);
+ _Fixedbuf_sink<_CharT> __sink(__buf);
+ __format::__write_escaped(__sink.out(), __in, _Term_apos);
- const basic_string_view<_CharT> __escaped = __sink.view();
- size_t __estimated_width;
- if (__escaped[1] == _Esc::_S_bslash()[0]) // escape sequence
- __estimated_width = __escaped.size();
- else
- __estimated_width = 2 + _S_character_width(__c);
- return __format::__write_padded_as_spec(__escaped,
- __estimated_width,
+ __in = __sink.view();
+ if (__in[1] == _Escapes<_CharT>::_S_bslash()[0]) // escape sequence
+ __width = __in.size();
+ return __format::__write_padded_as_spec(__in, __width,
__fc, _M_spec);
}
constexpr
__formatter_ptr(_Spec<_CharT> __spec) noexcept
: _M_spec(__spec)
- { }
+ { _M_set_default(_Pres_p); }
constexpr typename basic_format_parse_context<_CharT>::iterator
- parse(basic_format_parse_context<_CharT>& __pc)
+ parse(basic_format_parse_context<_CharT>& __pc, _Pres_type __type = _Pres_p)
{
__format::_Spec<_CharT> __spec{};
const auto __last = __pc.end();
auto __first = __pc.begin();
- auto __finalize = [this, &__spec] {
+ auto __finalize = [this, &__spec, __type] {
_M_spec = __spec;
+ _M_set_default(__type);
};
auto __finished = [&] {
#endif
__first = __spec._M_parse_width(__first, __last, __pc);
+ if (__finished())
+ return __first;
- if (__first != __last)
+ if (*__first == 'p')
{
- if (*__first == 'p')
- ++__first;
+ __spec._M_type = _Pres_p;
+ _M_spec._M_alt = !_M_spec._M_alt;
+ ++__first;
+ }
#if __glibcxx_format >= 202304L
- else if (*__first == 'P')
- {
- __spec._M_type = __format::_Pres_P;
- ++__first;
- }
-#endif
+ else if (*__first == 'P')
+ {
+ __spec._M_type = _Pres_P;
+ _M_spec._M_alt = !_M_spec._M_alt;
+ ++__first;
}
+#endif
if (__finished())
return __first;
}
private:
+ [[__gnu__::__always_inline__]]
+ constexpr void
+ _M_set_default(_Pres_type __type)
+ {
+ if (_M_spec._M_type == _Pres_none && __type != _Pres_none)
+ {
+ _M_spec._M_type = __type;
+ _M_spec._M_alt = !_M_spec._M_alt;
+ }
+ }
+
__format::_Spec<_CharT> _M_spec{};
};
typename basic_format_context<_Out, _CharT>::iterator
format(_CharT __u, basic_format_context<_Out, _CharT>& __fc) const
{
- if (_M_f._M_spec._M_type == __format::_Pres_none
- || _M_f._M_spec._M_type == __format::_Pres_c)
+ if (_M_f._M_spec._M_type == __format::_Pres_c)
return _M_f._M_format_character(__u, __fc);
- else if (_M_f._M_spec._M_type == __format::_Pres_esc)
- return _M_f._M_format_character_escaped(__u, __fc);
else
return _M_f.format(static_cast<make_unsigned_t<_CharT>>(__u), __fc);
}
#if __glibcxx_format_ranges // C++ >= 23 && HOSTED
constexpr void
set_debug_format() noexcept
- { _M_f._M_spec._M_type = __format::_Pres_esc; }
+ { _M_f._M_spec._M_debug = true; }
#endif
private:
typename basic_format_context<_Out, wchar_t>::iterator
format(char __u, basic_format_context<_Out, wchar_t>& __fc) const
{
- if (_M_f._M_spec._M_type == __format::_Pres_none
- || _M_f._M_spec._M_type == __format::_Pres_c)
+ if (_M_f._M_spec._M_type == __format::_Pres_c)
return _M_f._M_format_character(__u, __fc);
- else if (_M_f._M_spec._M_type == __format::_Pres_esc)
- return _M_f._M_format_character_escaped(__u, __fc);
else
return _M_f.format(static_cast<unsigned char>(__u), __fc);
}
#if __glibcxx_format_ranges // C++ >= 23 && HOSTED
constexpr void
set_debug_format() noexcept
- { _M_f._M_spec._M_type = __format::_Pres_esc; }
+ { _M_f._M_spec._M_debug = true; }
#endif
private:
}
};
- enum _Arg_t : unsigned char {
+ enum class _Arg_t : unsigned char {
_Arg_none, _Arg_bool, _Arg_c, _Arg_i, _Arg_u, _Arg_ll, _Arg_ull,
_Arg_flt, _Arg_dbl, _Arg_ldbl, _Arg_str, _Arg_sv, _Arg_ptr, _Arg_handle,
_Arg_i128, _Arg_u128, _Arg_float128,
_Arg_ieee128 = _Arg_float128,
#endif
};
+ using enum _Arg_t;
template<typename _Context>
struct _Arg_value
{
__UINT64_TYPE__ __packed_types = 0;
for (auto __i = __types.rbegin(); __i != __types.rend(); ++__i)
- __packed_types = (__packed_types << _Bits) | *__i;
+ __packed_types = (__packed_types << _Bits) | (unsigned)*__i;
return __packed_types;
}
} // namespace __format
static constexpr int _S_packed_type_mask = 0b11111;
static constexpr int _S_max_packed_args = 12;
- static_assert( __format::_Arg_max_ <= (1 << _S_packed_type_bits) );
+ static_assert( (unsigned)__format::_Arg_max_ <= (1u << _S_packed_type_bits) );
template<typename... _Args>
using _Store = __format::_Arg_store<_Context, _Args...>;
if (*__first == '?')
{
++__first;
- __spec._M_type = __format::_Pres_esc;
+ __spec._M_debug = true;
if (__finished() || *__first != 's')
__throw_format_error("format error: '?' is allowed only in"
" combination with 's'");
++__first;
if constexpr (same_as<_Tp, _CharT>)
{
- if (__spec._M_type != __format::_Pres_esc)
- __spec._M_type = __format::_Pres_str;
+ __spec._M_type = __format::_Pres_s;
if (__finished())
return __finalize();
__throw_format_error("format error: element format specifier"
_M_format(_Rg& __rg, basic_format_context<_Out, _CharT>& __fc) const
{
if constexpr (same_as<_Tp, _CharT>)
- if (_M_spec._M_type == __format::_Pres_str
- || _M_spec._M_type == __format::_Pres_esc)
+ if (_M_spec._M_type == __format::_Pres_s)
{
__format::__formatter_str __fstr(_M_spec);
return __fstr._M_format_range(__rg, __fc);