[libcxx-commits] [libcxx] [RFC][libc++][format] Implements compile-time caching. (PR #132552)

Mark de Wever via libcxx-commits libcxx-commits at lists.llvm.org
Sat Mar 22 07:53:32 PDT 2025


https://github.com/mordante created https://github.com/llvm/llvm-project/pull/132552

This is a proof-of-concept patch to implement caching in basic_format_string. This is only intended for --this Discourse-- discussion.

The real patch needs to do things cleaner.

lit -a -Doptimization=speed libcxx/test/benchmarks/format/parsing.bench.cpp BEFORE
| ----------------------------------------------------------------------------
| Benchmark                                  Time             CPU   Iterations
| ----------------------------------------------------------------------------
| BM_empty                                6.18 ns         6.17 ns     87897649
| BM_curly_open                           11.2 ns         11.2 ns     62229450
| BM_curly_close                          10.7 ns         10.7 ns     65368952
| BM_pipe                                 12.9 ns         12.9 ns     54477724
| BM_int                                  47.6 ns         47.6 ns     14718165
| BM_int_formatted                        97.4 ns         97.4 ns      6953144
| BM_3_ints                                141 ns          141 ns      4944337
| BM_3_ints_formatted                      283 ns          283 ns      2464184
| BM_prefix_5_and_empty                   16.5 ns         16.5 ns     42569346
| BM_prefix_5_and_curly_open              17.8 ns         17.8 ns     39273542
| BM_prefix_5_and_curly_close             17.8 ns         17.8 ns     39140130
| BM_prefix_5_and_pipe                    19.5 ns         19.5 ns     35803584
| BM_prefix_5_and_int                     51.1 ns         51.0 ns     13436274
| BM_prefix_5_and_int_formatted            102 ns          102 ns      6793303
| BM_prefix_5_and_3_ints                   151 ns          151 ns      4573472
| BM_prefix_5_and_3_ints_formatted         291 ns          291 ns      2351850
| BM_prefix_10_and_empty                  24.9 ns         24.9 ns     28109036
| BM_prefix_10_and_curly_open             27.2 ns         27.2 ns     25713490
| BM_prefix_10_and_curly_close            30.2 ns         30.2 ns     23176332
| BM_prefix_10_and_pipe                   32.7 ns         32.6 ns     21420861
| BM_prefix_10_and_int                    61.6 ns         61.6 ns     11034101
| BM_prefix_10_and_int_formatted           120 ns          120 ns      5610531
| BM_prefix_10_and_3_ints                  156 ns          156 ns      4470738
| BM_prefix_10_and_3_ints_formatted        312 ns          311 ns      2217207
| BM_prefix_20_and_empty                  52.7 ns         52.7 ns     13138425
| BM_prefix_20_and_curly_open             58.8 ns         58.8 ns     11863712
| BM_prefix_20_and_curly_close            56.6 ns         56.6 ns     12407764
| BM_prefix_20_and_pipe                   56.5 ns         56.4 ns     12411003
| BM_prefix_20_and_int                    86.9 ns         86.8 ns      8089463
| BM_prefix_20_and_int_formatted           153 ns          153 ns      4582443
| BM_prefix_20_and_3_ints                  204 ns          204 ns      3354539
| BM_prefix_20_and_3_ints_formatted        349 ns          349 ns      2002457
| BM_prefix_40_and_empty                   116 ns          116 ns      6018618
| BM_prefix_40_and_curly_open              122 ns          122 ns      5655627
| BM_prefix_40_and_curly_close             121 ns          121 ns      5722255
| BM_prefix_40_and_pipe                    120 ns          120 ns      5823239
| BM_prefix_40_and_int                     158 ns          158 ns      4476432
| BM_prefix_40_and_int_formatted           207 ns          207 ns      3306182
| BM_prefix_40_and_3_ints                  255 ns          255 ns      2759277
| BM_prefix_40_and_3_ints_formatted        371 ns          370 ns      1884154

AFTER
| ----------------------------------------------------------------------------
| Benchmark                                  Time             CPU   Iterations
| ----------------------------------------------------------------------------
| BM_empty                                21.6 ns         21.6 ns     32391462
| BM_curly_open                           22.9 ns         22.9 ns     30588441
| BM_curly_close                          22.9 ns         22.9 ns     30592394
| BM_pipe                                 26.0 ns         26.0 ns     26953859
| BM_int                                  60.5 ns         60.5 ns     11577625
| BM_int_formatted                        81.3 ns         81.2 ns      8594596
| BM_3_ints                                156 ns          156 ns      4409940
| BM_3_ints_formatted                      220 ns          220 ns      3187392
| BM_prefix_5_and_empty                   26.0 ns         26.0 ns     26936925
| BM_prefix_5_and_curly_open              26.0 ns         26.0 ns     26965917
| BM_prefix_5_and_curly_close             26.0 ns         26.0 ns     26961356
| BM_prefix_5_and_pipe                    26.0 ns         26.0 ns     26959641
| BM_prefix_5_and_int                     64.1 ns         64.1 ns     10920953
| BM_prefix_5_and_int_formatted           84.4 ns         84.4 ns      8037453
| BM_prefix_5_and_3_ints                   165 ns          165 ns      4217532
| BM_prefix_5_and_3_ints_formatted         247 ns          247 ns      2823553
| BM_prefix_10_and_empty                  26.0 ns         26.0 ns     26949246
| BM_prefix_10_and_curly_open             26.0 ns         26.0 ns     26955524
| BM_prefix_10_and_curly_close            26.0 ns         26.0 ns     26956279
| BM_prefix_10_and_pipe                   26.0 ns         26.0 ns     26947963
| BM_prefix_10_and_int                    59.8 ns         59.8 ns     11674226
| BM_prefix_10_and_int_formatted          87.1 ns         87.0 ns      7814714
| BM_prefix_10_and_3_ints                  165 ns          165 ns      4149378
| BM_prefix_10_and_3_ints_formatted        245 ns          245 ns      2884163
| BM_prefix_20_and_empty                  29.3 ns         29.3 ns     23873593
| BM_prefix_20_and_curly_open             28.5 ns         28.4 ns     24625298
| BM_prefix_20_and_curly_close            27.5 ns         27.5 ns     25438870
| BM_prefix_20_and_pipe                   29.4 ns         29.3 ns     23863557
| BM_prefix_20_and_int                    57.6 ns         57.6 ns     12247413
| BM_prefix_20_and_int_formatted           104 ns          104 ns      6690343
| BM_prefix_20_and_3_ints                  183 ns          183 ns      3828112
| BM_prefix_20_and_3_ints_formatted        244 ns          244 ns      2833578
| BM_prefix_40_and_empty                  37.5 ns         37.5 ns     18650804
| BM_prefix_40_and_curly_open             36.8 ns         36.8 ns     19094990
| BM_prefix_40_and_curly_close            37.1 ns         37.0 ns     18904266
| BM_prefix_40_and_pipe                   36.5 ns         36.5 ns     19147265
| BM_prefix_40_and_int                    81.1 ns         81.0 ns      8611919
| BM_prefix_40_and_int_formatted           102 ns          102 ns      6854642
| BM_prefix_40_and_3_ints                  181 ns          181 ns      3828217
| BM_prefix_40_and_3_ints_formatted        243 ns          243 ns      2814977

>From e02e5bac0bbdd2e62d8cd9d07c2f0e7810b393b9 Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Sat, 22 Mar 2025 15:48:57 +0100
Subject: [PATCH] [RFC][libc++][format] Implements compile-time caching.

This is a proof-of-concept patch to implement caching in
basic_format_string. This is only intended for --this Discourse--
discussion.

The real patch needs to do things cleaner.

lit -a -Doptimization=speed libcxx/test/benchmarks/format/parsing.bench.cpp
BEFORE
| ----------------------------------------------------------------------------
| Benchmark                                  Time             CPU   Iterations
| ----------------------------------------------------------------------------
| BM_empty                                6.18 ns         6.17 ns     87897649
| BM_curly_open                           11.2 ns         11.2 ns     62229450
| BM_curly_close                          10.7 ns         10.7 ns     65368952
| BM_pipe                                 12.9 ns         12.9 ns     54477724
| BM_int                                  47.6 ns         47.6 ns     14718165
| BM_int_formatted                        97.4 ns         97.4 ns      6953144
| BM_3_ints                                141 ns          141 ns      4944337
| BM_3_ints_formatted                      283 ns          283 ns      2464184
| BM_prefix_5_and_empty                   16.5 ns         16.5 ns     42569346
| BM_prefix_5_and_curly_open              17.8 ns         17.8 ns     39273542
| BM_prefix_5_and_curly_close             17.8 ns         17.8 ns     39140130
| BM_prefix_5_and_pipe                    19.5 ns         19.5 ns     35803584
| BM_prefix_5_and_int                     51.1 ns         51.0 ns     13436274
| BM_prefix_5_and_int_formatted            102 ns          102 ns      6793303
| BM_prefix_5_and_3_ints                   151 ns          151 ns      4573472
| BM_prefix_5_and_3_ints_formatted         291 ns          291 ns      2351850
| BM_prefix_10_and_empty                  24.9 ns         24.9 ns     28109036
| BM_prefix_10_and_curly_open             27.2 ns         27.2 ns     25713490
| BM_prefix_10_and_curly_close            30.2 ns         30.2 ns     23176332
| BM_prefix_10_and_pipe                   32.7 ns         32.6 ns     21420861
| BM_prefix_10_and_int                    61.6 ns         61.6 ns     11034101
| BM_prefix_10_and_int_formatted           120 ns          120 ns      5610531
| BM_prefix_10_and_3_ints                  156 ns          156 ns      4470738
| BM_prefix_10_and_3_ints_formatted        312 ns          311 ns      2217207
| BM_prefix_20_and_empty                  52.7 ns         52.7 ns     13138425
| BM_prefix_20_and_curly_open             58.8 ns         58.8 ns     11863712
| BM_prefix_20_and_curly_close            56.6 ns         56.6 ns     12407764
| BM_prefix_20_and_pipe                   56.5 ns         56.4 ns     12411003
| BM_prefix_20_and_int                    86.9 ns         86.8 ns      8089463
| BM_prefix_20_and_int_formatted           153 ns          153 ns      4582443
| BM_prefix_20_and_3_ints                  204 ns          204 ns      3354539
| BM_prefix_20_and_3_ints_formatted        349 ns          349 ns      2002457
| BM_prefix_40_and_empty                   116 ns          116 ns      6018618
| BM_prefix_40_and_curly_open              122 ns          122 ns      5655627
| BM_prefix_40_and_curly_close             121 ns          121 ns      5722255
| BM_prefix_40_and_pipe                    120 ns          120 ns      5823239
| BM_prefix_40_and_int                     158 ns          158 ns      4476432
| BM_prefix_40_and_int_formatted           207 ns          207 ns      3306182
| BM_prefix_40_and_3_ints                  255 ns          255 ns      2759277
| BM_prefix_40_and_3_ints_formatted        371 ns          370 ns      1884154

AFTER
| ----------------------------------------------------------------------------
| Benchmark                                  Time             CPU   Iterations
| ----------------------------------------------------------------------------
| BM_empty                                21.6 ns         21.6 ns     32391462
| BM_curly_open                           22.9 ns         22.9 ns     30588441
| BM_curly_close                          22.9 ns         22.9 ns     30592394
| BM_pipe                                 26.0 ns         26.0 ns     26953859
| BM_int                                  60.5 ns         60.5 ns     11577625
| BM_int_formatted                        81.3 ns         81.2 ns      8594596
| BM_3_ints                                156 ns          156 ns      4409940
| BM_3_ints_formatted                      220 ns          220 ns      3187392
| BM_prefix_5_and_empty                   26.0 ns         26.0 ns     26936925
| BM_prefix_5_and_curly_open              26.0 ns         26.0 ns     26965917
| BM_prefix_5_and_curly_close             26.0 ns         26.0 ns     26961356
| BM_prefix_5_and_pipe                    26.0 ns         26.0 ns     26959641
| BM_prefix_5_and_int                     64.1 ns         64.1 ns     10920953
| BM_prefix_5_and_int_formatted           84.4 ns         84.4 ns      8037453
| BM_prefix_5_and_3_ints                   165 ns          165 ns      4217532
| BM_prefix_5_and_3_ints_formatted         247 ns          247 ns      2823553
| BM_prefix_10_and_empty                  26.0 ns         26.0 ns     26949246
| BM_prefix_10_and_curly_open             26.0 ns         26.0 ns     26955524
| BM_prefix_10_and_curly_close            26.0 ns         26.0 ns     26956279
| BM_prefix_10_and_pipe                   26.0 ns         26.0 ns     26947963
| BM_prefix_10_and_int                    59.8 ns         59.8 ns     11674226
| BM_prefix_10_and_int_formatted          87.1 ns         87.0 ns      7814714
| BM_prefix_10_and_3_ints                  165 ns          165 ns      4149378
| BM_prefix_10_and_3_ints_formatted        245 ns          245 ns      2884163
| BM_prefix_20_and_empty                  29.3 ns         29.3 ns     23873593
| BM_prefix_20_and_curly_open             28.5 ns         28.4 ns     24625298
| BM_prefix_20_and_curly_close            27.5 ns         27.5 ns     25438870
| BM_prefix_20_and_pipe                   29.4 ns         29.3 ns     23863557
| BM_prefix_20_and_int                    57.6 ns         57.6 ns     12247413
| BM_prefix_20_and_int_formatted           104 ns          104 ns      6690343
| BM_prefix_20_and_3_ints                  183 ns          183 ns      3828112
| BM_prefix_20_and_3_ints_formatted        244 ns          244 ns      2833578
| BM_prefix_40_and_empty                  37.5 ns         37.5 ns     18650804
| BM_prefix_40_and_curly_open             36.8 ns         36.8 ns     19094990
| BM_prefix_40_and_curly_close            37.1 ns         37.0 ns     18904266
| BM_prefix_40_and_pipe                   36.5 ns         36.5 ns     19147265
| BM_prefix_40_and_int                    81.1 ns         81.0 ns      8611919
| BM_prefix_40_and_int_formatted           102 ns          102 ns      6854642
| BM_prefix_40_and_3_ints                  181 ns          181 ns      3828217
| BM_prefix_40_and_3_ints_formatted        243 ns          243 ns      2814977
---
 libcxx/include/__format/format_functions.h    | 388 +++++++++++++++++-
 .../test/benchmarks/format/parsing.bench.cpp  | 355 ++++++++++++++++
 .../format/format.functions/cache.pass.cpp    |  97 +++++
 3 files changed, 835 insertions(+), 5 deletions(-)
 create mode 100644 libcxx/test/benchmarks/format/parsing.bench.cpp
 create mode 100644 libcxx/test/std/utilities/format/format.functions/cache.pass.cpp

diff --git a/libcxx/include/__format/format_functions.h b/libcxx/include/__format/format_functions.h
index 5feaf7e5a064a..98e2d97718831 100644
--- a/libcxx/include/__format/format_functions.h
+++ b/libcxx/include/__format/format_functions.h
@@ -76,6 +76,9 @@ template <class... _Args>
 }
 #  endif
 
+template <output_iterator<const char&> _OutIt>
+_LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _OutIt vformat_to(_OutIt __out_it, string_view __fmt, format_args __args);
+
 namespace __format {
 
 /// Helper class parse and handle argument.
@@ -147,6 +150,40 @@ struct _LIBCPP_TEMPLATE_VIS __compile_time_basic_format_context {
   size_t __size_;
 };
 
+template <class _CharT>
+union __any_std_formatter {
+#  define _LIBCCP_IMPLICIT_UNION(__type, __member_name)                                                                \
+    constexpr __any_std_formatter(formatter<__type, _CharT> __formatter) : __member_name(__formatter) {}               \
+    explicit constexpr operator formatter<__type, _CharT>() const { return __member_name; }                            \
+    formatter<__type, _CharT> __member_name
+
+  _LIBCCP_IMPLICIT_UNION(bool, __b);
+  _LIBCCP_IMPLICIT_UNION(_CharT, __c);
+
+  _LIBCCP_IMPLICIT_UNION(int, __i);
+  _LIBCCP_IMPLICIT_UNION(long long, __ll);
+#  if _LIBCPP_HAS_INT128
+  _LIBCCP_IMPLICIT_UNION(__int128_t, __i128);
+#  endif
+
+  _LIBCCP_IMPLICIT_UNION(unsigned, __u);
+  _LIBCCP_IMPLICIT_UNION(unsigned long long, __ull);
+#  if _LIBCPP_HAS_INT128
+  _LIBCCP_IMPLICIT_UNION(__uint128_t, __u128);
+#  endif
+
+  _LIBCCP_IMPLICIT_UNION(float, __f);
+  _LIBCCP_IMPLICIT_UNION(double, __d);
+  _LIBCCP_IMPLICIT_UNION(long double, __ld);
+
+  _LIBCCP_IMPLICIT_UNION(const _CharT*, __cs);
+  _LIBCCP_IMPLICIT_UNION(basic_string_view<_CharT>, __sv);
+
+  _LIBCCP_IMPLICIT_UNION(const void*, __p);
+
+#  undef _LIBCCP_IMPLICIT_UNION
+};
+
 // [format.string.std]/8
 // If { arg-idopt } is used in a width or precision, the value of the
 // corresponding formatting argument is used in its place. If the
@@ -156,7 +193,7 @@ struct _LIBCPP_TEMPLATE_VIS __compile_time_basic_format_context {
 //
 // _HasPrecision does the formatter have a precision?
 template <class _CharT, class _Tp, bool _HasPrecision = false>
-_LIBCPP_HIDE_FROM_ABI constexpr void __compile_time_validate_argument(
+_LIBCPP_HIDE_FROM_ABI constexpr __any_std_formatter<_CharT> __compile_time_validate_argument(
     basic_format_parse_context<_CharT>& __parse_ctx, __compile_time_basic_format_context<_CharT>& __ctx) {
   auto __validate_type = [](__arg_t __type) {
     // LWG3720 originally allowed "signed or unsigned integer types", however
@@ -185,11 +222,13 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __compile_time_validate_argument(
   if constexpr (_HasPrecision)
     if (__formatter.__parser_.__precision_as_arg_)
       __validate_type(__ctx.arg(__formatter.__parser_.__precision_));
+
+  return __formatter;
 }
 
 // This function is not user facing, so it can directly use the non-standard types of the "variant".
 template <class _CharT>
-_LIBCPP_HIDE_FROM_ABI constexpr void __compile_time_visit_format_arg(
+_LIBCPP_HIDE_FROM_ABI constexpr __any_std_formatter<_CharT> __compile_time_visit_format_arg(
     basic_format_parse_context<_CharT>& __parse_ctx,
     __compile_time_basic_format_context<_CharT>& __ctx,
     __arg_t __type) {
@@ -210,7 +249,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __compile_time_visit_format_arg(
 #  else
     std::__throw_format_error("Invalid argument");
 #  endif
-    return;
+    break;
   case __arg_t::__unsigned:
     return __format::__compile_time_validate_argument<_CharT, unsigned>(__parse_ctx, __ctx);
   case __arg_t::__unsigned_long_long:
@@ -221,7 +260,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __compile_time_visit_format_arg(
 #  else
     std::__throw_format_error("Invalid argument");
 #  endif
-    return;
+    break;
   case __arg_t::__float:
     return __format::__compile_time_validate_argument<_CharT, float, true>(__parse_ctx, __ctx);
   case __arg_t::__double:
@@ -368,6 +407,67 @@ struct _LIBCPP_TEMPLATE_VIS basic_format_string {
   consteval basic_format_string(const _Tp& __str) : __str_{__str} {
     __format::__vformat_to(basic_format_parse_context<_CharT>{__str_, sizeof...(_Args)},
                            _Context{__types_.data(), __handles_.data(), sizeof...(_Args)});
+
+    basic_format_parse_context<_CharT> __parse_ctx{__str_, sizeof...(_Args)};
+    _Context __ctx{__types_.data(), __handles_.data(), sizeof...(_Args)};
+
+    __state_ = [&] {
+      auto __begin         = __str_.begin();
+      auto __end           = __str_.end();
+      auto __element_begin = __str_.begin();
+
+      while (__begin != __end) {
+        switch (*__begin) {
+        case _CharT('{'):
+          if (__begin + 1 == __end)
+            std::__throw_format_error("The format string terminates at a '{'");
+
+          if (*(__begin + 1) == _CharT('{')) {
+            if (!__add_curly_brace(__element_begin, __begin, __element_type::__curly_brace_open))
+              return __element_state::__parsing_partial;
+
+          } else {
+            if (__element_begin != __begin) {
+              // Add the parsed string before the replaacemet
+              if (!__add_string(__element_begin, __begin))
+                return __element_state::__parsing_partial;
+            }
+
+            __format::__parse_number_result __r = __process_arg_id(__begin + 1, __end, __parse_ctx);
+            __format::__arg_t __type            = __ctx.arg(__r.__value);
+            if (__type == __format::__arg_t::__none)
+              std::__throw_format_error("The argument index value is too large for the number of arguments supplied");
+            else if (__type == __format::__arg_t::__handle)
+              //
+              // TODO ADD Handle support
+              //
+              return __element_state::__do_not_use;
+            else {
+              if (!__add_std_formatter(__element_begin, __begin, __end, __r.__value, __type, __parse_ctx, __ctx))
+                return __element_state::__parsing_partial;
+            }
+          }
+          break;
+
+        case _CharT('}'):
+          if (__begin + 1 == __end || *(__begin + 1) != _CharT('}'))
+            std::__throw_format_error("The format string contains an invalid escape sequence");
+
+          if (!__add_curly_brace(__element_begin, __begin, __element_type::__curly_brace_close))
+            return __element_state::__parsing_partial;
+          break;
+
+        default:
+          ++__begin;
+        }
+      }
+      // Handle last element. This should always be a string element, else there is a logic errror.
+      if (__element_count_ == 0 || __element_begin != __end) {
+        if (!__add_string(__element_begin, __begin))
+          return __element_state::__parsing_partial;
+      }
+      return __element_state::__parsing_completed;
+    }();
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr basic_string_view<_CharT> get() const noexcept { return __str_; }
@@ -375,6 +475,26 @@ struct _LIBCPP_TEMPLATE_VIS basic_format_string {
   _LIBCPP_HIDE_FROM_ABI basic_format_string(__runtime_format_string<_CharT> __s) noexcept : __str_(__s.__str_) {}
 #  endif
 
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI string __vformat(format_args __args) {
+    __format::__allocating_buffer<char> __buffer;
+    switch (__state_) {
+    case __element_state::__parsing_partial:
+#  ifdef _LIBCPP_DEBUG_BASIC_FORMAT_STRING_CACHE
+      __buffer.__copy(std::string_view{"PART:"});
+#  endif
+      [[fallthrough]];
+    case __element_state::__do_not_use:
+      std::vformat_to(__buffer.__make_output_iterator(), __str_, __args);
+      break;
+
+    case __element_state::__parsing_completed:
+      __format(__buffer, __args);
+      break;
+    }
+
+    return string{__buffer.__view()};
+  }
+
 private:
   basic_string_view<_CharT> __str_;
 
@@ -391,6 +511,264 @@ struct _LIBCPP_TEMPLATE_VIS basic_format_string {
 
     return __handle;
   }()...};
+
+  using __iterator = basic_string_view<_CharT>::iterator;
+
+  enum class __element_state {
+    // The __elements_ should not be used.
+    //
+    // This is intened to be removed once everything works
+    __do_not_use,
+
+    // The entire __str_ has been parsed and stored in __elements_
+    __parsing_completed,
+    // The input __str_ has been partially parsed and stored in __elements_.
+    //
+    // This happens when the __str_ needed more entries than available in __elemenst_,
+    // so the output can first process __elements_ and then do run-time parsing for
+    // the remaining part of __str_.
+    //
+    //
+    // TODO test autotic numbering keeps the right elements
+    //
+    __parsing_partial,
+
+  };
+
+  __element_state __state_;
+
+  enum class __element_type {
+    // This is a formatter for one of the non-handle types.
+    //
+    // All these formatters use the same state for parsing this state is stored in the array.
+    // This means at run-time there is no need to parse this replacement-field.
+    __formatter_std,
+    // A handle formatter
+    //
+    // This replacement-field is validated at compile-time, but its state is not stored.
+    // This means it needs to be parsed again at run-time.
+    __formatter_handle,
+
+    // This part of the format-string contains a wstring.
+    //
+    // This can be copied to the output verbatim.
+    __wstring,
+    // This part of the format-string contains a string with valid UTF-8.
+    //
+    // This can be copied to the output verbatim.
+    __string_valid_utf8,
+    // This part of the format-string contains a string with invalid UTF-8.
+    //
+    // Since UTF-8 is self syncing the end of the string can be determined
+    //
+    // The distiction between wstring, string (|in)valid UTF-8 is needed for
+    // std::string. When the string contains invalid Unicode the recommended
+    // practice is to diagnose this. std::print only takes `char` as input
+    // character type. So when using std::print the algorithm can use this
+    // information to diagnose things. (Diagnose means using Unicode
+    // replacement characters.)
+    __string_invalid_utf8,
+    // Found {{ in the input, unescape to {.
+    __curly_brace_open,
+    // Found }} in the input, unescape to }.
+    //
+    // Note it would be possible to use __char and then store '{' or '}' in
+    // the __parser_state_'s fill field. However that feels clumsy and there
+    // are only 2 escape characters.
+    __curly_brace_close,
+    // The field is empty.
+    //
+    // The number of fields is hard-coded so this is a sentinal value.
+    // Once an element is empty, all remaining elements are empty too.
+    __empty,
+  };
+
+  struct __element_formatter {
+    uint32_t __id = 0;
+    __format::__any_std_formatter<_CharT> __formatter;
+  };
+
+  struct __element {
+    __element_type __type;
+    union {
+      __element_formatter __formatter;
+      basic_string_view<_CharT> __string;
+      int __dummy;
+    } __data_ = {.__dummy = 0};
+  };
+
+  size_t __element_count_ = 0;
+
+  // The elements found in __str_
+  static constexpr size_t __n_elements_       = 32;
+  array<__element, __n_elements_> __elements_ = [] {
+    array<__element, __n_elements_> __result;
+    for (size_t __i = 0; __i < __result.size(); ++__i) {
+      __result[__i] = {__element_type::__empty};
+    }
+    return __result;
+  }();
+
+  // TODO add UTF-8 validation to this function.
+  [[nodiscard]] consteval bool __add_string(__iterator __begin, __iterator __end) {
+    if (__element_count_ == __elements_.size())
+      return false;
+
+    __elements_[__element_count_++] = {
+        same_as<_CharT, char> ? __element_type::__string_valid_utf8 : __element_type::__wstring,
+        {.__string = {__begin, __end}}};
+
+    return true;
+  }
+
+  // Adds an escaped curly brace, with an optional string prefix, to the parsed elements.
+  //
+  // When the input is like
+  //   abc{{
+  //   ^  ^ ^
+  //   1  2 3
+  //
+  // 1. input value of __element_begin
+  // 2. input value of __begin
+  // 3. output value of __element_begin and __begin
+  //
+  // pre __begin + 1 == __begin + 2 == ('{' || '}')
+  // pre __type = *(__begin + 1) == '{' ? __curly_brace_open
+  //                                    : *(__begin + 1) == '}' ? __curly_brace_close
+  //                                                            : pre-condition failure
+  //
+  // __begin and __element_begin may point to the same element, in that case
+  // there is no parsed string. For example, "{}{{" or "{{" at the start of the
+  // input.
+  //
+  // When there is a string prefix the curly brace is stored in the string.
+  // This saves 1 entry.
+  [[nodiscard]] consteval bool
+  __add_curly_brace(__iterator& __element_begin, __iterator& __begin, __element_type __type) {
+    if (__element_begin == __begin) {
+      // No string before.
+      if (__element_count_ == __elements_.size())
+        return false;
+      __elements_[__element_count_++] = {__type};
+    } else {
+      // Merge with string.
+      if (!__add_string(__element_begin, __begin + 1))
+        return false;
+    }
+
+    __begin += 2;
+    __element_begin = __begin;
+    return true;
+  }
+
+  [[nodiscard]] consteval bool __add_std_formatter(
+      __iterator& __element_begin,
+      __iterator& __begin,
+      __iterator __end,
+      uint32_t __arg_id,
+      __format::__arg_t __type,
+      basic_format_parse_context<_CharT>& __parse_ctx,
+      _Context& __ctx) {
+    if (__element_count_ == __elements_.size())
+      return false;
+
+    __format::__any_std_formatter<_CharT> __formatter = __compile_time_visit_format_arg(__parse_ctx, __ctx, __type);
+    __begin                                           = __parse_ctx.begin();
+    if (__begin == __end || *__begin != _CharT('}'))
+      std::__throw_format_error("The replacement field misses a terminating '}'");
+
+    ++__begin;
+    __element_begin = __begin;
+
+    __elements_[__element_count_++] = {__element_type::__formatter_std, {.__formatter{__arg_id, __formatter}}
+
+    };
+
+    return true;
+  }
+
+  [[nodiscard]] consteval __format::__parse_number_result<__iterator>
+  __process_arg_id(__iterator __begin, __iterator __end, basic_format_parse_context<_CharT>& __parse_ctx) {
+    __format::__parse_number_result __result = __format::__parse_arg_id(__begin, __end, __parse_ctx);
+    if (__result.__last == __end)
+      std::__throw_format_error("The argument index should end with a ':' or a '}'");
+
+    //
+    // Note we always need to parse to get the proper default parser settings,
+    // this differs from where we copy pasted the data
+    //
+    // bool __parse = *__result.__last == _CharT(':');
+    switch (*__result.__last) {
+    case _CharT(':'):
+      // The arg-id has a format-specifier, advance the input to the format-spec.
+      __parse_ctx.advance_to(__result.__last + 1);
+      break;
+    case _CharT('}'):
+      // The arg-id has no format-specifier.
+      __parse_ctx.advance_to(__result.__last);
+      break;
+    default:
+      std::__throw_format_error("The argument index should end with a ':' or a '}'");
+    }
+
+    return __result;
+  }
+
+  void __format(__format::__allocating_buffer<char>& __buffer, format_args __args) {
+#  ifdef _LIBCPP_DEBUG_BASIC_FORMAT_STRING_CACHE
+    __buffer.__copy(std::string_view{"FULL:"});
+#  endif
+    auto __ctx = std::__format_context_create(__buffer.__make_output_iterator(), __args);
+
+    for (auto& __element : __elements_) {
+      if (__element.__type == __element_type::__empty)
+        break;
+
+      switch (__element.__type) {
+      case __element_type::__formatter_std:
+      {
+        std::__visit_format_arg(
+            [&](auto __arg) {
+              if constexpr (same_as<decltype(__arg), monostate>)
+                std::__throw_format_error("The argument index value is too large for the number of arguments supplied");
+              else if constexpr (same_as<decltype(__arg), typename basic_format_arg<decltype(__ctx)>::handle>)
+                std::__throw_format_error("Not implemented");
+              else {
+                // TODO __formatter.__formatter looks odd, maybe the first should
+                // be __replacement_field.
+                const auto& __formatter =
+                    static_cast<formatter<decltype(__arg), _CharT>>(__element.__data_.__formatter.__formatter);
+
+                __ctx.advance_to(__formatter.format(__arg, __ctx));
+              }
+            },
+            __ctx.arg(__element.__data_.__formatter.__id));
+
+      } break;
+
+      case __element_type::__formatter_handle:
+        break;
+      case __element_type::__string_invalid_utf8:
+        if (false) {
+          break;
+        }
+        [[fallthrough]];
+      case __element_type::__string_valid_utf8:
+        [[fallthrough]];
+      case __element_type::__wstring:
+        __buffer.__copy(__element.__data_.__string);
+        break;
+      case __element_type::__curly_brace_open:
+        __buffer.push_back(_CharT('{'));
+        break;
+      case __element_type::__curly_brace_close:
+        __buffer.push_back(_CharT('}'));
+        break;
+      case __element_type::__empty:
+        break;
+      }
+    }
+  }
 };
 
 template <class... _Args>
@@ -471,7 +849,7 @@ vformat(wstring_view __fmt, wformat_args __args) {
 template <class... _Args>
 [[nodiscard]] _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI string
 format(format_string<_Args...> __fmt, _Args&&... __args) {
-  return std::vformat(__fmt.get(), std::make_format_args(__args...));
+  return __fmt.__vformat(std::make_format_args(__args...));
 }
 
 #  if _LIBCPP_HAS_WIDE_CHARACTERS
diff --git a/libcxx/test/benchmarks/format/parsing.bench.cpp b/libcxx/test/benchmarks/format/parsing.bench.cpp
new file mode 100644
index 0000000000000..c31778efcc8c0
--- /dev/null
+++ b/libcxx/test/benchmarks/format/parsing.bench.cpp
@@ -0,0 +1,355 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// This benchmark does formatting but the main goal is to test the parsing.
+// Calling std::format without formatting args may me somewhat unrealistc,
+// however it is realistic for std::print. Unfortunately std::print is hard
+// to benchmark since the writing to the terminal is not what is inteded to
+// be measured.
+
+#include <cstdio>
+#include <format>
+#include <array>
+#include <string>
+#include <string_view>
+
+#include "benchmark/benchmark.h"
+
+// No prefix
+
+static void BM_empty(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("");
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_empty);
+
+static void BM_curly_open(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("{{");
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_curly_open);
+
+static void BM_curly_close(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("}}");
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_curly_close);
+
+static void BM_pipe(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("||");
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_pipe);
+
+static void BM_int(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("{}", 42);
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_int);
+
+static void BM_int_formatted(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("{0:-^#6x}", 42);
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_int_formatted);
+
+static void BM_3_ints(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("{},{},{}", 42, 0, 99);
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_3_ints);
+
+static void BM_3_ints_formatted(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("{0:-^#6x},{0:-^#6x},{0:-^#6x}", 42);
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_3_ints_formatted);
+
+// Prefix 5
+
+static void BM_prefix_5_and_empty(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("aaaa'");
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_prefix_5_and_empty);
+
+static void BM_prefix_5_and_curly_open(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("aaaa'{{");
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_prefix_5_and_curly_open);
+
+static void BM_prefix_5_and_curly_close(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("aaaa'}}");
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_prefix_5_and_curly_close);
+
+static void BM_prefix_5_and_pipe(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("aaaa'||");
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_prefix_5_and_pipe);
+
+static void BM_prefix_5_and_int(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("aaaa'{}", 42);
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_prefix_5_and_int);
+
+static void BM_prefix_5_and_int_formatted(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("aaaa'{0:-^#6x}", 42);
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_prefix_5_and_int_formatted);
+
+static void BM_prefix_5_and_3_ints(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("aaaa'{},{},{}", 42, 0, 99);
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_prefix_5_and_3_ints);
+
+static void BM_prefix_5_and_3_ints_formatted(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("aaaa'{0:-^#6x},{0:-^#6x},{0:-^#6x}", 42);
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_prefix_5_and_3_ints_formatted);
+
+// Prefix 10
+
+static void BM_prefix_10_and_empty(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("aaaa'aaaa'");
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_prefix_10_and_empty);
+
+static void BM_prefix_10_and_curly_open(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("aaaa'aaaa'{{");
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_prefix_10_and_curly_open);
+
+static void BM_prefix_10_and_curly_close(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("aaaa'aaaa'}}");
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_prefix_10_and_curly_close);
+
+static void BM_prefix_10_and_pipe(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("aaaa'aaaa'||");
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_prefix_10_and_pipe);
+
+static void BM_prefix_10_and_int(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("aaaa'aaaa'{}", 42);
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_prefix_10_and_int);
+
+static void BM_prefix_10_and_int_formatted(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("aaaa'aaaa'{0:-^#6x}", 42);
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_prefix_10_and_int_formatted);
+
+static void BM_prefix_10_and_3_ints(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("aaaa'aaaa'{},{},{}", 42, 0, 99);
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_prefix_10_and_3_ints);
+
+static void BM_prefix_10_and_3_ints_formatted(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("aaaa'aaaa'{0:-^#6x},{0:-^#6x},{0:-^#6x}", 42);
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_prefix_10_and_3_ints_formatted);
+
+// Prefix 20
+
+static void BM_prefix_20_and_empty(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("aaaa'aaaa'aaaa'aaaa'");
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_prefix_20_and_empty);
+
+static void BM_prefix_20_and_curly_open(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("aaaa'aaaa'aaaa'aaaa'{{");
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_prefix_20_and_curly_open);
+
+static void BM_prefix_20_and_curly_close(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("aaaa'aaaa'aaaa'aaaa'}}");
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_prefix_20_and_curly_close);
+
+static void BM_prefix_20_and_pipe(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("aaaa'aaaa'aaaa'aaaa'||");
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_prefix_20_and_pipe);
+
+static void BM_prefix_20_and_int(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("aaaa'aaaa'aaaa'aaaa'{}", 42);
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_prefix_20_and_int);
+
+static void BM_prefix_20_and_int_formatted(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("aaaa'aaaa'aaaa'aaaa'{0:-^#6x}", 42);
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_prefix_20_and_int_formatted);
+
+static void BM_prefix_20_and_3_ints(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("aaaa'aaaa'aaaa'aaaa'{},{},{}", 42, 0, 99);
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_prefix_20_and_3_ints);
+
+static void BM_prefix_20_and_3_ints_formatted(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("aaaa'aaaa'aaaa'aaaa'{0:-^#6x},{0:-^#6x},{0:-^#6x}", 42);
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_prefix_20_and_3_ints_formatted);
+
+// Prefix 40
+
+static void BM_prefix_40_and_empty(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'");
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_prefix_40_and_empty);
+
+static void BM_prefix_40_and_curly_open(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'{{");
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_prefix_40_and_curly_open);
+
+static void BM_prefix_40_and_curly_close(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'}}");
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_prefix_40_and_curly_close);
+
+static void BM_prefix_40_and_pipe(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'||");
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_prefix_40_and_pipe);
+
+static void BM_prefix_40_and_int(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'{}", 42);
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_prefix_40_and_int);
+
+static void BM_prefix_40_and_int_formatted(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'{0:-^#6x}", 42);
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_prefix_40_and_int_formatted);
+
+static void BM_prefix_40_and_3_ints(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'{},{},{}", 42, 0, 99);
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_prefix_40_and_3_ints);
+
+static void BM_prefix_40_and_3_ints_formatted(benchmark::State& state) {
+  for (auto _ : state) {
+    auto s = std::format("aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'{0:-^#6x},{0:-^#6x},{0:-^#6x}", 42);
+    benchmark::DoNotOptimize(s);
+  }
+}
+BENCHMARK(BM_prefix_40_and_3_ints_formatted);
+
+BENCHMARK_MAIN();
diff --git a/libcxx/test/std/utilities/format/format.functions/cache.pass.cpp b/libcxx/test/std/utilities/format/format.functions/cache.pass.cpp
new file mode 100644
index 0000000000000..a1d8ced4ae289
--- /dev/null
+++ b/libcxx/test/std/utilities/format/format.functions/cache.pass.cpp
@@ -0,0 +1,97 @@
+//===----------------------------------------------------------------------===//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
+
+
+// TODO FMT This test should not require std::to_chars(floating-point)
+// XFAIL: availability-fp_to_chars-missing
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DEBUG_BASIC_FORMAT_STRING_CACHE
+
+// <format>
+
+
+// This is a test for the new caching mechanism.
+
+#include <format>
+#include <cassert>
+#include <vector>
+
+#include "make_string.h"
+#include "test_format_string.h"
+
+#include <iostream>
+#include <type_traits>
+
+#define SV(S) MAKE_STRING_VIEW(CharT, S)
+template < class CharT, class... Args>
+void check(std::basic_string_view<CharT> expected, test_format_string<CharT, Args...> fmt, Args&&... args) {
+  std::basic_string<CharT> out = std::format(fmt, std::forward<Args>(args)...);
+#ifndef TEST_HAS_NO_LOCALIZATION
+  if constexpr (std::same_as<CharT, char>)
+    if (out != expected)
+      std::cerr << "\nFormat string   " << fmt.get() << "\nExpected output " << expected << "\nActual output   " << out
+                << '\n';
+#endif
+  assert(out == expected);
+};
+
+template <class CharT>
+struct String {
+  std::basic_string<CharT> s;
+};
+
+template <class CharT>
+struct std::formatter<String<CharT>, CharT> : std::formatter<std::basic_string_view<CharT>> {
+  template <class FormatContext>
+  typename FormatContext::iterator format(const String<CharT>& str, FormatContext& ctx) const {
+    return std::formatter<std::basic_string_view<CharT>>::format(std::basic_string_view<CharT >{str.s}, ctx);
+  }
+};
+
+template <class CharT>
+static void test() {
+  check(SV("FULL:{"), SV("{{"));
+  check(SV("FULL:foo{"), SV("foo{{"));
+  check(SV("FULL:foo{{"), SV("foo{{{{"));
+
+  check(SV("FULL:}"), SV("}}"));
+  check(SV("FULL:foo}"), SV("foo}}"));
+  check(SV("FULL:foo}}"), SV("foo}}}}"));
+
+  check(SV("FULL:foo{}"), SV("foo{{}}"));
+  check(SV("FULL:foo{{}}"), SV("foo{{{{}}}}"));
+  check(SV("FULL:foo}{"), SV("foo}}{{"));
+  check(SV("FULL:foo}}{{"), SV("foo}}}}{{{{"));
+
+  check(SV("FULL:ZZZ"), SV("ZZZ"));
+  check(SV("FULL:Z{Z"), SV("Z{{Z"));
+  check(SV("FULL:Z}Z"), SV("Z}}Z"));
+  check(SV("FULL:Z{Z{"), SV("Z{{Z{{"));
+  check(SV("FULL:Z}Z{Z"), SV("Z}}Z{{Z"));
+
+  check(SV("FULL:ZZZ"), SV("{}"), SV("ZZZ"));
+  check(SV("FULL:42"), SV("{}"), 42);
+  check(SV("FULL:true"), SV("{}"), true);
+
+  check(SV("FULL:ZZZ"), SV("{:}"), SV("ZZZ"));
+  check(SV("FULL:0x0042"), SV("{:#06x}"), 0x42);
+  check(SV("FULL:0x0042=answer"), SV("{:#06x}={}"), 0x42, SV("answer"));
+
+//  check(SV("FULL:hello world"), SV("{} world"), String<char>{"hello"});
+//  check(SV("FULL:hello world"), SV("{0:} world"), String<char>{"hello"});
+
+  // TODO TEST WITH ARG EATER
+}
+
+int main(int, char**) {
+  test<char>();
+
+  return 0;
+}



More information about the libcxx-commits mailing list