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