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/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_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 | |
55 | namespace __format { |
56 | |
57 | // A helper to limit the total size of code units written. |
58 | class _LIBCPP_HIDE_FROM_ABI __max_output_size { |
59 | public: |
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 | |
74 | private: |
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. |
181 | template <__fmt_char_type _CharT> |
182 | class __output_buffer { |
183 | public: |
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 | |
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(code_units: __n); |
267 | if (__n == 0) |
268 | return; |
269 | } |
270 | |
271 | do { |
272 | __prepare_write(code_units: __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(code_units: __n); |
285 | if (__n == 0) |
286 | return; |
287 | } |
288 | |
289 | do { |
290 | __prepare_write(code_units: __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 | |
301 | private: |
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 | |
318 | template <class _OutIt, class _CharT> |
319 | concept __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 an |
323 | // `#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 a |
329 | /// \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 specializing |
332 | /// \ref __enable_insertable. |
333 | template <class _Container> |
334 | concept __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. |
341 | template <class _It> |
342 | struct __back_insert_iterator_container { |
343 | using type _LIBCPP_NODEBUG = void; |
344 | }; |
345 | |
346 | template <__insertable _Container> |
347 | struct __back_insert_iterator_container<back_insert_iterator<_Container>> { |
348 | using type _LIBCPP_NODEBUG = _Container; |
349 | }; |
350 | |
351 | // A dynamically growing buffer. |
352 | template <__fmt_char_type _CharT> |
353 | class __allocating_buffer : public __output_buffer<_CharT> { |
354 | public: |
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 | |
371 | private: |
372 | using _Alloc _LIBCPP_NODEBUG = allocator<_CharT>; |
373 | |
374 | // Since allocating is expensive the class has a small internal buffer. When |
375 | // 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 explicit |
388 | // 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(capacity: 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. |
409 | template <class _OutIt, __fmt_char_type _CharT> |
410 | class __direct_iterator_buffer : public __output_buffer<_CharT> { |
411 | public: |
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 | |
422 | private: |
423 | // The function format_to expects a buffer large enough for the output. The |
424 | // function format_to_n has its own helper class that restricts the number of |
425 | // write options. So this function class can pretend to have an infinite |
426 | // buffer. |
427 | static constexpr size_t __buffer_size = -1; |
428 | |
429 | _OutIt __out_it_; |
430 | |
431 | _LIBCPP_HIDE_FROM_ABI static void |
432 | __prepare_write([[maybe_unused]] __output_buffer<_CharT>& __buffer, [[maybe_unused]] size_t __size_hint) { |
433 | std::__throw_length_error(msg: "__direct_iterator_buffer" ); |
434 | } |
435 | }; |
436 | |
437 | // A buffer that writes its output to the end of a container. |
438 | template <class _OutIt, __fmt_char_type _CharT> |
439 | class __container_inserter_buffer : public __output_buffer<_CharT> { |
440 | public: |
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 | |
454 | private: |
455 | typename __back_insert_iterator_container<_OutIt>::type* __container_; |
456 | |
457 | // This class uses a fixed size buffer and appends the elements in |
458 | // __buffer_size chunks. An alternative would be to use an allocating buffer |
459 | // and append the output in a single write operation. Benchmarking showed no |
460 | // 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 void |
470 | __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 benefit |
478 | // from allocating and then inserting. |
479 | template <class _OutIt, __fmt_char_type _CharT> |
480 | class __iterator_buffer : public __allocating_buffer<_CharT> { |
481 | public: |
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 | |
492 | private: |
493 | _OutIt __out_it_; |
494 | }; |
495 | |
496 | // Selects the type of the buffer used for the output iterator. |
497 | template <class _OutIt, __fmt_char_type _CharT> |
498 | class __buffer_selector { |
499 | using _Container _LIBCPP_NODEBUG = __back_insert_iterator_container<_OutIt>::type; |
500 | |
501 | public: |
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. |
511 | template <class _OutIt, __fmt_char_type _CharT> |
512 | class __format_to_n_buffer : private __buffer_selector<_OutIt, _CharT>::type { |
513 | public: |
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(x&: __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 | |
527 | private: |
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 is |
534 | // discarded. |
535 | template <__fmt_char_type _CharT> |
536 | class __formatted_size_buffer : private __output_buffer<_CharT> { |
537 | public: |
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(x&: __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 is |
546 | // consistent with similar objects. |
547 | [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t __result() && { return __max_output_size_.__code_units_written(); } |
548 | |
549 | private: |
550 | __max_output_size __max_output_size_{0}; |
551 | |
552 | _LIBCPP_HIDE_FROM_ABI static void |
553 | __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 to |
563 | // specify the minimum width for the entire formatted range. The width of the |
564 | // range is not known until the range is formatted. Formatting is done to an |
565 | // output_iterator so there's no guarantee it would be possible to add the fill |
566 | // to the front of the output. Instead the range is formatted to a temporary |
567 | // buffer and that buffer is formatted as a string. |
568 | // |
569 | // There is an issue with that approach, the format context used in |
570 | // std::formatter<T>::format contains the output iterator used as part of its |
571 | // type. So using this output iterator means there needs to be a new format |
572 | // 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 the |
574 | // __iterator of this container. |
575 | // |
576 | // This class uses its own buffer management, since using vector |
577 | // would lead to a circular include with formatter for vector<bool>. |
578 | template <__fmt_char_type _CharT> |
579 | class __retarget_buffer { |
580 | using _Alloc _LIBCPP_NODEBUG = allocator<_CharT>; |
581 | |
582 | public: |
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 happen |
611 | // when elements added. So use a hard-coded minimum size. |
612 | // |
613 | // Note a size < 2 will not work |
614 | // - 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 never |
616 | // grows. |
617 | auto __result = std::__allocate_at_least(__alloc_, std::max(a: __size_hint, b: 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 | |
675 | private: |
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 this |
685 | // 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 __format |
701 | |
702 | #endif // _LIBCPP_STD_VER >= 20 |
703 | |
704 | _LIBCPP_END_NAMESPACE_STD |
705 | |
706 | _LIBCPP_POP_MACROS |
707 | |
708 | #endif // _LIBCPP___FORMAT_BUFFER_H |
709 | |