libstdc++-v3/include/bits/formatfwd.h | 8 +
libstdc++-v3/include/std/format | 396 +++++++++++++-----
libstdc++-v3/testsuite/std/format/debug.cc | 386 ++++++++++++++++-
.../testsuite/std/format/ranges/sequence.cc | 116 +++++
.../testsuite/std/format/ranges/string.cc | 63 +++
libstdc++-v3/testsuite/std/format/tuple.cc | 93 ++++
6 files changed, 957 insertions(+), 105 deletions(-)
diff --git a/libstdc++-v3/include/bits/formatfwd.h
b/libstdc++-v3/include/bits/formatfwd.h
index 9ba658b078a..2d54ee5d30b 100644
--- a/libstdc++-v3/include/bits/formatfwd.h
+++ b/libstdc++-v3/include/bits/formatfwd.h
@@ -131,6 +131,14 @@ namespace __format
= ranges::input_range<const _Rg>
&& formattable<ranges::range_reference_t<const _Rg>, _CharT>;
+ // _Rg& and const _Rg& are both formattable and use same formatter
+ // specialization for their references.
+ template<typename _Rg, typename _CharT>
+ concept __simply_formattable_range
+ = __const_formattable_range<_Rg, _CharT>
+ && same_as<remove_cvref_t<ranges::range_reference_t<_Rg>>,
+ remove_cvref_t<ranges::range_reference_t<const _Rg>>>;
+
template<typename _Rg, typename _CharT>
using __maybe_const_range
= __conditional_t<__const_formattable_range<_Rg, _CharT>, const _Rg, _Rg>;
diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format
index 7d3067098be..355db5f2a60 100644
--- a/libstdc++-v3/include/std/format
+++ b/libstdc++-v3/include/std/format
@@ -56,7 +56,7 @@
#include <bits/ranges_base.h> // input_range, range_reference_t
#include <bits/ranges_util.h> // subrange
#include <bits/ranges_algobase.h> // ranges::copy
-#include <bits/stl_iterator.h> // back_insert_iterator
+#include <bits/stl_iterator.h> // back_insert_iterator, counted_iterator
#include <bits/stl_pair.h> // __is_pair
#include <bits/unicode.h> // __is_scalar_value, _Utf_view, etc.
#include <bits/utility.h> // tuple_size_v
@@ -99,19 +99,12 @@ namespace __format
// Size for stack located buffer
template<typename _CharT>
- constexpr size_t __stackbuf_size = 32 * sizeof(void*) / sizeof(_CharT);
+ constexpr size_t __stackbuf_size = 32 * sizeof(void*) / sizeof(_CharT);
// Type-erased character sinks.
template<typename _CharT> class _Sink;
template<typename _CharT> class _Fixedbuf_sink;
- template<typename _Seq> class _Seq_sink;
-
- template<typename _CharT, typename _Alloc = allocator<_CharT>>
- using _Str_sink
- = _Seq_sink<basic_string<_CharT, char_traits<_CharT>, _Alloc>>;
-
- // template<typename _CharT, typename _Alloc = allocator<_CharT>>
- // using _Vec_sink = _Seq_sink<vector<_CharT, _Alloc>>;
+ template<typename _Out, typename _CharT> class _Padding_sink;
// Output iterator that writes to a type-erase character sink.
template<typename _CharT>
@@ -892,6 +885,25 @@ namespace __format
__spec._M_fill);
}
+ template<typename _CharT>
+ size_t
+ __truncate(basic_string_view<_CharT>& __s, size_t __prec)
+ {
+ if constexpr (__unicode::__literal_encoding_is_unicode<_CharT>())
+ {
+ if (__prec != (size_t)-1)
+ return __unicode::__truncate(__s, __prec);
+ else
+ return __unicode::__field_width(__s);
+ }
+ else
+ {
+ __s = __s.substr(0, __prec);
+ return __s.size();
+ }
+ }
+
+
// Values are indices into _Escapes::all.
enum class _Term_char : unsigned char {
_Tc_quote = 12,
@@ -1327,82 +1339,110 @@ namespace __format
format(basic_string_view<_CharT> __s,
basic_format_context<_Out, _CharT>& __fc) const
{
- constexpr auto __term = __format::_Term_char::_Tc_quote;
- const auto __write_direct = [&]
- {
- if (_M_spec._M_type == _Pres_esc)
- return __format::__write_escaped(__fc.out(), __s, __term);
- else
- return __format::__write(__fc.out(), __s);
- };
+ if (_M_spec._M_type == _Pres_esc)
+ return _M_format_escaped(__s, __fc);
if (_M_spec._M_width_kind == _WP_none
&& _M_spec._M_prec_kind == _WP_none)
- return __write_direct();
+ return __format::__write(__fc.out(), __s);
- const size_t __prec =
- _M_spec._M_prec_kind != _WP_none
- ? _M_spec._M_get_precision(__fc)
- : basic_string_view<_CharT>::npos;
+ const size_t __maxwidth = _M_spec._M_get_precision(__fc);
+ const size_t __width = __format::__truncate(__s, __maxwidth);
+ return __format::__write_padded_as_spec(__s, __width, __fc, _M_spec);
+ }
- const size_t __estimated_width = _S_trunc(__s, __prec);
- // N.B. Escaping only increases width
- if (_M_spec._M_get_width(__fc) <= __estimated_width
- && _M_spec._M_prec_kind == _WP_none)
- return __write_direct();
+ template<typename _Out>
+ _Out
+ _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);
- if (_M_spec._M_type != _Pres_esc)
- return __format::__write_padded_as_spec(__s, __estimated_width,
- __fc, _M_spec);
+ 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);
- __format::_Str_sink<_CharT> __sink;
- __format::__write_escaped(__sink.out(), __s, __term);
- basic_string_view<_CharT> __escaped(__sink.view().data(),
- __sink.view().size());
- const size_t __escaped_width = _S_trunc(__escaped, __prec);
// 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.
- return __format::__write_padded_as_spec(__escaped, __escaped_width,
- __fc, _M_spec);
+ _Padding_sink<_Out, _CharT> __sink(__fc.out(), __padwidth,
__maxwidth);
+ __format::__write_escaped(__sink.out(), __s, __term);
+ return __sink._M_finish(_M_spec._M_align, _M_spec._M_fill);
}
#if __glibcxx_format_ranges // C++ >= 23 && HOSTED
template<ranges::input_range _Rg, typename _Out>
requires same_as<remove_cvref_t<ranges::range_reference_t<_Rg>>, _CharT>
- typename basic_format_context<_Out, _CharT>::iterator
+ _Out
_M_format_range(_Rg&& __rg, basic_format_context<_Out, _CharT>& __fc)
const
{
+ using _Range = remove_reference_t<_Rg>;
using _String = basic_string<_CharT>;
using _String_view = basic_string_view<_CharT>;
- if constexpr (ranges::forward_range<_Rg> || ranges::sized_range<_Rg>)
+ if constexpr (!is_lvalue_reference_v<_Rg>)
+ return _M_format_range<_Range&>(__rg, __fc);
+ else if constexpr (!is_const_v<_Range>
+ && __simply_formattable_range<_Range, _CharT>)
+ return _M_format_range<const _Range&>(__rg, __fc);
+ else if constexpr (ranges::contiguous_range<_Rg>)
+ {
+ _String_view __str(ranges::data(__rg),
+ size_t(ranges::distance(__rg)));
+ return format(__str, __fc);
+ }
+ else if (_M_spec._M_type != _Pres_esc)
+ {
+ const size_t __padwidth = _M_spec._M_get_width(__fc);
+ if (__padwidth == 0 && _M_spec._M_prec_kind == _WP_none)
+ return ranges::copy(__rg, __fc.out()).out;
+
+ _Padding_sink<_Out, _CharT> __sink(__fc.out(), __padwidth,
+
_M_spec._M_get_precision(__fc));
+ ranges::copy(__rg, __sink.out());
+ return __sink._M_finish(_M_spec._M_align, _M_spec._M_fill);
+ }
+ else if constexpr (ranges::forward_range<_Rg> ||
ranges::sized_range<_Rg>)
{
const size_t __n(ranges::distance(__rg));
- if constexpr (ranges::contiguous_range<_Rg>)
- return format(_String_view(ranges::data(__rg), __n), __fc);
- else if (__n <= __format::__stackbuf_size<_CharT>)
+ size_t __w = __n;
+ if constexpr (!__unicode::__literal_encoding_is_unicode<_CharT>())
+ if (size_t __max = _M_spec._M_get_precision(__fc); __n > __max)
+ __w == __max;
+
+ if (__w <= __format::__stackbuf_size<_CharT>)
{
_CharT __buf[__format::__stackbuf_size<_CharT>];
- ranges::copy(__rg, __buf);
- return format(_String_view(__buf, __n), __fc);
+ ranges::copy_n(ranges::begin(__rg), __w, __buf);
+ return _M_format_escaped(_String_view(__buf, __n), __fc);
}
- else if constexpr (ranges::sized_range<_Rg>)
- return format(_String(from_range, __rg), __fc);
else if constexpr (ranges::random_access_range<_Rg>)
{
ranges::iterator_t<_Rg> __first = ranges::begin(__rg);
- ranges::subrange __sub(__first, __first + __n);
- return format(_String(from_range, __sub), __fc);
+ ranges::subrange __sub(__first, __first + __w);
+ return _M_format_escaped(_String(from_range, __sub), __fc);
}
+ else if (__w <= __n)
+ {
+ ranges::subrange __sub(counted_iterator(ranges::begin(__rg)),
+ default_sentinel);
+ return _M_format_escaped(_String(from_range, __sub), __fc);
+ }
+ else if constexpr (ranges::sized_range<_Rg>)
+ return _M_format_escaped(_String(from_range, __rg), __fc);
else
{
// N.B. preserve the computed size
ranges::subrange __sub(__rg, __n);
- return format(_String(from_range, __sub), __fc);
+ return _M_format_escaped(_String(from_range, __sub), __fc);
}
}
else
- return format(_String(from_range, __rg), __fc);
+ return _M_format_escaped(_String(from_range, __rg), __fc);
}
constexpr void
@@ -1411,23 +1451,6 @@ namespace __format
#endif
private:
- static size_t
- _S_trunc(basic_string_view<_CharT>& __s, size_t __prec)
- {
- if constexpr (__unicode::__literal_encoding_is_unicode<_CharT>())
- {
- if (__prec != basic_string_view<_CharT>::npos)
- return __unicode::__truncate(__s, __prec);
- else
- return __unicode::__field_width(__s);
- }
- else
- {
- __s = __s.substr(0, __prec);
- return __s.size();
- }
- }
-
_Spec<_CharT> _M_spec{};
};
@@ -3271,12 +3294,12 @@ namespace __format
// A sink that fills a sequence (e.g. std::string, std::vector, std::deque).
// Writes to a buffer then appends that to the sequence when it fills up.
template<typename _Seq>
- class _Seq_sink final : public _Buf_sink<typename _Seq::value_type>
+ class _Seq_sink : public _Buf_sink<typename _Seq::value_type>
{
using _CharT = typename _Seq::value_type;
_Seq _M_seq;
-
+ protected:
// Transfer buffer contents to the sequence, so buffer can be refilled.
void
_M_overflow() override
@@ -3348,6 +3371,17 @@ namespace __format
}
}
+ void _M_trim(span<const _CharT> __s)
+ requires __is_specialization_of<_Seq, basic_string>
+ {
+ _GLIBCXX_DEBUG_ASSERT(__s.data() == this->_M_buf
+ || __s.data() == _M_seq.data());
+ if (__s.data() == _M_seq.data())
+ _M_seq.resize(__s.size());
+ else
+ this->_M_reset(this->_M_buf, __s.size());
+ }
+
public:
// TODO: for SSO string, use SSO buffer as initial span, then switch
// to _M_buf if it overflows? Or even do that for all unused capacity?
@@ -3373,7 +3407,7 @@ namespace __format
// A writable span that views everything written to the sink.
// Will be either a view over _M_seq or the used part of _M_buf.
span<_CharT>
- view()
+ _M_span()
{
auto __s = this->_M_used();
if (_M_seq.size())
@@ -3384,9 +3418,21 @@ namespace __format
}
return __s;
}
+
+ basic_string_view<_CharT>
+ view()
+ {
+ auto __span = _M_span();
+ return basic_string_view<_CharT>(__span.data(), __span.size());
+ }
};
- // A sink that writes to an output iterator.
+ template<typename _CharT, typename _Alloc = allocator<_CharT>>
+ using _Str_sink
+ = _Seq_sink<basic_string<_CharT, char_traits<_CharT>, _Alloc>>;
+
+ // template<typename _CharT, typename _Alloc = allocator<_CharT>>
+ // using _Vec_sink = _Seq_sink<vector<_CharTthis-> sink that writes to an
output iterator.
// Writes to a fixed-size buffer and then flushes to the output iterator
// when the buffer fills up.
template<typename _CharT, typename _OutIter>
@@ -3554,6 +3600,171 @@ namespace __format
}
};
+ // A sink for handling the padded outputs (_M_padwidth) or truncated
+ // (_M_maxwidth). The handling is done by writting to buffer (_Str_strink)
+ : _M_padwidth(__padwidth), _M_maxwidth(__maxwidth),
+ _M_out(std::move(__out)), _M_printwidth(0)
+ { }
+
+ _Out
+ _M_finish(_Align __align, char32_t __fill_char)
+ {
+ // Handle any characters in the buffer.
+ if (auto __rem = this->_M_used().size())
+ if (_M_ignoring())
+ this->_M_rewind();
+ else if (!_M_buffering())
+ _M_flush();
+ else
+ _M_update(__rem);
+
+ if (!_M_buffering() || !_M_force_update())
+ // Characters were already written to _M_out.
+ if (_M_printwidth >= _M_padwidth)
+ return std::move(_M_out);
+
+ const auto __str = this->view();
+ if (_M_printwidth >= _M_padwidth)
+ return __format::__write(std::move(_M_out), __str);
+
+ const size_t __nfill = _M_padwidth - _M_printwidth;
+ return __format::__write_padded(std::move(_M_out), __str,
+ __align, __nfill, __fill_char);
+ }
+ };
+
enum _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,
@@ -5160,7 +5371,8 @@ namespace __format
// as we need to format to temporary buffer, using the same iterator.
static_assert(is_same_v<_Out, __format::_Sink_iter<_CharT>>);
- if (__spec._M_get_width(__fc) == 0)
+ const size_t __padwidth = __spec._M_get_width(__fc);
+ if (__padwidth == 0)
return __call(__fc);
struct _Restore_out
@@ -5169,48 +5381,30 @@ namespace __format
: _M_ctx(std::addressof(__fc)), _M_out(__fc.out())
{ }
- void _M_trigger()
+ void
+ _M_disarm()
+ { _M_ctx = nullptr; }
+
+ ~_Restore_out()
{
if (_M_ctx)
_M_ctx->advance_to(_M_out);
- _M_ctx = nullptr;
}
- ~_Restore_out()
- { _M_trigger(); }
-
private:
basic_format_context<_Sink_iter<_CharT>, _CharT>* _M_ctx;
_Sink_iter<_CharT> _M_out;
};
_Restore_out __restore(__fc);
- // TODO Consider double sinking, first buffer of width
- // size and then original sink, if first buffer is overun
- // we do not need to align
- _Str_sink<_CharT> __buf;
- __fc.advance_to(__buf.out());
+ _Padding_sink<_Sink_iter<_CharT>, _CharT> __sink(__fc.out(), __padwidth);
+ __fc.advance_to(__sink.out());
__call(__fc);
- __restore._M_trigger();
-
- basic_string_view<_CharT> __str(__buf.view());
- size_t __width;
- if constexpr (__unicode::__literal_encoding_is_unicode<_CharT>())
- __width = __unicode::__field_width(__str);
- else
- __width = __str.size();
-
- return __format::__write_padded_as_spec(__str, __width, __fc, __spec);
+ __fc.advance_to(__sink._M_finish(__spec._M_align, __spec._M_fill));
+ __restore._M_disarm();
+ return __fc.out();
}
- // _Rg& and const _Rg& are both formattable and use same formatter
- // specialization for their references.
- template<typename _Rg, typename _CharT>
- concept __simply_formattable_range
- = __const_formattable_range<_Rg, _CharT>
- && same_as<remove_cvref_t<ranges::range_reference_t<_Rg>>,
- remove_cvref_t<ranges::range_reference_t<const _Rg>>>;
-
template<size_t _Pos, typename _Tp, typename _CharT>
struct __indexed_formatter_storage
{
diff --git a/libstdc++-v3/testsuite/std/format/debug.cc
b/libstdc++-v3/testsuite/std/format/debug.cc
index 71bb7f4a0fe..d3b025eabb4 100644
--- a/libstdc++-v3/testsuite/std/format/debug.cc
+++ b/libstdc++-v3/testsuite/std/format/debug.cc
@@ -2,6 +2,7 @@
// { dg-options "-fexec-charset=UTF-8 -fwide-exec-charset=UTF-32BE
-DUNICODE_ENC" { target be } }
// { dg-do run { target c++23 } }
// { dg-add-options no_pch }
+// { dg-timeout-factor 2 }
#include <format>
#include <testsuite_hooks.h>
@@ -114,11 +115,11 @@ test_extended_ascii()
}
}
-#if UNICODE_ENC
template<typename _CharT>
void
test_unicode_escapes()
{
+#if UNICODE_ENC
std::basic_string<_CharT> res;
const auto in = WIDEN(
@@ -160,12 +161,14 @@ test_unicode_escapes()
res = fdebug(in[5]);
VERIFY( res == WIDEN("'\U0001f984'") );
}
+#endif // UNICODE_ENC
}
template<typename _CharT>
void
test_grapheme_extend()
{
+#if UNICODE_ENC
std::basic_string<_CharT> res;
const auto vin = WIDEN("o\u0302\u0323");
@@ -184,12 +187,14 @@ test_grapheme_extend()
res = fdebug(in[1]);
VERIFY( res == WIDEN(R"('\u{302}')") );
}
+#endif // UNICODE_ENC
}
template<typename _CharT>
void
test_replacement_char()
{
+#if UNICODE_ENC
std::basic_string<_CharT> repl = WIDEN("\uFFFD");
std::basic_string<_CharT> res = fdebug(repl);
VERIFY( res == WIDEN("\"\uFFFD\"") );
@@ -197,11 +202,13 @@ test_replacement_char()
repl = WIDEN("\uFFFD\uFFFD");
res = fdebug(repl);
VERIFY( res == WIDEN("\"\uFFFD\uFFFD\"") );
+#endif // UNICODE_ENC
}
void
test_ill_formed_utf8_seq()
{
+#if UNICODE_ENC
std::string_view seq = "\xf0\x9f\xa6\x84"; // \U0001F984
std::string res;
@@ -233,11 +240,13 @@ test_ill_formed_utf8_seq()
VERIFY( res == R"('\x{84}')" );
res = fdebug(seq.substr(3, 1));
VERIFY( res == R"("\x{84}")" );
+#endif // UNICODE_ENC
}
void
test_ill_formed_utf32()
{
+#if UNICODE_ENC
std::wstring res;
wchar_t ic1 = static_cast<wchar_t>(0xff'ffff);
@@ -255,8 +264,8 @@ test_ill_formed_utf32()
std::wstring is2(1, ic2);
res = fdebug(is2);
VERIFY( res == LR"("\x{ffffffff}")" );
-}
#endif // UNICODE_ENC
+}
template<typename _CharT>
void
@@ -331,6 +340,375 @@ test_prec()
#endif // UNICODE_ENC
}
+bool strip_quote(std::string_view& v)
+{
+ if (!v.starts_with('"'))
+ return false;
+ v.remove_prefix(1);
+ return true;
+}
+
+bool strip_quotes(std::string_view& v)
+{
+ if (!v.starts_with('"') || !v.ends_with('"'))
+ return false;
+ v.remove_prefix(1);
+ v.remove_suffix(1);
+ return true;
+}
+
+bool strip_prefix(std::string_view& v, size_t n, char c)
+{
+ size_t pos = v.find_first_not_of(c);
+ if (pos == std::string_view::npos)
+ pos = v.size();
+ if (pos != n)
+ return false;
+ v.remove_prefix(n);
+ return true;
+}
+
+void test_padding()
+{
+ std::string res;
+ std::string_view resv;
+
+ // width and size are 26
+ std::string in = "abcdefghijklmnopqrstuvwxyz";
+ in += in; // width and size are 52
+ in += in; // width and size are 104
+ in += in; // width and size are 208
+ in += in; // width and size are 416
+ std::string_view inv = in;
+
+ resv = res = std::format("{}", in);
+ VERIFY( resv == inv );
+
+ resv = res = std::format("{:.500}", in);
+ VERIFY( resv == inv );
+
+ resv = res = std::format("{:.400}", in);
+ VERIFY( resv == inv.substr(0, 400) );
+
+ resv = res = std::format("{:.200}", in);
+ VERIFY( resv == inv.substr(0, 200) );
+
+ resv = res = std::format("{:.10}", in);
+ VERIFY( resv == inv.substr(0, 10) );
+
+ resv = res = std::format("{:.0}", in);
+ VERIFY( resv == "" );
+
+ resv = res = std::format("{:*>20}", in);
+ VERIFY( resv == inv );
+
+ resv = res = std::format("{:*>20.500}", in);
+ VERIFY( resv == inv );
+
+ resv = res = std::format("{:*>20.400}", in);
+ VERIFY( resv == inv.substr(0, 400) );
+
+ resv = res = std::format("{:*>20.200}", in);
+ VERIFY( resv == inv.substr(0, 200) );
+
+ resv = res = std::format("{:*>20.10}", in);
+ VERIFY( strip_prefix(resv, 10, '*') );
+ VERIFY( resv == inv.substr(0, 10) );
+
+ resv = res = std::format("{:*>20.0}", in);
+ VERIFY( strip_prefix(resv, 20, '*') );
+ VERIFY( resv == "" );
+
+ resv = res = std::format("{:*>450}", in);
+ VERIFY( strip_prefix(resv, 34, '*') );
+ VERIFY( resv == inv );
+
+ resv = res = std::format("{:*>450.500}", in);
+ VERIFY( strip_prefix(resv, 34, '*') );
+ VERIFY( resv == inv );
+
+ resv = res = std::format("{:*>450.420}", in);
+ VERIFY( strip_prefix(resv, 34, '*') );
+ VERIFY( resv == inv );
+
+ resv = res = std::format("{:*>450.400}", in);
+ VERIFY( strip_prefix(resv, 50, '*') );
+ VERIFY( resv == inv.substr(0, 400) );
+
+ resv = res = std::format("{:*>450.200}", in);
+ VERIFY( strip_prefix(resv, 250, '*') );
+ VERIFY( resv == inv.substr(0, 200) );
+
+ resv = res = std::format("{:*>450.10}", in);
+ VERIFY( strip_prefix(resv, 440, '*') );
+ VERIFY( resv == inv.substr(0, 10) );
+
+ resv = res = std::format("{:*>450.0}", in);
+ VERIFY( strip_prefix(resv, 450, '*') );
+ VERIFY( resv == "" );
+
+ resv = res = std::format("{:?}", in);
+ VERIFY( strip_quotes(resv) );
+ VERIFY( resv == inv );
+
+ resv = res = std::format("{:.500?}", in);
+ VERIFY( strip_quotes(resv) );
+ VERIFY( resv == inv );
+
+ resv = res = std::format("{:.400?}", in);
+ VERIFY( strip_quote(resv) );
+ VERIFY( resv == inv.substr(0, 399) );
+
+ resv = res = std::format("{:.200?}", in);
+ VERIFY( strip_quote(resv) );
+ VERIFY( resv == inv.substr(0, 199) );
+
+ resv = res = std::format("{:.10?}", in);
+ VERIFY( strip_quote(resv) );
+ VERIFY( resv == inv.substr(0, 9) );
+
+ resv = res = std::format("{:.1?}", in);
+ VERIFY( strip_quote(resv) );
+ VERIFY( resv == "" );
+
+ resv = res = std::format("{:.0?}", in);
+ VERIFY( resv == "" );
+
+ resv = res = std::format("{:*>20?}", in);
+ VERIFY( strip_quotes(resv) );
+ VERIFY( resv == inv );
+
+ resv = res = std::format("{:*>20.500?}", in);
+ VERIFY( strip_quotes(resv) );
+ VERIFY( resv == inv );
+
+ resv = res = std::format("{:*>20.400?}", in);
+ VERIFY( strip_quote(resv) );
+ VERIFY( resv == inv.substr(0, 399) );
+
+ resv = res = std::format("{:*>20.200?}", in);
+ VERIFY( strip_quote(resv) );
+ VERIFY( resv == inv.substr(0, 199) );
+
+ resv = res = std::format("{:*>20.10?}", in);
+ VERIFY( strip_prefix(resv, 10, '*') );
+ VERIFY( strip_quote(resv) );
+ VERIFY( resv == inv.substr(0, 9) );
+
+ resv = res = std::format("{:*>20.1?}", in);
+ VERIFY( strip_prefix(resv, 19, '*') );
+ VERIFY( strip_quote(resv) );
+ VERIFY( resv == "" );
+
+ resv = res = std::format("{:*>20.0?}", in);
+ VERIFY( strip_prefix(resv, 20, '*') );
+ VERIFY( resv == "" );
+
+ resv = res = std::format("{:*>450?}", in);
+ VERIFY( strip_prefix(resv, 32, '*') );
+ VERIFY( strip_quotes(resv) );
+ VERIFY( resv == inv );
+
+ resv = res = std::format("{:*>450.500?}", in);
+ VERIFY( strip_prefix(resv, 32, '*') );
+ VERIFY( strip_quotes(resv) );
+ VERIFY( resv == inv );
+
+ resv = res = std::format("{:*>450.420?}", in);
+ VERIFY( strip_prefix(resv, 32, '*') );
+ VERIFY( strip_quotes(resv) );
+ VERIFY( resv == inv );
+
+ resv = res = std::format("{:*>450.400?}", in);
+ VERIFY( strip_prefix(resv, 50, '*') );
+ VERIFY( strip_quote(resv) );
+ VERIFY( resv == inv.substr(0, 399) );
+
+ resv = res = std::format("{:*>450.200?}", in);
+ VERIFY( strip_prefix(resv, 250, '*') );
+ VERIFY( strip_quote(resv) );
+ VERIFY( resv == inv.substr(0, 199) );
+
+ resv = res = std::format("{:*>450.10?}", in);
+ VERIFY( strip_prefix(resv, 440, '*') );
+ VERIFY( strip_quote(resv) );
+ VERIFY( resv == inv.substr(0, 9) );
+
+ resv = res = std::format("{:*>450.1?}", in);
+ VERIFY( strip_prefix(resv, 449, '*') );
+ VERIFY( strip_quote(resv) );
+ VERIFY( resv == "" );
+
+ resv = res = std::format("{:*>450.0?}", in);
+ VERIFY( strip_prefix(resv, 450, '*') );
+ VERIFY( resv == "" );
+
+#if UNICODE_ENC
+ // width is 3, size is 15
+ in = "o\u0302\u0323i\u0302\u0323u\u0302\u0323";
+ in += in; // width is 6, size is 30
+ in += in; // width is 12, size is 60
+ in += in; // width is 24, size is 120
+ in += in; // width is 48, size is 240
+ in += in; // width is 96, size is 480
+ in += in; // width is 192, size is 960
+ inv = in;
+
+ resv = res = std::format("{:}", in);
+ VERIFY( resv == inv );
+
+ resv = res = std::format("{:.200}", in);
+ VERIFY( resv == inv );
+
+ resv = res = std::format("{:.96}", in);
+ VERIFY( resv == inv.substr(0, 480) );
+
+ resv = res = std::format("{:.12}", in);
+ VERIFY( resv == inv.substr(0, 60) );
+
+ resv = res = std::format("{:.3}", in);
+ VERIFY( resv == inv.substr(0, 15) );
+
+ resv = res = std::format("{:.0}", in);
+ VERIFY( resv == "" );
+
+ resv = res = std::format("{:*>10}", in);
+ VERIFY( resv == inv );
+
+ resv = res = std::format("{:*>10.200}", in);
+ VERIFY( resv == inv );
+
+ resv = res = std::format("{:*>10.96}", in);
+ VERIFY( resv == inv.substr(0, 480) );
+
+ resv = res = std::format("{:*>10.12}", in);
+ VERIFY( resv == inv.substr(0, 60) );
+
+ resv = res = std::format("{:*>10.3}", in);
+ VERIFY( strip_prefix(resv, 7, '*') );
+ VERIFY( resv == inv.substr(0, 15) );
+
+ resv = res = std::format("{:*>10.0}", in);
+ VERIFY( strip_prefix(resv, 10, '*') );
+ VERIFY( resv == "" );
+
+ resv = res = std::format("{:*>240s}", in);
+ VERIFY( strip_prefix(resv, 48, '*') );
+ VERIFY( resv == inv );
+
+ resv = res = std::format("{:*>240.200s}", in);
+ VERIFY( strip_prefix(resv, 48, '*') );
+ VERIFY( resv == inv );
+
+ resv = res = std::format("{:*>240.96s}", in);
+ VERIFY( strip_prefix(resv, 144, '*') );
+ VERIFY( resv == inv.substr(0, 480) );
+
+ resv = res = std::format("{:*>240.12}", in);
+ VERIFY( strip_prefix(resv, 228, '*') );
+ VERIFY( resv == inv.substr(0, 60) );
+
+ resv = res = std::format("{:*>240.3s}", in);
+ VERIFY( strip_prefix(resv, 237, '*') );
+ VERIFY( resv == inv.substr(0, 15) );
+
+ resv = res = std::format("{:*>240.0s}", in);
+ VERIFY( strip_prefix(resv, 240, '*') );
+ VERIFY( resv == "" );
+
+ resv = res = std::format("{:?}", in);
+ VERIFY( strip_quotes(resv) );
+ VERIFY( resv == inv );
+
+ resv = res = std::format("{:.200?}", in);
+ VERIFY( strip_quotes(resv) );
+ VERIFY( resv == inv );
+
+ resv = res = std::format("{:.97?}", in);
+ VERIFY( strip_quote(resv) );
+ VERIFY( resv == inv.substr(0, 480) );
+
+ resv = res = std::format("{:.13?}", in);
+ VERIFY( strip_quote(resv) );
+ VERIFY( resv == inv.substr(0, 60) );
+
+ resv = res = std::format("{:.4?}", in);
+ VERIFY( strip_quote(resv) );
+ VERIFY( resv == inv.substr(0, 15) );
+
+ resv = res = std::format("{:.1?}", in);
+ VERIFY( strip_quote(resv) );
+ VERIFY( resv == "" );
+
+ resv = res = std::format("{:.0?}", in);
+ VERIFY( resv == "" );
+
+ resv = res = std::format("{:*>10?}", in);
+ VERIFY( strip_quotes(resv) );
+ VERIFY( resv == inv );
+
+ resv = res = std::format("{:*>10.200?}", in);
+ VERIFY( strip_quotes(resv) );
+ VERIFY( resv == inv );
+
+ resv = res = std::format("{:*>10.97?}", in);
+ VERIFY( strip_quote(resv) );
+ VERIFY( resv == inv.substr(0, 480) );
+
+ resv = res = std::format("{:*>10.13?}", in);
+ VERIFY( strip_quote(resv) );
+ VERIFY( resv == inv.substr(0, 60) );
+
+ resv = res = std::format("{:*>10.4?}", in);
+ VERIFY( strip_prefix(resv, 6, '*') );
+ VERIFY( strip_quote(resv) );
+ VERIFY( resv == inv.substr(0, 15) );
+
+ resv = res = std::format("{:*>10.1?}", in);
+ VERIFY( strip_prefix(resv, 9, '*') );
+ VERIFY( strip_quote(resv) );
+ VERIFY( resv == "" );
+
+ resv = res = std::format("{:*>10.0?}", in);
+ VERIFY( strip_prefix(resv, 10, '*') );
+ VERIFY( resv == "" );
+
+ resv = res = std::format("{:*>240?}", in);
+ VERIFY( strip_prefix(resv, 46, '*') );
+ VERIFY( strip_quotes(resv) );
+ VERIFY( resv == inv );
+
+ resv = res = std::format("{:*>240.200?}", in);
+ VERIFY( strip_prefix(resv, 46, '*') );
+ VERIFY( strip_quotes(resv) );
+ VERIFY( resv == inv );
+
+ resv = res = std::format("{:*>240.97?}", in);
+ VERIFY( strip_prefix(resv, 143, '*') );
+ VERIFY( strip_quote(resv) );
+ VERIFY( resv == inv.substr(0, 480) );
+
+ resv = res = std::format("{:*>240.13?}", in);
+ VERIFY( strip_prefix(resv, 227, '*') );
+ VERIFY( strip_quote(resv) );
+ VERIFY( resv == inv.substr(0, 60) );
+
+ resv = res = std::format("{:*>240.4?}", in);
+ VERIFY( strip_prefix(resv, 236, '*') );
+ VERIFY( strip_quote(resv) );
+ VERIFY( resv == inv.substr(0, 15) );
+
+ resv = res = std::format("{:*>240.1?}", in);
+ VERIFY( strip_prefix(resv, 239, '*') );
+ VERIFY( strip_quote(resv) );
+ VERIFY( resv == "" );
+
+ resv = res = std::format("{:*>240.0?}", in);
+ VERIFY( strip_prefix(resv, 240, '*') );
+ VERIFY( resv == "" );
+#endif // UNICODE_ENC
+}
+
void test_char_as_wchar()
{
std::wstring res;
@@ -435,7 +813,6 @@ int main()
test_extended_ascii<char>();
test_extended_ascii<wchar_t>();
-#if UNICODE_ENC
test_unicode_escapes<char>();
test_unicode_escapes<wchar_t>();
test_grapheme_extend<char>();
@@ -444,12 +821,13 @@ int main()
test_replacement_char<wchar_t>();
test_ill_formed_utf8_seq();
test_ill_formed_utf32();
-#endif // UNICODE_ENC
test_fill<char>();
test_fill<wchar_t>();
test_prec<char>();
test_prec<wchar_t>();
+ test_padding();
+
test_formatters_c();
}
diff --git a/libstdc++-v3/testsuite/std/format/ranges/sequence.cc
b/libstdc++-v3/testsuite/std/format/ranges/sequence.cc
index f05f6ec1e1c..75fe4c19a52 100644
--- a/libstdc++-v3/testsuite/std/format/ranges/sequence.cc
+++ b/libstdc++-v3/testsuite/std/format/ranges/sequence.cc
@@ -1,4 +1,5 @@
// { dg-do run { target c++23 } }
+// { dg-options "-fexec-charset=UTF-8" }
// { dg-timeout-factor 2 }
#include <array>
@@ -6,6 +7,7 @@
#include <list>
#include <ranges>
#include <span>
+#include <string>
#include <testsuite_hooks.h>
#include <testsuite_iterators.h>
#include <vector>
@@ -199,9 +201,123 @@ test_nested()
VERIFY( res == "+[01, 02, 11, 12]+" );
}
+bool strip_quote(std::string_view& v)
+{
+ if (!v.starts_with('"'))
+ return false;
+ v.remove_prefix(1);
+ return true;
+}
+
+bool strip_prefix(std::string_view& v, std::string_view expected, bool quoted
= false)
+{
+ if (quoted && !strip_quote(v))
+ return false;
+ if (!v.starts_with(expected))
+ return false;
+ v.remove_prefix(expected.size());
+ if (quoted && !strip_quote(v))
+ return false;
+ return true;
+}
+
+bool strip_squares(std::string_view& v)
+{
+ if (!v.starts_with('[') || !v.ends_with(']'))
+ return false;
+ v.remove_prefix(1);
+ v.remove_suffix(1);
+ return true;
+}
+
+bool strip_prefix(std::string_view& v, size_t n, char c)
+{
+ size_t pos = v.find_first_not_of(c);
+ if (pos == std::string_view::npos)
+ pos = v.size();
+ if (pos != n)
+ return false;
+ v.remove_prefix(n);
+ return true;
+}
+
+void test_padding()
+{
+ std::string res;
+ std::string_view resv;
+
+ // width is 3, size is 15
+ std::string in = "o\u0302\u0323i\u0302\u0323u\u0302\u0323";
+ in += in; // width is 6, size is 30
+ in += in; // width is 12, size is 60
+ in += in; // width is 24, size is 120
+ in += in; // width is 48, size is 240
+ // width is 192, size is 960
+ std::vector<std::string> const vs{in, in, in, in};
+
+ auto const check_elems = [=](std::string_view& v, bool quoted)
+ {
+ VERIFY( strip_prefix(v, in, quoted) );
+ VERIFY( strip_prefix(v, ", ", false) );
+ VERIFY( strip_prefix(v, in, quoted) );
+ VERIFY( strip_prefix(v, ", ", false) );
+ VERIFY( strip_prefix(v, in, quoted) );
+ VERIFY( strip_prefix(v, ", ", false) );
+ VERIFY( strip_prefix(v, in, quoted) );
+ return v.empty();
+ };
+
+ resv = res = std::format("{}", vs);
+ VERIFY( strip_squares(resv) );
+ VERIFY( check_elems(resv, true) );
+
+ resv = res = std::format("{:n}", vs);
+ VERIFY( check_elems(resv, true) );
+
+ resv = res = std::format("{::}", vs);
+ VERIFY( strip_squares(resv) );
+ VERIFY( check_elems(resv, false) );
+
+ resv = res = std::format("{:n:}", vs);
+ VERIFY( check_elems(resv, false) );
+
+ resv = res = std::format("{:*>10}", vs);
+ VERIFY( strip_squares(resv) );
+ VERIFY( check_elems(resv, true) );
+
+ resv = res = std::format("{:*>10n}", vs);
+ VERIFY( check_elems(resv, true) );
+
+ resv = res = std::format("{:*>10:}", vs);
+ VERIFY( strip_squares(resv) );
+ VERIFY( check_elems(resv, false) );
+
+ resv = res = std::format("{:*>10n:}", vs);
+ VERIFY( check_elems(resv, false) );
+
+ resv = res = std::format("{:*>240}", vs);
+ VERIFY( strip_prefix(resv, 32, '*') );
+ VERIFY( strip_squares(resv) );
+ VERIFY( check_elems(resv, true) );
+
+ resv = res = std::format("{:*>240n}", vs);
+ VERIFY( strip_prefix(resv, 34, '*') );
+ VERIFY( check_elems(resv, true) );
+
+ resv = res = std::format("{:*>240:}", vs);
+ VERIFY( strip_prefix(resv, 40, '*') );
+ VERIFY( strip_squares(resv) );
+ VERIFY( check_elems(resv, false) );
+
+ resv = res = std::format("{:*>240n:}", vs);
+ VERIFY( strip_prefix(resv, 42, '*') );
+ VERIFY( check_elems(resv, false) );
+}
+
int main()
{
test_format_string();
test_outputs();
test_nested();
+ test_padding();
}
diff --git a/libstdc++-v3/testsuite/std/format/ranges/string.cc
b/libstdc++-v3/testsuite/std/format/ranges/string.cc
index cf39aa66e07..cebdd530168 100644
--- a/libstdc++-v3/testsuite/std/format/ranges/string.cc
+++ b/libstdc++-v3/testsuite/std/format/ranges/string.cc
@@ -1,7 +1,9 @@
// { dg-do run { target c++23 } }
+// { dg-options "-fexec-charset=UTF-8" }
// { dg-timeout-factor 2 }
#include <format>
+#include <forward_list>
#include <span>
#include <testsuite_hooks.h>
#include <testsuite_iterators.h>
@@ -218,6 +220,67 @@ test_nested()
VERIFY( std::format("{::?s}", vv) == R"(["str1", "str2"])" );
}
+bool strip_quotes(std::string_view& v)
+{
+ if (!v.starts_with('"') || !v.ends_with('"'))
+ return false;
+ v.remove_prefix(1);
+ v.remove_suffix(1);
+ return true;
+}
+
+bool strip_prefix(std::string_view& v, size_t n, char c)
+{
+ size_t pos = v.find_first_not_of(c);
+ if (pos == std::string_view::npos)
+ pos = v.size();
+ if (pos != n)
+ return false;
+ v.remove_prefix(n);
+ return true;
+}
+
+
+void test_padding()
+{
+ std::string res;
+ std::string_view resv;
+
+ // width is 3, size is 15
+ std::string in = "o\u0302\u0323i\u0302\u0323u\u0302\u0323";
+ in += in; // width is 6, size is 30
+ in += in; // width is 12, size is 60
+ in += in; // width is 24, size is 120
+ in += in; // width is 48, size is 240
+ in += in; // width is 96, size is 480
+ in += in; // width is 192, size is 960
+
+ std::forward_list<char> lc(std::from_range, in);
+
+ resv = res = std::format("{:s}", lc);
+ VERIFY( resv == in );
+
+ resv = res = std::format("{:*>10s}", lc);
+ VERIFY( resv == in );
+
+ resv = res = std::format("{:*>240s}", lc);
+ VERIFY( strip_prefix(resv, 48, '*') );
+ VERIFY( resv == in );
+
+ resv = res = std::format("{:?s}", lc);
+ VERIFY( strip_quotes(resv) );
+ VERIFY( resv == in );
+
+ resv = res = std::format("{:*>10?s}", lc);
+ VERIFY( strip_quotes(resv) );
+ VERIFY( resv == in );
+
+ resv = res = std::format("{:*>240?s}", lc);
+ VERIFY( strip_prefix(resv, 46, '*') );
+ VERIFY( strip_quotes(resv) );
+ VERIFY( resv == in );
+}
+
int main()
{
test_format_string();
diff --git a/libstdc++-v3/testsuite/std/format/tuple.cc
b/libstdc++-v3/testsuite/std/format/tuple.cc
index 62f9d293aab..ff0359b9aba 100644
--- a/libstdc++-v3/testsuite/std/format/tuple.cc
+++ b/libstdc++-v3/testsuite/std/format/tuple.cc
@@ -1,4 +1,6 @@
// { dg-do run { target c++23 } }
+// { dg-options "-fexec-charset=UTF-8" }
+// { dg-timeout-factor 2 }
#include <format>
#include <string>
@@ -250,10 +252,101 @@ void test_nested()
VERIFY( res == R"((): (1, "abc"))" );
}
+bool strip_quote(std::string_view& v)
+{
+ if (!v.starts_with('"'))
+ return false;
+ v.remove_prefix(1);
+ return true;
+}
+
+bool strip_prefix(std::string_view& v, std::string_view expected, bool quoted
= false)
+{
+ if (quoted && !strip_quote(v))
+ return false;
+ if (!v.starts_with(expected))
+ return false;
+ v.remove_prefix(expected.size());
+ if (quoted && !strip_quote(v))
+ return false;
+ return true;
+}
+
+bool strip_parens(std::string_view& v)
+{
+ if (!v.starts_with('(') || !v.ends_with(')'))
+ return false;
+ v.remove_prefix(1);
+ v.remove_suffix(1);
+ return true;
+}
+
+bool strip_prefix(std::string_view& v, size_t n, char c)
+{
+ size_t pos = v.find_first_not_of(c);
+ if (pos == std::string_view::npos)
+ pos = v.size();
+ if (pos != n)
+ return false;
+ v.remove_prefix(n);
+ return true;
+}
+
+void test_padding()
+{
+ std::string res;
+ std::string_view resv;
+
+ // width is 3, size is 15
+ std::string in = "o\u0302\u0323i\u0302\u0323u\u0302\u0323";
+ in += in; // width is 6, size is 30
+ in += in; // width is 12, size is 60
+ in += in; // width is 24, size is 120
+ in += in; // width is 48, size is 240
+ // width is 192, size is 960
+ auto const ts = std::make_tuple(in, in, in, in);
+
+ auto const check_elems = [=](std::string_view& v)
+ {
+ VERIFY( strip_prefix(v, in, true) );
+ VERIFY( strip_prefix(v, ", ", false) );
+ VERIFY( strip_prefix(v, in, true) );
+ VERIFY( strip_prefix(v, ", ", false) );
+ VERIFY( strip_prefix(v, in, true) );
+ VERIFY( strip_prefix(v, ", ", false) );
+ VERIFY( strip_prefix(v, in, true) );
+ return v.empty();
+ };
+
+ resv = res = std::format("{}", ts);
+ VERIFY( strip_parens(resv) );
+ VERIFY( check_elems(resv) );
+
+ resv = res = std::format("{:n}", ts);
+ VERIFY( check_elems(resv) );
+
+ resv = res = std::format("{:*>10}", ts);
+ VERIFY( strip_parens(resv) );
+ VERIFY( check_elems(resv) );
+
+ resv = res = std::format("{:*>10n}", ts);
+ VERIFY( check_elems(resv) );
+
+ resv = res = std::format("{:*>240}", ts);
+ VERIFY( strip_prefix(resv, 32, '*') );
+ VERIFY( strip_parens(resv) );
+ VERIFY( check_elems(resv) );
+
+ resv = res = std::format("{:*>240n}", ts);
+ VERIFY( strip_prefix(resv, 34, '*') );
+ VERIFY( check_elems(resv) );
+}
+
int main()
{
test_format_string();
test_outputs<char>();
test_outputs<wchar_t>();
test_nested();
+ test_padding();
}
--
2.49.0