On Thu, Apr 10, 2025 at 6:39 PM Jonathan Wakely <jwak...@redhat.com> wrote:

> On 10/04/25 11:24 +0200, Tomasz Kamiński wrote:
> >This patch implements part P2286R8 that specified debug (escaped)
> >format for the strings and characters sequences. This include both
> >handling of the '?' format specifier and set_debug_format member.
> >
> >To indicate partial support we define __glibcxx_format_ranges macro
> >value 1, without defining __cpp_lib_format_ranges.
> >
> >We provide two separate escaping routines depending on the literal
> >encoding for the corresponding character types. If the character
> >encoding is Unicode, we follow the specification for the standard
> >(__format::__write_escaped_unicode).
> >For other encodings, we escape only characters in range [0x00, 0x80),
> >interpreting them as ACII values: [0x00, 0x20), 0x7f and  '\t', '\r',
>
> "ASCII"
>
> >'\n', '\\', '"', '\'' are escaped. We assume every character outside
> >this range is printable (__format::_write_escaped_ascii).
> >In particular we do not yet implement special handling of shift
> >sequences.
> >
> >For Unicode escaping a new __escape_edges table is introduced,
> >that encodes information if character belongs to General_Category
> >that is escaped by the standard (Control or Other). This table
> >is generated from DerivedGeneralCategory.txt provided by Unicode.
> >Only boolean flag is preserved to reduce the number of entires.
>
> "entries"
>
> >The additional rules for escaping are handled by __should_escape_unicode.
> >
> >When width or precision is specified, we emit escaped string to the
> temporary
> >buffer and format the resulting string according to the format spec.
> >For characters use a fixed size stack buffer, for which a new
> _Fixedbuf_sink is
> >introduced. For strings, we use _Str_sink and to avoid allocations,
> >we compute the estimated size of (possibly truncated) input, and if it is
> >larger than width field we print directly.
> >
> >       PR libstdc++/109162
> >
> >contrib/ChangeLog:
> >
> >       * unicode/README: Mentioned DerivedGeneralCategory.txt.
> >       * unicode/gen_libstdcxx_unicode_data.py: Generation __escape_edges
> >       table from DerivedGeneralCategory.txt. Update file name in
> comments.
> >       * unicode/DerivedGeneralCategory.txt: Copy of file distrubuted by
>
> "distributed"
>
> >       Unicode Consortium.
> >
> ftp://ftp.unicode.org/Public/UNIDATA/extracted/DerivedGeneralCategory.txt.
>
>
> I still don't think we want the URL in the ChangeLog.
>
> >libstdc++-v3/ChangeLog:
> >
> >       * include/bits/chrono_io.h (__detail::_Widen): Moved to std/format
> file.
> >       * include/bits/unicode-data.h: Regnerate.
> >       * include/bits/unicode.h (__unicode::_Utf_iterator::_M_units)
> >       (__unicode::__should_escape_category): Define.
>
> What happened to the changes to bits/version.def and bits/version.h ?
>
> I thought you were going to change version.def to use no_stdname but
> now it's not in the patch at all.
>
The are already in master, as I merged them with formatter for
vector<bool>::reference:
https://gcc.gnu.org/cgit/gcc/commit/?id=84d668b0ca67c5d3fe6430f101d61e60aa796a81

>
> >       * include/std/format (_GLIBCXX_WIDEN_, _GLIBCXX_WIDEN): Copied from
> >       include/bits/chrono_io.h.
> >       (__format::_Widen): Moved from include/bits/chrono_io.h.
> >       (__format::_Term_char, __format::_Escapes, __format::_Separators)
> >       (__format::__should_escape_ascii,
> __format::__should_escape_unicode)
> >       (__format::__write_escape_seq, __format::__write_escaped_char)
> >       (__format::__write_escaped_acii, __format::__write_escaped_unicode)
> >       (__format::__write_escaped): Define.
> >       (__formatter_str::_S_format): Extracted truncation of character
> >       sequences.
> >       (__formatter_str::format): Handle _Pres_esc.
> >       (__formatter_int::_M_do_parse) [__glibcxx_format_ranges]: Parse
> '?'.
> >       (__formatter_int::_M_format_character_escaped): Define.
> >       (formatter<_CharT, _CharT>::format, formatter<char,
> wchar_t>::format):
> >       Handle _Pres_esc.
> >       (__formatter_str::set_debug_format,
> formatter<...>::set_debug_format)
> >       Guard with __glibcxx_format_ranges.
> >       (__format::_Fixedbuf_sink): Define.
> >       * testsuite/std/format/debug.cc: New test.
> >       * testsuite/std/format/debug_nonunicode.cc: New test.
> >       * testsuite/std/format/parse_ctx.cc (escaped_strings_supported):
> Define
> >       to true if __glibcxx_format_ranges is defined.
> >       * testsuite/std/format/string.cc (escaped_strings_supported):
> Define to
> >        true if __glibcxx_format_ranges is defined.
> >---
> > This I believe address all review suggestions. I have also followed
> > Patrick's suggestions and added debug_nonunicode.cc test file.
> > Which helped to surface problem, where _GLIBCXX_WIDEN("\FFFFD") was
> > ill-formed for non-unicode encodings.
> >
> > contrib/unicode/DerivedGeneralCategory.txt    | 4323 +++++++++++++++++
> > contrib/unicode/README                        |    3 +-
> > contrib/unicode/gen_libstdcxx_unicode_data.py |   47 +-
> > libstdc++-v3/include/bits/chrono_io.h         |   16 +-
> > libstdc++-v3/include/bits/unicode-data.h      |  260 +-
> > libstdc++-v3/include/bits/unicode.h           |   17 +
> > libstdc++-v3/include/std/format               |  492 +-
> > .../23_containers/vector/bool/format.cc       |    3 +-
> > libstdc++-v3/testsuite/std/format/debug.cc    |  454 ++
> > .../testsuite/std/format/debug_nounicode.cc   |    5 +
> > .../testsuite/std/format/parse_ctx.cc         |    2 +-
> > libstdc++-v3/testsuite/std/format/string.cc   |    2 +-
> > 12 files changed, 5538 insertions(+), 86 deletions(-)
> > create mode 100644 contrib/unicode/DerivedGeneralCategory.txt
> > create mode 100644 libstdc++-v3/testsuite/std/format/debug.cc
> > create mode 100644 libstdc++-v3/testsuite/std/format/debug_nounicode.cc
>
> [snip]
>
> >diff --git a/libstdc++-v3/include/bits/unicode.h
> b/libstdc++-v3/include/bits/unicode.h
> >index 99d972eccff..f1b6bf49c54 100644
> >--- a/libstdc++-v3/include/bits/unicode.h
> >+++ b/libstdc++-v3/include/bits/unicode.h
> >@@ -150,6 +150,11 @@ namespace __unicode
> >       base() const requires forward_iterator<_Iter>
> >       { return _M_curr(); }
> >
> >+      [[nodiscard]]
> >+      constexpr iter_difference_t<_Iter>
> >+      _M_units() const requires forward_iterator<_Iter>
> >+      { return _M_to_increment; }
> >+
> >       [[nodiscard]]
> >       constexpr value_type
> >       operator*() const { return _M_buf[_M_buf_index]; }
> >@@ -609,6 +614,18 @@ inline namespace __v16_0_0
> >     return (__p - __width_edges) % 2 + 1;
> >   }
> >
> >+  // @pre c <= 0x10FFFF
> >+  constexpr bool
> >+  __should_escape_category(char32_t __c) noexcept
> >+  {
> >+    constexpr uint32_t __mask = 0x01;
> >+    auto* __end = std::end(__escape_edges);
> >+    auto* __p = std::lower_bound(__escape_edges, __end,
> >+                               (__c << 1u) + 2);
> >+    return __p[-1] & __mask;
> >+  }
> >+
> >+
> >   // @pre c <= 0x10FFFF
> >   constexpr _Gcb_property
> >   __grapheme_cluster_break_property(char32_t __c) noexcept
> >diff --git a/libstdc++-v3/include/std/format
> b/libstdc++-v3/include/std/format
> >index 2e9319cdda6..b905d8c012d 100644
> >--- a/libstdc++-v3/include/std/format
> >+++ b/libstdc++-v3/include/std/format
> >@@ -80,8 +80,35 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > /// @cond undocumented
> > namespace __format
> > {
> >-  // Type-erased character sink.
> >+  // STATICALLY-WIDEN, see C++20 [time.general]
> >+  // It doesn't matter for format strings (which can only be char or
> wchar_t)
> >+  // but this returns the narrow string for anything that isn't wchar_t.
> This
> >+  // is done because const char* can be inserted into any ostream type,
> and
> >+  // will be widened at runtime if necessary.
> >+  template<typename _CharT>
> >+    consteval auto
> >+    _Widen(const char* __narrow, const wchar_t* __wide)
> >+    {
> >+      if constexpr (is_same_v<_CharT, wchar_t>)
> >+      return __wide;
> >+      else
> >+      return __narrow;
> >+    }
> >+#define _GLIBCXX_WIDEN_(C, S) ::std::__format::_Widen<C>(S, L##S)
> >+#define _GLIBCXX_WIDEN(S) _GLIBCXX_WIDEN_(_CharT, S)
> >+
> >+  // 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>>;
> >+
> >   // Output iterator that writes to a type-erase character sink.
> >   template<typename _CharT>
> >     class _Sink_iter;
> >@@ -848,6 +875,286 @@ namespace __format
> >                                     __spec._M_fill);
> >     }
> >
> >+  // Valus are indicies into _Escapes::all.
>
> "Values" and "indices"
>
> >+  enum class _Term_char : unsigned char {
> >+    _Tc_quote = 12,
> >+    _Tc_apos = 15
> >+  };
> >+
> >+  template<typename _CharT>
> >+    struct _Escapes
> >+    {
> >+      using _Str_view = basic_string_view<_CharT>;
> >+
> >+      static consteval
> >+      _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_newline()
> >+      { return _S_all().substr(3, 3); }
> >+
> >+      static consteval
> >+      _Str_view _S_return()
> >+      { return _S_all().substr(6, 3); }
> >+
> >+      static consteval
> >+      _Str_view _S_bslash()
> >+      { return _S_all().substr(9, 3); }
> >+
> >+      static consteval
> >+      _Str_view _S_quote()
> >+      { return _S_all().substr(12, 3); }
> >+
> >+      static consteval
> >+      _Str_view _S_apos()
> >+      { return _S_all().substr(15, 3); }
> >+
> >+      static consteval
> >+      _Str_view _S_u()
> >+      { return _S_all().substr(18, 2); }
> >+
> >+      static consteval
> >+      _Str_view _S_x()
> >+      { return _S_all().substr(20, 2); }
> >+    };
> >+
> >+  template<typename _CharT>
> >+    struct _Separators
> >+    {
> >+      using _Str_view = basic_string_view<_CharT>;
> >+
> >+      static consteval
> >+      _Str_view _S_all()
> >+      { return _GLIBCXX_WIDEN("{}"); }
> >+
> >+      static consteval
> >+      _Str_view _S_braces()
> >+      { return _S_all().substr(0, 2); }
> >+    };
> >+
> >+  template<typename _CharT>
> >+    constexpr bool __should_escape_ascii(_CharT __c, _Term_char __term)
> >+    {
> >+      using _Esc = _Escapes<_CharT>;
> >+      switch (__c)
> >+      {
> >+        case _Esc::_S_tab()[0]:
> >+        case _Esc::_S_newline()[0]:
> >+        case _Esc::_S_return()[0]:
> >+        case _Esc::_S_bslash()[0]:
> >+          return true;
> >+        case _Esc::_S_quote()[0]:
> >+          return __term == _Term_char::_Tc_quote;
> >+        case _Esc::_S_apos()[0]:
> >+          return __term == _Term_char::_Tc_apos;
> >+        default:
> >+          return (__c >= 0 && __c < 0x20) || __c == 0x7f;
> >+      };
> >+  }
> >+
> >+  // @pre __c <= 0x10FFFF
> >+  constexpr bool __should_escape_unicode(char32_t __c, bool __prev_esc)
> >+  {
> >+    if (__unicode::__should_escape_category(__c))
> >+      return __c != U' ';
> >+    if (!__prev_esc)
> >+      return false;
> >+    return __unicode::__grapheme_cluster_break_property(__c)
> >+           == __unicode::_Gcb_property::_Gcb_Extend;
> >+  }
> >+
> >+  using uint_least32_t = __UINT_LEAST32_TYPE__;
> >+  template<typename _Out, typename _CharT>
> >+    _Out
> >+    __write_escape_seq(_Out __out, uint_least32_t __val,
> >+                     basic_string_view<_CharT> __prefix)
> >+    {
> >+      using _Str_view = basic_string_view<_CharT>;
> >+      constexpr size_t __max = 8;
> >+      char __buf[__max];
> >+      const string_view __narrow(
> >+      __buf,
> >+      std::__to_chars_i<uint_least32_t>(__buf, __buf + __max, __val,
> 16).ptr);
> >+
> >+      __out = __format::__write(__out, __prefix);
> >+      *__out = _Separators<_CharT>::_S_braces()[0];
> >+      ++__out;
> >+      if constexpr (is_same_v<char, _CharT>)
> >+      __out = __format::__write(__out, __narrow);
> >+#ifdef _GLIBCXX_USE_WCHAR_T
> >+      else
> >+      {
> >+        _CharT __wbuf[__max];
> >+        const size_t __n = __narrow.size();
> >+        std::__to_wstring_numeric(__narrow.data(), __n, __wbuf);
> >+        __out = __format::__write(__out, _Str_view(__wbuf, __n));
> >+      }
> >+#endif
> >+      *__out = _Separators<_CharT>::_S_braces()[1];
> >+      return ++__out;
> >+    }
> >+
> >+  template<typename _Out, typename _CharT>
> >+    _Out
> >+    __write_escaped_char(_Out __out, _CharT __c)
> >+    {
> >+      using _UChar = make_unsigned_t<_CharT>;
> >+      using _Esc = _Escapes<_CharT>;
> >+      switch (__c)
> >+      {
> >+        case _Esc::_S_tab()[0]:
> >+          return __format::__write(__out, _Esc::_S_tab().substr(1, 2));
> >+        case _Esc::_S_newline()[0]:
> >+          return __format::__write(__out, _Esc::_S_newline().substr(1,
> 2));
> >+        case _Esc::_S_return()[0]:
> >+          return __format::__write(__out, _Esc::_S_return().substr(1,
> 2));
> >+        case _Esc::_S_bslash()[0]:
> >+          return __format::__write(__out, _Esc::_S_bslash().substr(1,
> 2));
> >+        case _Esc::_S_quote()[0]:
> >+          return __format::__write(__out, _Esc::_S_quote().substr(1, 2));
> >+        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());
> >+      }
> >+    }
> >+
> >+  template<typename _CharT, typename _Out>
> >+    _Out
> >+    __write_escaped_ascii(_Out __out,
> >+                        basic_string_view<_CharT> __str,
> >+                        _Term_char __term)
> >+    {
> >+      using _Str_view = basic_string_view<_CharT>;
> >+      auto __first = __str.begin();
> >+      auto const __last = __str.end();
> >+      while (__first != __last)
> >+      {
> >+      auto __print = __first;
> >+      // assume anything outside ASCII is printable
> >+      while (__print != __last
> >+               && !__format::__should_escape_ascii(*__print, __term))
> >+        ++__print;
> >+
> >+      if (__print != __first)
> >+        __out = __format::__write(__out, _Str_view(__first, __print));
> >+
> >+      if (__print == __last)
> >+        return __out;
> >+
> >+      __first = __print;
> >+      __out = __format::__write_escaped_char(__out, *__first);
> >+      ++__first;
> >+      }
> >+      return __out;
> >+    }
> >+
> >+  template<typename _CharT, typename _Out>
> >+    _Out
> >+    __write_escaped_unicode(_Out __out,
> >+                          basic_string_view<_CharT> __str,
> >+                          _Term_char __term)
> >+    {
> >+      using _Str_view = basic_string_view<_CharT>;
> >+      using _UChar = make_unsigned_t<_CharT>;
> >+      using _Esc = _Escapes<_CharT>;
> >+
> >+      static constexpr char32_t __replace = U'\uFFFD';
> >+      static constexpr _Str_view __replace_rep = []
> >+        {
> >+        // N.B. "\uFFFD" is ill-formed if encoding is not unicode.
> >+          if constexpr (is_same_v<char, _CharT>)
> >+          return "\xEF\xBF\xBD";
> >+        else
> >+          return L"\xFFFD";
> >+        }();
> >+
> >+      __unicode::_Utf_view<char32_t, _Str_view> __v(std::move(__str));
> >+      auto __first = __v.begin();
> >+      auto const __last = __v.end();
> >+
> >+      bool __prev_esc = true;
> >+      while (__first != __last)
> >+      {
> >+        bool __esc_ascii = false;
> >+        bool __esc_unicode = false;
> >+        bool __esc_replace = false;
> >+        auto __should_escape = [&](auto const& __it)
> >+          {
> >+            if (*__it <= 0x7f)
> >+              return __esc_ascii
> >+                       = __format::__should_escape_ascii(*__it.base(),
> __term);
> >+            if (__format::__should_escape_unicode(*__it, __prev_esc))
> >+              return __esc_unicode = true;
> >+            if (*__it == __replace)
> >+              {
> >+                _Str_view __units(__it.base(), __it._M_units());
> >+                return __esc_replace = (__units != __replace_rep);
> >+              }
> >+            return false;
> >+          };
> >+
> >+        auto __print = __first;
> >+        while (__print != __last && !__should_escape(__print))
> >+          {
> >+            __prev_esc = false;
> >+            ++__print;
> >+          }
> >+
> >+        if (__print != __first)
> >+          __out = __format::__write(__out, _Str_view(__first.base(),
> __print.base()));
> >+
> >+        if (__print == __last)
> >+          return __out;
> >+
> >+        __first = __print;
> >+        if (__esc_ascii)
> >+          __out = __format::__write_escaped_char(__out, *__first.base());
> >+        else if (__esc_unicode)
> >+          __out = __format::__write_escape_seq(__out, *__first,
> _Esc::_S_u());
> >+        else // __esc_replace
> >+          for (_CharT __c : _Str_view(__first.base(),
> __first._M_units()))
> >+            __out = __format::__write_escape_seq(__out,
> >+
>  static_cast<_UChar>(__c),
> >+                                                 _Esc::_S_x());
> >+        __prev_esc = true;
> >+        ++__first;
> >+
> >+      }
> >+      return __out;
> >+    }
> >+
> >+  template<typename _CharT, typename _Out>
> >+    _Out
> >+    __write_escaped(_Out __out,  basic_string_view<_CharT> __str,
> _Term_char __term)
> >+    {
> >+      *__out = _Escapes<_CharT>::_S_term(__term);
> >+      ++__out;
> >+
> >+      if constexpr (__unicode::__literal_encoding_is_unicode<_CharT>())
> >+      __out = __format::__write_escaped_unicode(__out, __str, __term);
> >+      else if constexpr (is_same_v<char, _CharT>
> >+                        &&
> __unicode::__literal_encoding_is_extended_ascii())
> >+      __out = __format::__write_escaped_ascii(__out, __str, __term);
> >+      else
> >+      // TODO Handle non-ascii extended encoding
> >+      __out = __format::__write_escaped_ascii(__out, __str, __term);
> >+
> >+      *__out = _Escapes<_CharT>::_S_term(__term);
> >+      return ++__out;
> >+    }
> >+
> >   // A lightweight optional<locale>.
> >   struct _Optional_locale
> >   {
> >@@ -961,7 +1268,7 @@ namespace __format
> >
> >       if (*__first == 's')
> >         ++__first;
> >-#if __cpp_lib_format_ranges
> >+#if __glibcxx_format_ranges // C++ >= 23 && HOSTED
> >       else if (*__first == '?')
> >         {
> >           __spec._M_type = _Pres_esc;
> >@@ -980,43 +1287,71 @@ namespace __format
> >       format(basic_string_view<_CharT> __s,
> >              basic_format_context<_Out, _CharT>& __fc) const
> >       {
> >-        if (_M_spec._M_type == _Pres_esc)
> >+        constexpr auto __term = __format::_Term_char::_Tc_quote;
> >+        const auto __write_direct = [&]
> >           {
> >-            // TODO: C++23 escaped string presentation
> >-          }
> >+            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_width_kind == _WP_none
> >               && _M_spec._M_prec_kind == _WP_none)
> >-          return __format::__write(__fc.out(), __s);
> >+          return __write_direct();
> >
> >-        size_t __estimated_width;
> >-        if constexpr (__unicode::__literal_encoding_is_unicode<_CharT>())
> >-          {
> >-            if (_M_spec._M_prec_kind != _WP_none)
> >-              {
> >-                size_t __prec = _M_spec._M_get_precision(__fc);
> >-                __estimated_width = __unicode::__truncate(__s, __prec);
> >-              }
> >-            else
> >-              __estimated_width = __unicode::__field_width(__s);
> >-          }
> >-        else
> >-          {
> >-            __s = __s.substr(0, _M_spec._M_get_precision(__fc));
> >-            __estimated_width = __s.size();
> >-          }
> >+        const size_t __prec =
> >+          _M_spec._M_prec_kind != _WP_none
> >+            ? _M_spec._M_get_precision(__fc)
> >+            : basic_string_view<_CharT>::npos;
> >
> >-        return __format::__write_padded_as_spec(__s, __estimated_width,
> >+        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();
> >+
> >+        if (_M_spec._M_type != _Pres_esc)
> >+          return __format::__write_padded_as_spec(__s, __estimated_width,
> >+                                                  __fc, _M_spec);
> >+
> >+        __format::_Str_sink<_CharT> __sink;
> >+        __format::_Sink_iter<_CharT> __out(__sink);
> >+        __format::__write_escaped(__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);
> >       }
> >
> >-#if __cpp_lib_format_ranges
> >+#if __glibcxx_format_ranges // C++ >= 23 && HOSTED
> >       constexpr void
> >       set_debug_format() noexcept
> >       { _M_spec._M_type = _Pres_esc; }
> > #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{};
> >     };
> >
> >@@ -1120,7 +1455,7 @@ namespace __format
> >               ++__first;
> >             }
> >           break;
> >-#if __cpp_lib_format_ranges
> >+#if __glibcxx_format_ranges // C++ >= 23 && HOSTED
> >         case '?':
> >           if (__type == _AsChar)
> >             {
> >@@ -1272,7 +1607,7 @@ namespace __format
> >       _S_character_width(_CharT __c)
> >       {
> >       // N.B. single byte cannot encode charcter of width greater than 1
> >-      if constexpr (sizeof(_CharT) > 1u &&
> >+      if constexpr (sizeof(_CharT) > 1u &&
> >                       __unicode::__literal_encoding_is_unicode<_CharT>())
> >         return __unicode::__field_width(__c);
> >       else
> >@@ -1286,7 +1621,34 @@ namespace __format
> >       {
> >         return __format::__write_padded_as_spec({&__c, 1u},
> >                                                 _S_character_width(__c),
> >-                                                __fc, _M_spec);
> >+                                                __fc, _M_spec);
> >+      }
> >+
> >+      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);
> >+
> >+        _CharT __buf[12];
> >+        __format::_Fixedbuf_sink<_CharT> __sink(__buf);
> >+        __format::_Sink_iter<_CharT> __out(__sink);
> >+        __format::__write_escaped(__out, __in, __term);
> >+
> >+        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,
> >+                                                __fc, _M_spec);
> >       }
> >
> >       template<typename _Int>
> >@@ -1973,15 +2335,12 @@ namespace __format
> >             || _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)
> >-          {
> >-            // TODO
> >-            return __fc.out();
> >-          }
> >+          return _M_f._M_format_character_escaped(__u, __fc);
> >         else
> >           return _M_f.format(static_cast<make_unsigned_t<_CharT>>(__u),
> __fc);
> >       }
> >
> >-#if __cpp_lib_format_ranges
> >+#if __glibcxx_format_ranges // C++ >= 23 && HOSTED
> >       constexpr void
> >       set_debug_format() noexcept
> >       { _M_f._M_spec._M_type = __format::_Pres_esc; }
> >@@ -2012,15 +2371,12 @@ namespace __format
> >             || _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)
> >-          {
> >-            // TODO
> >-            return __fc.out();
> >-          }
> >+          return _M_f._M_format_character_escaped(__u, __fc);
> >         else
> >           return _M_f.format(static_cast<unsigned char>(__u), __fc);
> >       }
> >
> >-#if __cpp_lib_format_ranges
> >+#if __glibcxx_format_ranges // C++ >= 23 && HOSTED
> >       constexpr void
> >       set_debug_format() noexcept
> >       { _M_f._M_spec._M_type = __format::_Pres_esc; }
> >@@ -2050,7 +2406,7 @@ namespace __format
> >       format(_CharT* __u, basic_format_context<_Out, _CharT>& __fc) const
> >       { return _M_f.format(__u, __fc); }
> >
> >-#if __cpp_lib_format_ranges
> >+#if __glibcxx_format_ranges // C++ >= 23 && HOSTED
> >       constexpr void set_debug_format() noexcept {
> _M_f.set_debug_format(); }
> > #endif
> >
> >@@ -2075,7 +2431,7 @@ namespace __format
> >              basic_format_context<_Out, _CharT>& __fc) const
> >       { return _M_f.format(__u, __fc); }
> >
> >-#if __cpp_lib_format_ranges
> >+#if __glibcxx_format_ranges // C++ >= 23 && HOSTED
> >       constexpr void set_debug_format() noexcept {
> _M_f.set_debug_format(); }
> > #endif
> >
> >@@ -2099,7 +2455,7 @@ namespace __format
> >              basic_format_context<_Out, _CharT>& __fc) const
> >       { return _M_f.format({__u, _Nm}, __fc); }
> >
> >-#if __cpp_lib_format_ranges
> >+#if __glibcxx_format_ranges // C++ >= 23 && HOSTED
> >       constexpr void set_debug_format() noexcept {
> _M_f.set_debug_format(); }
> > #endif
> >
> >@@ -2123,7 +2479,7 @@ namespace __format
> >              basic_format_context<_Out, char>& __fc) const
> >       { return _M_f.format(__u, __fc); }
> >
> >-#if __cpp_lib_format_ranges
> >+#if __glibcxx_format_ranges // C++ >= 23 && HOSTED
> >       constexpr void set_debug_format() noexcept {
> _M_f.set_debug_format(); }
> > #endif
> >
> >@@ -2148,7 +2504,7 @@ namespace __format
> >              basic_format_context<_Out, wchar_t>& __fc) const
> >       { return _M_f.format(__u, __fc); }
> >
> >-#if __cpp_lib_format_ranges
> >+#if __glibcxx_format_ranges // C++ >= 23 && HOSTED
> >       constexpr void set_debug_format() noexcept {
> _M_f.set_debug_format(); }
> > #endif
> >
> >@@ -2173,7 +2529,7 @@ namespace __format
> >              basic_format_context<_Out, char>& __fc) const
> >       { return _M_f.format(__u, __fc); }
> >
> >-#if __cpp_lib_format_ranges
> >+#if __glibcxx_format_ranges // C++ >= 23 && HOSTED
> >       constexpr void set_debug_format() noexcept {
> _M_f.set_debug_format(); }
> > #endif
> >
> >@@ -2198,7 +2554,7 @@ namespace __format
> >              basic_format_context<_Out, wchar_t>& __fc) const
> >       { return _M_f.format(__u, __fc); }
> >
> >-#if __cpp_lib_format_ranges
> >+#if __glibcxx_format_ranges // C++ >= 23 && HOSTED
> >       constexpr void set_debug_format() noexcept {
> _M_f.set_debug_format(); }
> > #endif
> >
> >@@ -2859,6 +3215,32 @@ namespace __format
> >       { return _Sink_iter<_CharT>(*this); }
> >     };
> >
> >+
> >+  template<typename _CharT>
> >+    class _Fixedbuf_sink final : public _Sink<_CharT>
> >+    {
> >+      void
> >+      _M_overflow() override
> >+      {
> >+      __glibcxx_assert(false);
> >+      this->_M_rewind();
> >+      }
> >+
> >+    public:
> >+      [[__gnu__::__always_inline__]]
> >+      constexpr explicit
> >+      _Fixedbuf_sink(span<_CharT> __buf)
> >+      : _Sink<_CharT>(__buf)
> >+      { }
> >+
> >+      constexpr basic_string_view<_CharT>
> >+      view() const
> >+      {
> >+      auto __s = this->_M_used();
> >+      return basic_string_view<_CharT>(__s.data(), __s.size());
> >+      }
> >+    };
> >+
> >   // A sink with an internal buffer. This is used to implement concrete
> sinks.
> >   template<typename _CharT>
> >     class _Buf_sink : public _Sink<_CharT>
> >@@ -2993,13 +3375,6 @@ namespace __format
> >       }
> >     };
> >
> >-  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>>;
> >-
> >   // A 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.
> >@@ -3675,17 +4050,17 @@ namespace __format
> >         return _M_visit([&__vis]<typename _Tp>(_Tp& __val) ->
> decltype(auto)
> >           {
> >             constexpr bool __user_facing = __is_one_of<_Tp,
> >-              monostate, bool, _CharT,
> >-              int, unsigned int, long long int, unsigned long long int,
> >-              float, double, long double,
> >-              const _CharT*, basic_string_view<_CharT>,
> >-              const void*, handle>::value;
> >+              monostate, bool, _CharT,
> >+              int, unsigned int, long long int, unsigned long long int,
> >+              float, double, long double,
> >+              const _CharT*, basic_string_view<_CharT>,
> >+              const void*, handle>::value;
> >            if constexpr (__user_facing)
> >              return std::forward<_Visitor>(__vis)(__val);
> >            else
> >              {
> >-               handle __h(__val);
> >-               return std::forward<_Visitor>(__vis)(__h);
> >+               handle __h(__val);
> >+               return std::forward<_Visitor>(__vis)(__h);
> >              }
> >          }, __type);
> >       }
> >@@ -4781,6 +5156,7 @@ namespace __format
> >     : __format::__range_default_formatter<format_kind<_Rg>, _Rg, _CharT>
> >     { };
> > #endif // C++23 formatting ranges
> >+#undef _GLIBCXX_WIDEN
> >
> > _GLIBCXX_END_NAMESPACE_VERSION
> > } // namespace std
> >diff --git a/libstdc++-v3/testsuite/23_containers/vector/bool/format.cc
> b/libstdc++-v3/testsuite/23_containers/vector/bool/format.cc
> >index 16f6e86dcee..2586225dd05 100644
> >--- a/libstdc++-v3/testsuite/23_containers/vector/bool/format.cc
> >+++ b/libstdc++-v3/testsuite/23_containers/vector/bool/format.cc
> >@@ -3,7 +3,6 @@
> >
> > #include <format>
> > #include <vector>
> >-#include <chrono> // For _Widen
> > #include <testsuite_hooks.h>
> >
> > static_assert(!std::formattable<std::vector<bool>::reference, int>);
> >@@ -21,7 +20,7 @@ is_format_string_for(const char* str, Args&&... args)
> >   }
> > }
> >
> >-#define WIDEN_(C, S) ::std::chrono::__detail::_Widen<C>(S, L##S)
> >+#define WIDEN_(C, S) ::std::__format::_Widen<C>(S, L##S)
> > #define WIDEN(S) WIDEN_(_CharT, S)
> >
> > void
> >diff --git a/libstdc++-v3/testsuite/std/format/debug.cc
> b/libstdc++-v3/testsuite/std/format/debug.cc
> >new file mode 100644
> >index 00000000000..07cd1e0e349
> >--- /dev/null
> >+++ b/libstdc++-v3/testsuite/std/format/debug.cc
> >@@ -0,0 +1,454 @@
> >+// { dg-options "-fexec-charset=UTF-8 -fwide-exec-charset=UTF-32LE
> -DUNICODE_ENC" }
> >+// { dg-do run { target c++23 } }
> >+// { dg-add-options no_pch }
> >+
> >+#include <format>
> >+#include <testsuite_hooks.h>
> >+
> >+std::string
> >+fdebug(char t)
> >+{ return std::format("{:?}", t); }
> >+
> >+std::wstring
> >+fdebug(wchar_t t)
> >+{ return std::format(L"{:?}", t); }
> >+
> >+std::string
> >+fdebug(std::string_view t)
> >+{ return std::format("{:?}", t); }
> >+
> >+std::wstring
> >+fdebug(std::wstring_view t)
> >+{ return std::format(L"{:?}", t); }
> >+
> >+
> >+#define WIDEN_(C, S) ::std::__format::_Widen<C>(S, L##S)
> >+#define WIDEN(S) WIDEN_(_CharT, S)
> >+
> >+template<typename _CharT>
> >+void
> >+test_basic_escapes()
> >+{
> >+  std::basic_string<_CharT> res;
> >+
> >+  const auto tab = WIDEN("\t");
> >+  res = fdebug(tab);
> >+  VERIFY( res == WIDEN(R"("\t")") );
> >+  res = fdebug(tab[0]);
> >+  VERIFY( res == WIDEN(R"('\t')") );
> >+
> >+  const auto nline = WIDEN("\n");
> >+  res = fdebug(nline);
> >+  VERIFY( res == WIDEN(R"("\n")") );
> >+  res = fdebug(nline[0]);
> >+  VERIFY( res == WIDEN(R"('\n')") );
> >+
> >+  const auto carret = WIDEN("\r");
> >+  res = fdebug(carret);
> >+  VERIFY( res == WIDEN(R"("\r")") );
> >+  res = fdebug(carret[0]);
> >+  VERIFY( res == WIDEN(R"('\r')") );
> >+
> >+  const auto bslash = WIDEN("\\");
> >+  res = fdebug(bslash);
> >+  VERIFY( res == WIDEN(R"("\\")") );
> >+  res = fdebug(bslash[0]);
> >+  VERIFY( res == WIDEN(R"('\\')") );
> >+
> >+  const auto quote = WIDEN("\"");
> >+  res = fdebug(quote);
> >+  VERIFY( res == WIDEN(R"("\"")") );
> >+  res = fdebug(quote[0]);
> >+  VERIFY( res == WIDEN(R"('"')") );
> >+
> >+  const auto apos = WIDEN("\'");
> >+  res = fdebug(apos);
> >+  VERIFY( res == WIDEN(R"("'")") );
> >+  res = fdebug(apos[0]);
> >+  VERIFY( res == WIDEN(R"('\'')") );
> >+}
> >+
> >+template<typename _CharT>
> >+void
> >+test_ascii_escapes()
> >+{
> >+  std::basic_string<_CharT> res;
> >+
> >+  const auto in = WIDEN("\x10 abcde\x7f\t0123");
> >+  res = fdebug(in);
> >+  VERIFY( res == WIDEN(R"("\u{10} abcde\u{7f}\t0123")") );
> >+  res = fdebug(in[0]);
> >+  VERIFY( res == WIDEN(R"('\u{10}')") );
> >+  res = fdebug(in[1]);
> >+  VERIFY( res == WIDEN(R"(' ')") );
> >+  res = fdebug(in[2]);
> >+  VERIFY( res == WIDEN(R"('a')") );
> >+}
> >+
> >+template<typename _CharT>
> >+void
> >+test_extended_ascii()
> >+{
> >+  std::basic_string<_CharT> res;
> >+
> >+  const auto in = WIDEN("Åëÿ");
> >+  res = fdebug(in);
> >+  VERIFY( res == WIDEN(R"("Åëÿ")") );
> >+
> >+  static constexpr bool __test_characters
> >+#if UNICODE_ENC
> >+    = sizeof(_CharT) >= 2;
> >+#else // ISO8859-1
> >+    = true;
> >+#endif // UNICODE_ENC
> >+
> >+  if constexpr (__test_characters)
> >+  {
> >+    res = fdebug(in[0]);
> >+    VERIFY( res == WIDEN(R"('Å')") );
> >+    res = fdebug(in[1]);
> >+    VERIFY( res == WIDEN(R"('ë')") );
> >+    res = fdebug(in[2]);
> >+    VERIFY( res == WIDEN(R"('ÿ')") );
> >+  }
> >+}
> >+
> >+#if UNICODE_ENC
> >+template<typename _CharT>
> >+void
> >+test_unicode_escapes()
> >+{
> >+  std::basic_string<_CharT> res;
> >+
> >+  const auto in = WIDEN(
> >+    "\u008a"     // Cc, Control,             Line Tabulation Set,
> >+    "\u00ad"     // Cf, Format,              Soft Hyphen
> >+    "\u1d3d"     // Lm, Modifier letter,     Modifier Letter Capital Ou
> >+    "\u00a0"     // Zs, Space Separator,     No-Break Space (NBSP)
> >+    "\u2029"     // Zp, Paragraph Separator, Paragraph Separator
> >+    "\U0001f984" // So, Other Symbol,        Unicorn Face
> >+  );
> >+  const auto out = WIDEN("\""
> >+   R"(\u{8a})"
> >+   R"(\u{ad})"
> >+   "\u1d3d"
> >+   R"(\u{a0})"
> >+   R"(\u{2029})"
> >+   "\U0001f984"
> >+  "\"");
> >+
> >+  res = fdebug(in);
> >+  VERIFY( res == out );
> >+
> >+  if constexpr (sizeof(_CharT) >= 2)
> >+  {
> >+    res = fdebug(in[0]);
> >+    VERIFY( res == WIDEN(R"('\u{8a}')") );
> >+    res = fdebug(in[1]);
> >+    VERIFY( res == WIDEN(R"('\u{ad}')") );
> >+    res = fdebug(in[2]);
> >+    VERIFY( res == WIDEN("'\u1d3d'") );
> >+    res = fdebug(in[3]);
> >+    VERIFY( res == WIDEN(R"('\u{a0}')") );
> >+    res = fdebug(in[4]);
> >+    VERIFY( res == WIDEN(R"('\u{2029}')") );
> >+  }
> >+
> >+  if constexpr (sizeof(_CharT) >= 4)
> >+  {
> >+    res = fdebug(in[5]);
> >+    VERIFY( res == WIDEN("'\U0001f984'") );
> >+  }
> >+}
> >+
> >+template<typename _CharT>
> >+void
> >+test_grapheme_extend()
> >+{
> >+  std::basic_string<_CharT> res;
> >+
> >+  const auto vin = WIDEN("o\u0302\u0323");
> >+  res = fdebug(vin);
> >+  VERIFY( res == WIDEN("\"o\u0302\u0323\"") );
> >+
> >+  std::basic_string_view<_CharT> in = WIDEN("\t\u0302\u0323");
> >+  res = fdebug(in);
> >+  VERIFY( res == WIDEN(R"("\t\u{302}\u{323}")") );
> >+
> >+  res = fdebug(in.substr(1));
> >+  VERIFY( res == WIDEN(R"("\u{302}\u{323}")") );
> >+
> >+  if constexpr (sizeof(_CharT) >= 2)
> >+  {
> >+    res = fdebug(in[1]);
> >+    VERIFY( res == WIDEN(R"('\u{302}')") );
> >+  }
> >+}
> >+
> >+template<typename _CharT>
> >+void
> >+test_replacement_char()
> >+{
> >+  std::basic_string<_CharT> repl = WIDEN("\uFFFD");
> >+  std::basic_string<_CharT> res = fdebug(repl);
> >+  VERIFY( res == WIDEN("\"\uFFFD\"") );
> >+
> >+  repl = WIDEN("\uFFFD\uFFFD");
> >+  res = fdebug(repl);
> >+  VERIFY( res == WIDEN("\"\uFFFD\uFFFD\"") );
> >+}
> >+
> >+void
> >+test_ill_formed_utf8_seq()
> >+{
> >+  std::string_view seq = "\xf0\x9f\xa6\x84"; //  \U0001F984
> >+  std::string res;
> >+
> >+  res = fdebug(seq);
> >+  VERIFY( res == "\"\U0001F984\"" );
> >+
> >+  res = fdebug(seq.substr(1));
> >+  VERIFY( res == R"("\x{9f}\x{a6}\x{84}")" );
> >+
> >+  res = fdebug(seq.substr(2));
> >+  VERIFY( res == R"("\x{a6}\x{84}")" );
> >+
> >+  res = fdebug(seq[0]);
> >+  VERIFY( res == R"('\x{f0}')" );
> >+  res = fdebug(seq.substr(0, 1));
> >+  VERIFY( res == R"("\x{f0}")" );
> >+
> >+  res = fdebug(seq[1]);
> >+  VERIFY( res == R"('\x{9f}')" );
> >+  res = fdebug(seq.substr(1, 1));
> >+  VERIFY( res == R"("\x{9f}")" );
> >+
> >+  res = fdebug(seq[2]);
> >+  VERIFY( res == R"('\x{a6}')" );
> >+  res = fdebug(seq.substr(2, 1));
> >+  VERIFY( res == R"("\x{a6}")" );
> >+
> >+  res = fdebug(seq[3]);
> >+  VERIFY( res == R"('\x{84}')" );
> >+  res = fdebug(seq.substr(3, 1));
> >+  VERIFY( res == R"("\x{84}")" );
> >+}
> >+
> >+void
> >+test_ill_formed_utf32()
> >+{
> >+  std::wstring res;
> >+
> >+  wchar_t ic1 = static_cast<wchar_t>(0xff'ffff);
> >+  res = fdebug(ic1);
> >+  VERIFY( res == LR"('\x{ffffff}')" );
> >+
> >+  std::wstring is1(1, ic1);
> >+  res = fdebug(is1);
> >+  VERIFY( res == LR"("\x{ffffff}")" );
> >+
> >+  wchar_t ic2 = static_cast<wchar_t>(0xffff'ffff);
> >+  res = fdebug(ic2);
> >+  VERIFY( res == LR"('\x{ffffffff}')" );
> >+
> >+  std::wstring is2(1, ic2);
> >+  res = fdebug(is2);
> >+  VERIFY( res == LR"("\x{ffffffff}")" );
> >+}
> >+#endif // UNICODE_ENC
> >+
> >+template<typename _CharT>
> >+void
> >+test_fill()
> >+{
> >+  std::basic_string<_CharT> res;
> >+
> >+  std::basic_string_view<_CharT> in = WIDEN("a\t\x10\u00ad");
> >+  res = std::format(WIDEN("{:10?}"), in.substr(0, 1));
> >+  VERIFY( res == WIDEN(R"("a"       )") );
> >+
> >+  res = std::format(WIDEN("{:->10?}"), in.substr(1, 1));
> >+  VERIFY( res == WIDEN(R"(------"\t")") );
> >+
> >+  res = std::format(WIDEN("{:+<10?}"), in.substr(2, 1));
> >+  VERIFY( res == WIDEN(R"("\u{10}"++)") );
> >+
> >+
> >+  res = std::format(WIDEN("{:10?}"), in[0]);
> >+  VERIFY( res == WIDEN(R"('a'       )") );
> >+
> >+  res = std::format(WIDEN("{:->10?}"), in[1]);
> >+  VERIFY( res == WIDEN(R"(------'\t')") );
> >+
> >+  res = std::format(WIDEN("{:+<10?}"), in[2]);
> >+  VERIFY( res == WIDEN(R"('\u{10}'++)") );
> >+
> >+#if UNICODE_ENC
> >+  res = std::format(WIDEN("{:=^10?}"), in.substr(3));
> >+  VERIFY( res == WIDEN(R"(="\u{ad}"=)") );
> >+
> >+  // width is 2
> >+  std::basic_string_view<_CharT> in2 = WIDEN("\u1100");
> >+  res = std::format(WIDEN("{:*^10?}"), in2);
> >+  VERIFY( res == WIDEN("***\"\u1100\"***") );
> >+
> >+  if constexpr (sizeof(_CharT) >= 2)
> >+  {
> >+    res = std::format(WIDEN("{:=^10?}"), in[3]);
> >+    VERIFY( res == WIDEN(R"(='\u{ad}'=)") );
> >+
> >+    res = std::format(WIDEN("{:*^10?}"), in2[0]);
> >+    VERIFY( res == WIDEN("***'\u1100'***") );
> >+  }
> >+#endif // UNICODE_ENC
> >+}
> >+
> >+template<typename _CharT>
> >+void
> >+test_prec()
> >+{
> >+  std::basic_string<_CharT> res;
> >+  // with ? escpaed presentation is copied to ouput, same as source
> >+
> >+  std::basic_string_view<_CharT> in = WIDEN("a\t\x10\u00ad");
> >+  res = std::format(WIDEN("{:.2?}"), in.substr(0, 1));
> >+  VERIFY( res == WIDEN(R"("a)") );
> >+
> >+  res = std::format(WIDEN("{:.4?}"), in.substr(1, 1));
> >+  VERIFY( res == WIDEN(R"("\t")") );
> >+
> >+  res = std::format(WIDEN("{:.5?}"), in.substr(2, 1));
> >+  VERIFY( res == WIDEN(R"("\u{1)") );
> >+
> >+#if UNICODE_ENC
> >+  res = std::format(WIDEN("{:.10?}"), in.substr(3));
> >+  VERIFY( res == WIDEN(R"("\u{ad}")") );
> >+
> >+  std::basic_string_view<_CharT> in2 = WIDEN("\u1100");
> >+  res = std::format(WIDEN("{:.3?}"), in2);
> >+  VERIFY( res == WIDEN("\"\u1100") );
> >+#endif // UNICODE_ENC
> >+}
> >+
> >+void test_char_as_wchar()
> >+{
> >+  std::wstring res;
> >+
> >+  res = std::format(L"{:?}", 'a');
> >+  VERIFY( res == LR"('a')" );
> >+
> >+  res = std::format(L"{:?}", '\t');
> >+  VERIFY( res == LR"('\t')" );
> >+
> >+  res = std::format(L"{:+<10?}", '\x10');
> >+  VERIFY( res == LR"('\u{10}'++)" );
> >+}
> >+
> >+template<typename T>
> >+struct DebugWrapper
> >+{
> >+  T val;
> >+};
> >+
> >+template<typename T, typename CharT>
> >+struct std::formatter<DebugWrapper<T>, CharT>
> >+{
> >+  constexpr std::basic_format_parse_context<CharT>::iterator
> >+  parse(std::basic_format_parse_context<CharT>& pc)
> >+  {
> >+    auto out = under.parse(pc);
> >+    under.set_debug_format();
> >+    return out;
> >+  }
> >+
> >+  template<typename Out>
> >+  Out format(DebugWrapper<T> const& t,
> >+           std::basic_format_context<Out, CharT>& fc) const
> >+  { return under.format(t.val, fc); }
> >+
> >+private:
> >+  std::formatter<T, CharT> under;
> >+};
> >+
> >+template<typename _CharT, typename StrT>
> >+void
> >+test_formatter_str()
> >+{
> >+  _CharT buf[]{ 'a', 'b', 'c', 0 };
> >+  DebugWrapper<StrT> in{ buf };
> >+  std::basic_string<_CharT> res = std::format(WIDEN("{:?}"), in );
> >+  VERIFY( res == WIDEN(R"("abc")") );
> >+}
> >+
> >+template<typename _CharT>
> >+void
> >+test_formatter_arr()
> >+{
> >+  std::basic_string<_CharT> res;
> >+
> >+  DebugWrapper<_CharT[3]> in3{ 'a', 'b', 'c' };
> >+  res = std::format(WIDEN("{:?}"), in3 );
> >+  VERIFY( res == WIDEN(R"("abc")") );
> >+
> >+  // We print all characters, including null-terminator
> >+  DebugWrapper<_CharT[4]> in4{ 'a', 'b', 'c', 0 };
> >+  res = std::format(WIDEN("{:?}"), in4 );
> >+  VERIFY( res == WIDEN(R"("abc\u{0}")") );
> >+}
> >+
> >+template<typename _CharT, typename SrcT>
> >+void
> >+test_formatter_char()
> >+{
> >+  DebugWrapper<SrcT> in{ 'a' };
> >+  std::basic_string<_CharT> res = std::format(WIDEN("{:?}"), in);
> >+  VERIFY( res == WIDEN(R"('a')") );
> >+}
> >+
> >+template<typename CharT>
> >+void
> >+test_formatters()
> >+{
> >+  test_formatter_char<CharT, CharT>();
> >+  test_formatter_str<CharT, CharT*>();
> >+  test_formatter_str<CharT, const CharT*>();
> >+  test_formatter_str<CharT, std::basic_string<CharT>>();
> >+  test_formatter_str<CharT, std::basic_string_view<CharT>>();
> >+  test_formatter_arr<CharT>();
> >+}
> >+
> >+void
> >+test_formatters_c()
> >+{
> >+  test_formatters<char>();
> >+  test_formatters<wchar_t>();
> >+  test_formatter_char<wchar_t, char>();
> >+}
> >+
> >+int main()
> >+{
> >+  test_basic_escapes<char>();
> >+  test_basic_escapes<wchar_t>();
> >+  test_ascii_escapes<char>();
> >+  test_ascii_escapes<wchar_t>();
> >+  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>();
> >+  test_grapheme_extend<wchar_t>();
> >+  test_replacement_char<char>();
> >+  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_formatters_c();
> >+}
> >diff --git a/libstdc++-v3/testsuite/std/format/debug_nounicode.cc
> b/libstdc++-v3/testsuite/std/format/debug_nounicode.cc
> >new file mode 100644
> >index 00000000000..5c03171d71a
> >--- /dev/null
> >+++ b/libstdc++-v3/testsuite/std/format/debug_nounicode.cc
> >@@ -0,0 +1,5 @@
> >+// { dg-options "-fexec-charset=ISO8859-1 -fwide-exec-charset=UTF-32LE" }
> >+// { dg-do run { target c++23 } }
> >+// { dg-add-options no_pch }
> >+
> >+#include "debug.cc"
> >diff --git a/libstdc++-v3/testsuite/std/format/parse_ctx.cc
> b/libstdc++-v3/testsuite/std/format/parse_ctx.cc
> >index b5dd7cdba78..b338ac7b762 100644
> >--- a/libstdc++-v3/testsuite/std/format/parse_ctx.cc
> >+++ b/libstdc++-v3/testsuite/std/format/parse_ctx.cc
> >@@ -108,7 +108,7 @@ is_std_format_spec_for(std::string_view spec)
> >   }
> > }
> >
> >-#if __cpp_lib_format_ranges
> >+#if __glibcxx_format_ranges
> > constexpr bool escaped_strings_supported = true;
> > #else
> > constexpr bool escaped_strings_supported = false;
> >diff --git a/libstdc++-v3/testsuite/std/format/string.cc
> b/libstdc++-v3/testsuite/std/format/string.cc
> >index ee987a15ec3..76614d4bc3e 100644
> >--- a/libstdc++-v3/testsuite/std/format/string.cc
> >+++ b/libstdc++-v3/testsuite/std/format/string.cc
> >@@ -62,7 +62,7 @@ test_indexing()
> >   VERIFY( ! is_format_string_for("{} {0}", 1) );
> > }
> >
> >-#if __cpp_lib_format_ranges
> >+#if __glibcxx_format_ranges
> > constexpr bool escaped_strings_supported = true;
> > #else
> > constexpr bool escaped_strings_supported = false;
> >--
> >2.49.0
> >
> >
>
>

Reply via email to