709 lines · c
1// -*- C++ -*-2//===----------------------------------------------------------------------===//3//4// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.5// See https://llvm.org/LICENSE.txt for license information.6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception7//8//===----------------------------------------------------------------------===//9 10#ifndef _LIBCPP___FORMAT_BUFFER_H11#define _LIBCPP___FORMAT_BUFFER_H12 13#include <__algorithm/copy_n.h>14#include <__algorithm/fill_n.h>15#include <__algorithm/max.h>16#include <__algorithm/min.h>17#include <__algorithm/ranges_copy.h>18#include <__algorithm/transform.h>19#include <__algorithm/unwrap_iter.h>20#include <__concepts/same_as.h>21#include <__config>22#include <__format/concepts.h>23#include <__format/enable_insertable.h>24#include <__format/format_to_n_result.h>25#include <__iterator/back_insert_iterator.h>26#include <__iterator/concepts.h>27#include <__iterator/incrementable_traits.h>28#include <__iterator/iterator_traits.h>29#include <__iterator/wrap_iter.h>30#include <__memory/addressof.h>31#include <__memory/allocate_at_least.h>32#include <__memory/allocator.h>33#include <__memory/allocator_traits.h>34#include <__memory/construct_at.h>35#include <__memory/destroy.h>36#include <__memory/uninitialized_algorithms.h>37#include <__type_traits/add_pointer.h>38#include <__type_traits/conditional.h>39#include <__utility/exception_guard.h>40#include <__utility/move.h>41#include <stdexcept>42#include <string_view>43 44#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)45# pragma GCC system_header46#endif47 48_LIBCPP_PUSH_MACROS49#include <__undef_macros>50 51_LIBCPP_BEGIN_NAMESPACE_STD52 53#if _LIBCPP_STD_VER >= 2054 55namespace __format {56 57// A helper to limit the total size of code units written.58class _LIBCPP_HIDE_FROM_ABI __max_output_size {59public:60 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI explicit __max_output_size(size_t __max_size) : __max_size_{__max_size} {}61 62 // This function adjusts the size of a (bulk) write operations. It ensures the63 // number of code units written by a __output_buffer never exceeds64 // __max_size_ code units.65 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t __write_request(size_t __code_units) {66 size_t __result =67 __code_units_written_ < __max_size_ ? std::min(__code_units, __max_size_ - __code_units_written_) : 0;68 __code_units_written_ += __code_units;69 return __result;70 }71 72 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t __code_units_written() const noexcept { return __code_units_written_; }73 74private:75 size_t __max_size_;76 // The code units that would have been written if there was no limit.77 // format_to_n returns this value.78 size_t __code_units_written_{0};79};80 81/// A "buffer" that handles writing to the proper iterator.82///83/// This helper is used together with the @ref back_insert_iterator to offer84/// type-erasure for the formatting functions. This reduces the number to85/// template instantiations.86///87/// The design is the following:88/// - There is an external object that connects the buffer to the output.89/// - This buffer object:90/// - inherits publicly from this class.91/// - has a static or dynamic buffer.92/// - has a static member function to make space in its buffer write93/// operations. This can be done by increasing the size of the internal94/// buffer or by writing the contents of the buffer to the output iterator.95///96/// This member function is a constructor argument, so its name is not97/// fixed. The code uses the name __prepare_write.98/// - The number of output code units can be limited by a __max_output_size99/// object. This is used in format_to_n This object:100/// - Contains the maximum number of code units to be written.101/// - Contains the number of code units that are requested to be written.102/// This number is returned to the user of format_to_n.103/// - The write functions call the object's __request_write member function.104/// This function:105/// - Updates the number of code units that are requested to be written.106/// - Returns the number of code units that can be written without107/// exceeding the maximum number of code units to be written.108///109/// Documentation for the buffer usage members:110/// - __ptr_111/// The start of the buffer.112/// - __capacity_113/// The number of code units that can be written. This means114/// [__ptr_, __ptr_ + __capacity_) is a valid range to write to.115/// - __size_116/// The number of code units written in the buffer. The next code unit will117/// be written at __ptr_ + __size_. This __size_ may NOT contain the total118/// number of code units written by the __output_buffer. Whether or not it119/// does depends on the sub-class used. Typically the total number of code120/// units written is not interesting. It is interesting for format_to_n which121/// has its own way to track this number.122///123/// Documentation for the modifying buffer operations:124/// The subclasses have a function with the following signature:125///126/// static void __prepare_write(127/// __output_buffer<_CharT>& __buffer, size_t __code_units);128///129/// This function is called when a write function writes more code units than130/// the buffer's available space. When an __max_output_size object is provided131/// the number of code units is the number of code units returned from132/// __max_output_size::__request_write function.133///134/// - The __buffer contains *this. Since the class containing this function135/// inherits from __output_buffer it's safe to cast it to the subclass being136/// used.137/// - The __code_units is the number of code units the caller will write + 1.138/// - This value does not take the available space of the buffer into account.139/// - The push_back function is more efficient when writing before resizing,140/// this means the buffer should always have room for one code unit. Hence141/// the + 1 is the size.142/// - When the function returns there is room for at least one additional code143/// unit. There is no requirement there is room for __code_units code units:144/// - The class has some "bulk" operations. For example, __copy which copies145/// the contents of a basic_string_view to the output. If the sub-class has146/// a fixed size buffer the size of the basic_string_view may be larger147/// than the buffer. In that case it's impossible to honor the requested148/// size.149/// - When the buffer has room for at least one code unit the function may be150/// a no-op.151/// - When the function makes space for more code units it uses one for these152/// functions to signal the change:153/// - __buffer_flushed()154/// - This function is typically used for a fixed sized buffer.155/// - The current contents of [__ptr_, __ptr_ + __size_) have been156/// processed.157/// - __ptr_ remains unchanged.158/// - __capacity_ remains unchanged.159/// - __size_ will be set to 0.160/// - __buffer_moved(_CharT* __ptr, size_t __capacity)161/// - This function is typically used for a dynamic sized buffer. There the162/// location of the buffer changes due to reallocations.163/// - __ptr_ will be set to __ptr. (This value may be the old value of164/// __ptr_).165/// - __capacity_ will be set to __capacity. (This value may be the old166/// value of __capacity_).167/// - __size_ remains unchanged,168/// - The range [__ptr, __ptr + __size_) contains the original data of the169/// range [__ptr_, __ptr_ + __size_).170///171/// The push_back function expects a valid buffer and a capacity of at least 1.172/// This means:173/// - The class is constructed with a valid buffer,174/// - __buffer_moved is called with a valid buffer is used before the first175/// write operation,176/// - no write function is ever called, or177/// - the class is constructed with a __max_output_size object with __max_size 0.178///179/// The latter option allows formatted_size to use the output buffer without180/// ever writing anything to the buffer.181template <__fmt_char_type _CharT>182class __output_buffer {183public:184 using value_type _LIBCPP_NODEBUG = _CharT;185 using __prepare_write_type _LIBCPP_NODEBUG = void (*)(__output_buffer<_CharT>&, size_t);186 187 [[nodiscard]]188 _LIBCPP_HIDE_FROM_ABI explicit __output_buffer(_CharT* __ptr, size_t __capacity, __prepare_write_type __function)189 : __output_buffer{__ptr, __capacity, __function, nullptr} {}190 191 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI explicit __output_buffer(192 _CharT* __ptr, size_t __capacity, __prepare_write_type __function, __max_output_size* __max_output_size)193 : __ptr_(__ptr), __capacity_(__capacity), __prepare_write_(__function), __max_output_size_(__max_output_size) {}194 195 _LIBCPP_HIDE_FROM_ABI void __buffer_flushed() { __size_ = 0; }196 197 _LIBCPP_HIDE_FROM_ABI void __buffer_moved(_CharT* __ptr, size_t __capacity) {198 __ptr_ = __ptr;199 __capacity_ = __capacity;200 }201 202 _LIBCPP_HIDE_FROM_ABI auto __make_output_iterator() { return std::back_insert_iterator{*this}; }203 204 // Used in std::back_insert_iterator.205 _LIBCPP_HIDE_FROM_ABI void push_back(_CharT __c) {206 if (__max_output_size_ && __max_output_size_->__write_request(1) == 0)207 return;208 209 _LIBCPP_ASSERT_INTERNAL(210 __ptr_ && __size_ < __capacity_ && __available() >= 1, "attempted to write outside the buffer");211 212 __ptr_[__size_++] = __c;213 214 // Profiling showed flushing after adding is more efficient than flushing215 // when entering the function.216 if (__size_ == __capacity_)217 __prepare_write(0);218 }219 220 /// Copies the input __str to the buffer.221 ///222 /// Since some of the input is generated by std::to_chars, there needs to be a223 /// conversion when _CharT is wchar_t.224 template <__fmt_char_type _InCharT>225 _LIBCPP_HIDE_FROM_ABI void __copy(basic_string_view<_InCharT> __str) {226 // When the underlying iterator is a simple iterator the __capacity_ is227 // infinite. For a string or container back_inserter it isn't. This means228 // that adding a large string to the buffer can cause some overhead. In that229 // case a better approach could be:230 // - flush the buffer231 // - container.append(__str.begin(), __str.end());232 // The same holds true for the fill.233 // For transform it might be slightly harder, however the use case for234 // transform is slightly less common; it converts hexadecimal values to235 // upper case. For integral these strings are short.236 // TODO FMT Look at the improvements above.237 size_t __n = __str.size();238 if (__max_output_size_) {239 __n = __max_output_size_->__write_request(__n);240 if (__n == 0)241 return;242 }243 244 const _InCharT* __first = __str.data();245 do {246 __prepare_write(__n);247 size_t __chunk = std::min(__n, __available());248 std::copy_n(__first, __chunk, std::addressof(__ptr_[__size_]));249 __size_ += __chunk;250 __first += __chunk;251 __n -= __chunk;252 } while (__n);253 }254 255 /// A std::transform wrapper.256 ///257 /// Like @ref __copy it may need to do type conversion.258 template <contiguous_iterator _Iterator,259 class _UnaryOperation,260 __fmt_char_type _InCharT = typename iterator_traits<_Iterator>::value_type>261 _LIBCPP_HIDE_FROM_ABI void __transform(_Iterator __first, _Iterator __last, _UnaryOperation __operation) {262 _LIBCPP_ASSERT_INTERNAL(__first <= __last, "not a valid range");263 264 size_t __n = static_cast<size_t>(__last - __first);265 if (__max_output_size_) {266 __n = __max_output_size_->__write_request(__n);267 if (__n == 0)268 return;269 }270 271 do {272 __prepare_write(__n);273 size_t __chunk = std::min(__n, __available());274 std::transform(__first, __first + __chunk, std::addressof(__ptr_[__size_]), __operation);275 __size_ += __chunk;276 __first += __chunk;277 __n -= __chunk;278 } while (__n);279 }280 281 /// A \c fill_n wrapper.282 _LIBCPP_HIDE_FROM_ABI void __fill(size_t __n, _CharT __value) {283 if (__max_output_size_) {284 __n = __max_output_size_->__write_request(__n);285 if (__n == 0)286 return;287 }288 289 do {290 __prepare_write(__n);291 size_t __chunk = std::min(__n, __available());292 std::fill_n(std::addressof(__ptr_[__size_]), __chunk, __value);293 __size_ += __chunk;294 __n -= __chunk;295 } while (__n);296 }297 298 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t __capacity() const { return __capacity_; }299 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t __size() const { return __size_; }300 301private:302 _CharT* __ptr_;303 size_t __capacity_;304 size_t __size_{0};305 void (*__prepare_write_)(__output_buffer<_CharT>&, size_t);306 __max_output_size* __max_output_size_;307 308 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t __available() const { return __capacity_ - __size_; }309 310 _LIBCPP_HIDE_FROM_ABI void __prepare_write(size_t __code_units) {311 // Always have space for one additional code unit. This is a precondition of the push_back function.312 __code_units += 1;313 if (__available() < __code_units)314 __prepare_write_(*this, __code_units + 1);315 }316};317 318template <class _OutIt, class _CharT>319concept __enable_direct_output =320 __fmt_char_type<_CharT> &&321 (same_as<_OutIt, _CharT*>322 // TODO(hardening): the following check might not apply to hardened iterators and might need to be wrapped in an323 // `#ifdef`.324 || same_as<_OutIt, __wrap_iter<_CharT*>>);325 326/// Concept to see whether a \a _Container is insertable.327///328/// The concept is used to validate whether multiple calls to a329/// \ref back_insert_iterator can be replace by a call to \c _Container::insert.330///331/// \note a \a _Container needs to opt-in to the concept by specializing332/// \ref __enable_insertable.333template <class _Container>334concept __insertable =335 __enable_insertable<_Container> && __fmt_char_type<typename _Container::value_type> &&336 requires(_Container& __t,337 add_pointer_t<typename _Container::value_type> __first,338 add_pointer_t<typename _Container::value_type> __last) { __t.insert(__t.end(), __first, __last); };339 340/// Extract the container type of a \ref back_insert_iterator.341template <class _It>342struct __back_insert_iterator_container {343 using type _LIBCPP_NODEBUG = void;344};345 346template <__insertable _Container>347struct __back_insert_iterator_container<back_insert_iterator<_Container>> {348 using type _LIBCPP_NODEBUG = _Container;349};350 351// A dynamically growing buffer.352template <__fmt_char_type _CharT>353class __allocating_buffer : public __output_buffer<_CharT> {354public:355 __allocating_buffer(const __allocating_buffer&) = delete;356 __allocating_buffer& operator=(const __allocating_buffer&) = delete;357 358 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __allocating_buffer() : __allocating_buffer{nullptr} {}359 360 [[nodiscard]]361 _LIBCPP_HIDE_FROM_ABI explicit __allocating_buffer(__max_output_size* __max_output_size)362 : __output_buffer<_CharT>{__small_buffer_, __buffer_size_, __prepare_write, __max_output_size} {}363 364 _LIBCPP_HIDE_FROM_ABI ~__allocating_buffer() {365 if (__ptr_ != __small_buffer_)366 _Alloc{}.deallocate(__ptr_, this->__capacity());367 }368 369 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI basic_string_view<_CharT> __view() { return {__ptr_, this->__size()}; }370 371private:372 using _Alloc _LIBCPP_NODEBUG = allocator<_CharT>;373 374 // Since allocating is expensive the class has a small internal buffer. When375 // its capacity is exceeded a dynamic buffer will be allocated.376 static constexpr size_t __buffer_size_ = 256;377 _CharT __small_buffer_[__buffer_size_];378 379 _CharT* __ptr_{__small_buffer_};380 381 _LIBCPP_HIDE_FROM_ABI void __grow_buffer(size_t __capacity) {382 if (__capacity < __buffer_size_)383 return;384 385 _LIBCPP_ASSERT_INTERNAL(__capacity > this->__capacity(), "the buffer must grow");386 387 // _CharT is an implicit lifetime type so can be used without explicit388 // construction or destruction.389 _Alloc __alloc;390 auto __result = std::__allocate_at_least(__alloc, __capacity);391 std::copy_n(__ptr_, this->__size(), __result.ptr);392 if (__ptr_ != __small_buffer_)393 __alloc.deallocate(__ptr_, this->__capacity());394 395 __ptr_ = __result.ptr;396 this->__buffer_moved(__ptr_, __result.count);397 }398 399 _LIBCPP_HIDE_FROM_ABI void __prepare_write(size_t __size_hint) {400 __grow_buffer(std::max<size_t>(this->__capacity() + __size_hint, this->__capacity() * 1.6));401 }402 403 _LIBCPP_HIDE_FROM_ABI static void __prepare_write(__output_buffer<_CharT>& __buffer, size_t __size_hint) {404 static_cast<__allocating_buffer<_CharT>&>(__buffer).__prepare_write(__size_hint);405 }406};407 408// A buffer that directly writes to the underlying buffer.409template <class _OutIt, __fmt_char_type _CharT>410class __direct_iterator_buffer : public __output_buffer<_CharT> {411public:412 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI explicit __direct_iterator_buffer(_OutIt __out_it)413 : __direct_iterator_buffer{__out_it, nullptr} {}414 415 [[nodiscard]]416 _LIBCPP_HIDE_FROM_ABI explicit __direct_iterator_buffer(_OutIt __out_it, __max_output_size* __max_output_size)417 : __output_buffer<_CharT>{std::__unwrap_iter(__out_it), __buffer_size, __prepare_write, __max_output_size},418 __out_it_(__out_it) {}419 420 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _OutIt __out_it() && { return __out_it_ + this->__size(); }421 422private:423 // The function format_to expects a buffer large enough for the output. The424 // function format_to_n has its own helper class that restricts the number of425 // write options. So this function class can pretend to have an infinite426 // buffer.427 static constexpr size_t __buffer_size = -1;428 429 _OutIt __out_it_;430 431 _LIBCPP_HIDE_FROM_ABI static void432 __prepare_write([[maybe_unused]] __output_buffer<_CharT>& __buffer, [[maybe_unused]] size_t __size_hint) {433 std::__throw_length_error("__direct_iterator_buffer");434 }435};436 437// A buffer that writes its output to the end of a container.438template <class _OutIt, __fmt_char_type _CharT>439class __container_inserter_buffer : public __output_buffer<_CharT> {440public:441 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI explicit __container_inserter_buffer(_OutIt __out_it)442 : __container_inserter_buffer{__out_it, nullptr} {}443 444 [[nodiscard]]445 _LIBCPP_HIDE_FROM_ABI explicit __container_inserter_buffer(_OutIt __out_it, __max_output_size* __max_output_size)446 : __output_buffer<_CharT>{__small_buffer_, __buffer_size, __prepare_write, __max_output_size},447 __container_{__out_it.__get_container()} {}448 449 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI auto __out_it() && {450 __container_->insert(__container_->end(), __small_buffer_, __small_buffer_ + this->__size());451 return std::back_inserter(*__container_);452 }453 454private:455 typename __back_insert_iterator_container<_OutIt>::type* __container_;456 457 // This class uses a fixed size buffer and appends the elements in458 // __buffer_size chunks. An alternative would be to use an allocating buffer459 // and append the output in a single write operation. Benchmarking showed no460 // performance difference.461 static constexpr size_t __buffer_size = 256;462 _CharT __small_buffer_[__buffer_size];463 464 _LIBCPP_HIDE_FROM_ABI void __prepare_write() {465 __container_->insert(__container_->end(), __small_buffer_, __small_buffer_ + this->__size());466 this->__buffer_flushed();467 }468 469 _LIBCPP_HIDE_FROM_ABI static void470 __prepare_write(__output_buffer<_CharT>& __buffer, [[maybe_unused]] size_t __size_hint) {471 static_cast<__container_inserter_buffer<_OutIt, _CharT>&>(__buffer).__prepare_write();472 }473};474 475// A buffer that writes to an iterator.476//477// Unlike the __container_inserter_buffer this class' performance does benefit478// from allocating and then inserting.479template <class _OutIt, __fmt_char_type _CharT>480class __iterator_buffer : public __allocating_buffer<_CharT> {481public:482 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI explicit __iterator_buffer(_OutIt __out_it)483 : __allocating_buffer<_CharT>{}, __out_it_{std::move(__out_it)} {}484 485 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI explicit __iterator_buffer(_OutIt __out_it, __max_output_size* __max_output_size)486 : __allocating_buffer<_CharT>{__max_output_size}, __out_it_{std::move(__out_it)} {}487 488 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI auto __out_it() && {489 return std::ranges::copy(this->__view(), std::move(__out_it_)).out;490 }491 492private:493 _OutIt __out_it_;494};495 496// Selects the type of the buffer used for the output iterator.497template <class _OutIt, __fmt_char_type _CharT>498class __buffer_selector {499 using _Container _LIBCPP_NODEBUG = __back_insert_iterator_container<_OutIt>::type;500 501public:502 using type _LIBCPP_NODEBUG =503 conditional_t<!same_as<_Container, void>,504 __container_inserter_buffer<_OutIt, _CharT>,505 conditional_t<__enable_direct_output<_OutIt, _CharT>,506 __direct_iterator_buffer<_OutIt, _CharT>,507 __iterator_buffer<_OutIt, _CharT>>>;508};509 510// A buffer that counts and limits the number of insertions.511template <class _OutIt, __fmt_char_type _CharT>512class __format_to_n_buffer : private __buffer_selector<_OutIt, _CharT>::type {513public:514 using _Base _LIBCPP_NODEBUG = __buffer_selector<_OutIt, _CharT>::type;515 516 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __format_to_n_buffer(_OutIt __out_it, iter_difference_t<_OutIt> __n)517 : _Base{std::move(__out_it), std::addressof(__max_output_size_)},518 __max_output_size_{__n < 0 ? size_t{0} : static_cast<size_t>(__n)} {}519 520 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI auto __make_output_iterator() { return _Base::__make_output_iterator(); }521 522 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI format_to_n_result<_OutIt> __result() && {523 return {static_cast<_Base&&>(*this).__out_it(),524 static_cast<iter_difference_t<_OutIt>>(__max_output_size_.__code_units_written())};525 }526 527private:528 __max_output_size __max_output_size_;529};530 531// A buffer that counts the number of insertions.532//533// Since formatted_size only needs to know the size, the output itself is534// discarded.535template <__fmt_char_type _CharT>536class __formatted_size_buffer : private __output_buffer<_CharT> {537public:538 using _Base _LIBCPP_NODEBUG = __output_buffer<_CharT>;539 540 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __formatted_size_buffer()541 : _Base{nullptr, 0, __prepare_write, std::addressof(__max_output_size_)} {}542 543 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI auto __make_output_iterator() { return _Base::__make_output_iterator(); }544 545 // This function does not need to be r-value qualified, however this is546 // consistent with similar objects.547 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t __result() && { return __max_output_size_.__code_units_written(); }548 549private:550 __max_output_size __max_output_size_{0};551 552 _LIBCPP_HIDE_FROM_ABI static void553 __prepare_write([[maybe_unused]] __output_buffer<_CharT>& __buffer, [[maybe_unused]] size_t __size_hint) {554 // Note this function does not satisfy the requirement of giving a 1 code unit buffer.555 _LIBCPP_ASSERT_INTERNAL(556 false, "Since __max_output_size_.__max_size_ == 0 there should never be call to this function.");557 }558};559 560// A dynamically growing buffer intended to be used for retargeting a context.561//562// P2286 Formatting ranges adds range formatting support. It allows the user to563// specify the minimum width for the entire formatted range. The width of the564// range is not known until the range is formatted. Formatting is done to an565// output_iterator so there's no guarantee it would be possible to add the fill566// to the front of the output. Instead the range is formatted to a temporary567// buffer and that buffer is formatted as a string.568//569// There is an issue with that approach, the format context used in570// std::formatter<T>::format contains the output iterator used as part of its571// type. So using this output iterator means there needs to be a new format572// context and the format arguments need to be retargeted to the new context.573// This retargeting is done by a basic_format_context specialized for the574// __iterator of this container.575//576// This class uses its own buffer management, since using vector577// would lead to a circular include with formatter for vector<bool>.578template <__fmt_char_type _CharT>579class __retarget_buffer {580 using _Alloc _LIBCPP_NODEBUG = allocator<_CharT>;581 582public:583 using value_type _LIBCPP_NODEBUG = _CharT;584 585 struct __iterator {586 using difference_type _LIBCPP_NODEBUG = ptrdiff_t;587 using value_type _LIBCPP_NODEBUG = _CharT;588 589 _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(__retarget_buffer& __buffer)590 : __buffer_(std::addressof(__buffer)) {}591 _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator=(const _CharT& __c) {592 __buffer_->push_back(__c);593 return *this;594 }595 _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator=(_CharT&& __c) {596 __buffer_->push_back(__c);597 return *this;598 }599 600 _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator*() { return *this; }601 _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() { return *this; }602 _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int) { return *this; }603 __retarget_buffer* __buffer_;604 };605 606 __retarget_buffer(const __retarget_buffer&) = delete;607 __retarget_buffer& operator=(const __retarget_buffer&) = delete;608 609 _LIBCPP_HIDE_FROM_ABI explicit __retarget_buffer(size_t __size_hint) {610 // When the initial size is very small a lot of resizes happen611 // when elements added. So use a hard-coded minimum size.612 //613 // Note a size < 2 will not work614 // - 0 there is no buffer, while push_back requires 1 empty element.615 // - 1 multiplied by the grow factor is 1 and thus the buffer never616 // grows.617 auto __result = std::__allocate_at_least(__alloc_, std::max(__size_hint, 256 / sizeof(_CharT)));618 __ptr_ = __result.ptr;619 __capacity_ = __result.count;620 }621 622 _LIBCPP_HIDE_FROM_ABI ~__retarget_buffer() {623 std::destroy_n(__ptr_, __size_);624 allocator_traits<_Alloc>::deallocate(__alloc_, __ptr_, __capacity_);625 }626 627 _LIBCPP_HIDE_FROM_ABI __iterator __make_output_iterator() { return __iterator{*this}; }628 629 _LIBCPP_HIDE_FROM_ABI void push_back(_CharT __c) {630 std::construct_at(__ptr_ + __size_, __c);631 ++__size_;632 633 if (__size_ == __capacity_)634 __grow_buffer();635 }636 637 template <__fmt_char_type _InCharT>638 _LIBCPP_HIDE_FROM_ABI void __copy(basic_string_view<_InCharT> __str) {639 size_t __n = __str.size();640 if (__size_ + __n >= __capacity_)641 // Push_back requires the buffer to have room for at least one character.642 __grow_buffer(__size_ + __n + 1);643 644 std::uninitialized_copy_n(__str.data(), __n, __ptr_ + __size_);645 __size_ += __n;646 }647 648 template <contiguous_iterator _Iterator,649 class _UnaryOperation,650 __fmt_char_type _InCharT = typename iterator_traits<_Iterator>::value_type>651 _LIBCPP_HIDE_FROM_ABI void __transform(_Iterator __first, _Iterator __last, _UnaryOperation __operation) {652 _LIBCPP_ASSERT_INTERNAL(__first <= __last, "not a valid range");653 654 size_t __n = static_cast<size_t>(__last - __first);655 if (__size_ + __n >= __capacity_)656 // Push_back requires the buffer to have room for at least one character.657 __grow_buffer(__size_ + __n + 1);658 659 std::uninitialized_default_construct_n(__ptr_ + __size_, __n);660 std::transform(__first, __last, __ptr_ + __size_, std::move(__operation));661 __size_ += __n;662 }663 664 _LIBCPP_HIDE_FROM_ABI void __fill(size_t __n, _CharT __value) {665 if (__size_ + __n >= __capacity_)666 // Push_back requires the buffer to have room for at least one character.667 __grow_buffer(__size_ + __n + 1);668 669 std::uninitialized_fill_n(__ptr_ + __size_, __n, __value);670 __size_ += __n;671 }672 673 _LIBCPP_HIDE_FROM_ABI basic_string_view<_CharT> __view() { return {__ptr_, __size_}; }674 675private:676 _LIBCPP_HIDE_FROM_ABI void __grow_buffer() { __grow_buffer(__capacity_ * 1.6); }677 678 _LIBCPP_HIDE_FROM_ABI void __grow_buffer(size_t __capacity) {679 _LIBCPP_ASSERT_INTERNAL(__capacity > __capacity_, "the buffer must grow");680 auto __result = std::__allocate_at_least(__alloc_, __capacity);681 auto __guard = std::__make_exception_guard([&] {682 allocator_traits<_Alloc>::deallocate(__alloc_, __result.ptr, __result.count);683 });684 // This shouldn't throw, but just to be safe. Note that at -O1 this685 // guard is optimized away so there is no runtime overhead.686 std::uninitialized_move_n(__ptr_, __size_, __result.ptr);687 __guard.__complete();688 std::destroy_n(__ptr_, __size_);689 allocator_traits<_Alloc>::deallocate(__alloc_, __ptr_, __capacity_);690 691 __ptr_ = __result.ptr;692 __capacity_ = __result.count;693 }694 _LIBCPP_NO_UNIQUE_ADDRESS _Alloc __alloc_;695 _CharT* __ptr_;696 size_t __capacity_;697 size_t __size_{0};698};699 700} // namespace __format701 702#endif // _LIBCPP_STD_VER >= 20703 704_LIBCPP_END_NAMESPACE_STD705 706_LIBCPP_POP_MACROS707 708#endif // _LIBCPP___FORMAT_BUFFER_H709