| 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 | |