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/ranges_copy_n.h> 19 #include <__algorithm/transform.h> 20 #include <__algorithm/unwrap_iter.h> 21 #include <__concepts/same_as.h> 22 #include <__config> 23 #include <__format/concepts.h> 24 #include <__format/enable_insertable.h> 25 #include <__format/format_to_n_result.h> 26 #include <__iterator/back_insert_iterator.h> 27 #include <__iterator/concepts.h> 28 #include <__iterator/incrementable_traits.h> 29 #include <__iterator/iterator_traits.h> 30 #include <__iterator/wrap_iter.h> 31 #include <__memory/addressof.h> 32 #include <__memory/allocate_at_least.h> 33 #include <__memory/allocator.h> 34 #include <__memory/allocator_traits.h> 35 #include <__memory/construct_at.h> 36 #include <__memory/ranges_construct_at.h> 37 #include <__memory/uninitialized_algorithms.h> 38 #include <__type_traits/add_pointer.h> 39 #include <__type_traits/conditional.h> 40 #include <__utility/exception_guard.h> 41 #include <__utility/move.h> 42 #include <stdexcept> 43 #include <string_view> 44 45 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) 46 # pragma GCC system_header 47 #endif 48 49 _LIBCPP_PUSH_MACROS 50 #include <__undef_macros> 51 52 _LIBCPP_BEGIN_NAMESPACE_STD 53 54 #if _LIBCPP_STD_VER >= 20 55 56 namespace __format { 57 58 // A helper to limit the total size of code units written. 59 class _LIBCPP_HIDE_FROM_ABI __max_output_size { 60 public: 61 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI explicit __max_output_size(size_t __max_size) : __max_size_{__max_size} {} 62 63 // This function adjusts the size of a (bulk) write operations. It ensures the 64 // number of code units written by a __output_buffer never exceeds 65 // __max_size_ code units. 66 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t __write_request(size_t __code_units) { 67 size_t __result = 68 __code_units_written_ < __max_size_ ? std::min(__code_units, __max_size_ - __code_units_written_) : 0; 69 __code_units_written_ += __code_units; 70 return __result; 71 } 72 73 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t __code_units_written() const noexcept { return __code_units_written_; } 74 75 private: 76 size_t __max_size_; 77 // The code units that would have been written if there was no limit. 78 // format_to_n returns this value. 79 size_t __code_units_written_{0}; 80 }; 81 82 /// A "buffer" that handles writing to the proper iterator. 83 /// 84 /// This helper is used together with the @ref back_insert_iterator to offer 85 /// type-erasure for the formatting functions. This reduces the number to 86 /// template instantiations. 87 /// 88 /// The design is the following: 89 /// - There is an external object that connects the buffer to the output. 90 /// - This buffer object: 91 /// - inherits publicly from this class. 92 /// - has a static or dynamic buffer. 93 /// - has a static member function to make space in its buffer write 94 /// operations. This can be done by increasing the size of the internal 95 /// buffer or by writing the contents of the buffer to the output iterator. 96 /// 97 /// This member function is a constructor argument, so its name is not 98 /// fixed. The code uses the name __prepare_write. 99 /// - The number of output code units can be limited by a __max_output_size 100 /// object. This is used in format_to_n This object: 101 /// - Contains the maximum number of code units to be written. 102 /// - Contains the number of code units that are requested to be written. 103 /// This number is returned to the user of format_to_n. 104 /// - The write functions call the object's __request_write member function. 105 /// This function: 106 /// - Updates the number of code units that are requested to be written. 107 /// - Returns the number of code units that can be written without 108 /// exceeding the maximum number of code units to be written. 109 /// 110 /// Documentation for the buffer usage members: 111 /// - __ptr_ 112 /// The start of the buffer. 113 /// - __capacity_ 114 /// The number of code units that can be written. This means 115 /// [__ptr_, __ptr_ + __capacity_) is a valid range to write to. 116 /// - __size_ 117 /// The number of code units written in the buffer. The next code unit will 118 /// be written at __ptr_ + __size_. This __size_ may NOT contain the total 119 /// number of code units written by the __output_buffer. Whether or not it 120 /// does depends on the sub-class used. Typically the total number of code 121 /// units written is not interesting. It is interesting for format_to_n which 122 /// has its own way to track this number. 123 /// 124 /// Documentation for the modifying buffer operations: 125 /// The subclasses have a function with the following signature: 126 /// 127 /// static void __prepare_write( 128 /// __output_buffer<_CharT>& __buffer, size_t __code_units); 129 /// 130 /// This function is called when a write function writes more code units than 131 /// the buffer's available space. When an __max_output_size object is provided 132 /// the number of code units is the number of code units returned from 133 /// __max_output_size::__request_write function. 134 /// 135 /// - The __buffer contains *this. Since the class containing this function 136 /// inherits from __output_buffer it's safe to cast it to the subclass being 137 /// used. 138 /// - The __code_units is the number of code units the caller will write + 1. 139 /// - This value does not take the available space of the buffer into account. 140 /// - The push_back function is more efficient when writing before resizing, 141 /// this means the buffer should always have room for one code unit. Hence 142 /// the + 1 is the size. 143 /// - When the function returns there is room for at least one additional code 144 /// unit. There is no requirement there is room for __code_units code units: 145 /// - The class has some "bulk" operations. For example, __copy which copies 146 /// the contents of a basic_string_view to the output. If the sub-class has 147 /// a fixed size buffer the size of the basic_string_view may be larger 148 /// than the buffer. In that case it's impossible to honor the requested 149 /// size. 150 /// - When the buffer has room for at least one code unit the function may be 151 /// a no-op. 152 /// - When the function makes space for more code units it uses one for these 153 /// functions to signal the change: 154 /// - __buffer_flushed() 155 /// - This function is typically used for a fixed sized buffer. 156 /// - The current contents of [__ptr_, __ptr_ + __size_) have been 157 /// processed. 158 /// - __ptr_ remains unchanged. 159 /// - __capacity_ remains unchanged. 160 /// - __size_ will be set to 0. 161 /// - __buffer_moved(_CharT* __ptr, size_t __capacity) 162 /// - This function is typically used for a dynamic sized buffer. There the 163 /// location of the buffer changes due to reallocations. 164 /// - __ptr_ will be set to __ptr. (This value may be the old value of 165 /// __ptr_). 166 /// - __capacity_ will be set to __capacity. (This value may be the old 167 /// value of __capacity_). 168 /// - __size_ remains unchanged, 169 /// - The range [__ptr, __ptr + __size_) contains the original data of the 170 /// range [__ptr_, __ptr_ + __size_). 171 /// 172 /// The push_back function expects a valid buffer and a capacity of at least 1. 173 /// This means: 174 /// - The class is constructed with a valid buffer, 175 /// - __buffer_moved is called with a valid buffer is used before the first 176 /// write operation, 177 /// - no write function is ever called, or 178 /// - the class is constructed with a __max_output_size object with __max_size 0. 179 /// 180 /// The latter option allows formatted_size to use the output buffer without 181 /// ever writing anything to the buffer. 182 template <__fmt_char_type _CharT> 183 class _LIBCPP_TEMPLATE_VIS __output_buffer { 184 public: 185 using value_type _LIBCPP_NODEBUG = _CharT; 186 using __prepare_write_type _LIBCPP_NODEBUG = void (*)(__output_buffer<_CharT>&, size_t); 187 188 [[nodiscard]] 189 _LIBCPP_HIDE_FROM_ABI explicit __output_buffer(_CharT* __ptr, size_t __capacity, __prepare_write_type __function) 190 : __output_buffer{__ptr, __capacity, __function, nullptr} {} 191 192 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI explicit __output_buffer( 193 _CharT* __ptr, size_t __capacity, __prepare_write_type __function, __max_output_size* __max_output_size) 194 : __ptr_(__ptr), __capacity_(__capacity), __prepare_write_(__function), __max_output_size_(__max_output_size) {} 195 196 _LIBCPP_HIDE_FROM_ABI void __buffer_flushed() { __size_ = 0; } 197 198 _LIBCPP_HIDE_FROM_ABI void __buffer_moved(_CharT* __ptr, size_t __capacity) { 199 __ptr_ = __ptr; 200 __capacity_ = __capacity; 201 } 202 203 _LIBCPP_HIDE_FROM_ABI auto __make_output_iterator() { return std::back_insert_iterator{*this}; } 204 205 // Used in std::back_insert_iterator. 206 _LIBCPP_HIDE_FROM_ABI void push_back(_CharT __c) { 207 if (__max_output_size_ && __max_output_size_->__write_request(1) == 0) 208 return; 209 210 _LIBCPP_ASSERT_INTERNAL( 211 __ptr_ && __size_ < __capacity_ && __available() >= 1, "attempted to write outside the buffer"); 212 213 __ptr_[__size_++] = __c; 214 215 // Profiling showed flushing after adding is more efficient than flushing 216 // when entering the function. 217 if (__size_ == __capacity_) 218 __prepare_write(0); 219 } 220 221 /// Copies the input __str to the buffer. 222 /// 223 /// Since some of the input is generated by std::to_chars, there needs to be a 224 /// conversion when _CharT is wchar_t. 225 template <__fmt_char_type _InCharT> 226 _LIBCPP_HIDE_FROM_ABI void __copy(basic_string_view<_InCharT> __str) { 227 // When the underlying iterator is a simple iterator the __capacity_ is 228 // infinite. For a string or container back_inserter it isn't. This means 229 // that adding a large string to the buffer can cause some overhead. In that 230 // case a better approach could be: 231 // - flush the buffer 232 // - container.append(__str.begin(), __str.end()); 233 // The same holds true for the fill. 234 // For transform it might be slightly harder, however the use case for 235 // transform is slightly less common; it converts hexadecimal values to 236 // upper case. For integral these strings are short. 237 // TODO FMT Look at the improvements above. 238 size_t __n = __str.size(); 239 if (__max_output_size_) { 240 __n = __max_output_size_->__write_request(__n); 241 if (__n == 0) 242 return; 243 } 244 245 const _InCharT* __first = __str.data(); 246 do { 247 __prepare_write(__n); 248 size_t __chunk = std::min(__n, __available()); 249 std::copy_n(__first, __chunk, std::addressof(__ptr_[__size_])); 250 __size_ += __chunk; 251 __first += __chunk; 252 __n -= __chunk; 253 } while (__n); 254 } 255 256 /// A std::transform wrapper. 257 /// 258 /// Like @ref __copy it may need to do type conversion. 259 template <contiguous_iterator _Iterator, 260 class _UnaryOperation, 261 __fmt_char_type _InCharT = typename iterator_traits<_Iterator>::value_type> 262 _LIBCPP_HIDE_FROM_ABI void __transform(_Iterator __first, _Iterator __last, _UnaryOperation __operation) { 263 _LIBCPP_ASSERT_INTERNAL(__first <= __last, "not a valid range"); 264 265 size_t __n = static_cast<size_t>(__last - __first); 266 if (__max_output_size_) { 267 __n = __max_output_size_->__write_request(__n); 268 if (__n == 0) 269 return; 270 } 271 272 do { 273 __prepare_write(__n); 274 size_t __chunk = std::min(__n, __available()); 275 std::transform(__first, __first + __chunk, std::addressof(__ptr_[__size_]), __operation); 276 __size_ += __chunk; 277 __first += __chunk; 278 __n -= __chunk; 279 } while (__n); 280 } 281 282 /// A \c fill_n wrapper. 283 _LIBCPP_HIDE_FROM_ABI void __fill(size_t __n, _CharT __value) { 284 if (__max_output_size_) { 285 __n = __max_output_size_->__write_request(__n); 286 if (__n == 0) 287 return; 288 } 289 290 do { 291 __prepare_write(__n); 292 size_t __chunk = std::min(__n, __available()); 293 std::fill_n(std::addressof(__ptr_[__size_]), __chunk, __value); 294 __size_ += __chunk; 295 __n -= __chunk; 296 } while (__n); 297 } 298 299 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t __capacity() const { return __capacity_; } 300 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t __size() const { return __size_; } 301 302 private: 303 _CharT* __ptr_; 304 size_t __capacity_; 305 size_t __size_{0}; 306 void (*__prepare_write_)(__output_buffer<_CharT>&, size_t); 307 __max_output_size* __max_output_size_; 308 309 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t __available() const { return __capacity_ - __size_; } 310 311 _LIBCPP_HIDE_FROM_ABI void __prepare_write(size_t __code_units) { 312 // Always have space for one additional code unit. This is a precondition of the push_back function. 313 __code_units += 1; 314 if (__available() < __code_units) 315 __prepare_write_(*this, __code_units + 1); 316 } 317 }; 318 319 template <class _OutIt, class _CharT> 320 concept __enable_direct_output = 321 __fmt_char_type<_CharT> && 322 (same_as<_OutIt, _CharT*> 323 // TODO(hardening): the following check might not apply to hardened iterators and might need to be wrapped in an 324 // `#ifdef`. 325 || same_as<_OutIt, __wrap_iter<_CharT*>>); 326 327 /// Concept to see whether a \a _Container is insertable. 328 /// 329 /// The concept is used to validate whether multiple calls to a 330 /// \ref back_insert_iterator can be replace by a call to \c _Container::insert. 331 /// 332 /// \note a \a _Container needs to opt-in to the concept by specializing 333 /// \ref __enable_insertable. 334 template <class _Container> 335 concept __insertable = 336 __enable_insertable<_Container> && __fmt_char_type<typename _Container::value_type> && 337 requires(_Container& __t, 338 add_pointer_t<typename _Container::value_type> __first, 339 add_pointer_t<typename _Container::value_type> __last) { __t.insert(__t.end(), __first, __last); }; 340 341 /// Extract the container type of a \ref back_insert_iterator. 342 template <class _It> 343 struct _LIBCPP_TEMPLATE_VIS __back_insert_iterator_container { 344 using type _LIBCPP_NODEBUG = void; 345 }; 346 347 template <__insertable _Container> 348 struct _LIBCPP_TEMPLATE_VIS __back_insert_iterator_container<back_insert_iterator<_Container>> { 349 using type _LIBCPP_NODEBUG = _Container; 350 }; 351 352 // A dynamically growing buffer. 353 template <__fmt_char_type _CharT> 354 class _LIBCPP_TEMPLATE_VIS __allocating_buffer : public __output_buffer<_CharT> { 355 public: 356 __allocating_buffer(const __allocating_buffer&) = delete; 357 __allocating_buffer& operator=(const __allocating_buffer&) = delete; 358 359 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __allocating_buffer() : __allocating_buffer{nullptr} {} 360 361 [[nodiscard]] 362 _LIBCPP_HIDE_FROM_ABI explicit __allocating_buffer(__max_output_size* __max_output_size) 363 : __output_buffer<_CharT>{__small_buffer_, __buffer_size_, __prepare_write, __max_output_size} {} 364 365 _LIBCPP_HIDE_FROM_ABI ~__allocating_buffer() { 366 if (__ptr_ != __small_buffer_) 367 _Alloc{}.deallocate(__ptr_, this->__capacity()); 368 } 369 370 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI basic_string_view<_CharT> __view() { return {__ptr_, this->__size()}; } 371 372 private: 373 using _Alloc _LIBCPP_NODEBUG = allocator<_CharT>; 374 375 // Since allocating is expensive the class has a small internal buffer. When 376 // its capacity is exceeded a dynamic buffer will be allocated. 377 static constexpr size_t __buffer_size_ = 256; 378 _CharT __small_buffer_[__buffer_size_]; 379 380 _CharT* __ptr_{__small_buffer_}; 381 382 _LIBCPP_HIDE_FROM_ABI void __grow_buffer(size_t __capacity) { 383 if (__capacity < __buffer_size_) 384 return; 385 386 _LIBCPP_ASSERT_INTERNAL(__capacity > this->__capacity(), "the buffer must grow"); 387 388 // _CharT is an implicit lifetime type so can be used without explicit 389 // construction or destruction. 390 _Alloc __alloc; 391 auto __result = std::__allocate_at_least(__alloc, __capacity); 392 std::copy_n(__ptr_, this->__size(), __result.ptr); 393 if (__ptr_ != __small_buffer_) 394 __alloc.deallocate(__ptr_, this->__capacity()); 395 396 __ptr_ = __result.ptr; 397 this->__buffer_moved(__ptr_, __result.count); 398 } 399 400 _LIBCPP_HIDE_FROM_ABI void __prepare_write(size_t __size_hint) { 401 __grow_buffer(std::max<size_t>(this->__capacity() + __size_hint, this->__capacity() * 1.6)); 402 } 403 404 _LIBCPP_HIDE_FROM_ABI static void __prepare_write(__output_buffer<_CharT>& __buffer, size_t __size_hint) { 405 static_cast<__allocating_buffer<_CharT>&>(__buffer).__prepare_write(__size_hint); 406 } 407 }; 408 409 // A buffer that directly writes to the underlying buffer. 410 template <class _OutIt, __fmt_char_type _CharT> 411 class _LIBCPP_TEMPLATE_VIS __direct_iterator_buffer : public __output_buffer<_CharT> { 412 public: 413 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI explicit __direct_iterator_buffer(_OutIt __out_it) 414 : __direct_iterator_buffer{__out_it, nullptr} {} 415 416 [[nodiscard]] 417 _LIBCPP_HIDE_FROM_ABI explicit __direct_iterator_buffer(_OutIt __out_it, __max_output_size* __max_output_size) 418 : __output_buffer<_CharT>{std::__unwrap_iter(__out_it), __buffer_size, __prepare_write, __max_output_size}, 419 __out_it_(__out_it) {} 420 421 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _OutIt __out_it() && { return __out_it_ + this->__size(); } 422 423 private: 424 // The function format_to expects a buffer large enough for the output. The 425 // function format_to_n has its own helper class that restricts the number of 426 // write options. So this function class can pretend to have an infinite 427 // buffer. 428 static constexpr size_t __buffer_size = -1; 429 430 _OutIt __out_it_; 431 432 _LIBCPP_HIDE_FROM_ABI static void 433 __prepare_write([[maybe_unused]] __output_buffer<_CharT>& __buffer, [[maybe_unused]] size_t __size_hint) { 434 std::__throw_length_error("__direct_iterator_buffer"); 435 } 436 }; 437 438 // A buffer that writes its output to the end of a container. 439 template <class _OutIt, __fmt_char_type _CharT> 440 class _LIBCPP_TEMPLATE_VIS __container_inserter_buffer : public __output_buffer<_CharT> { 441 public: 442 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI explicit __container_inserter_buffer(_OutIt __out_it) 443 : __container_inserter_buffer{__out_it, nullptr} {} 444 445 [[nodiscard]] 446 _LIBCPP_HIDE_FROM_ABI explicit __container_inserter_buffer(_OutIt __out_it, __max_output_size* __max_output_size) 447 : __output_buffer<_CharT>{__small_buffer_, __buffer_size, __prepare_write, __max_output_size}, 448 __container_{__out_it.__get_container()} {} 449 450 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI auto __out_it() && { 451 __container_->insert(__container_->end(), __small_buffer_, __small_buffer_ + this->__size()); 452 return std::back_inserter(*__container_); 453 } 454 455 private: 456 typename __back_insert_iterator_container<_OutIt>::type* __container_; 457 458 // This class uses a fixed size buffer and appends the elements in 459 // __buffer_size chunks. An alternative would be to use an allocating buffer 460 // and append the output in a single write operation. Benchmarking showed no 461 // performance difference. 462 static constexpr size_t __buffer_size = 256; 463 _CharT __small_buffer_[__buffer_size]; 464 465 _LIBCPP_HIDE_FROM_ABI void __prepare_write() { 466 __container_->insert(__container_->end(), __small_buffer_, __small_buffer_ + this->__size()); 467 this->__buffer_flushed(); 468 } 469 470 _LIBCPP_HIDE_FROM_ABI static void 471 __prepare_write(__output_buffer<_CharT>& __buffer, [[maybe_unused]] size_t __size_hint) { 472 static_cast<__container_inserter_buffer<_OutIt, _CharT>&>(__buffer).__prepare_write(); 473 } 474 }; 475 476 // A buffer that writes to an iterator. 477 // 478 // Unlike the __container_inserter_buffer this class' performance does benefit 479 // from allocating and then inserting. 480 template <class _OutIt, __fmt_char_type _CharT> 481 class _LIBCPP_TEMPLATE_VIS __iterator_buffer : public __allocating_buffer<_CharT> { 482 public: 483 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI explicit __iterator_buffer(_OutIt __out_it) 484 : __allocating_buffer<_CharT>{}, __out_it_{std::move(__out_it)} {} 485 486 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI explicit __iterator_buffer(_OutIt __out_it, __max_output_size* __max_output_size) 487 : __allocating_buffer<_CharT>{__max_output_size}, __out_it_{std::move(__out_it)} {} 488 489 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI auto __out_it() && { 490 return std::ranges::copy(this->__view(), std::move(__out_it_)).out; 491 } 492 493 private: 494 _OutIt __out_it_; 495 }; 496 497 // Selects the type of the buffer used for the output iterator. 498 template <class _OutIt, __fmt_char_type _CharT> 499 class _LIBCPP_TEMPLATE_VIS __buffer_selector { 500 using _Container _LIBCPP_NODEBUG = __back_insert_iterator_container<_OutIt>::type; 501 502 public: 503 using type _LIBCPP_NODEBUG = 504 conditional_t<!same_as<_Container, void>, 505 __container_inserter_buffer<_OutIt, _CharT>, 506 conditional_t<__enable_direct_output<_OutIt, _CharT>, 507 __direct_iterator_buffer<_OutIt, _CharT>, 508 __iterator_buffer<_OutIt, _CharT>>>; 509 }; 510 511 // A buffer that counts and limits the number of insertions. 512 template <class _OutIt, __fmt_char_type _CharT> 513 class _LIBCPP_TEMPLATE_VIS __format_to_n_buffer : private __buffer_selector<_OutIt, _CharT>::type { 514 public: 515 using _Base _LIBCPP_NODEBUG = __buffer_selector<_OutIt, _CharT>::type; 516 517 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __format_to_n_buffer(_OutIt __out_it, iter_difference_t<_OutIt> __n) 518 : _Base{std::move(__out_it), std::addressof(__max_output_size_)}, 519 __max_output_size_{__n < 0 ? size_t{0} : static_cast<size_t>(__n)} {} 520 521 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI auto __make_output_iterator() { return _Base::__make_output_iterator(); } 522 523 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI format_to_n_result<_OutIt> __result() && { 524 return {static_cast<_Base&&>(*this).__out_it(), 525 static_cast<iter_difference_t<_OutIt>>(__max_output_size_.__code_units_written())}; 526 } 527 528 private: 529 __max_output_size __max_output_size_; 530 }; 531 532 // A buffer that counts the number of insertions. 533 // 534 // Since formatted_size only needs to know the size, the output itself is 535 // discarded. 536 template <__fmt_char_type _CharT> 537 class _LIBCPP_TEMPLATE_VIS __formatted_size_buffer : private __output_buffer<_CharT> { 538 public: 539 using _Base _LIBCPP_NODEBUG = __output_buffer<_CharT>; 540 541 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __formatted_size_buffer() 542 : _Base{nullptr, 0, __prepare_write, std::addressof(__max_output_size_)} {} 543 544 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI auto __make_output_iterator() { return _Base::__make_output_iterator(); } 545 546 // This function does not need to be r-value qualified, however this is 547 // consistent with similar objects. 548 [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t __result() && { return __max_output_size_.__code_units_written(); } 549 550 private: 551 __max_output_size __max_output_size_{0}; 552 553 _LIBCPP_HIDE_FROM_ABI static void 554 __prepare_write([[maybe_unused]] __output_buffer<_CharT>& __buffer, [[maybe_unused]] size_t __size_hint) { 555 // Note this function does not satisfy the requirement of giving a 1 code unit buffer. 556 _LIBCPP_ASSERT_INTERNAL( 557 false, "Since __max_output_size_.__max_size_ == 0 there should never be call to this function."); 558 } 559 }; 560 561 // A dynamically growing buffer intended to be used for retargeting a context. 562 // 563 // P2286 Formatting ranges adds range formatting support. It allows the user to 564 // specify the minimum width for the entire formatted range. The width of the 565 // range is not known until the range is formatted. Formatting is done to an 566 // output_iterator so there's no guarantee it would be possible to add the fill 567 // to the front of the output. Instead the range is formatted to a temporary 568 // buffer and that buffer is formatted as a string. 569 // 570 // There is an issue with that approach, the format context used in 571 // std::formatter<T>::format contains the output iterator used as part of its 572 // type. So using this output iterator means there needs to be a new format 573 // context and the format arguments need to be retargeted to the new context. 574 // This retargeting is done by a basic_format_context specialized for the 575 // __iterator of this container. 576 // 577 // This class uses its own buffer management, since using vector 578 // would lead to a circular include with formatter for vector<bool>. 579 template <__fmt_char_type _CharT> 580 class _LIBCPP_TEMPLATE_VIS __retarget_buffer { 581 using _Alloc _LIBCPP_NODEBUG = allocator<_CharT>; 582 583 public: 584 using value_type _LIBCPP_NODEBUG = _CharT; 585 586 struct __iterator { 587 using difference_type _LIBCPP_NODEBUG = ptrdiff_t; 588 using value_type _LIBCPP_NODEBUG = _CharT; 589 590 _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(__retarget_buffer& __buffer) 591 : __buffer_(std::addressof(__buffer)) {} 592 _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator=(const _CharT& __c) { 593 __buffer_->push_back(__c); 594 return *this; 595 } 596 _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator=(_CharT&& __c) { 597 __buffer_->push_back(__c); 598 return *this; 599 } 600 601 _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator*() { return *this; } 602 _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() { return *this; } 603 _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int) { return *this; } 604 __retarget_buffer* __buffer_; 605 }; 606 607 __retarget_buffer(const __retarget_buffer&) = delete; 608 __retarget_buffer& operator=(const __retarget_buffer&) = delete; 609 610 _LIBCPP_HIDE_FROM_ABI explicit __retarget_buffer(size_t __size_hint) { 611 // When the initial size is very small a lot of resizes happen 612 // when elements added. So use a hard-coded minimum size. 613 // 614 // Note a size < 2 will not work 615 // - 0 there is no buffer, while push_back requires 1 empty element. 616 // - 1 multiplied by the grow factor is 1 and thus the buffer never 617 // grows. 618 auto __result = std::__allocate_at_least(__alloc_, std::max(__size_hint, 256 / sizeof(_CharT))); 619 __ptr_ = __result.ptr; 620 __capacity_ = __result.count; 621 } 622 623 _LIBCPP_HIDE_FROM_ABI ~__retarget_buffer() { 624 ranges::destroy_n(__ptr_, __size_); 625 allocator_traits<_Alloc>::deallocate(__alloc_, __ptr_, __capacity_); 626 } 627 628 _LIBCPP_HIDE_FROM_ABI __iterator __make_output_iterator() { return __iterator{*this}; } 629 630 _LIBCPP_HIDE_FROM_ABI void push_back(_CharT __c) { 631 std::construct_at(__ptr_ + __size_, __c); 632 ++__size_; 633 634 if (__size_ == __capacity_) 635 __grow_buffer(); 636 } 637 638 template <__fmt_char_type _InCharT> 639 _LIBCPP_HIDE_FROM_ABI void __copy(basic_string_view<_InCharT> __str) { 640 size_t __n = __str.size(); 641 if (__size_ + __n >= __capacity_) 642 // Push_back requires the buffer to have room for at least one character. 643 __grow_buffer(__size_ + __n + 1); 644 645 std::uninitialized_copy_n(__str.data(), __n, __ptr_ + __size_); 646 __size_ += __n; 647 } 648 649 template <contiguous_iterator _Iterator, 650 class _UnaryOperation, 651 __fmt_char_type _InCharT = typename iterator_traits<_Iterator>::value_type> 652 _LIBCPP_HIDE_FROM_ABI void __transform(_Iterator __first, _Iterator __last, _UnaryOperation __operation) { 653 _LIBCPP_ASSERT_INTERNAL(__first <= __last, "not a valid range"); 654 655 size_t __n = static_cast<size_t>(__last - __first); 656 if (__size_ + __n >= __capacity_) 657 // Push_back requires the buffer to have room for at least one character. 658 __grow_buffer(__size_ + __n + 1); 659 660 std::uninitialized_default_construct_n(__ptr_ + __size_, __n); 661 std::transform(__first, __last, __ptr_ + __size_, std::move(__operation)); 662 __size_ += __n; 663 } 664 665 _LIBCPP_HIDE_FROM_ABI void __fill(size_t __n, _CharT __value) { 666 if (__size_ + __n >= __capacity_) 667 // Push_back requires the buffer to have room for at least one character. 668 __grow_buffer(__size_ + __n + 1); 669 670 std::uninitialized_fill_n(__ptr_ + __size_, __n, __value); 671 __size_ += __n; 672 } 673 674 _LIBCPP_HIDE_FROM_ABI basic_string_view<_CharT> __view() { return {__ptr_, __size_}; } 675 676 private: 677 _LIBCPP_HIDE_FROM_ABI void __grow_buffer() { __grow_buffer(__capacity_ * 1.6); } 678 679 _LIBCPP_HIDE_FROM_ABI void __grow_buffer(size_t __capacity) { 680 _LIBCPP_ASSERT_INTERNAL(__capacity > __capacity_, "the buffer must grow"); 681 auto __result = std::__allocate_at_least(__alloc_, __capacity); 682 auto __guard = std::__make_exception_guard([&] { 683 allocator_traits<_Alloc>::deallocate(__alloc_, __result.ptr, __result.count); 684 }); 685 // This shouldn't throw, but just to be safe. Note that at -O1 this 686 // guard is optimized away so there is no runtime overhead. 687 std::uninitialized_move_n(__ptr_, __size_, __result.ptr); 688 __guard.__complete(); 689 ranges::destroy_n(__ptr_, __size_); 690 allocator_traits<_Alloc>::deallocate(__alloc_, __ptr_, __capacity_); 691 692 __ptr_ = __result.ptr; 693 __capacity_ = __result.count; 694 } 695 _LIBCPP_NO_UNIQUE_ADDRESS _Alloc __alloc_; 696 _CharT* __ptr_; 697 size_t __capacity_; 698 size_t __size_{0}; 699 }; 700 701 } // namespace __format 702 703 #endif // _LIBCPP_STD_VER >= 20 704 705 _LIBCPP_END_NAMESPACE_STD 706 707 _LIBCPP_POP_MACROS 708 709 #endif // _LIBCPP___FORMAT_BUFFER_H 710