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(__code_units, __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(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(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(__n); 240 if (__n == 0) 241 return; 242 } 243 244 const _InCharT* __first = __str.data(); 245 do { 246 __prepare_write(__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(__n); 267 if (__n == 0) 268 return; 269 } 270 271 do { 272 __prepare_write(__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(__n); 285 if (__n == 0) 286 return; 287 } 288 289 do { 290 __prepare_write(__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(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("__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(__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(__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(__size_hint, 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