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> 30*06c3fb27SDimitry Andric #include <__memory/addressof.h> 31*06c3fb27SDimitry Andric #include <__memory/allocate_at_least.h> 32*06c3fb27SDimitry Andric #include <__memory/allocator_traits.h> 33*06c3fb27SDimitry Andric #include <__memory/construct_at.h> 34*06c3fb27SDimitry Andric #include <__memory/ranges_construct_at.h> 35*06c3fb27SDimitry Andric #include <__memory/uninitialized_algorithms.h> 36*06c3fb27SDimitry Andric #include <__type_traits/add_pointer.h> 37*06c3fb27SDimitry Andric #include <__type_traits/conditional.h> 38*06c3fb27SDimitry 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 52*06c3fb27SDimitry 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> 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 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 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. 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> 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 98bdd1243dSDimitry Andric // adding a large string the 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); 110*06c3fb27SDimitry Andric if (__n < __capacity_) { // push_back requires the buffer to have room for at least one character (so use <). 111bdd1243dSDimitry Andric _VSTD::copy_n(__str.data(), __n, _VSTD::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*06c3fb27SDimitry Andric _LIBCPP_ASSERT_UNCATEGORIZED(__size_ == 0, "the buffer should be flushed by __flush_on_overflow"); 119bdd1243dSDimitry Andric const _InCharT* __first = __str.data(); 120bdd1243dSDimitry Andric do { 121bdd1243dSDimitry Andric size_t __chunk = _VSTD::min(__n, __capacity_); 122bdd1243dSDimitry Andric _VSTD::copy_n(__first, __chunk, _VSTD::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. 133bdd1243dSDimitry Andric template <__fmt_char_type _InCharT, class _UnaryOperation> 134bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI void __transform(const _InCharT* __first, const _InCharT* __last, _UnaryOperation __operation) { 135*06c3fb27SDimitry Andric _LIBCPP_ASSERT_UNCATEGORIZED(__first <= __last, "not a valid range"); 136bdd1243dSDimitry Andric 137bdd1243dSDimitry Andric size_t __n = static_cast<size_t>(__last - __first); 138bdd1243dSDimitry Andric __flush_on_overflow(__n); 139*06c3fb27SDimitry Andric if (__n < __capacity_) { // push_back requires the buffer to have room for at least one character (so use <). 140bdd1243dSDimitry Andric _VSTD::transform(__first, __last, _VSTD::addressof(__ptr_[__size_]), _VSTD::move(__operation)); 141bdd1243dSDimitry Andric __size_ += __n; 142bdd1243dSDimitry Andric return; 143bdd1243dSDimitry Andric } 144bdd1243dSDimitry Andric 145bdd1243dSDimitry Andric // The output doesn't fit in the internal buffer. 146bdd1243dSDimitry Andric // Transform the data in "__capacity_" sized chunks. 147*06c3fb27SDimitry Andric _LIBCPP_ASSERT_UNCATEGORIZED(__size_ == 0, "the buffer should be flushed by __flush_on_overflow"); 148bdd1243dSDimitry Andric do { 149bdd1243dSDimitry Andric size_t __chunk = _VSTD::min(__n, __capacity_); 150bdd1243dSDimitry Andric _VSTD::transform(__first, __first + __chunk, _VSTD::addressof(__ptr_[__size_]), __operation); 151bdd1243dSDimitry Andric __size_ = __chunk; 152bdd1243dSDimitry Andric __first += __chunk; 153bdd1243dSDimitry Andric __n -= __chunk; 154bdd1243dSDimitry Andric __flush(); 155bdd1243dSDimitry Andric } while (__n); 156bdd1243dSDimitry Andric } 157bdd1243dSDimitry Andric 158bdd1243dSDimitry Andric /// A \c fill_n wrapper. 159bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI void __fill(size_t __n, _CharT __value) { 160bdd1243dSDimitry Andric __flush_on_overflow(__n); 161*06c3fb27SDimitry Andric if (__n < __capacity_) { // push_back requires the buffer to have room for at least one character (so use <). 162bdd1243dSDimitry Andric _VSTD::fill_n(_VSTD::addressof(__ptr_[__size_]), __n, __value); 163bdd1243dSDimitry Andric __size_ += __n; 164bdd1243dSDimitry Andric return; 165bdd1243dSDimitry Andric } 166bdd1243dSDimitry Andric 167bdd1243dSDimitry Andric // The output doesn't fit in the internal buffer. 168bdd1243dSDimitry Andric // Fill the buffer in "__capacity_" sized chunks. 169*06c3fb27SDimitry Andric _LIBCPP_ASSERT_UNCATEGORIZED(__size_ == 0, "the buffer should be flushed by __flush_on_overflow"); 170bdd1243dSDimitry Andric do { 171bdd1243dSDimitry Andric size_t __chunk = _VSTD::min(__n, __capacity_); 172bdd1243dSDimitry Andric _VSTD::fill_n(_VSTD::addressof(__ptr_[__size_]), __chunk, __value); 173bdd1243dSDimitry Andric __size_ = __chunk; 174bdd1243dSDimitry Andric __n -= __chunk; 175bdd1243dSDimitry Andric __flush(); 176bdd1243dSDimitry Andric } while (__n); 177bdd1243dSDimitry Andric } 178bdd1243dSDimitry Andric 179bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI void __flush() { 18081ad6265SDimitry Andric __flush_(__ptr_, __size_, __obj_); 18181ad6265SDimitry Andric __size_ = 0; 18281ad6265SDimitry Andric } 18381ad6265SDimitry Andric 18481ad6265SDimitry Andric private: 18581ad6265SDimitry Andric _CharT* __ptr_; 18681ad6265SDimitry Andric size_t __capacity_; 18781ad6265SDimitry Andric size_t __size_{0}; 18881ad6265SDimitry Andric void (*__flush_)(_CharT*, size_t, void*); 18981ad6265SDimitry Andric void* __obj_; 190bdd1243dSDimitry Andric 191bdd1243dSDimitry Andric /// Flushes the buffer when the output operation would overflow the buffer. 192bdd1243dSDimitry Andric /// 193bdd1243dSDimitry Andric /// A simple approach for the overflow detection would be something along the 194bdd1243dSDimitry Andric /// lines: 195bdd1243dSDimitry Andric /// \code 196bdd1243dSDimitry Andric /// // The internal buffer is large enough. 197bdd1243dSDimitry Andric /// if (__n <= __capacity_) { 198bdd1243dSDimitry Andric /// // Flush when we really would overflow. 199bdd1243dSDimitry Andric /// if (__size_ + __n >= __capacity_) 200bdd1243dSDimitry Andric /// __flush(); 201bdd1243dSDimitry Andric /// ... 202bdd1243dSDimitry Andric /// } 203bdd1243dSDimitry Andric /// \endcode 204bdd1243dSDimitry Andric /// 205bdd1243dSDimitry Andric /// This approach works for all cases but one: 206bdd1243dSDimitry Andric /// A __format_to_n_buffer_base where \ref __enable_direct_output is true. 207bdd1243dSDimitry Andric /// In that case the \ref __capacity_ of the buffer changes during the first 208bdd1243dSDimitry Andric /// \ref __flush. During that operation the output buffer switches from its 209bdd1243dSDimitry Andric /// __writer_ to its __storage_. The \ref __capacity_ of the former depends 210bdd1243dSDimitry Andric /// on the value of n, of the latter is a fixed size. For example: 211bdd1243dSDimitry Andric /// - a format_to_n call with a 10'000 char buffer, 212bdd1243dSDimitry Andric /// - the buffer is filled with 9'500 chars, 213bdd1243dSDimitry Andric /// - adding 1'000 elements would overflow the buffer so the buffer gets 214bdd1243dSDimitry Andric /// changed and the \ref __capacity_ decreases from 10'000 to 215bdd1243dSDimitry Andric /// __buffer_size (256 at the time of writing). 216bdd1243dSDimitry Andric /// 217bdd1243dSDimitry Andric /// This means that the \ref __flush for this class may need to copy a part of 218bdd1243dSDimitry Andric /// the internal buffer to the proper output. In this example there will be 219bdd1243dSDimitry Andric /// 500 characters that need this copy operation. 220bdd1243dSDimitry Andric /// 221bdd1243dSDimitry Andric /// Note it would be more efficient to write 500 chars directly and then swap 222bdd1243dSDimitry Andric /// the buffers. This would make the code more complex and \ref format_to_n is 223bdd1243dSDimitry Andric /// not the most common use case. Therefore the optimization isn't done. 224bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI void __flush_on_overflow(size_t __n) { 225bdd1243dSDimitry Andric if (__size_ + __n >= __capacity_) 226bdd1243dSDimitry Andric __flush(); 227bdd1243dSDimitry Andric } 22881ad6265SDimitry Andric }; 22981ad6265SDimitry Andric 23081ad6265SDimitry Andric /// A storage using an internal buffer. 23181ad6265SDimitry Andric /// 23281ad6265SDimitry Andric /// This storage is used when writing a single element to the output iterator 23381ad6265SDimitry Andric /// is expensive. 234bdd1243dSDimitry Andric template <__fmt_char_type _CharT> 23581ad6265SDimitry Andric class _LIBCPP_TEMPLATE_VIS __internal_storage { 23681ad6265SDimitry Andric public: 237bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI _CharT* __begin() { return __buffer_; } 23881ad6265SDimitry Andric 23981ad6265SDimitry Andric static constexpr size_t __buffer_size = 256 / sizeof(_CharT); 24081ad6265SDimitry Andric 24181ad6265SDimitry Andric private: 24281ad6265SDimitry Andric _CharT __buffer_[__buffer_size]; 24381ad6265SDimitry Andric }; 24481ad6265SDimitry Andric 24581ad6265SDimitry Andric /// A storage writing directly to the storage. 24681ad6265SDimitry Andric /// 24781ad6265SDimitry Andric /// This requires the storage to be a contiguous buffer of \a _CharT. 24881ad6265SDimitry Andric /// Since the output is directly written to the underlying storage this class 24981ad6265SDimitry Andric /// is just an empty class. 250bdd1243dSDimitry Andric template <__fmt_char_type _CharT> 25181ad6265SDimitry Andric class _LIBCPP_TEMPLATE_VIS __direct_storage {}; 25281ad6265SDimitry Andric 25381ad6265SDimitry Andric template <class _OutIt, class _CharT> 254bdd1243dSDimitry Andric concept __enable_direct_output = __fmt_char_type<_CharT> && 25581ad6265SDimitry Andric (same_as<_OutIt, _CharT*> 256*06c3fb27SDimitry Andric // TODO(hardening): the following check might not apply to hardened iterators and might need to be wrapped in an 257*06c3fb27SDimitry Andric // `#ifdef`. 25881ad6265SDimitry Andric || same_as<_OutIt, __wrap_iter<_CharT*>> 25981ad6265SDimitry Andric ); 26081ad6265SDimitry Andric 26181ad6265SDimitry Andric /// Write policy for directly writing to the underlying output. 262bdd1243dSDimitry Andric template <class _OutIt, __fmt_char_type _CharT> 26381ad6265SDimitry Andric class _LIBCPP_TEMPLATE_VIS __writer_direct { 26481ad6265SDimitry Andric public: 26581ad6265SDimitry Andric _LIBCPP_HIDE_FROM_ABI explicit __writer_direct(_OutIt __out_it) 26681ad6265SDimitry Andric : __out_it_(__out_it) {} 26781ad6265SDimitry Andric 268bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI _OutIt __out_it() { return __out_it_; } 26981ad6265SDimitry Andric 270bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI void __flush(_CharT*, size_t __n) { 27181ad6265SDimitry Andric // _OutIt can be a __wrap_iter<CharT*>. Therefore the original iterator 27281ad6265SDimitry Andric // is adjusted. 273bdd1243dSDimitry Andric __out_it_ += __n; 27481ad6265SDimitry Andric } 27581ad6265SDimitry Andric 27681ad6265SDimitry Andric private: 27781ad6265SDimitry Andric _OutIt __out_it_; 27881ad6265SDimitry Andric }; 27981ad6265SDimitry Andric 28081ad6265SDimitry Andric /// Write policy for copying the buffer to the output. 281bdd1243dSDimitry Andric template <class _OutIt, __fmt_char_type _CharT> 28281ad6265SDimitry Andric class _LIBCPP_TEMPLATE_VIS __writer_iterator { 28381ad6265SDimitry Andric public: 28481ad6265SDimitry Andric _LIBCPP_HIDE_FROM_ABI explicit __writer_iterator(_OutIt __out_it) 28581ad6265SDimitry Andric : __out_it_{_VSTD::move(__out_it)} {} 28681ad6265SDimitry Andric 287bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI _OutIt __out_it() && { return std::move(__out_it_); } 28881ad6265SDimitry Andric 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> && 30781ad6265SDimitry Andric requires(_Container& __t, add_pointer_t<typename _Container::value_type> __first, 30881ad6265SDimitry Andric add_pointer_t<typename _Container::value_type> __last) { __t.insert(__t.end(), __first, __last); }; 30981ad6265SDimitry Andric 31081ad6265SDimitry Andric /// Extract the container type of a \ref back_insert_iterator. 31181ad6265SDimitry Andric template <class _It> 31281ad6265SDimitry Andric struct _LIBCPP_TEMPLATE_VIS __back_insert_iterator_container { 31381ad6265SDimitry Andric using type = void; 31481ad6265SDimitry Andric }; 31581ad6265SDimitry Andric 31681ad6265SDimitry Andric template <__insertable _Container> 31781ad6265SDimitry Andric struct _LIBCPP_TEMPLATE_VIS __back_insert_iterator_container<back_insert_iterator<_Container>> { 31881ad6265SDimitry Andric using type = _Container; 31981ad6265SDimitry Andric }; 32081ad6265SDimitry Andric 32181ad6265SDimitry Andric /// Write policy for inserting the buffer in a container. 32281ad6265SDimitry Andric template <class _Container> 32381ad6265SDimitry Andric class _LIBCPP_TEMPLATE_VIS __writer_container { 32481ad6265SDimitry Andric public: 32581ad6265SDimitry Andric using _CharT = typename _Container::value_type; 32681ad6265SDimitry Andric 32781ad6265SDimitry Andric _LIBCPP_HIDE_FROM_ABI explicit __writer_container(back_insert_iterator<_Container> __out_it) 32881ad6265SDimitry Andric : __container_{__out_it.__get_container()} {} 32981ad6265SDimitry Andric 330bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI auto __out_it() { return std::back_inserter(*__container_); } 33181ad6265SDimitry Andric 332bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI void __flush(_CharT* __ptr, size_t __n) { 333bdd1243dSDimitry Andric __container_->insert(__container_->end(), __ptr, __ptr + __n); 33481ad6265SDimitry Andric } 33581ad6265SDimitry Andric 33681ad6265SDimitry Andric private: 33781ad6265SDimitry Andric _Container* __container_; 33881ad6265SDimitry Andric }; 33981ad6265SDimitry Andric 34081ad6265SDimitry Andric /// Selects the type of the writer used for the output iterator. 34181ad6265SDimitry Andric template <class _OutIt, class _CharT> 34281ad6265SDimitry Andric class _LIBCPP_TEMPLATE_VIS __writer_selector { 34381ad6265SDimitry Andric using _Container = typename __back_insert_iterator_container<_OutIt>::type; 34481ad6265SDimitry Andric 34581ad6265SDimitry Andric public: 34681ad6265SDimitry Andric using type = conditional_t<!same_as<_Container, void>, __writer_container<_Container>, 34781ad6265SDimitry Andric conditional_t<__enable_direct_output<_OutIt, _CharT>, __writer_direct<_OutIt, _CharT>, 34881ad6265SDimitry Andric __writer_iterator<_OutIt, _CharT>>>; 34981ad6265SDimitry Andric }; 35081ad6265SDimitry Andric 35181ad6265SDimitry Andric /// The generic formatting buffer. 352bdd1243dSDimitry Andric template <class _OutIt, __fmt_char_type _CharT> 35381ad6265SDimitry Andric requires(output_iterator<_OutIt, const _CharT&>) class _LIBCPP_TEMPLATE_VIS 35481ad6265SDimitry Andric __format_buffer { 35581ad6265SDimitry Andric using _Storage = 35681ad6265SDimitry Andric conditional_t<__enable_direct_output<_OutIt, _CharT>, 35781ad6265SDimitry Andric __direct_storage<_CharT>, __internal_storage<_CharT>>; 35881ad6265SDimitry Andric 35981ad6265SDimitry Andric public: 36081ad6265SDimitry Andric _LIBCPP_HIDE_FROM_ABI explicit __format_buffer(_OutIt __out_it) 36181ad6265SDimitry Andric requires(same_as<_Storage, __internal_storage<_CharT>>) 362bdd1243dSDimitry Andric : __output_(__storage_.__begin(), __storage_.__buffer_size, this), __writer_(_VSTD::move(__out_it)) {} 36381ad6265SDimitry Andric 36481ad6265SDimitry Andric _LIBCPP_HIDE_FROM_ABI explicit __format_buffer(_OutIt __out_it) requires( 36581ad6265SDimitry Andric same_as<_Storage, __direct_storage<_CharT>>) 36681ad6265SDimitry Andric : __output_(_VSTD::__unwrap_iter(__out_it), size_t(-1), this), 36781ad6265SDimitry Andric __writer_(_VSTD::move(__out_it)) {} 36881ad6265SDimitry Andric 369bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI auto __make_output_iterator() { return __output_.__make_output_iterator(); } 37081ad6265SDimitry Andric 371bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI void __flush(_CharT* __ptr, size_t __n) { __writer_.__flush(__ptr, __n); } 37281ad6265SDimitry Andric 373bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI _OutIt __out_it() && { 374bdd1243dSDimitry Andric __output_.__flush(); 375bdd1243dSDimitry Andric return _VSTD::move(__writer_).__out_it(); 37681ad6265SDimitry Andric } 37781ad6265SDimitry Andric 37881ad6265SDimitry Andric private: 37981ad6265SDimitry Andric _LIBCPP_NO_UNIQUE_ADDRESS _Storage __storage_; 38081ad6265SDimitry Andric __output_buffer<_CharT> __output_; 38181ad6265SDimitry Andric typename __writer_selector<_OutIt, _CharT>::type __writer_; 38281ad6265SDimitry Andric }; 38381ad6265SDimitry Andric 38481ad6265SDimitry Andric /// A buffer that counts the number of insertions. 38581ad6265SDimitry Andric /// 38681ad6265SDimitry Andric /// Since \ref formatted_size only needs to know the size, the output itself is 38781ad6265SDimitry Andric /// discarded. 388bdd1243dSDimitry Andric template <__fmt_char_type _CharT> 38981ad6265SDimitry Andric class _LIBCPP_TEMPLATE_VIS __formatted_size_buffer { 39081ad6265SDimitry Andric public: 391bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI auto __make_output_iterator() { return __output_.__make_output_iterator(); } 39281ad6265SDimitry Andric 393bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI void __flush(const _CharT*, size_t __n) { __size_ += __n; } 39481ad6265SDimitry Andric 395bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI size_t __result() && { 396bdd1243dSDimitry Andric __output_.__flush(); 39781ad6265SDimitry Andric return __size_; 39881ad6265SDimitry Andric } 39981ad6265SDimitry Andric 40081ad6265SDimitry Andric private: 40181ad6265SDimitry Andric __internal_storage<_CharT> __storage_; 402bdd1243dSDimitry Andric __output_buffer<_CharT> __output_{__storage_.__begin(), __storage_.__buffer_size, this}; 40381ad6265SDimitry Andric size_t __size_{0}; 40481ad6265SDimitry Andric }; 40581ad6265SDimitry Andric 40681ad6265SDimitry Andric /// The base of a buffer that counts and limits the number of insertions. 407bdd1243dSDimitry Andric template <class _OutIt, __fmt_char_type _CharT, bool> 40881ad6265SDimitry Andric requires(output_iterator<_OutIt, const _CharT&>) 40981ad6265SDimitry Andric struct _LIBCPP_TEMPLATE_VIS __format_to_n_buffer_base { 41081ad6265SDimitry Andric using _Size = iter_difference_t<_OutIt>; 41181ad6265SDimitry Andric 41281ad6265SDimitry Andric public: 413bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI explicit __format_to_n_buffer_base(_OutIt __out_it, _Size __max_size) 414bdd1243dSDimitry Andric : __writer_(_VSTD::move(__out_it)), __max_size_(_VSTD::max(_Size(0), __max_size)) {} 41581ad6265SDimitry Andric 416bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI void __flush(_CharT* __ptr, size_t __n) { 417bdd1243dSDimitry Andric if (_Size(__size_) <= __max_size_) 418bdd1243dSDimitry Andric __writer_.__flush(__ptr, _VSTD::min(_Size(__n), __max_size_ - __size_)); 419bdd1243dSDimitry Andric __size_ += __n; 42081ad6265SDimitry Andric } 42181ad6265SDimitry Andric 42281ad6265SDimitry Andric protected: 42381ad6265SDimitry Andric __internal_storage<_CharT> __storage_; 424bdd1243dSDimitry Andric __output_buffer<_CharT> __output_{__storage_.__begin(), __storage_.__buffer_size, this}; 42581ad6265SDimitry Andric typename __writer_selector<_OutIt, _CharT>::type __writer_; 42681ad6265SDimitry Andric 427bdd1243dSDimitry Andric _Size __max_size_; 42881ad6265SDimitry Andric _Size __size_{0}; 42981ad6265SDimitry Andric }; 43081ad6265SDimitry Andric 43181ad6265SDimitry Andric /// The base of a buffer that counts and limits the number of insertions. 43281ad6265SDimitry Andric /// 43381ad6265SDimitry Andric /// This version is used when \c __enable_direct_output<_OutIt, _CharT> == true. 43481ad6265SDimitry Andric /// 435bdd1243dSDimitry Andric /// This class limits the size available to the direct writer so it will not 43681ad6265SDimitry Andric /// exceed the maximum number of code units. 437bdd1243dSDimitry Andric template <class _OutIt, __fmt_char_type _CharT> 43881ad6265SDimitry Andric requires(output_iterator<_OutIt, const _CharT&>) 43981ad6265SDimitry Andric class _LIBCPP_TEMPLATE_VIS __format_to_n_buffer_base<_OutIt, _CharT, true> { 44081ad6265SDimitry Andric using _Size = iter_difference_t<_OutIt>; 44181ad6265SDimitry Andric 44281ad6265SDimitry Andric public: 443bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI explicit __format_to_n_buffer_base(_OutIt __out_it, _Size __max_size) 444bdd1243dSDimitry Andric : __output_(_VSTD::__unwrap_iter(__out_it), __max_size, this), 445bdd1243dSDimitry Andric __writer_(_VSTD::move(__out_it)), 446bdd1243dSDimitry Andric __max_size_(__max_size) { 447bdd1243dSDimitry Andric if (__max_size <= 0) [[unlikely]] 448bdd1243dSDimitry Andric __output_.__reset(__storage_.__begin(), __storage_.__buffer_size); 44981ad6265SDimitry Andric } 45081ad6265SDimitry Andric 451bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI void __flush(_CharT* __ptr, size_t __n) { 452bdd1243dSDimitry Andric // A __flush to the direct writer happens in the following occasions: 45381ad6265SDimitry Andric // - The format function has written the maximum number of allowed code 45481ad6265SDimitry Andric // units. At this point it's no longer valid to write to this writer. So 45581ad6265SDimitry Andric // switch to the internal storage. This internal storage doesn't need to 456bdd1243dSDimitry Andric // be written anywhere so the __flush for that storage writes no output. 457bdd1243dSDimitry Andric // - Like above, but the next "mass write" operation would overflow the 458bdd1243dSDimitry Andric // buffer. In that case the buffer is pre-emptively switched. The still 459bdd1243dSDimitry Andric // valid code units will be written separately. 46081ad6265SDimitry Andric // - The format_to_n function is finished. In this case there's no need to 46181ad6265SDimitry Andric // switch the buffer, but for simplicity the buffers are still switched. 462bdd1243dSDimitry Andric // When the __max_size <= 0 the constructor already switched the buffers. 463bdd1243dSDimitry Andric if (__size_ == 0 && __ptr != __storage_.__begin()) { 464bdd1243dSDimitry Andric __writer_.__flush(__ptr, __n); 465bdd1243dSDimitry Andric __output_.__reset(__storage_.__begin(), __storage_.__buffer_size); 466bdd1243dSDimitry Andric } else if (__size_ < __max_size_) { 467bdd1243dSDimitry Andric // Copies a part of the internal buffer to the output up to n characters. 468bdd1243dSDimitry Andric // See __output_buffer<_CharT>::__flush_on_overflow for more information. 469bdd1243dSDimitry Andric _Size __s = _VSTD::min(_Size(__n), __max_size_ - __size_); 470bdd1243dSDimitry Andric std::copy_n(__ptr, __s, __writer_.__out_it()); 471bdd1243dSDimitry Andric __writer_.__flush(__ptr, __s); 47281ad6265SDimitry Andric } 47381ad6265SDimitry Andric 474bdd1243dSDimitry Andric __size_ += __n; 47581ad6265SDimitry Andric } 47681ad6265SDimitry Andric 47781ad6265SDimitry Andric protected: 47881ad6265SDimitry Andric __internal_storage<_CharT> __storage_; 47981ad6265SDimitry Andric __output_buffer<_CharT> __output_; 48081ad6265SDimitry Andric __writer_direct<_OutIt, _CharT> __writer_; 48181ad6265SDimitry Andric 482bdd1243dSDimitry Andric _Size __max_size_; 48381ad6265SDimitry Andric _Size __size_{0}; 48481ad6265SDimitry Andric }; 48581ad6265SDimitry Andric 48681ad6265SDimitry Andric /// The buffer that counts and limits the number of insertions. 487bdd1243dSDimitry Andric template <class _OutIt, __fmt_char_type _CharT> 48881ad6265SDimitry Andric requires(output_iterator<_OutIt, const _CharT&>) 48981ad6265SDimitry Andric struct _LIBCPP_TEMPLATE_VIS __format_to_n_buffer final 49081ad6265SDimitry Andric : public __format_to_n_buffer_base< _OutIt, _CharT, __enable_direct_output<_OutIt, _CharT>> { 49181ad6265SDimitry Andric using _Base = __format_to_n_buffer_base<_OutIt, _CharT, __enable_direct_output<_OutIt, _CharT>>; 49281ad6265SDimitry Andric using _Size = iter_difference_t<_OutIt>; 49381ad6265SDimitry Andric 49481ad6265SDimitry Andric public: 495bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI explicit __format_to_n_buffer(_OutIt __out_it, _Size __max_size) 496bdd1243dSDimitry Andric : _Base(_VSTD::move(__out_it), __max_size) {} 497bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI auto __make_output_iterator() { return this->__output_.__make_output_iterator(); } 49881ad6265SDimitry Andric 499bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI format_to_n_result<_OutIt> __result() && { 500bdd1243dSDimitry Andric this->__output_.__flush(); 501bdd1243dSDimitry Andric return {_VSTD::move(this->__writer_).__out_it(), this->__size_}; 50281ad6265SDimitry Andric } 50381ad6265SDimitry Andric }; 504bdd1243dSDimitry Andric 505bdd1243dSDimitry Andric // A dynamically growing buffer intended to be used for retargeting a context. 506bdd1243dSDimitry Andric // 507bdd1243dSDimitry Andric // P2286 Formatting ranges adds range formatting support. It allows the user to 508bdd1243dSDimitry Andric // specify the minimum width for the entire formatted range. The width of the 509bdd1243dSDimitry Andric // range is not known until the range is formatted. Formatting is done to an 510bdd1243dSDimitry Andric // output_iterator so there's no guarantee it would be possible to add the fill 511bdd1243dSDimitry Andric // to the front of the output. Instead the range is formatted to a temporary 512bdd1243dSDimitry Andric // buffer and that buffer is formatted as a string. 513bdd1243dSDimitry Andric // 514bdd1243dSDimitry Andric // There is an issue with that approach, the format context used in 515bdd1243dSDimitry Andric // std::formatter<T>::format contains the output iterator used as part of its 516bdd1243dSDimitry Andric // type. So using this output iterator means there needs to be a new format 517bdd1243dSDimitry Andric // context and the format arguments need to be retargeted to the new context. 518bdd1243dSDimitry Andric // This retargeting is done by a basic_format_context specialized for the 519bdd1243dSDimitry Andric // __iterator of this container. 520*06c3fb27SDimitry Andric // 521*06c3fb27SDimitry Andric // This class uses its own buffer management, since using vector 522*06c3fb27SDimitry Andric // would lead to a circular include with formatter for vector<bool>. 523bdd1243dSDimitry Andric template <__fmt_char_type _CharT> 524bdd1243dSDimitry Andric class _LIBCPP_TEMPLATE_VIS __retarget_buffer { 525*06c3fb27SDimitry Andric using _Alloc = allocator<_CharT>; 526*06c3fb27SDimitry Andric 527bdd1243dSDimitry Andric public: 528bdd1243dSDimitry Andric using value_type = _CharT; 529bdd1243dSDimitry Andric 530bdd1243dSDimitry Andric struct __iterator { 531bdd1243dSDimitry Andric using difference_type = ptrdiff_t; 532*06c3fb27SDimitry Andric using value_type = _CharT; 533bdd1243dSDimitry Andric 534bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(__retarget_buffer& __buffer) 535bdd1243dSDimitry Andric : __buffer_(std::addressof(__buffer)) {} 536bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator=(const _CharT& __c) { 537bdd1243dSDimitry Andric __buffer_->push_back(__c); 538bdd1243dSDimitry Andric return *this; 539bdd1243dSDimitry Andric } 540bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator=(_CharT&& __c) { 541bdd1243dSDimitry Andric __buffer_->push_back(__c); 542bdd1243dSDimitry Andric return *this; 543bdd1243dSDimitry Andric } 544bdd1243dSDimitry Andric 545bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator*() { return *this; } 546bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() { return *this; } 547bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int) { return *this; } 548bdd1243dSDimitry Andric __retarget_buffer* __buffer_; 549bdd1243dSDimitry Andric }; 550bdd1243dSDimitry Andric 551*06c3fb27SDimitry Andric __retarget_buffer(const __retarget_buffer&) = delete; 552*06c3fb27SDimitry Andric __retarget_buffer& operator=(const __retarget_buffer&) = delete; 553*06c3fb27SDimitry Andric 554*06c3fb27SDimitry Andric _LIBCPP_HIDE_FROM_ABI explicit __retarget_buffer(size_t __size_hint) { 555*06c3fb27SDimitry Andric // When the initial size is very small a lot of resizes happen 556*06c3fb27SDimitry Andric // when elements added. So use a hard-coded minimum size. 557*06c3fb27SDimitry Andric // 558*06c3fb27SDimitry Andric // Note a size < 2 will not work 559*06c3fb27SDimitry Andric // - 0 there is no buffer, while push_back requires 1 empty element. 560*06c3fb27SDimitry Andric // - 1 multiplied by the grow factor is 1 and thus the buffer never 561*06c3fb27SDimitry Andric // grows. 562*06c3fb27SDimitry Andric auto __result = std::__allocate_at_least(__alloc_, std::max(__size_hint, 256 / sizeof(_CharT))); 563*06c3fb27SDimitry Andric __ptr_ = __result.ptr; 564*06c3fb27SDimitry Andric __capacity_ = __result.count; 565*06c3fb27SDimitry Andric } 566*06c3fb27SDimitry Andric 567*06c3fb27SDimitry Andric _LIBCPP_HIDE_FROM_ABI ~__retarget_buffer() { 568*06c3fb27SDimitry Andric ranges::destroy_n(__ptr_, __size_); 569*06c3fb27SDimitry Andric allocator_traits<_Alloc>::deallocate(__alloc_, __ptr_, __capacity_); 570*06c3fb27SDimitry Andric } 571bdd1243dSDimitry Andric 572bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI __iterator __make_output_iterator() { return __iterator{*this}; } 573bdd1243dSDimitry Andric 574*06c3fb27SDimitry Andric _LIBCPP_HIDE_FROM_ABI void push_back(_CharT __c) { 575*06c3fb27SDimitry Andric std::construct_at(__ptr_ + __size_, __c); 576*06c3fb27SDimitry Andric ++__size_; 577*06c3fb27SDimitry Andric 578*06c3fb27SDimitry Andric if (__size_ == __capacity_) 579*06c3fb27SDimitry Andric __grow_buffer(); 580*06c3fb27SDimitry Andric } 581bdd1243dSDimitry Andric 582bdd1243dSDimitry Andric template <__fmt_char_type _InCharT> 583bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI void __copy(basic_string_view<_InCharT> __str) { 584*06c3fb27SDimitry Andric size_t __n = __str.size(); 585*06c3fb27SDimitry Andric if (__size_ + __n >= __capacity_) 586*06c3fb27SDimitry Andric // Push_back requires the buffer to have room for at least one character. 587*06c3fb27SDimitry Andric __grow_buffer(__size_ + __n + 1); 588*06c3fb27SDimitry Andric 589*06c3fb27SDimitry Andric std::uninitialized_copy_n(__str.data(), __n, __ptr_ + __size_); 590*06c3fb27SDimitry Andric __size_ += __n; 591bdd1243dSDimitry Andric } 592bdd1243dSDimitry Andric 593bdd1243dSDimitry Andric template <__fmt_char_type _InCharT, class _UnaryOperation> 594bdd1243dSDimitry Andric _LIBCPP_HIDE_FROM_ABI void __transform(const _InCharT* __first, const _InCharT* __last, _UnaryOperation __operation) { 595*06c3fb27SDimitry Andric _LIBCPP_ASSERT_UNCATEGORIZED(__first <= __last, "not a valid range"); 596*06c3fb27SDimitry Andric 597*06c3fb27SDimitry Andric size_t __n = static_cast<size_t>(__last - __first); 598*06c3fb27SDimitry Andric if (__size_ + __n >= __capacity_) 599*06c3fb27SDimitry Andric // Push_back requires the buffer to have room for at least one character. 600*06c3fb27SDimitry Andric __grow_buffer(__size_ + __n + 1); 601*06c3fb27SDimitry Andric 602*06c3fb27SDimitry Andric std::uninitialized_default_construct_n(__ptr_ + __size_, __n); 603*06c3fb27SDimitry Andric std::transform(__first, __last, __ptr_ + __size_, std::move(__operation)); 604*06c3fb27SDimitry Andric __size_ += __n; 605bdd1243dSDimitry Andric } 606bdd1243dSDimitry Andric 607*06c3fb27SDimitry Andric _LIBCPP_HIDE_FROM_ABI void __fill(size_t __n, _CharT __value) { 608*06c3fb27SDimitry Andric if (__size_ + __n >= __capacity_) 609*06c3fb27SDimitry Andric // Push_back requires the buffer to have room for at least one character. 610*06c3fb27SDimitry Andric __grow_buffer(__size_ + __n + 1); 611bdd1243dSDimitry Andric 612*06c3fb27SDimitry Andric std::uninitialized_fill_n(__ptr_ + __size_, __n, __value); 613*06c3fb27SDimitry Andric __size_ += __n; 614*06c3fb27SDimitry Andric } 615*06c3fb27SDimitry Andric 616*06c3fb27SDimitry Andric _LIBCPP_HIDE_FROM_ABI basic_string_view<_CharT> __view() { return {__ptr_, __size_}; } 617bdd1243dSDimitry Andric 618bdd1243dSDimitry Andric private: 619*06c3fb27SDimitry Andric _LIBCPP_HIDE_FROM_ABI void __grow_buffer() { __grow_buffer(__capacity_ * 1.6); } 620*06c3fb27SDimitry Andric 621*06c3fb27SDimitry Andric _LIBCPP_HIDE_FROM_ABI void __grow_buffer(size_t __capacity) { 622*06c3fb27SDimitry Andric _LIBCPP_ASSERT_UNCATEGORIZED(__capacity > __capacity_, "the buffer must grow"); 623*06c3fb27SDimitry Andric auto __result = std::__allocate_at_least(__alloc_, __capacity); 624*06c3fb27SDimitry Andric auto __guard = std::__make_exception_guard([&] { 625*06c3fb27SDimitry Andric allocator_traits<_Alloc>::deallocate(__alloc_, __result.ptr, __result.count); 626*06c3fb27SDimitry Andric }); 627*06c3fb27SDimitry Andric // This shouldn't throw, but just to be safe. Note that at -O1 this 628*06c3fb27SDimitry Andric // guard is optimized away so there is no runtime overhead. 629*06c3fb27SDimitry Andric std::uninitialized_move_n(__ptr_, __size_, __result.ptr); 630*06c3fb27SDimitry Andric __guard.__complete(); 631*06c3fb27SDimitry Andric ranges::destroy_n(__ptr_, __size_); 632*06c3fb27SDimitry Andric allocator_traits<_Alloc>::deallocate(__alloc_, __ptr_, __capacity_); 633*06c3fb27SDimitry Andric 634*06c3fb27SDimitry Andric __ptr_ = __result.ptr; 635*06c3fb27SDimitry Andric __capacity_ = __result.count; 636*06c3fb27SDimitry Andric } 637*06c3fb27SDimitry Andric _LIBCPP_NO_UNIQUE_ADDRESS _Alloc __alloc_; 638*06c3fb27SDimitry Andric _CharT* __ptr_; 639*06c3fb27SDimitry Andric size_t __capacity_; 640*06c3fb27SDimitry Andric size_t __size_{0}; 641bdd1243dSDimitry Andric }; 642bdd1243dSDimitry Andric 64381ad6265SDimitry Andric } // namespace __format 64481ad6265SDimitry Andric 645*06c3fb27SDimitry Andric #endif //_LIBCPP_STD_VER >= 20 64681ad6265SDimitry Andric 64781ad6265SDimitry Andric _LIBCPP_END_NAMESPACE_STD 64881ad6265SDimitry Andric 64981ad6265SDimitry Andric _LIBCPP_POP_MACROS 65081ad6265SDimitry Andric 65181ad6265SDimitry Andric #endif // _LIBCPP___FORMAT_BUFFER_H 652