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_n.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_traits.h> 33 #include <__memory/construct_at.h> 34 #include <__memory/ranges_construct_at.h> 35 #include <__memory/uninitialized_algorithms.h> 36 #include <__type_traits/add_pointer.h> 37 #include <__type_traits/conditional.h> 38 #include <__utility/exception_guard.h> 39 #include <__utility/move.h> 40 #include <cstddef> 41 #include <string_view> 42 43 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) 44 # pragma GCC system_header 45 #endif 46 47 _LIBCPP_PUSH_MACROS 48 #include <__undef_macros> 49 50 _LIBCPP_BEGIN_NAMESPACE_STD 51 52 #if _LIBCPP_STD_VER >= 20 53 54 namespace __format { 55 56 /// A "buffer" that handles writing to the proper iterator. 57 /// 58 /// This helper is used together with the @ref back_insert_iterator to offer 59 /// type-erasure for the formatting functions. This reduces the number to 60 /// template instantiations. 61 template <__fmt_char_type _CharT> 62 class _LIBCPP_TEMPLATE_VIS __output_buffer { 63 public: 64 using value_type = _CharT; 65 66 template <class _Tp> 67 _LIBCPP_HIDE_FROM_ABI explicit __output_buffer(_CharT* __ptr, size_t __capacity, _Tp* __obj) 68 : __ptr_(__ptr), 69 __capacity_(__capacity), 70 __flush_([](_CharT* __p, size_t __n, void* __o) { static_cast<_Tp*>(__o)->__flush(__p, __n); }), 71 __obj_(__obj) {} 72 73 _LIBCPP_HIDE_FROM_ABI void __reset(_CharT* __ptr, size_t __capacity) { 74 __ptr_ = __ptr; 75 __capacity_ = __capacity; 76 } 77 78 _LIBCPP_HIDE_FROM_ABI auto __make_output_iterator() { return std::back_insert_iterator{*this}; } 79 80 // Used in std::back_insert_iterator. 81 _LIBCPP_HIDE_FROM_ABI void push_back(_CharT __c) { 82 __ptr_[__size_++] = __c; 83 84 // Profiling showed flushing after adding is more efficient than flushing 85 // when entering the function. 86 if (__size_ == __capacity_) 87 __flush(); 88 } 89 90 /// Copies the input __str to the buffer. 91 /// 92 /// Since some of the input is generated by std::to_chars, there needs to be a 93 /// conversion when _CharT is wchar_t. 94 template <__fmt_char_type _InCharT> 95 _LIBCPP_HIDE_FROM_ABI void __copy(basic_string_view<_InCharT> __str) { 96 // When the underlying iterator is a simple iterator the __capacity_ is 97 // infinite. For a string or container back_inserter it isn't. This means 98 // that adding a large string to the buffer can cause some overhead. In that 99 // case a better approach could be: 100 // - flush the buffer 101 // - container.append(__str.begin(), __str.end()); 102 // The same holds true for the fill. 103 // For transform it might be slightly harder, however the use case for 104 // transform is slightly less common; it converts hexadecimal values to 105 // upper case. For integral these strings are short. 106 // TODO FMT Look at the improvements above. 107 size_t __n = __str.size(); 108 109 __flush_on_overflow(__n); 110 if (__n < __capacity_) { // push_back requires the buffer to have room for at least one character (so use <). 111 std::copy_n(__str.data(), __n, std::addressof(__ptr_[__size_])); 112 __size_ += __n; 113 return; 114 } 115 116 // The output doesn't fit in the internal buffer. 117 // Copy the data in "__capacity_" sized chunks. 118 _LIBCPP_ASSERT_INTERNAL(__size_ == 0, "the buffer should be flushed by __flush_on_overflow"); 119 const _InCharT* __first = __str.data(); 120 do { 121 size_t __chunk = std::min(__n, __capacity_); 122 std::copy_n(__first, __chunk, std::addressof(__ptr_[__size_])); 123 __size_ = __chunk; 124 __first += __chunk; 125 __n -= __chunk; 126 __flush(); 127 } while (__n); 128 } 129 130 /// A std::transform wrapper. 131 /// 132 /// Like @ref __copy it may need to do type conversion. 133 template <contiguous_iterator _Iterator, 134 class _UnaryOperation, 135 __fmt_char_type _InCharT = typename iterator_traits<_Iterator>::value_type> 136 _LIBCPP_HIDE_FROM_ABI void __transform(_Iterator __first, _Iterator __last, _UnaryOperation __operation) { 137 _LIBCPP_ASSERT_INTERNAL(__first <= __last, "not a valid range"); 138 139 size_t __n = static_cast<size_t>(__last - __first); 140 __flush_on_overflow(__n); 141 if (__n < __capacity_) { // push_back requires the buffer to have room for at least one character (so use <). 142 std::transform(__first, __last, std::addressof(__ptr_[__size_]), std::move(__operation)); 143 __size_ += __n; 144 return; 145 } 146 147 // The output doesn't fit in the internal buffer. 148 // Transform the data in "__capacity_" sized chunks. 149 _LIBCPP_ASSERT_INTERNAL(__size_ == 0, "the buffer should be flushed by __flush_on_overflow"); 150 do { 151 size_t __chunk = std::min(__n, __capacity_); 152 std::transform(__first, __first + __chunk, std::addressof(__ptr_[__size_]), __operation); 153 __size_ = __chunk; 154 __first += __chunk; 155 __n -= __chunk; 156 __flush(); 157 } while (__n); 158 } 159 160 /// A \c fill_n wrapper. 161 _LIBCPP_HIDE_FROM_ABI void __fill(size_t __n, _CharT __value) { 162 __flush_on_overflow(__n); 163 if (__n < __capacity_) { // push_back requires the buffer to have room for at least one character (so use <). 164 std::fill_n(std::addressof(__ptr_[__size_]), __n, __value); 165 __size_ += __n; 166 return; 167 } 168 169 // The output doesn't fit in the internal buffer. 170 // Fill the buffer in "__capacity_" sized chunks. 171 _LIBCPP_ASSERT_INTERNAL(__size_ == 0, "the buffer should be flushed by __flush_on_overflow"); 172 do { 173 size_t __chunk = std::min(__n, __capacity_); 174 std::fill_n(std::addressof(__ptr_[__size_]), __chunk, __value); 175 __size_ = __chunk; 176 __n -= __chunk; 177 __flush(); 178 } while (__n); 179 } 180 181 _LIBCPP_HIDE_FROM_ABI void __flush() { 182 __flush_(__ptr_, __size_, __obj_); 183 __size_ = 0; 184 } 185 186 private: 187 _CharT* __ptr_; 188 size_t __capacity_; 189 size_t __size_{0}; 190 void (*__flush_)(_CharT*, size_t, void*); 191 void* __obj_; 192 193 /// Flushes the buffer when the output operation would overflow the buffer. 194 /// 195 /// A simple approach for the overflow detection would be something along the 196 /// lines: 197 /// \code 198 /// // The internal buffer is large enough. 199 /// if (__n <= __capacity_) { 200 /// // Flush when we really would overflow. 201 /// if (__size_ + __n >= __capacity_) 202 /// __flush(); 203 /// ... 204 /// } 205 /// \endcode 206 /// 207 /// This approach works for all cases but one: 208 /// A __format_to_n_buffer_base where \ref __enable_direct_output is true. 209 /// In that case the \ref __capacity_ of the buffer changes during the first 210 /// \ref __flush. During that operation the output buffer switches from its 211 /// __writer_ to its __storage_. The \ref __capacity_ of the former depends 212 /// on the value of n, of the latter is a fixed size. For example: 213 /// - a format_to_n call with a 10'000 char buffer, 214 /// - the buffer is filled with 9'500 chars, 215 /// - adding 1'000 elements would overflow the buffer so the buffer gets 216 /// changed and the \ref __capacity_ decreases from 10'000 to 217 /// __buffer_size (256 at the time of writing). 218 /// 219 /// This means that the \ref __flush for this class may need to copy a part of 220 /// the internal buffer to the proper output. In this example there will be 221 /// 500 characters that need this copy operation. 222 /// 223 /// Note it would be more efficient to write 500 chars directly and then swap 224 /// the buffers. This would make the code more complex and \ref format_to_n is 225 /// not the most common use case. Therefore the optimization isn't done. 226 _LIBCPP_HIDE_FROM_ABI void __flush_on_overflow(size_t __n) { 227 if (__size_ + __n >= __capacity_) 228 __flush(); 229 } 230 }; 231 232 /// A storage using an internal buffer. 233 /// 234 /// This storage is used when writing a single element to the output iterator 235 /// is expensive. 236 template <__fmt_char_type _CharT> 237 class _LIBCPP_TEMPLATE_VIS __internal_storage { 238 public: 239 _LIBCPP_HIDE_FROM_ABI _CharT* __begin() { return __buffer_; } 240 241 static constexpr size_t __buffer_size = 256 / sizeof(_CharT); 242 243 private: 244 _CharT __buffer_[__buffer_size]; 245 }; 246 247 /// A storage writing directly to the storage. 248 /// 249 /// This requires the storage to be a contiguous buffer of \a _CharT. 250 /// Since the output is directly written to the underlying storage this class 251 /// is just an empty class. 252 template <__fmt_char_type _CharT> 253 class _LIBCPP_TEMPLATE_VIS __direct_storage {}; 254 255 template <class _OutIt, class _CharT> 256 concept __enable_direct_output = 257 __fmt_char_type<_CharT> && 258 (same_as<_OutIt, _CharT*> 259 // TODO(hardening): the following check might not apply to hardened iterators and might need to be wrapped in an 260 // `#ifdef`. 261 || same_as<_OutIt, __wrap_iter<_CharT*>>); 262 263 /// Write policy for directly writing to the underlying output. 264 template <class _OutIt, __fmt_char_type _CharT> 265 class _LIBCPP_TEMPLATE_VIS __writer_direct { 266 public: 267 _LIBCPP_HIDE_FROM_ABI explicit __writer_direct(_OutIt __out_it) : __out_it_(__out_it) {} 268 269 _LIBCPP_HIDE_FROM_ABI _OutIt __out_it() { return __out_it_; } 270 271 _LIBCPP_HIDE_FROM_ABI void __flush(_CharT*, size_t __n) { 272 // _OutIt can be a __wrap_iter<CharT*>. Therefore the original iterator 273 // is adjusted. 274 __out_it_ += __n; 275 } 276 277 private: 278 _OutIt __out_it_; 279 }; 280 281 /// Write policy for copying the buffer to the output. 282 template <class _OutIt, __fmt_char_type _CharT> 283 class _LIBCPP_TEMPLATE_VIS __writer_iterator { 284 public: 285 _LIBCPP_HIDE_FROM_ABI explicit __writer_iterator(_OutIt __out_it) : __out_it_{std::move(__out_it)} {} 286 287 _LIBCPP_HIDE_FROM_ABI _OutIt __out_it() && { return std::move(__out_it_); } 288 289 _LIBCPP_HIDE_FROM_ABI void __flush(_CharT* __ptr, size_t __n) { 290 __out_it_ = std::ranges::copy_n(__ptr, __n, std::move(__out_it_)).out; 291 } 292 293 private: 294 _OutIt __out_it_; 295 }; 296 297 /// Concept to see whether a \a _Container is insertable. 298 /// 299 /// The concept is used to validate whether multiple calls to a 300 /// \ref back_insert_iterator can be replace by a call to \c _Container::insert. 301 /// 302 /// \note a \a _Container needs to opt-in to the concept by specializing 303 /// \ref __enable_insertable. 304 template <class _Container> 305 concept __insertable = 306 __enable_insertable<_Container> && __fmt_char_type<typename _Container::value_type> && 307 requires(_Container& __t, 308 add_pointer_t<typename _Container::value_type> __first, 309 add_pointer_t<typename _Container::value_type> __last) { __t.insert(__t.end(), __first, __last); }; 310 311 /// Extract the container type of a \ref back_insert_iterator. 312 template <class _It> 313 struct _LIBCPP_TEMPLATE_VIS __back_insert_iterator_container { 314 using type = void; 315 }; 316 317 template <__insertable _Container> 318 struct _LIBCPP_TEMPLATE_VIS __back_insert_iterator_container<back_insert_iterator<_Container>> { 319 using type = _Container; 320 }; 321 322 /// Write policy for inserting the buffer in a container. 323 template <class _Container> 324 class _LIBCPP_TEMPLATE_VIS __writer_container { 325 public: 326 using _CharT = typename _Container::value_type; 327 328 _LIBCPP_HIDE_FROM_ABI explicit __writer_container(back_insert_iterator<_Container> __out_it) 329 : __container_{__out_it.__get_container()} {} 330 331 _LIBCPP_HIDE_FROM_ABI auto __out_it() { return std::back_inserter(*__container_); } 332 333 _LIBCPP_HIDE_FROM_ABI void __flush(_CharT* __ptr, size_t __n) { 334 __container_->insert(__container_->end(), __ptr, __ptr + __n); 335 } 336 337 private: 338 _Container* __container_; 339 }; 340 341 /// Selects the type of the writer used for the output iterator. 342 template <class _OutIt, class _CharT> 343 class _LIBCPP_TEMPLATE_VIS __writer_selector { 344 using _Container = typename __back_insert_iterator_container<_OutIt>::type; 345 346 public: 347 using type = 348 conditional_t<!same_as<_Container, void>, 349 __writer_container<_Container>, 350 conditional_t<__enable_direct_output<_OutIt, _CharT>, 351 __writer_direct<_OutIt, _CharT>, 352 __writer_iterator<_OutIt, _CharT>>>; 353 }; 354 355 /// The generic formatting buffer. 356 template <class _OutIt, __fmt_char_type _CharT> 357 requires(output_iterator<_OutIt, const _CharT&>) 358 class _LIBCPP_TEMPLATE_VIS __format_buffer { 359 using _Storage = 360 conditional_t<__enable_direct_output<_OutIt, _CharT>, __direct_storage<_CharT>, __internal_storage<_CharT>>; 361 362 public: 363 _LIBCPP_HIDE_FROM_ABI explicit __format_buffer(_OutIt __out_it) 364 requires(same_as<_Storage, __internal_storage<_CharT>>) 365 : __output_(__storage_.__begin(), __storage_.__buffer_size, this), __writer_(std::move(__out_it)) {} 366 367 _LIBCPP_HIDE_FROM_ABI explicit __format_buffer(_OutIt __out_it) 368 requires(same_as<_Storage, __direct_storage<_CharT>>) 369 : __output_(std::__unwrap_iter(__out_it), size_t(-1), this), __writer_(std::move(__out_it)) {} 370 371 _LIBCPP_HIDE_FROM_ABI auto __make_output_iterator() { return __output_.__make_output_iterator(); } 372 373 _LIBCPP_HIDE_FROM_ABI void __flush(_CharT* __ptr, size_t __n) { __writer_.__flush(__ptr, __n); } 374 375 _LIBCPP_HIDE_FROM_ABI _OutIt __out_it() && { 376 __output_.__flush(); 377 return std::move(__writer_).__out_it(); 378 } 379 380 private: 381 _LIBCPP_NO_UNIQUE_ADDRESS _Storage __storage_; 382 __output_buffer<_CharT> __output_; 383 typename __writer_selector<_OutIt, _CharT>::type __writer_; 384 }; 385 386 /// A buffer that counts the number of insertions. 387 /// 388 /// Since \ref formatted_size only needs to know the size, the output itself is 389 /// discarded. 390 template <__fmt_char_type _CharT> 391 class _LIBCPP_TEMPLATE_VIS __formatted_size_buffer { 392 public: 393 _LIBCPP_HIDE_FROM_ABI auto __make_output_iterator() { return __output_.__make_output_iterator(); } 394 395 _LIBCPP_HIDE_FROM_ABI void __flush(const _CharT*, size_t __n) { __size_ += __n; } 396 397 _LIBCPP_HIDE_FROM_ABI size_t __result() && { 398 __output_.__flush(); 399 return __size_; 400 } 401 402 private: 403 __internal_storage<_CharT> __storage_; 404 __output_buffer<_CharT> __output_{__storage_.__begin(), __storage_.__buffer_size, this}; 405 size_t __size_{0}; 406 }; 407 408 /// The base of a buffer that counts and limits the number of insertions. 409 template <class _OutIt, __fmt_char_type _CharT, bool> 410 requires(output_iterator<_OutIt, const _CharT&>) 411 struct _LIBCPP_TEMPLATE_VIS __format_to_n_buffer_base { 412 using _Size = iter_difference_t<_OutIt>; 413 414 public: 415 _LIBCPP_HIDE_FROM_ABI explicit __format_to_n_buffer_base(_OutIt __out_it, _Size __max_size) 416 : __writer_(std::move(__out_it)), __max_size_(std::max(_Size(0), __max_size)) {} 417 418 _LIBCPP_HIDE_FROM_ABI void __flush(_CharT* __ptr, size_t __n) { 419 if (_Size(__size_) <= __max_size_) 420 __writer_.__flush(__ptr, std::min(_Size(__n), __max_size_ - __size_)); 421 __size_ += __n; 422 } 423 424 protected: 425 __internal_storage<_CharT> __storage_; 426 __output_buffer<_CharT> __output_{__storage_.__begin(), __storage_.__buffer_size, this}; 427 typename __writer_selector<_OutIt, _CharT>::type __writer_; 428 429 _Size __max_size_; 430 _Size __size_{0}; 431 }; 432 433 /// The base of a buffer that counts and limits the number of insertions. 434 /// 435 /// This version is used when \c __enable_direct_output<_OutIt, _CharT> == true. 436 /// 437 /// This class limits the size available to the direct writer so it will not 438 /// exceed the maximum number of code units. 439 template <class _OutIt, __fmt_char_type _CharT> 440 requires(output_iterator<_OutIt, const _CharT&>) 441 class _LIBCPP_TEMPLATE_VIS __format_to_n_buffer_base<_OutIt, _CharT, true> { 442 using _Size = iter_difference_t<_OutIt>; 443 444 public: 445 _LIBCPP_HIDE_FROM_ABI explicit __format_to_n_buffer_base(_OutIt __out_it, _Size __max_size) 446 : __output_(std::__unwrap_iter(__out_it), __max_size, this), 447 __writer_(std::move(__out_it)), 448 __max_size_(__max_size) { 449 if (__max_size <= 0) [[unlikely]] 450 __output_.__reset(__storage_.__begin(), __storage_.__buffer_size); 451 } 452 453 _LIBCPP_HIDE_FROM_ABI void __flush(_CharT* __ptr, size_t __n) { 454 // A __flush to the direct writer happens in the following occasions: 455 // - The format function has written the maximum number of allowed code 456 // units. At this point it's no longer valid to write to this writer. So 457 // switch to the internal storage. This internal storage doesn't need to 458 // be written anywhere so the __flush for that storage writes no output. 459 // - Like above, but the next "mass write" operation would overflow the 460 // buffer. In that case the buffer is pre-emptively switched. The still 461 // valid code units will be written separately. 462 // - The format_to_n function is finished. In this case there's no need to 463 // switch the buffer, but for simplicity the buffers are still switched. 464 // When the __max_size <= 0 the constructor already switched the buffers. 465 if (__size_ == 0 && __ptr != __storage_.__begin()) { 466 __writer_.__flush(__ptr, __n); 467 __output_.__reset(__storage_.__begin(), __storage_.__buffer_size); 468 } else if (__size_ < __max_size_) { 469 // Copies a part of the internal buffer to the output up to n characters. 470 // See __output_buffer<_CharT>::__flush_on_overflow for more information. 471 _Size __s = std::min(_Size(__n), __max_size_ - __size_); 472 std::copy_n(__ptr, __s, __writer_.__out_it()); 473 __writer_.__flush(__ptr, __s); 474 } 475 476 __size_ += __n; 477 } 478 479 protected: 480 __internal_storage<_CharT> __storage_; 481 __output_buffer<_CharT> __output_; 482 __writer_direct<_OutIt, _CharT> __writer_; 483 484 _Size __max_size_; 485 _Size __size_{0}; 486 }; 487 488 /// The buffer that counts and limits the number of insertions. 489 template <class _OutIt, __fmt_char_type _CharT> 490 requires(output_iterator<_OutIt, const _CharT&>) 491 struct _LIBCPP_TEMPLATE_VIS __format_to_n_buffer final 492 : public __format_to_n_buffer_base< _OutIt, _CharT, __enable_direct_output<_OutIt, _CharT>> { 493 using _Base = __format_to_n_buffer_base<_OutIt, _CharT, __enable_direct_output<_OutIt, _CharT>>; 494 using _Size = iter_difference_t<_OutIt>; 495 496 public: 497 _LIBCPP_HIDE_FROM_ABI explicit __format_to_n_buffer(_OutIt __out_it, _Size __max_size) 498 : _Base(std::move(__out_it), __max_size) {} 499 _LIBCPP_HIDE_FROM_ABI auto __make_output_iterator() { return this->__output_.__make_output_iterator(); } 500 501 _LIBCPP_HIDE_FROM_ABI format_to_n_result<_OutIt> __result() && { 502 this->__output_.__flush(); 503 return {std::move(this->__writer_).__out_it(), this->__size_}; 504 } 505 }; 506 507 // A dynamically growing buffer intended to be used for retargeting a context. 508 // 509 // P2286 Formatting ranges adds range formatting support. It allows the user to 510 // specify the minimum width for the entire formatted range. The width of the 511 // range is not known until the range is formatted. Formatting is done to an 512 // output_iterator so there's no guarantee it would be possible to add the fill 513 // to the front of the output. Instead the range is formatted to a temporary 514 // buffer and that buffer is formatted as a string. 515 // 516 // There is an issue with that approach, the format context used in 517 // std::formatter<T>::format contains the output iterator used as part of its 518 // type. So using this output iterator means there needs to be a new format 519 // context and the format arguments need to be retargeted to the new context. 520 // This retargeting is done by a basic_format_context specialized for the 521 // __iterator of this container. 522 // 523 // This class uses its own buffer management, since using vector 524 // would lead to a circular include with formatter for vector<bool>. 525 template <__fmt_char_type _CharT> 526 class _LIBCPP_TEMPLATE_VIS __retarget_buffer { 527 using _Alloc = allocator<_CharT>; 528 529 public: 530 using value_type = _CharT; 531 532 struct __iterator { 533 using difference_type = ptrdiff_t; 534 using value_type = _CharT; 535 536 _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(__retarget_buffer& __buffer) 537 : __buffer_(std::addressof(__buffer)) {} 538 _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator=(const _CharT& __c) { 539 __buffer_->push_back(__c); 540 return *this; 541 } 542 _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator=(_CharT&& __c) { 543 __buffer_->push_back(__c); 544 return *this; 545 } 546 547 _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator*() { return *this; } 548 _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() { return *this; } 549 _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int) { return *this; } 550 __retarget_buffer* __buffer_; 551 }; 552 553 __retarget_buffer(const __retarget_buffer&) = delete; 554 __retarget_buffer& operator=(const __retarget_buffer&) = delete; 555 556 _LIBCPP_HIDE_FROM_ABI explicit __retarget_buffer(size_t __size_hint) { 557 // When the initial size is very small a lot of resizes happen 558 // when elements added. So use a hard-coded minimum size. 559 // 560 // Note a size < 2 will not work 561 // - 0 there is no buffer, while push_back requires 1 empty element. 562 // - 1 multiplied by the grow factor is 1 and thus the buffer never 563 // grows. 564 auto __result = std::__allocate_at_least(__alloc_, std::max(__size_hint, 256 / sizeof(_CharT))); 565 __ptr_ = __result.ptr; 566 __capacity_ = __result.count; 567 } 568 569 _LIBCPP_HIDE_FROM_ABI ~__retarget_buffer() { 570 ranges::destroy_n(__ptr_, __size_); 571 allocator_traits<_Alloc>::deallocate(__alloc_, __ptr_, __capacity_); 572 } 573 574 _LIBCPP_HIDE_FROM_ABI __iterator __make_output_iterator() { return __iterator{*this}; } 575 576 _LIBCPP_HIDE_FROM_ABI void push_back(_CharT __c) { 577 std::construct_at(__ptr_ + __size_, __c); 578 ++__size_; 579 580 if (__size_ == __capacity_) 581 __grow_buffer(); 582 } 583 584 template <__fmt_char_type _InCharT> 585 _LIBCPP_HIDE_FROM_ABI void __copy(basic_string_view<_InCharT> __str) { 586 size_t __n = __str.size(); 587 if (__size_ + __n >= __capacity_) 588 // Push_back requires the buffer to have room for at least one character. 589 __grow_buffer(__size_ + __n + 1); 590 591 std::uninitialized_copy_n(__str.data(), __n, __ptr_ + __size_); 592 __size_ += __n; 593 } 594 595 template <contiguous_iterator _Iterator, 596 class _UnaryOperation, 597 __fmt_char_type _InCharT = typename iterator_traits<_Iterator>::value_type> 598 _LIBCPP_HIDE_FROM_ABI void __transform(_Iterator __first, _Iterator __last, _UnaryOperation __operation) { 599 _LIBCPP_ASSERT_INTERNAL(__first <= __last, "not a valid range"); 600 601 size_t __n = static_cast<size_t>(__last - __first); 602 if (__size_ + __n >= __capacity_) 603 // Push_back requires the buffer to have room for at least one character. 604 __grow_buffer(__size_ + __n + 1); 605 606 std::uninitialized_default_construct_n(__ptr_ + __size_, __n); 607 std::transform(__first, __last, __ptr_ + __size_, std::move(__operation)); 608 __size_ += __n; 609 } 610 611 _LIBCPP_HIDE_FROM_ABI void __fill(size_t __n, _CharT __value) { 612 if (__size_ + __n >= __capacity_) 613 // Push_back requires the buffer to have room for at least one character. 614 __grow_buffer(__size_ + __n + 1); 615 616 std::uninitialized_fill_n(__ptr_ + __size_, __n, __value); 617 __size_ += __n; 618 } 619 620 _LIBCPP_HIDE_FROM_ABI basic_string_view<_CharT> __view() { return {__ptr_, __size_}; } 621 622 private: 623 _LIBCPP_HIDE_FROM_ABI void __grow_buffer() { __grow_buffer(__capacity_ * 1.6); } 624 625 _LIBCPP_HIDE_FROM_ABI void __grow_buffer(size_t __capacity) { 626 _LIBCPP_ASSERT_INTERNAL(__capacity > __capacity_, "the buffer must grow"); 627 auto __result = std::__allocate_at_least(__alloc_, __capacity); 628 auto __guard = std::__make_exception_guard([&] { 629 allocator_traits<_Alloc>::deallocate(__alloc_, __result.ptr, __result.count); 630 }); 631 // This shouldn't throw, but just to be safe. Note that at -O1 this 632 // guard is optimized away so there is no runtime overhead. 633 std::uninitialized_move_n(__ptr_, __size_, __result.ptr); 634 __guard.__complete(); 635 ranges::destroy_n(__ptr_, __size_); 636 allocator_traits<_Alloc>::deallocate(__alloc_, __ptr_, __capacity_); 637 638 __ptr_ = __result.ptr; 639 __capacity_ = __result.count; 640 } 641 _LIBCPP_NO_UNIQUE_ADDRESS _Alloc __alloc_; 642 _CharT* __ptr_; 643 size_t __capacity_; 644 size_t __size_{0}; 645 }; 646 647 } // namespace __format 648 649 #endif //_LIBCPP_STD_VER >= 20 650 651 _LIBCPP_END_NAMESPACE_STD 652 653 _LIBCPP_POP_MACROS 654 655 #endif // _LIBCPP___FORMAT_BUFFER_H 656