181ad6265SDimitry Andric // -*- C++ -*- 281ad6265SDimitry Andric //===----------------------------------------------------------------------===// 381ad6265SDimitry Andric // 481ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 581ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 681ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 781ad6265SDimitry Andric // 881ad6265SDimitry Andric //===----------------------------------------------------------------------===// 981ad6265SDimitry Andric 1081ad6265SDimitry Andric #ifndef _LIBCPP___FORMAT_BUFFER_H 1181ad6265SDimitry Andric #define _LIBCPP___FORMAT_BUFFER_H 1281ad6265SDimitry Andric 1381ad6265SDimitry Andric #include <__algorithm/copy_n.h> 14bdd1243dSDimitry Andric #include <__algorithm/fill_n.h> 1581ad6265SDimitry Andric #include <__algorithm/max.h> 1681ad6265SDimitry Andric #include <__algorithm/min.h> 17bdd1243dSDimitry Andric #include <__algorithm/ranges_copy_n.h> 18bdd1243dSDimitry Andric #include <__algorithm/transform.h> 1981ad6265SDimitry Andric #include <__algorithm/unwrap_iter.h> 20bdd1243dSDimitry Andric #include <__concepts/same_as.h> 2181ad6265SDimitry Andric #include <__config> 22bdd1243dSDimitry Andric #include <__format/concepts.h> 2381ad6265SDimitry Andric #include <__format/enable_insertable.h> 2481ad6265SDimitry Andric #include <__format/format_to_n_result.h> 2581ad6265SDimitry Andric #include <__iterator/back_insert_iterator.h> 2681ad6265SDimitry Andric #include <__iterator/concepts.h> 2781ad6265SDimitry Andric #include <__iterator/incrementable_traits.h> 2881ad6265SDimitry Andric #include <__iterator/iterator_traits.h> 2981ad6265SDimitry Andric #include <__iterator/wrap_iter.h> 3006c3fb27SDimitry Andric #include <__memory/addressof.h> 3106c3fb27SDimitry Andric #include <__memory/allocate_at_least.h> 3206c3fb27SDimitry Andric #include <__memory/allocator_traits.h> 3306c3fb27SDimitry Andric #include <__memory/construct_at.h> 3406c3fb27SDimitry Andric #include <__memory/ranges_construct_at.h> 3506c3fb27SDimitry Andric #include <__memory/uninitialized_algorithms.h> 3606c3fb27SDimitry Andric #include <__type_traits/add_pointer.h> 3706c3fb27SDimitry Andric #include <__type_traits/conditional.h> 3806c3fb27SDimitry Andric #include <__utility/exception_guard.h> 3981ad6265SDimitry Andric #include <__utility/move.h> 4081ad6265SDimitry Andric #include <cstddef> 41bdd1243dSDimitry Andric #include <string_view> 4281ad6265SDimitry Andric 4381ad6265SDimitry Andric #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) 4481ad6265SDimitry Andric # pragma GCC system_header 4581ad6265SDimitry Andric #endif 4681ad6265SDimitry Andric 4781ad6265SDimitry Andric _LIBCPP_PUSH_MACROS 4881ad6265SDimitry Andric #include <__undef_macros> 4981ad6265SDimitry Andric 5081ad6265SDimitry Andric _LIBCPP_BEGIN_NAMESPACE_STD 5181ad6265SDimitry Andric 5206c3fb27SDimitry Andric #if _LIBCPP_STD_VER >= 20 5381ad6265SDimitry Andric 5481ad6265SDimitry Andric namespace __format { 5581ad6265SDimitry Andric 5681ad6265SDimitry Andric /// A "buffer" that handles writing to the proper iterator. 5781ad6265SDimitry Andric /// 5881ad6265SDimitry Andric /// This helper is used together with the @ref back_insert_iterator to offer 5981ad6265SDimitry Andric /// type-erasure for the formatting functions. This reduces the number to 6081ad6265SDimitry Andric /// template instantiations. 61bdd1243dSDimitry Andric template <__fmt_char_type _CharT> 6281ad6265SDimitry Andric class _LIBCPP_TEMPLATE_VIS __output_buffer { 6381ad6265SDimitry Andric public: 6481ad6265SDimitry Andric using value_type = _CharT; 6581ad6265SDimitry Andric 6681ad6265SDimitry Andric template <class _Tp> __output_buffer(_CharT * __ptr,size_t __capacity,_Tp * __obj)67bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI explicit __output_buffer(_CharT* __ptr, size_t __capacity, _Tp* __obj) 68bdd1243dSDimitry Andric : __ptr_(__ptr), 69bdd1243dSDimitry Andric __capacity_(__capacity), 70bdd1243dSDimitry Andric __flush_([](_CharT* __p, size_t __n, void* __o) { static_cast<_Tp*>(__o)->__flush(__p, __n); }), 7181ad6265SDimitry Andric __obj_(__obj) {} 7281ad6265SDimitry Andric __reset(_CharT * __ptr,size_t __capacity)73bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI void __reset(_CharT* __ptr, size_t __capacity) { 7481ad6265SDimitry Andric __ptr_ = __ptr; 7581ad6265SDimitry Andric __capacity_ = __capacity; 7681ad6265SDimitry Andric } 7781ad6265SDimitry Andric __make_output_iterator()78bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI auto __make_output_iterator() { return std::back_insert_iterator{*this}; } 7981ad6265SDimitry Andric 80bdd1243dSDimitry Andric // Used in std::back_insert_iterator. push_back(_CharT __c)8181ad6265SDimitry Andric _LIBCPP_HIDE_FROM_ABI void push_back(_CharT __c) { 8281ad6265SDimitry Andric __ptr_[__size_++] = __c; 8381ad6265SDimitry Andric 8481ad6265SDimitry Andric // Profiling showed flushing after adding is more efficient than flushing 8581ad6265SDimitry Andric // when entering the function. 8681ad6265SDimitry Andric if (__size_ == __capacity_) 87bdd1243dSDimitry Andric __flush(); 8881ad6265SDimitry Andric } 8981ad6265SDimitry Andric 90bdd1243dSDimitry Andric /// Copies the input __str to the buffer. 91bdd1243dSDimitry Andric /// 92bdd1243dSDimitry Andric /// Since some of the input is generated by std::to_chars, there needs to be a 93bdd1243dSDimitry Andric /// conversion when _CharT is wchar_t. 94bdd1243dSDimitry Andric template <__fmt_char_type _InCharT> __copy(basic_string_view<_InCharT> __str)95bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI void __copy(basic_string_view<_InCharT> __str) { 96bdd1243dSDimitry Andric // When the underlying iterator is a simple iterator the __capacity_ is 97bdd1243dSDimitry Andric // infinite. For a string or container back_inserter it isn't. This means 985f757f3fSDimitry Andric // that adding a large string to the buffer can cause some overhead. In that 99bdd1243dSDimitry Andric // case a better approach could be: 100bdd1243dSDimitry Andric // - flush the buffer 101bdd1243dSDimitry Andric // - container.append(__str.begin(), __str.end()); 102bdd1243dSDimitry Andric // The same holds true for the fill. 103bdd1243dSDimitry Andric // For transform it might be slightly harder, however the use case for 104bdd1243dSDimitry Andric // transform is slightly less common; it converts hexadecimal values to 105bdd1243dSDimitry Andric // upper case. For integral these strings are short. 106bdd1243dSDimitry Andric // TODO FMT Look at the improvements above. 107bdd1243dSDimitry Andric size_t __n = __str.size(); 108bdd1243dSDimitry Andric 109bdd1243dSDimitry Andric __flush_on_overflow(__n); 11006c3fb27SDimitry Andric if (__n < __capacity_) { // push_back requires the buffer to have room for at least one character (so use <). 1115f757f3fSDimitry Andric std::copy_n(__str.data(), __n, std::addressof(__ptr_[__size_])); 112bdd1243dSDimitry Andric __size_ += __n; 113bdd1243dSDimitry Andric return; 114bdd1243dSDimitry Andric } 115bdd1243dSDimitry Andric 116bdd1243dSDimitry Andric // The output doesn't fit in the internal buffer. 117bdd1243dSDimitry Andric // Copy the data in "__capacity_" sized chunks. 118*1db9f3b2SDimitry Andric _LIBCPP_ASSERT_INTERNAL(__size_ == 0, "the buffer should be flushed by __flush_on_overflow"); 119bdd1243dSDimitry Andric const _InCharT* __first = __str.data(); 120bdd1243dSDimitry Andric do { 1215f757f3fSDimitry Andric size_t __chunk = std::min(__n, __capacity_); 1225f757f3fSDimitry Andric std::copy_n(__first, __chunk, std::addressof(__ptr_[__size_])); 123bdd1243dSDimitry Andric __size_ = __chunk; 124bdd1243dSDimitry Andric __first += __chunk; 125bdd1243dSDimitry Andric __n -= __chunk; 126bdd1243dSDimitry Andric __flush(); 127bdd1243dSDimitry Andric } while (__n); 128bdd1243dSDimitry Andric } 129bdd1243dSDimitry Andric 130bdd1243dSDimitry Andric /// A std::transform wrapper. 131bdd1243dSDimitry Andric /// 132bdd1243dSDimitry Andric /// Like @ref __copy it may need to do type conversion. 133cb14a3feSDimitry Andric template <contiguous_iterator _Iterator, 134cb14a3feSDimitry Andric class _UnaryOperation, 135cb14a3feSDimitry Andric __fmt_char_type _InCharT = typename iterator_traits<_Iterator>::value_type> __transform(_Iterator __first,_Iterator __last,_UnaryOperation __operation)136cb14a3feSDimitry Andric _LIBCPP_HIDE_FROM_ABI void __transform(_Iterator __first, _Iterator __last, _UnaryOperation __operation) { 137*1db9f3b2SDimitry Andric _LIBCPP_ASSERT_INTERNAL(__first <= __last, "not a valid range"); 138bdd1243dSDimitry Andric 139bdd1243dSDimitry Andric size_t __n = static_cast<size_t>(__last - __first); 140bdd1243dSDimitry Andric __flush_on_overflow(__n); 14106c3fb27SDimitry Andric if (__n < __capacity_) { // push_back requires the buffer to have room for at least one character (so use <). 1425f757f3fSDimitry Andric std::transform(__first, __last, std::addressof(__ptr_[__size_]), std::move(__operation)); 143bdd1243dSDimitry Andric __size_ += __n; 144bdd1243dSDimitry Andric return; 145bdd1243dSDimitry Andric } 146bdd1243dSDimitry Andric 147bdd1243dSDimitry Andric // The output doesn't fit in the internal buffer. 148bdd1243dSDimitry Andric // Transform the data in "__capacity_" sized chunks. 149*1db9f3b2SDimitry Andric _LIBCPP_ASSERT_INTERNAL(__size_ == 0, "the buffer should be flushed by __flush_on_overflow"); 150bdd1243dSDimitry Andric do { 1515f757f3fSDimitry Andric size_t __chunk = std::min(__n, __capacity_); 1525f757f3fSDimitry Andric std::transform(__first, __first + __chunk, std::addressof(__ptr_[__size_]), __operation); 153bdd1243dSDimitry Andric __size_ = __chunk; 154bdd1243dSDimitry Andric __first += __chunk; 155bdd1243dSDimitry Andric __n -= __chunk; 156bdd1243dSDimitry Andric __flush(); 157bdd1243dSDimitry Andric } while (__n); 158bdd1243dSDimitry Andric } 159bdd1243dSDimitry Andric 160bdd1243dSDimitry Andric /// A \c fill_n wrapper. __fill(size_t __n,_CharT __value)161bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI void __fill(size_t __n, _CharT __value) { 162bdd1243dSDimitry Andric __flush_on_overflow(__n); 16306c3fb27SDimitry Andric if (__n < __capacity_) { // push_back requires the buffer to have room for at least one character (so use <). 1645f757f3fSDimitry Andric std::fill_n(std::addressof(__ptr_[__size_]), __n, __value); 165bdd1243dSDimitry Andric __size_ += __n; 166bdd1243dSDimitry Andric return; 167bdd1243dSDimitry Andric } 168bdd1243dSDimitry Andric 169bdd1243dSDimitry Andric // The output doesn't fit in the internal buffer. 170bdd1243dSDimitry Andric // Fill the buffer in "__capacity_" sized chunks. 171*1db9f3b2SDimitry Andric _LIBCPP_ASSERT_INTERNAL(__size_ == 0, "the buffer should be flushed by __flush_on_overflow"); 172bdd1243dSDimitry Andric do { 1735f757f3fSDimitry Andric size_t __chunk = std::min(__n, __capacity_); 1745f757f3fSDimitry Andric std::fill_n(std::addressof(__ptr_[__size_]), __chunk, __value); 175bdd1243dSDimitry Andric __size_ = __chunk; 176bdd1243dSDimitry Andric __n -= __chunk; 177bdd1243dSDimitry Andric __flush(); 178bdd1243dSDimitry Andric } while (__n); 179bdd1243dSDimitry Andric } 180bdd1243dSDimitry Andric __flush()181bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI void __flush() { 18281ad6265SDimitry Andric __flush_(__ptr_, __size_, __obj_); 18381ad6265SDimitry Andric __size_ = 0; 18481ad6265SDimitry Andric } 18581ad6265SDimitry Andric 18681ad6265SDimitry Andric private: 18781ad6265SDimitry Andric _CharT* __ptr_; 18881ad6265SDimitry Andric size_t __capacity_; 18981ad6265SDimitry Andric size_t __size_{0}; 19081ad6265SDimitry Andric void (*__flush_)(_CharT*, size_t, void*); 19181ad6265SDimitry Andric void* __obj_; 192bdd1243dSDimitry Andric 193bdd1243dSDimitry Andric /// Flushes the buffer when the output operation would overflow the buffer. 194bdd1243dSDimitry Andric /// 195bdd1243dSDimitry Andric /// A simple approach for the overflow detection would be something along the 196bdd1243dSDimitry Andric /// lines: 197bdd1243dSDimitry Andric /// \code 198bdd1243dSDimitry Andric /// // The internal buffer is large enough. 199bdd1243dSDimitry Andric /// if (__n <= __capacity_) { 200bdd1243dSDimitry Andric /// // Flush when we really would overflow. 201bdd1243dSDimitry Andric /// if (__size_ + __n >= __capacity_) 202bdd1243dSDimitry Andric /// __flush(); 203bdd1243dSDimitry Andric /// ... 204bdd1243dSDimitry Andric /// } 205bdd1243dSDimitry Andric /// \endcode 206bdd1243dSDimitry Andric /// 207bdd1243dSDimitry Andric /// This approach works for all cases but one: 208bdd1243dSDimitry Andric /// A __format_to_n_buffer_base where \ref __enable_direct_output is true. 209bdd1243dSDimitry Andric /// In that case the \ref __capacity_ of the buffer changes during the first 210bdd1243dSDimitry Andric /// \ref __flush. During that operation the output buffer switches from its 211bdd1243dSDimitry Andric /// __writer_ to its __storage_. The \ref __capacity_ of the former depends 212bdd1243dSDimitry Andric /// on the value of n, of the latter is a fixed size. For example: 213bdd1243dSDimitry Andric /// - a format_to_n call with a 10'000 char buffer, 214bdd1243dSDimitry Andric /// - the buffer is filled with 9'500 chars, 215bdd1243dSDimitry Andric /// - adding 1'000 elements would overflow the buffer so the buffer gets 216bdd1243dSDimitry Andric /// changed and the \ref __capacity_ decreases from 10'000 to 217bdd1243dSDimitry Andric /// __buffer_size (256 at the time of writing). 218bdd1243dSDimitry Andric /// 219bdd1243dSDimitry Andric /// This means that the \ref __flush for this class may need to copy a part of 220bdd1243dSDimitry Andric /// the internal buffer to the proper output. In this example there will be 221bdd1243dSDimitry Andric /// 500 characters that need this copy operation. 222bdd1243dSDimitry Andric /// 223bdd1243dSDimitry Andric /// Note it would be more efficient to write 500 chars directly and then swap 224bdd1243dSDimitry Andric /// the buffers. This would make the code more complex and \ref format_to_n is 225bdd1243dSDimitry Andric /// not the most common use case. Therefore the optimization isn't done. __flush_on_overflow(size_t __n)226bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI void __flush_on_overflow(size_t __n) { 227bdd1243dSDimitry Andric if (__size_ + __n >= __capacity_) 228bdd1243dSDimitry Andric __flush(); 229bdd1243dSDimitry Andric } 23081ad6265SDimitry Andric }; 23181ad6265SDimitry Andric 23281ad6265SDimitry Andric /// A storage using an internal buffer. 23381ad6265SDimitry Andric /// 23481ad6265SDimitry Andric /// This storage is used when writing a single element to the output iterator 23581ad6265SDimitry Andric /// is expensive. 236bdd1243dSDimitry Andric template <__fmt_char_type _CharT> 23781ad6265SDimitry Andric class _LIBCPP_TEMPLATE_VIS __internal_storage { 23881ad6265SDimitry Andric public: __begin()239bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI _CharT* __begin() { return __buffer_; } 24081ad6265SDimitry Andric 24181ad6265SDimitry Andric static constexpr size_t __buffer_size = 256 / sizeof(_CharT); 24281ad6265SDimitry Andric 24381ad6265SDimitry Andric private: 24481ad6265SDimitry Andric _CharT __buffer_[__buffer_size]; 24581ad6265SDimitry Andric }; 24681ad6265SDimitry Andric 24781ad6265SDimitry Andric /// A storage writing directly to the storage. 24881ad6265SDimitry Andric /// 24981ad6265SDimitry Andric /// This requires the storage to be a contiguous buffer of \a _CharT. 25081ad6265SDimitry Andric /// Since the output is directly written to the underlying storage this class 25181ad6265SDimitry Andric /// is just an empty class. 252bdd1243dSDimitry Andric template <__fmt_char_type _CharT> 25381ad6265SDimitry Andric class _LIBCPP_TEMPLATE_VIS __direct_storage {}; 25481ad6265SDimitry Andric 25581ad6265SDimitry Andric template <class _OutIt, class _CharT> 256cb14a3feSDimitry Andric concept __enable_direct_output = 257cb14a3feSDimitry Andric __fmt_char_type<_CharT> && 25881ad6265SDimitry Andric (same_as<_OutIt, _CharT*> 25906c3fb27SDimitry Andric // TODO(hardening): the following check might not apply to hardened iterators and might need to be wrapped in an 26006c3fb27SDimitry Andric // `#ifdef`. 261cb14a3feSDimitry Andric || same_as<_OutIt, __wrap_iter<_CharT*>>); 26281ad6265SDimitry Andric 26381ad6265SDimitry Andric /// Write policy for directly writing to the underlying output. 264bdd1243dSDimitry Andric template <class _OutIt, __fmt_char_type _CharT> 26581ad6265SDimitry Andric class _LIBCPP_TEMPLATE_VIS __writer_direct { 26681ad6265SDimitry Andric public: __writer_direct(_OutIt __out_it)267cb14a3feSDimitry Andric _LIBCPP_HIDE_FROM_ABI explicit __writer_direct(_OutIt __out_it) : __out_it_(__out_it) {} 26881ad6265SDimitry Andric __out_it()269bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI _OutIt __out_it() { return __out_it_; } 27081ad6265SDimitry Andric __flush(_CharT *,size_t __n)271bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI void __flush(_CharT*, size_t __n) { 27281ad6265SDimitry Andric // _OutIt can be a __wrap_iter<CharT*>. Therefore the original iterator 27381ad6265SDimitry Andric // is adjusted. 274bdd1243dSDimitry Andric __out_it_ += __n; 27581ad6265SDimitry Andric } 27681ad6265SDimitry Andric 27781ad6265SDimitry Andric private: 27881ad6265SDimitry Andric _OutIt __out_it_; 27981ad6265SDimitry Andric }; 28081ad6265SDimitry Andric 28181ad6265SDimitry Andric /// Write policy for copying the buffer to the output. 282bdd1243dSDimitry Andric template <class _OutIt, __fmt_char_type _CharT> 28381ad6265SDimitry Andric class _LIBCPP_TEMPLATE_VIS __writer_iterator { 28481ad6265SDimitry Andric public: __writer_iterator(_OutIt __out_it)285cb14a3feSDimitry Andric _LIBCPP_HIDE_FROM_ABI explicit __writer_iterator(_OutIt __out_it) : __out_it_{std::move(__out_it)} {} 28681ad6265SDimitry Andric __out_it()287bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI _OutIt __out_it() && { return std::move(__out_it_); } 28881ad6265SDimitry Andric __flush(_CharT * __ptr,size_t __n)289bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI void __flush(_CharT* __ptr, size_t __n) { 290bdd1243dSDimitry Andric __out_it_ = std::ranges::copy_n(__ptr, __n, std::move(__out_it_)).out; 29181ad6265SDimitry Andric } 29281ad6265SDimitry Andric 29381ad6265SDimitry Andric private: 29481ad6265SDimitry Andric _OutIt __out_it_; 29581ad6265SDimitry Andric }; 29681ad6265SDimitry Andric 29781ad6265SDimitry Andric /// Concept to see whether a \a _Container is insertable. 29881ad6265SDimitry Andric /// 29981ad6265SDimitry Andric /// The concept is used to validate whether multiple calls to a 30081ad6265SDimitry Andric /// \ref back_insert_iterator can be replace by a call to \c _Container::insert. 30181ad6265SDimitry Andric /// 30281ad6265SDimitry Andric /// \note a \a _Container needs to opt-in to the concept by specializing 30381ad6265SDimitry Andric /// \ref __enable_insertable. 30481ad6265SDimitry Andric template <class _Container> 30581ad6265SDimitry Andric concept __insertable = 306bdd1243dSDimitry Andric __enable_insertable<_Container> && __fmt_char_type<typename _Container::value_type> && requires(_Container & __t,add_pointer_t<typename _Container::value_type> __first,add_pointer_t<typename _Container::value_type> __last)307cb14a3feSDimitry Andric requires(_Container& __t, 308cb14a3feSDimitry Andric add_pointer_t<typename _Container::value_type> __first, 30981ad6265SDimitry Andric add_pointer_t<typename _Container::value_type> __last) { __t.insert(__t.end(), __first, __last); }; 31081ad6265SDimitry Andric 31181ad6265SDimitry Andric /// Extract the container type of a \ref back_insert_iterator. 31281ad6265SDimitry Andric template <class _It> 31381ad6265SDimitry Andric struct _LIBCPP_TEMPLATE_VIS __back_insert_iterator_container { 31481ad6265SDimitry Andric using type = void; 31581ad6265SDimitry Andric }; 31681ad6265SDimitry Andric 31781ad6265SDimitry Andric template <__insertable _Container> 31881ad6265SDimitry Andric struct _LIBCPP_TEMPLATE_VIS __back_insert_iterator_container<back_insert_iterator<_Container>> { 31981ad6265SDimitry Andric using type = _Container; 32081ad6265SDimitry Andric }; 32181ad6265SDimitry Andric 32281ad6265SDimitry Andric /// Write policy for inserting the buffer in a container. 32381ad6265SDimitry Andric template <class _Container> 32481ad6265SDimitry Andric class _LIBCPP_TEMPLATE_VIS __writer_container { 32581ad6265SDimitry Andric public: 32681ad6265SDimitry Andric using _CharT = typename _Container::value_type; 32781ad6265SDimitry Andric 32881ad6265SDimitry Andric _LIBCPP_HIDE_FROM_ABI explicit __writer_container(back_insert_iterator<_Container> __out_it) 32981ad6265SDimitry Andric : __container_{__out_it.__get_container()} {} 33081ad6265SDimitry Andric 331bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI auto __out_it() { return std::back_inserter(*__container_); } 33281ad6265SDimitry Andric 333bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI void __flush(_CharT* __ptr, size_t __n) { 334bdd1243dSDimitry Andric __container_->insert(__container_->end(), __ptr, __ptr + __n); 33581ad6265SDimitry Andric } 33681ad6265SDimitry Andric 33781ad6265SDimitry Andric private: 33881ad6265SDimitry Andric _Container* __container_; 33981ad6265SDimitry Andric }; 34081ad6265SDimitry Andric 34181ad6265SDimitry Andric /// Selects the type of the writer used for the output iterator. 34281ad6265SDimitry Andric template <class _OutIt, class _CharT> 34381ad6265SDimitry Andric class _LIBCPP_TEMPLATE_VIS __writer_selector { 34481ad6265SDimitry Andric using _Container = typename __back_insert_iterator_container<_OutIt>::type; 34581ad6265SDimitry Andric 34681ad6265SDimitry Andric public: 347cb14a3feSDimitry Andric using type = 348cb14a3feSDimitry Andric conditional_t<!same_as<_Container, void>, 349cb14a3feSDimitry Andric __writer_container<_Container>, 350cb14a3feSDimitry Andric conditional_t<__enable_direct_output<_OutIt, _CharT>, 351cb14a3feSDimitry Andric __writer_direct<_OutIt, _CharT>, 35281ad6265SDimitry Andric __writer_iterator<_OutIt, _CharT>>>; 35381ad6265SDimitry Andric }; 35481ad6265SDimitry Andric 35581ad6265SDimitry Andric /// The generic formatting buffer. 356bdd1243dSDimitry Andric template <class _OutIt, __fmt_char_type _CharT> 357cb14a3feSDimitry Andric requires(output_iterator<_OutIt, const _CharT&>) 358cb14a3feSDimitry Andric class _LIBCPP_TEMPLATE_VIS __format_buffer { 35981ad6265SDimitry Andric using _Storage = 360cb14a3feSDimitry Andric conditional_t<__enable_direct_output<_OutIt, _CharT>, __direct_storage<_CharT>, __internal_storage<_CharT>>; 36181ad6265SDimitry Andric 36281ad6265SDimitry Andric public: 36381ad6265SDimitry Andric _LIBCPP_HIDE_FROM_ABI explicit __format_buffer(_OutIt __out_it) 36481ad6265SDimitry Andric requires(same_as<_Storage, __internal_storage<_CharT>>) 3655f757f3fSDimitry Andric : __output_(__storage_.__begin(), __storage_.__buffer_size, this), __writer_(std::move(__out_it)) {} 36681ad6265SDimitry Andric 367cb14a3feSDimitry Andric _LIBCPP_HIDE_FROM_ABI explicit __format_buffer(_OutIt __out_it) 368cb14a3feSDimitry Andric requires(same_as<_Storage, __direct_storage<_CharT>>) 369cb14a3feSDimitry Andric : __output_(std::__unwrap_iter(__out_it), size_t(-1), this), __writer_(std::move(__out_it)) {} 37081ad6265SDimitry Andric 371bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI auto __make_output_iterator() { return __output_.__make_output_iterator(); } 37281ad6265SDimitry Andric 373bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI void __flush(_CharT* __ptr, size_t __n) { __writer_.__flush(__ptr, __n); } 37481ad6265SDimitry Andric 375bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI _OutIt __out_it() && { 376bdd1243dSDimitry Andric __output_.__flush(); 3775f757f3fSDimitry Andric return std::move(__writer_).__out_it(); 37881ad6265SDimitry Andric } 37981ad6265SDimitry Andric 38081ad6265SDimitry Andric private: 38181ad6265SDimitry Andric _LIBCPP_NO_UNIQUE_ADDRESS _Storage __storage_; 38281ad6265SDimitry Andric __output_buffer<_CharT> __output_; 38381ad6265SDimitry Andric typename __writer_selector<_OutIt, _CharT>::type __writer_; 38481ad6265SDimitry Andric }; 38581ad6265SDimitry Andric 38681ad6265SDimitry Andric /// A buffer that counts the number of insertions. 38781ad6265SDimitry Andric /// 38881ad6265SDimitry Andric /// Since \ref formatted_size only needs to know the size, the output itself is 38981ad6265SDimitry Andric /// discarded. 390bdd1243dSDimitry Andric template <__fmt_char_type _CharT> 39181ad6265SDimitry Andric class _LIBCPP_TEMPLATE_VIS __formatted_size_buffer { 39281ad6265SDimitry Andric public: 393bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI auto __make_output_iterator() { return __output_.__make_output_iterator(); } 39481ad6265SDimitry Andric 395bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI void __flush(const _CharT*, size_t __n) { __size_ += __n; } 39681ad6265SDimitry Andric 397bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI size_t __result() && { 398bdd1243dSDimitry Andric __output_.__flush(); 39981ad6265SDimitry Andric return __size_; 40081ad6265SDimitry Andric } 40181ad6265SDimitry Andric 40281ad6265SDimitry Andric private: 40381ad6265SDimitry Andric __internal_storage<_CharT> __storage_; 404bdd1243dSDimitry Andric __output_buffer<_CharT> __output_{__storage_.__begin(), __storage_.__buffer_size, this}; 40581ad6265SDimitry Andric size_t __size_{0}; 40681ad6265SDimitry Andric }; 40781ad6265SDimitry Andric 40881ad6265SDimitry Andric /// The base of a buffer that counts and limits the number of insertions. 409bdd1243dSDimitry Andric template <class _OutIt, __fmt_char_type _CharT, bool> 41081ad6265SDimitry Andric requires(output_iterator<_OutIt, const _CharT&>) 41181ad6265SDimitry Andric struct _LIBCPP_TEMPLATE_VIS __format_to_n_buffer_base { 41281ad6265SDimitry Andric using _Size = iter_difference_t<_OutIt>; 41381ad6265SDimitry Andric 41481ad6265SDimitry Andric public: 415bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI explicit __format_to_n_buffer_base(_OutIt __out_it, _Size __max_size) 4165f757f3fSDimitry Andric : __writer_(std::move(__out_it)), __max_size_(std::max(_Size(0), __max_size)) {} 41781ad6265SDimitry Andric 418bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI void __flush(_CharT* __ptr, size_t __n) { 419bdd1243dSDimitry Andric if (_Size(__size_) <= __max_size_) 4205f757f3fSDimitry Andric __writer_.__flush(__ptr, std::min(_Size(__n), __max_size_ - __size_)); 421bdd1243dSDimitry Andric __size_ += __n; 42281ad6265SDimitry Andric } 42381ad6265SDimitry Andric 42481ad6265SDimitry Andric protected: 42581ad6265SDimitry Andric __internal_storage<_CharT> __storage_; 426bdd1243dSDimitry Andric __output_buffer<_CharT> __output_{__storage_.__begin(), __storage_.__buffer_size, this}; 42781ad6265SDimitry Andric typename __writer_selector<_OutIt, _CharT>::type __writer_; 42881ad6265SDimitry Andric 429bdd1243dSDimitry Andric _Size __max_size_; 43081ad6265SDimitry Andric _Size __size_{0}; 43181ad6265SDimitry Andric }; 43281ad6265SDimitry Andric 43381ad6265SDimitry Andric /// The base of a buffer that counts and limits the number of insertions. 43481ad6265SDimitry Andric /// 43581ad6265SDimitry Andric /// This version is used when \c __enable_direct_output<_OutIt, _CharT> == true. 43681ad6265SDimitry Andric /// 437bdd1243dSDimitry Andric /// This class limits the size available to the direct writer so it will not 43881ad6265SDimitry Andric /// exceed the maximum number of code units. 439bdd1243dSDimitry Andric template <class _OutIt, __fmt_char_type _CharT> 44081ad6265SDimitry Andric requires(output_iterator<_OutIt, const _CharT&>) 44181ad6265SDimitry Andric class _LIBCPP_TEMPLATE_VIS __format_to_n_buffer_base<_OutIt, _CharT, true> { 44281ad6265SDimitry Andric using _Size = iter_difference_t<_OutIt>; 44381ad6265SDimitry Andric 44481ad6265SDimitry Andric public: 445bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI explicit __format_to_n_buffer_base(_OutIt __out_it, _Size __max_size) 4465f757f3fSDimitry Andric : __output_(std::__unwrap_iter(__out_it), __max_size, this), 4475f757f3fSDimitry Andric __writer_(std::move(__out_it)), 448bdd1243dSDimitry Andric __max_size_(__max_size) { 449bdd1243dSDimitry Andric if (__max_size <= 0) [[unlikely]] 450bdd1243dSDimitry Andric __output_.__reset(__storage_.__begin(), __storage_.__buffer_size); 45181ad6265SDimitry Andric } 45281ad6265SDimitry Andric 453bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI void __flush(_CharT* __ptr, size_t __n) { 454bdd1243dSDimitry Andric // A __flush to the direct writer happens in the following occasions: 45581ad6265SDimitry Andric // - The format function has written the maximum number of allowed code 45681ad6265SDimitry Andric // units. At this point it's no longer valid to write to this writer. So 45781ad6265SDimitry Andric // switch to the internal storage. This internal storage doesn't need to 458bdd1243dSDimitry Andric // be written anywhere so the __flush for that storage writes no output. 459bdd1243dSDimitry Andric // - Like above, but the next "mass write" operation would overflow the 460bdd1243dSDimitry Andric // buffer. In that case the buffer is pre-emptively switched. The still 461bdd1243dSDimitry Andric // valid code units will be written separately. 46281ad6265SDimitry Andric // - The format_to_n function is finished. In this case there's no need to 46381ad6265SDimitry Andric // switch the buffer, but for simplicity the buffers are still switched. 464bdd1243dSDimitry Andric // When the __max_size <= 0 the constructor already switched the buffers. 465bdd1243dSDimitry Andric if (__size_ == 0 && __ptr != __storage_.__begin()) { 466bdd1243dSDimitry Andric __writer_.__flush(__ptr, __n); 467bdd1243dSDimitry Andric __output_.__reset(__storage_.__begin(), __storage_.__buffer_size); 468bdd1243dSDimitry Andric } else if (__size_ < __max_size_) { 469bdd1243dSDimitry Andric // Copies a part of the internal buffer to the output up to n characters. 470bdd1243dSDimitry Andric // See __output_buffer<_CharT>::__flush_on_overflow for more information. 4715f757f3fSDimitry Andric _Size __s = std::min(_Size(__n), __max_size_ - __size_); 472bdd1243dSDimitry Andric std::copy_n(__ptr, __s, __writer_.__out_it()); 473bdd1243dSDimitry Andric __writer_.__flush(__ptr, __s); 47481ad6265SDimitry Andric } 47581ad6265SDimitry Andric 476bdd1243dSDimitry Andric __size_ += __n; 47781ad6265SDimitry Andric } 47881ad6265SDimitry Andric 47981ad6265SDimitry Andric protected: 48081ad6265SDimitry Andric __internal_storage<_CharT> __storage_; 48181ad6265SDimitry Andric __output_buffer<_CharT> __output_; 48281ad6265SDimitry Andric __writer_direct<_OutIt, _CharT> __writer_; 48381ad6265SDimitry Andric 484bdd1243dSDimitry Andric _Size __max_size_; 48581ad6265SDimitry Andric _Size __size_{0}; 48681ad6265SDimitry Andric }; 48781ad6265SDimitry Andric 48881ad6265SDimitry Andric /// The buffer that counts and limits the number of insertions. 489bdd1243dSDimitry Andric template <class _OutIt, __fmt_char_type _CharT> 49081ad6265SDimitry Andric requires(output_iterator<_OutIt, const _CharT&>) 49181ad6265SDimitry Andric struct _LIBCPP_TEMPLATE_VIS __format_to_n_buffer final 49281ad6265SDimitry Andric : public __format_to_n_buffer_base< _OutIt, _CharT, __enable_direct_output<_OutIt, _CharT>> { 49381ad6265SDimitry Andric using _Base = __format_to_n_buffer_base<_OutIt, _CharT, __enable_direct_output<_OutIt, _CharT>>; 49481ad6265SDimitry Andric using _Size = iter_difference_t<_OutIt>; 49581ad6265SDimitry Andric 49681ad6265SDimitry Andric public: 497bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI explicit __format_to_n_buffer(_OutIt __out_it, _Size __max_size) 4985f757f3fSDimitry Andric : _Base(std::move(__out_it), __max_size) {} 499bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI auto __make_output_iterator() { return this->__output_.__make_output_iterator(); } 50081ad6265SDimitry Andric 501bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI format_to_n_result<_OutIt> __result() && { 502bdd1243dSDimitry Andric this->__output_.__flush(); 5035f757f3fSDimitry Andric return {std::move(this->__writer_).__out_it(), this->__size_}; 50481ad6265SDimitry Andric } 50581ad6265SDimitry Andric }; 506bdd1243dSDimitry Andric 507bdd1243dSDimitry Andric // A dynamically growing buffer intended to be used for retargeting a context. 508bdd1243dSDimitry Andric // 509bdd1243dSDimitry Andric // P2286 Formatting ranges adds range formatting support. It allows the user to 510bdd1243dSDimitry Andric // specify the minimum width for the entire formatted range. The width of the 511bdd1243dSDimitry Andric // range is not known until the range is formatted. Formatting is done to an 512bdd1243dSDimitry Andric // output_iterator so there's no guarantee it would be possible to add the fill 513bdd1243dSDimitry Andric // to the front of the output. Instead the range is formatted to a temporary 514bdd1243dSDimitry Andric // buffer and that buffer is formatted as a string. 515bdd1243dSDimitry Andric // 516bdd1243dSDimitry Andric // There is an issue with that approach, the format context used in 517bdd1243dSDimitry Andric // std::formatter<T>::format contains the output iterator used as part of its 518bdd1243dSDimitry Andric // type. So using this output iterator means there needs to be a new format 519bdd1243dSDimitry Andric // context and the format arguments need to be retargeted to the new context. 520bdd1243dSDimitry Andric // This retargeting is done by a basic_format_context specialized for the 521bdd1243dSDimitry Andric // __iterator of this container. 52206c3fb27SDimitry Andric // 52306c3fb27SDimitry Andric // This class uses its own buffer management, since using vector 52406c3fb27SDimitry Andric // would lead to a circular include with formatter for vector<bool>. 525bdd1243dSDimitry Andric template <__fmt_char_type _CharT> 526bdd1243dSDimitry Andric class _LIBCPP_TEMPLATE_VIS __retarget_buffer { 52706c3fb27SDimitry Andric using _Alloc = allocator<_CharT>; 52806c3fb27SDimitry Andric 529bdd1243dSDimitry Andric public: 530bdd1243dSDimitry Andric using value_type = _CharT; 531bdd1243dSDimitry Andric 532bdd1243dSDimitry Andric struct __iterator { 533bdd1243dSDimitry Andric using difference_type = ptrdiff_t; 53406c3fb27SDimitry Andric using value_type = _CharT; 535bdd1243dSDimitry Andric 536bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(__retarget_buffer& __buffer) 537bdd1243dSDimitry Andric : __buffer_(std::addressof(__buffer)) {} 538bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator=(const _CharT& __c) { 539bdd1243dSDimitry Andric __buffer_->push_back(__c); 540bdd1243dSDimitry Andric return *this; 541bdd1243dSDimitry Andric } 542bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator=(_CharT&& __c) { 543bdd1243dSDimitry Andric __buffer_->push_back(__c); 544bdd1243dSDimitry Andric return *this; 545bdd1243dSDimitry Andric } 546bdd1243dSDimitry Andric 547bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator*() { return *this; } 548bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() { return *this; } 549bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int) { return *this; } 550bdd1243dSDimitry Andric __retarget_buffer* __buffer_; 551bdd1243dSDimitry Andric }; 552bdd1243dSDimitry Andric 55306c3fb27SDimitry Andric __retarget_buffer(const __retarget_buffer&) = delete; 55406c3fb27SDimitry Andric __retarget_buffer& operator=(const __retarget_buffer&) = delete; 55506c3fb27SDimitry Andric 55606c3fb27SDimitry Andric _LIBCPP_HIDE_FROM_ABI explicit __retarget_buffer(size_t __size_hint) { 55706c3fb27SDimitry Andric // When the initial size is very small a lot of resizes happen 55806c3fb27SDimitry Andric // when elements added. So use a hard-coded minimum size. 55906c3fb27SDimitry Andric // 56006c3fb27SDimitry Andric // Note a size < 2 will not work 56106c3fb27SDimitry Andric // - 0 there is no buffer, while push_back requires 1 empty element. 56206c3fb27SDimitry Andric // - 1 multiplied by the grow factor is 1 and thus the buffer never 56306c3fb27SDimitry Andric // grows. 56406c3fb27SDimitry Andric auto __result = std::__allocate_at_least(__alloc_, std::max(__size_hint, 256 / sizeof(_CharT))); 56506c3fb27SDimitry Andric __ptr_ = __result.ptr; 56606c3fb27SDimitry Andric __capacity_ = __result.count; 56706c3fb27SDimitry Andric } 56806c3fb27SDimitry Andric 56906c3fb27SDimitry Andric _LIBCPP_HIDE_FROM_ABI ~__retarget_buffer() { 57006c3fb27SDimitry Andric ranges::destroy_n(__ptr_, __size_); 57106c3fb27SDimitry Andric allocator_traits<_Alloc>::deallocate(__alloc_, __ptr_, __capacity_); 57206c3fb27SDimitry Andric } 573bdd1243dSDimitry Andric 574bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI __iterator __make_output_iterator() { return __iterator{*this}; } 575bdd1243dSDimitry Andric 57606c3fb27SDimitry Andric _LIBCPP_HIDE_FROM_ABI void push_back(_CharT __c) { 57706c3fb27SDimitry Andric std::construct_at(__ptr_ + __size_, __c); 57806c3fb27SDimitry Andric ++__size_; 57906c3fb27SDimitry Andric 58006c3fb27SDimitry Andric if (__size_ == __capacity_) 58106c3fb27SDimitry Andric __grow_buffer(); 58206c3fb27SDimitry Andric } 583bdd1243dSDimitry Andric 584bdd1243dSDimitry Andric template <__fmt_char_type _InCharT> 585bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI void __copy(basic_string_view<_InCharT> __str) { 58606c3fb27SDimitry Andric size_t __n = __str.size(); 58706c3fb27SDimitry Andric if (__size_ + __n >= __capacity_) 58806c3fb27SDimitry Andric // Push_back requires the buffer to have room for at least one character. 58906c3fb27SDimitry Andric __grow_buffer(__size_ + __n + 1); 59006c3fb27SDimitry Andric 59106c3fb27SDimitry Andric std::uninitialized_copy_n(__str.data(), __n, __ptr_ + __size_); 59206c3fb27SDimitry Andric __size_ += __n; 593bdd1243dSDimitry Andric } 594bdd1243dSDimitry Andric 595cb14a3feSDimitry Andric template <contiguous_iterator _Iterator, 596cb14a3feSDimitry Andric class _UnaryOperation, 597cb14a3feSDimitry Andric __fmt_char_type _InCharT = typename iterator_traits<_Iterator>::value_type> 598cb14a3feSDimitry Andric _LIBCPP_HIDE_FROM_ABI void __transform(_Iterator __first, _Iterator __last, _UnaryOperation __operation) { 599*1db9f3b2SDimitry Andric _LIBCPP_ASSERT_INTERNAL(__first <= __last, "not a valid range"); 60006c3fb27SDimitry Andric 60106c3fb27SDimitry Andric size_t __n = static_cast<size_t>(__last - __first); 60206c3fb27SDimitry Andric if (__size_ + __n >= __capacity_) 60306c3fb27SDimitry Andric // Push_back requires the buffer to have room for at least one character. 60406c3fb27SDimitry Andric __grow_buffer(__size_ + __n + 1); 60506c3fb27SDimitry Andric 60606c3fb27SDimitry Andric std::uninitialized_default_construct_n(__ptr_ + __size_, __n); 60706c3fb27SDimitry Andric std::transform(__first, __last, __ptr_ + __size_, std::move(__operation)); 60806c3fb27SDimitry Andric __size_ += __n; 609bdd1243dSDimitry Andric } 610bdd1243dSDimitry Andric 61106c3fb27SDimitry Andric _LIBCPP_HIDE_FROM_ABI void __fill(size_t __n, _CharT __value) { 61206c3fb27SDimitry Andric if (__size_ + __n >= __capacity_) 61306c3fb27SDimitry Andric // Push_back requires the buffer to have room for at least one character. 61406c3fb27SDimitry Andric __grow_buffer(__size_ + __n + 1); 615bdd1243dSDimitry Andric 61606c3fb27SDimitry Andric std::uninitialized_fill_n(__ptr_ + __size_, __n, __value); 61706c3fb27SDimitry Andric __size_ += __n; 61806c3fb27SDimitry Andric } 61906c3fb27SDimitry Andric 62006c3fb27SDimitry Andric _LIBCPP_HIDE_FROM_ABI basic_string_view<_CharT> __view() { return {__ptr_, __size_}; } 621bdd1243dSDimitry Andric 622bdd1243dSDimitry Andric private: 62306c3fb27SDimitry Andric _LIBCPP_HIDE_FROM_ABI void __grow_buffer() { __grow_buffer(__capacity_ * 1.6); } 62406c3fb27SDimitry Andric 62506c3fb27SDimitry Andric _LIBCPP_HIDE_FROM_ABI void __grow_buffer(size_t __capacity) { 626*1db9f3b2SDimitry Andric _LIBCPP_ASSERT_INTERNAL(__capacity > __capacity_, "the buffer must grow"); 62706c3fb27SDimitry Andric auto __result = std::__allocate_at_least(__alloc_, __capacity); 62806c3fb27SDimitry Andric auto __guard = std::__make_exception_guard([&] { 62906c3fb27SDimitry Andric allocator_traits<_Alloc>::deallocate(__alloc_, __result.ptr, __result.count); 63006c3fb27SDimitry Andric }); 63106c3fb27SDimitry Andric // This shouldn't throw, but just to be safe. Note that at -O1 this 63206c3fb27SDimitry Andric // guard is optimized away so there is no runtime overhead. 63306c3fb27SDimitry Andric std::uninitialized_move_n(__ptr_, __size_, __result.ptr); 63406c3fb27SDimitry Andric __guard.__complete(); 63506c3fb27SDimitry Andric ranges::destroy_n(__ptr_, __size_); 63606c3fb27SDimitry Andric allocator_traits<_Alloc>::deallocate(__alloc_, __ptr_, __capacity_); 63706c3fb27SDimitry Andric 63806c3fb27SDimitry Andric __ptr_ = __result.ptr; 63906c3fb27SDimitry Andric __capacity_ = __result.count; 64006c3fb27SDimitry Andric } 64106c3fb27SDimitry Andric _LIBCPP_NO_UNIQUE_ADDRESS _Alloc __alloc_; 64206c3fb27SDimitry Andric _CharT* __ptr_; 64306c3fb27SDimitry Andric size_t __capacity_; 64406c3fb27SDimitry Andric size_t __size_{0}; 645bdd1243dSDimitry Andric }; 646bdd1243dSDimitry Andric 64781ad6265SDimitry Andric } // namespace __format 64881ad6265SDimitry Andric 64906c3fb27SDimitry Andric #endif //_LIBCPP_STD_VER >= 20 65081ad6265SDimitry Andric 65181ad6265SDimitry Andric _LIBCPP_END_NAMESPACE_STD 65281ad6265SDimitry Andric 65381ad6265SDimitry Andric _LIBCPP_POP_MACROS 65481ad6265SDimitry Andric 65581ad6265SDimitry Andric #endif // _LIBCPP___FORMAT_BUFFER_H 656