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