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-exception
7//
8//===----------------------------------------------------------------------===//
9
10#ifndef _LIBCPP___FORMAT_BUFFER_H
11#define _LIBCPP___FORMAT_BUFFER_H
12
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/enable_insertable.h>
23#include <__format/fmt_char_type.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_header
46#endif
47
48_LIBCPP_PUSH_MACROS
49#include <__undef_macros>
50
51_LIBCPP_BEGIN_NAMESPACE_STD
52
53#if _LIBCPP_STD_VER >= 20
54
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 the
63 // number of code units written by a __output_buffer never exceeds
64 // __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(a: __code_units, b: __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 offer
84/// type-erasure for the formatting functions. This reduces the number to
85/// 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 write
93/// operations. This can be done by increasing the size of the internal
94/// 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 not
97/// fixed. The code uses the name __prepare_write.
98/// - The number of output code units can be limited by a __max_output_size
99/// 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 without
107/// 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 means
114/// [__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 will
117/// be written at __ptr_ + __size_. This __size_ may NOT contain the total
118/// number of code units written by the __output_buffer. Whether or not it
119/// does depends on the sub-class used. Typically the total number of code
120/// units written is not interesting. It is interesting for format_to_n which
121/// 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 than
130/// the buffer's available space. When an __max_output_size object is provided
131/// the number of code units is the number of code units returned from
132/// __max_output_size::__request_write function.
133///
134/// - The __buffer contains *this. Since the class containing this function
135/// inherits from __output_buffer it's safe to cast it to the subclass being
136/// 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. Hence
141/// the + 1 is the size.
142/// - When the function returns there is room for at least one additional code
143/// unit. There is no requirement there is room for __code_units code units:
144/// - The class has some "bulk" operations. For example, __copy which copies
145/// the contents of a basic_string_view to the output. If the sub-class has
146/// a fixed size buffer the size of the basic_string_view may be larger
147/// than the buffer. In that case it's impossible to honor the requested
148/// size.
149/// - When the buffer has room for at least one code unit the function may be
150/// a no-op.
151/// - When the function makes space for more code units it uses one for these
152/// 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 been
156/// 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 the
162/// location of the buffer changes due to reallocations.
163/// - __ptr_ will be set to __ptr. (This value may be the old value of
164/// __ptr_).
165/// - __capacity_ will be set to __capacity. (This value may be the old
166/// value of __capacity_).
167/// - __size_ remains unchanged,
168/// - The range [__ptr, __ptr + __size_) contains the original data of the
169/// 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 first
175/// write operation,
176/// - no write function is ever called, or
177/// - 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 without
180/// 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(code_units: 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 flushing
215 // when entering the function.
216 if (__size_ == __capacity_)
217 __prepare_write(code_units: 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 a
223 /// 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_ is
227 // infinite. For a string or container back_inserter it isn't. This means
228 // that adding a large string to the buffer can cause some overhead. In that
229 // case a better approach could be:
230 // - flush the buffer
231 // - 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 for
234 // transform is slightly less common; it converts hexadecimal values to
235 // 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(code_units: __n);
240 if (__n == 0)
241 return;
242 }
243
244 const _InCharT* __first = __str.data();
245 do {
246 __prepare_write(code_units: __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 // push_back needs a free code unit, which the last chunk may have used.
255 if (__size_ == __capacity_)
256 __prepare_write(code_units: 0);
257 }
258
259 /// A std::transform wrapper.
260 ///
261 /// Like @ref __copy it may need to do type conversion.
262 template <contiguous_iterator _Iterator,
263 class _UnaryOperation,
264 __fmt_char_type _InCharT = typename iterator_traits<_Iterator>::value_type>
265 _LIBCPP_HIDE_FROM_ABI void __transform(_Iterator __first, _Iterator __last, _UnaryOperation __operation) {
266 _LIBCPP_ASSERT_INTERNAL(__first <= __last, "not a valid range");
267
268 size_t __n = static_cast<size_t>(__last - __first);
269 if (__max_output_size_) {
270 __n = __max_output_size_->__write_request(code_units: __n);
271 if (__n == 0)
272 return;
273 }
274
275 do {
276 __prepare_write(code_units: __n);
277 size_t __chunk = std::min(__n, __available());
278 std::transform(__first, __first + __chunk, std::addressof(__ptr_[__size_]), __operation);
279 __size_ += __chunk;
280 __first += __chunk;
281 __n -= __chunk;
282 } while (__n);
283
284 // push_back needs a free code unit, which the last chunk may have used.
285 if (__size_ == __capacity_)
286 __prepare_write(code_units: 0);
287 }
288
289 /// A \c fill_n wrapper.
290 _LIBCPP_HIDE_FROM_ABI void __fill(size_t __n, _CharT __value) {
291 if (__max_output_size_) {
292 __n = __max_output_size_->__write_request(code_units: __n);
293 if (__n == 0)
294 return;
295 }
296
297 do {
298 __prepare_write(code_units: __n);
299 size_t __chunk = std::min(__n, __available());
300 std::fill_n(std::addressof(__ptr_[__size_]), __chunk, __value);
301 __size_ += __chunk;
302 __n -= __chunk;
303 } while (__n);
304
305 // push_back needs a free code unit, which the last chunk may have used.
306 if (__size_ == __capacity_)
307 __prepare_write(code_units: 0);
308 }
309
310 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t __capacity() const { return __capacity_; }
311 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t __size() const { return __size_; }
312
313private:
314 _CharT* __ptr_;
315 size_t __capacity_;
316 size_t __size_{0};
317 void (*__prepare_write_)(__output_buffer<_CharT>&, size_t);
318 __max_output_size* __max_output_size_;
319
320 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t __available() const { return __capacity_ - __size_; }
321
322 _LIBCPP_HIDE_FROM_ABI void __prepare_write(size_t __code_units) {
323 // Always have space for one additional code unit. This is a precondition of the push_back function.
324 __code_units += 1;
325 if (__available() < __code_units)
326 __prepare_write_(*this, __code_units + 1);
327 }
328};
329
330template <class _OutIt, class _CharT>
331concept __enable_direct_output =
332 __fmt_char_type<_CharT> &&
333 (same_as<_OutIt, _CharT*>
334 // TODO(hardening): the following check might not apply to hardened iterators and might need to be wrapped in an
335 // `#ifdef`.
336 || same_as<_OutIt, __wrap_iter<_CharT*>>);
337
338/// Concept to see whether a \a _Container is insertable.
339///
340/// The concept is used to validate whether multiple calls to a
341/// \ref back_insert_iterator can be replace by a call to \c _Container::insert.
342///
343/// \note a \a _Container needs to opt-in to the concept by specializing
344/// \ref __enable_insertable.
345template <class _Container>
346concept __insertable =
347 __enable_insertable<_Container> && __fmt_char_type<typename _Container::value_type> &&
348 requires(_Container& __t,
349 add_pointer_t<typename _Container::value_type> __first,
350 add_pointer_t<typename _Container::value_type> __last) { __t.insert(__t.end(), __first, __last); };
351
352/// Extract the container type of a \ref back_insert_iterator.
353template <class _It>
354struct __back_insert_iterator_container {
355 using type _LIBCPP_NODEBUG = void;
356};
357
358template <__insertable _Container>
359struct __back_insert_iterator_container<back_insert_iterator<_Container>> {
360 using type _LIBCPP_NODEBUG = _Container;
361};
362
363// A dynamically growing buffer.
364template <__fmt_char_type _CharT>
365class __allocating_buffer : public __output_buffer<_CharT> {
366public:
367 __allocating_buffer(const __allocating_buffer&) = delete;
368 __allocating_buffer& operator=(const __allocating_buffer&) = delete;
369
370 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __allocating_buffer() : __allocating_buffer{nullptr} {}
371
372 [[nodiscard]]
373 _LIBCPP_HIDE_FROM_ABI explicit __allocating_buffer(__max_output_size* __max_output_size)
374 : __output_buffer<_CharT>{__small_buffer_, __buffer_size_, __prepare_write, __max_output_size} {}
375
376 _LIBCPP_HIDE_FROM_ABI ~__allocating_buffer() {
377 if (__ptr_ != __small_buffer_)
378 _Alloc{}.deallocate(__ptr_, this->__capacity());
379 }
380
381 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI basic_string_view<_CharT> __view() { return {__ptr_, this->__size()}; }
382
383private:
384 using _Alloc _LIBCPP_NODEBUG = allocator<_CharT>;
385
386 // Since allocating is expensive the class has a small internal buffer. When
387 // its capacity is exceeded a dynamic buffer will be allocated.
388 static constexpr size_t __buffer_size_ = 256;
389 _CharT __small_buffer_[__buffer_size_];
390
391 _CharT* __ptr_{__small_buffer_};
392
393 _LIBCPP_HIDE_FROM_ABI void __grow_buffer(size_t __capacity) {
394 if (__capacity < __buffer_size_)
395 return;
396
397 _LIBCPP_ASSERT_INTERNAL(__capacity > this->__capacity(), "the buffer must grow");
398
399 // _CharT is an implicit lifetime type so can be used without explicit
400 // construction or destruction.
401 _Alloc __alloc;
402 auto __result = std::__allocate_at_least(__alloc, __capacity);
403 std::copy_n(__ptr_, this->__size(), __result.ptr);
404 if (__ptr_ != __small_buffer_)
405 __alloc.deallocate(__ptr_, this->__capacity());
406
407 __ptr_ = __result.ptr;
408 this->__buffer_moved(__ptr_, __result.count);
409 }
410
411 _LIBCPP_HIDE_FROM_ABI void __prepare_write(size_t __size_hint) {
412 __grow_buffer(capacity: std::max<size_t>(this->__capacity() + __size_hint, this->__capacity() * 1.6));
413 }
414
415 _LIBCPP_HIDE_FROM_ABI static void __prepare_write(__output_buffer<_CharT>& __buffer, size_t __size_hint) {
416 static_cast<__allocating_buffer<_CharT>&>(__buffer).__prepare_write(__size_hint);
417 }
418};
419
420// A buffer that directly writes to the underlying buffer.
421template <class _OutIt, __fmt_char_type _CharT>
422class __direct_iterator_buffer : public __output_buffer<_CharT> {
423public:
424 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI explicit __direct_iterator_buffer(_OutIt __out_it)
425 : __direct_iterator_buffer{__out_it, nullptr} {}
426
427 [[nodiscard]]
428 _LIBCPP_HIDE_FROM_ABI explicit __direct_iterator_buffer(_OutIt __out_it, __max_output_size* __max_output_size)
429 : __output_buffer<_CharT>{std::__unwrap_iter(__out_it), __buffer_size, __prepare_write, __max_output_size},
430 __out_it_(__out_it) {}
431
432 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _OutIt __out_it() && { return __out_it_ + this->__size(); }
433
434private:
435 // The function format_to expects a buffer large enough for the output. The
436 // function format_to_n has its own helper class that restricts the number of
437 // write options. So this function class can pretend to have an infinite
438 // buffer.
439 static constexpr size_t __buffer_size = -1;
440
441 _OutIt __out_it_;
442
443 _LIBCPP_HIDE_FROM_ABI static void
444 __prepare_write([[maybe_unused]] __output_buffer<_CharT>& __buffer, [[maybe_unused]] size_t __size_hint) {
445 std::__throw_length_error(msg: "__direct_iterator_buffer");
446 }
447};
448
449// A buffer that writes its output to the end of a container.
450template <class _OutIt, __fmt_char_type _CharT>
451class __container_inserter_buffer : public __output_buffer<_CharT> {
452public:
453 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI explicit __container_inserter_buffer(_OutIt __out_it)
454 : __container_inserter_buffer{__out_it, nullptr} {}
455
456 [[nodiscard]]
457 _LIBCPP_HIDE_FROM_ABI explicit __container_inserter_buffer(_OutIt __out_it, __max_output_size* __max_output_size)
458 : __output_buffer<_CharT>{__small_buffer_, __buffer_size, __prepare_write, __max_output_size},
459 __container_{__out_it.__get_container()} {}
460
461 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI auto __out_it() && {
462 __container_->insert(__container_->end(), __small_buffer_, __small_buffer_ + this->__size());
463 return std::back_inserter(*__container_);
464 }
465
466private:
467 typename __back_insert_iterator_container<_OutIt>::type* __container_;
468
469 // This class uses a fixed size buffer and appends the elements in
470 // __buffer_size chunks. An alternative would be to use an allocating buffer
471 // and append the output in a single write operation. Benchmarking showed no
472 // performance difference.
473 static constexpr size_t __buffer_size = 256;
474 _CharT __small_buffer_[__buffer_size];
475
476 _LIBCPP_HIDE_FROM_ABI void __prepare_write() {
477 __container_->insert(__container_->end(), __small_buffer_, __small_buffer_ + this->__size());
478 this->__buffer_flushed();
479 }
480
481 _LIBCPP_HIDE_FROM_ABI static void
482 __prepare_write(__output_buffer<_CharT>& __buffer, [[maybe_unused]] size_t __size_hint) {
483 static_cast<__container_inserter_buffer<_OutIt, _CharT>&>(__buffer).__prepare_write();
484 }
485};
486
487// A buffer that writes to an iterator.
488//
489// Unlike the __container_inserter_buffer this class' performance does benefit
490// from allocating and then inserting.
491template <class _OutIt, __fmt_char_type _CharT>
492class __iterator_buffer : public __allocating_buffer<_CharT> {
493public:
494 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI explicit __iterator_buffer(_OutIt __out_it)
495 : __allocating_buffer<_CharT>{}, __out_it_{std::move(__out_it)} {}
496
497 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI explicit __iterator_buffer(_OutIt __out_it, __max_output_size* __max_output_size)
498 : __allocating_buffer<_CharT>{__max_output_size}, __out_it_{std::move(__out_it)} {}
499
500 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI auto __out_it() && {
501 return std::ranges::copy(this->__view(), std::move(__out_it_)).out;
502 }
503
504private:
505 _OutIt __out_it_;
506};
507
508// Selects the type of the buffer used for the output iterator.
509template <class _OutIt, __fmt_char_type _CharT>
510class __buffer_selector {
511 using _Container _LIBCPP_NODEBUG = __back_insert_iterator_container<_OutIt>::type;
512
513public:
514 using type _LIBCPP_NODEBUG =
515 conditional_t<!same_as<_Container, void>,
516 __container_inserter_buffer<_OutIt, _CharT>,
517 conditional_t<__enable_direct_output<_OutIt, _CharT>,
518 __direct_iterator_buffer<_OutIt, _CharT>,
519 __iterator_buffer<_OutIt, _CharT>>>;
520};
521
522// A buffer that counts and limits the number of insertions.
523template <class _OutIt, __fmt_char_type _CharT>
524class __format_to_n_buffer : private __buffer_selector<_OutIt, _CharT>::type {
525public:
526 using _Base _LIBCPP_NODEBUG = __buffer_selector<_OutIt, _CharT>::type;
527
528 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __format_to_n_buffer(_OutIt __out_it, iter_difference_t<_OutIt> __n)
529 : _Base{std::move(__out_it), std::addressof(x&: __max_output_size_)},
530 __max_output_size_{__n < 0 ? size_t{0} : static_cast<size_t>(__n)} {}
531
532 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI auto __make_output_iterator() { return _Base::__make_output_iterator(); }
533
534 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI format_to_n_result<_OutIt> __result() && {
535 return {static_cast<_Base&&>(*this).__out_it(),
536 static_cast<iter_difference_t<_OutIt>>(__max_output_size_.__code_units_written())};
537 }
538
539private:
540 __max_output_size __max_output_size_;
541};
542
543// A buffer that counts the number of insertions.
544//
545// Since formatted_size only needs to know the size, the output itself is
546// discarded.
547template <__fmt_char_type _CharT>
548class __formatted_size_buffer : private __output_buffer<_CharT> {
549public:
550 using _Base _LIBCPP_NODEBUG = __output_buffer<_CharT>;
551
552 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __formatted_size_buffer()
553 : _Base{nullptr, 0, __prepare_write, std::addressof(x&: __max_output_size_)} {}
554
555 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI auto __make_output_iterator() { return _Base::__make_output_iterator(); }
556
557 // This function does not need to be r-value qualified, however this is
558 // consistent with similar objects.
559 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t __result() && { return __max_output_size_.__code_units_written(); }
560
561private:
562 __max_output_size __max_output_size_{0};
563
564 _LIBCPP_HIDE_FROM_ABI static void
565 __prepare_write([[maybe_unused]] __output_buffer<_CharT>& __buffer, [[maybe_unused]] size_t __size_hint) {
566 // Note this function does not satisfy the requirement of giving a 1 code unit buffer.
567 _LIBCPP_ASSERT_INTERNAL(
568 false, "Since __max_output_size_.__max_size_ == 0 there should never be call to this function.");
569 }
570};
571
572// A dynamically growing buffer intended to be used for retargeting a context.
573//
574// P2286 Formatting ranges adds range formatting support. It allows the user to
575// specify the minimum width for the entire formatted range. The width of the
576// range is not known until the range is formatted. Formatting is done to an
577// output_iterator so there's no guarantee it would be possible to add the fill
578// to the front of the output. Instead the range is formatted to a temporary
579// buffer and that buffer is formatted as a string.
580//
581// There is an issue with that approach, the format context used in
582// std::formatter<T>::format contains the output iterator used as part of its
583// type. So using this output iterator means there needs to be a new format
584// context and the format arguments need to be retargeted to the new context.
585// This retargeting is done by a basic_format_context specialized for the
586// __iterator of this container.
587//
588// This class uses its own buffer management, since using vector
589// would lead to a circular include with formatter for vector<bool>.
590template <__fmt_char_type _CharT>
591class __retarget_buffer {
592 using _Alloc _LIBCPP_NODEBUG = allocator<_CharT>;
593
594public:
595 using value_type _LIBCPP_NODEBUG = _CharT;
596
597 struct __iterator {
598 using difference_type _LIBCPP_NODEBUG = ptrdiff_t;
599 using value_type _LIBCPP_NODEBUG = _CharT;
600
601 _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(__retarget_buffer& __buffer)
602 : __buffer_(std::addressof(__buffer)) {}
603 _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator=(const _CharT& __c) {
604 __buffer_->push_back(__c);
605 return *this;
606 }
607 _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator=(_CharT&& __c) {
608 __buffer_->push_back(__c);
609 return *this;
610 }
611
612 _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator*() { return *this; }
613 _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() { return *this; }
614 _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int) { return *this; }
615 __retarget_buffer* __buffer_;
616 };
617
618 __retarget_buffer(const __retarget_buffer&) = delete;
619 __retarget_buffer& operator=(const __retarget_buffer&) = delete;
620
621 _LIBCPP_HIDE_FROM_ABI explicit __retarget_buffer(size_t __size_hint) {
622 // When the initial size is very small a lot of resizes happen
623 // when elements added. So use a hard-coded minimum size.
624 //
625 // Note a size < 2 will not work
626 // - 0 there is no buffer, while push_back requires 1 empty element.
627 // - 1 multiplied by the grow factor is 1 and thus the buffer never
628 // grows.
629 auto __result = std::__allocate_at_least(__alloc_, std::max(a: __size_hint, b: 256 / sizeof(_CharT)));
630 __ptr_ = __result.ptr;
631 __capacity_ = __result.count;
632 }
633
634 _LIBCPP_HIDE_FROM_ABI ~__retarget_buffer() {
635 std::destroy_n(__ptr_, __size_);
636 allocator_traits<_Alloc>::deallocate(__alloc_, __ptr_, __capacity_);
637 }
638
639 _LIBCPP_HIDE_FROM_ABI __iterator __make_output_iterator() { return __iterator{*this}; }
640
641 _LIBCPP_HIDE_FROM_ABI void push_back(_CharT __c) {
642 std::construct_at(__ptr_ + __size_, __c);
643 ++__size_;
644
645 if (__size_ == __capacity_)
646 __grow_buffer();
647 }
648
649 template <__fmt_char_type _InCharT>
650 _LIBCPP_HIDE_FROM_ABI void __copy(basic_string_view<_InCharT> __str) {
651 size_t __n = __str.size();
652 if (__size_ + __n >= __capacity_)
653 // Push_back requires the buffer to have room for at least one character.
654 __grow_buffer(__size_ + __n + 1);
655
656 std::uninitialized_copy_n(__str.data(), __n, __ptr_ + __size_);
657 __size_ += __n;
658 }
659
660 template <contiguous_iterator _Iterator,
661 class _UnaryOperation,
662 __fmt_char_type _InCharT = typename iterator_traits<_Iterator>::value_type>
663 _LIBCPP_HIDE_FROM_ABI void __transform(_Iterator __first, _Iterator __last, _UnaryOperation __operation) {
664 _LIBCPP_ASSERT_INTERNAL(__first <= __last, "not a valid range");
665
666 size_t __n = static_cast<size_t>(__last - __first);
667 if (__size_ + __n >= __capacity_)
668 // Push_back requires the buffer to have room for at least one character.
669 __grow_buffer(__size_ + __n + 1);
670
671 std::uninitialized_default_construct_n(__ptr_ + __size_, __n);
672 std::transform(__first, __last, __ptr_ + __size_, std::move(__operation));
673 __size_ += __n;
674 }
675
676 _LIBCPP_HIDE_FROM_ABI void __fill(size_t __n, _CharT __value) {
677 if (__size_ + __n >= __capacity_)
678 // Push_back requires the buffer to have room for at least one character.
679 __grow_buffer(__size_ + __n + 1);
680
681 std::uninitialized_fill_n(__ptr_ + __size_, __n, __value);
682 __size_ += __n;
683 }
684
685 _LIBCPP_HIDE_FROM_ABI basic_string_view<_CharT> __view() { return {__ptr_, __size_}; }
686
687private:
688 _LIBCPP_HIDE_FROM_ABI void __grow_buffer() { __grow_buffer(__capacity_ * 1.6); }
689
690 _LIBCPP_HIDE_FROM_ABI void __grow_buffer(size_t __capacity) {
691 _LIBCPP_ASSERT_INTERNAL(__capacity > __capacity_, "the buffer must grow");
692 auto __result = std::__allocate_at_least(__alloc_, __capacity);
693 auto __guard = std::__make_exception_guard([&] {
694 allocator_traits<_Alloc>::deallocate(__alloc_, __result.ptr, __result.count);
695 });
696 // This shouldn't throw, but just to be safe. Note that at -O1 this
697 // guard is optimized away so there is no runtime overhead.
698 std::uninitialized_move_n(__ptr_, __size_, __result.ptr);
699 __guard.__complete();
700 std::destroy_n(__ptr_, __size_);
701 allocator_traits<_Alloc>::deallocate(__alloc_, __ptr_, __capacity_);
702
703 __ptr_ = __result.ptr;
704 __capacity_ = __result.count;
705 }
706 _LIBCPP_NO_UNIQUE_ADDRESS _Alloc __alloc_;
707 _CharT* __ptr_;
708 size_t __capacity_;
709 size_t __size_{0};
710};
711
712} // namespace __format
713
714#endif // _LIBCPP_STD_VER >= 20
715
716_LIBCPP_END_NAMESPACE_STD
717
718_LIBCPP_POP_MACROS
719
720#endif // _LIBCPP___FORMAT_BUFFER_H
721