1 // 2 // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com) 3 // 4 // Distributed under the Boost Software License, Version 1.0. (See accompanying 5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 // 7 // Official repository: https://github.com/boostorg/beast 8 // 9 10 #ifndef BOOST_BEAST_MULTI_BUFFER_HPP 11 #define BOOST_BEAST_MULTI_BUFFER_HPP 12 13 #include <boost/beast/core/detail/config.hpp> 14 #include <boost/beast/core/detail/allocator.hpp> 15 #include <boost/asio/buffer.hpp> 16 #include <boost/core/empty_value.hpp> 17 #include <boost/intrusive/list.hpp> 18 #include <boost/type_traits/type_with_alignment.hpp> 19 #include <iterator> 20 #include <limits> 21 #include <memory> 22 #include <type_traits> 23 24 namespace boost { 25 namespace beast { 26 27 /** A dynamic buffer providing sequences of variable length. 28 29 A dynamic buffer encapsulates memory storage that may be 30 automatically resized as required, where the memory is 31 divided into two regions: readable bytes followed by 32 writable bytes. These memory regions are internal to 33 the dynamic buffer, but direct access to the elements 34 is provided to permit them to be efficiently used with 35 I/O operations. 36 37 The implementation uses a sequence of one or more byte 38 arrays of varying sizes to represent the readable and 39 writable bytes. Additional byte array objects are 40 appended to the sequence to accommodate changes in the 41 desired size. The behavior and implementation of this 42 container is most similar to `std::deque`. 43 44 Objects of this type meet the requirements of <em>DynamicBuffer</em> 45 and have the following additional properties: 46 47 @li A mutable buffer sequence representing the readable 48 bytes is returned by @ref data when `this` is non-const. 49 50 @li Buffer sequences representing the readable and writable 51 bytes, returned by @ref data and @ref prepare, may have 52 length greater than one. 53 54 @li A configurable maximum size may be set upon construction 55 and adjusted afterwards. Calls to @ref prepare that would 56 exceed this size will throw `std::length_error`. 57 58 @li Sequences previously obtained using @ref data remain 59 valid after calls to @ref prepare or @ref commit. 60 61 @tparam Allocator The allocator to use for managing memory. 62 */ 63 template<class Allocator> 64 class basic_multi_buffer 65 #if ! BOOST_BEAST_DOXYGEN 66 : private boost::empty_value<Allocator> 67 #endif 68 { 69 // Fancy pointers are not supported 70 static_assert(std::is_pointer<typename 71 std::allocator_traits<Allocator>::pointer>::value, 72 "Allocator must use regular pointers"); 73 74 static bool constexpr default_nothrow = 75 std::is_nothrow_default_constructible<Allocator>::value; 76 77 // Storage for the list of buffers representing the input 78 // and output sequences. The allocation for each element 79 // contains `element` followed by raw storage bytes. 80 class element 81 : public boost::intrusive::list_base_hook< 82 boost::intrusive::link_mode< 83 boost::intrusive::normal_link>> 84 { 85 using size_type = typename 86 detail::allocator_traits<Allocator>::size_type; 87 88 size_type const size_; 89 90 public: 91 element(element const&) = delete; 92 93 explicit element(size_type n)94 element(size_type n) noexcept 95 : size_(n) 96 { 97 } 98 99 size_type size() const100 size() const noexcept 101 { 102 return size_; 103 } 104 105 char* data() const106 data() const noexcept 107 { 108 return const_cast<char*>( 109 reinterpret_cast<char const*>(this + 1)); 110 } 111 }; 112 113 template<bool> 114 class subrange; 115 116 using size_type = typename 117 detail::allocator_traits<Allocator>::size_type; 118 119 using align_type = typename 120 boost::type_with_alignment<alignof(element)>::type; 121 122 using rebind_type = typename 123 beast::detail::allocator_traits<Allocator>:: 124 template rebind_alloc<align_type>; 125 126 using alloc_traits = 127 beast::detail::allocator_traits<rebind_type>; 128 129 using list_type = typename boost::intrusive::make_list< 130 element, boost::intrusive::constant_time_size<true>>::type; 131 132 using iter = typename list_type::iterator; 133 134 using const_iter = typename list_type::const_iterator; 135 136 using pocma = typename 137 alloc_traits::propagate_on_container_move_assignment; 138 139 using pocca = typename 140 alloc_traits::propagate_on_container_copy_assignment; 141 142 static_assert(std::is_base_of<std::bidirectional_iterator_tag, 143 typename std::iterator_traits<iter>::iterator_category>::value, 144 "BidirectionalIterator type requirements not met"); 145 146 static_assert(std::is_base_of<std::bidirectional_iterator_tag, 147 typename std::iterator_traits<const_iter>::iterator_category>::value, 148 "BidirectionalIterator type requirements not met"); 149 150 std::size_t max_; 151 list_type list_; // list of allocated buffers 152 iter out_; // element that contains out_pos_ 153 size_type in_size_ = 0; // size of the input sequence 154 size_type in_pos_ = 0; // input offset in list_.front() 155 size_type out_pos_ = 0; // output offset in *out_ 156 size_type out_end_ = 0; // output end offset in list_.back() 157 158 public: 159 #if BOOST_BEAST_DOXYGEN 160 /// The ConstBufferSequence used to represent the readable bytes. 161 using const_buffers_type = __implementation_defined__; 162 163 /// The MutableBufferSequence used to represent the writable bytes. 164 using mutable_buffers_type = __implementation_defined__; 165 #else 166 using const_buffers_type = subrange<false>; 167 168 using mutable_buffers_type = subrange<true>; 169 #endif 170 171 /// The type of allocator used. 172 using allocator_type = Allocator; 173 174 /// Destructor 175 ~basic_multi_buffer(); 176 177 /** Constructor 178 179 After construction, @ref capacity will return zero, and 180 @ref max_size will return the largest value which may 181 be passed to the allocator's `allocate` function. 182 */ 183 basic_multi_buffer() noexcept(default_nothrow); 184 185 /** Constructor 186 187 After construction, @ref capacity will return zero, and 188 @ref max_size will return the specified value of `limit`. 189 190 @param limit The desired maximum size. 191 */ 192 explicit 193 basic_multi_buffer( 194 std::size_t limit) noexcept(default_nothrow); 195 196 /** Constructor 197 198 After construction, @ref capacity will return zero, and 199 @ref max_size will return the largest value which may 200 be passed to the allocator's `allocate` function. 201 202 @param alloc The allocator to use for the object. 203 204 @esafe 205 206 No-throw guarantee. 207 */ 208 explicit 209 basic_multi_buffer(Allocator const& alloc) noexcept; 210 211 /** Constructor 212 213 After construction, @ref capacity will return zero, and 214 @ref max_size will return the specified value of `limit`. 215 216 @param limit The desired maximum size. 217 218 @param alloc The allocator to use for the object. 219 220 @esafe 221 222 No-throw guarantee. 223 */ 224 basic_multi_buffer( 225 std::size_t limit, Allocator const& alloc) noexcept; 226 227 /** Move Constructor 228 229 The container is constructed with the contents of `other` 230 using move semantics. The maximum size will be the same 231 as the moved-from object. 232 233 Buffer sequences previously obtained from `other` using 234 @ref data or @ref prepare remain valid after the move. 235 236 @param other The object to move from. After the move, the 237 moved-from object will have zero capacity, zero readable 238 bytes, and zero writable bytes. 239 240 @esafe 241 242 No-throw guarantee. 243 */ 244 basic_multi_buffer(basic_multi_buffer&& other) noexcept; 245 246 /** Move Constructor 247 248 Using `alloc` as the allocator for the new container, the 249 contents of `other` are moved. If `alloc != other.get_allocator()`, 250 this results in a copy. The maximum size will be the same 251 as the moved-from object. 252 253 Buffer sequences previously obtained from `other` using 254 @ref data or @ref prepare become invalid after the move. 255 256 @param other The object to move from. After the move, 257 the moved-from object will have zero capacity, zero readable 258 bytes, and zero writable bytes. 259 260 @param alloc The allocator to use for the object. 261 262 @throws std::length_error if `other.size()` exceeds the 263 maximum allocation size of `alloc`. 264 */ 265 basic_multi_buffer( 266 basic_multi_buffer&& other, 267 Allocator const& alloc); 268 269 /** Copy Constructor 270 271 This container is constructed with the contents of `other` 272 using copy semantics. The maximum size will be the same 273 as the copied object. 274 275 @param other The object to copy from. 276 277 @throws std::length_error if `other.size()` exceeds the 278 maximum allocation size of the allocator. 279 */ 280 basic_multi_buffer(basic_multi_buffer const& other); 281 282 /** Copy Constructor 283 284 This container is constructed with the contents of `other` 285 using copy semantics and the specified allocator. The maximum 286 size will be the same as the copied object. 287 288 @param other The object to copy from. 289 290 @param alloc The allocator to use for the object. 291 292 @throws std::length_error if `other.size()` exceeds the 293 maximum allocation size of `alloc`. 294 */ 295 basic_multi_buffer(basic_multi_buffer const& other, 296 Allocator const& alloc); 297 298 /** Copy Constructor 299 300 This container is constructed with the contents of `other` 301 using copy semantics. The maximum size will be the same 302 as the copied object. 303 304 @param other The object to copy from. 305 306 @throws std::length_error if `other.size()` exceeds the 307 maximum allocation size of the allocator. 308 */ 309 template<class OtherAlloc> 310 basic_multi_buffer(basic_multi_buffer< 311 OtherAlloc> const& other); 312 313 /** Copy Constructor 314 315 This container is constructed with the contents of `other` 316 using copy semantics. The maximum size will be the same 317 as the copied object. 318 319 @param other The object to copy from. 320 321 @param alloc The allocator to use for the object. 322 323 @throws std::length_error if `other.size()` exceeds the 324 maximum allocation size of `alloc`. 325 */ 326 template<class OtherAlloc> 327 basic_multi_buffer( 328 basic_multi_buffer<OtherAlloc> const& other, 329 allocator_type const& alloc); 330 331 /** Move Assignment 332 333 The container is assigned with the contents of `other` 334 using move semantics. The maximum size will be the same 335 as the moved-from object. 336 337 Buffer sequences previously obtained from `other` using 338 @ref data or @ref prepare remain valid after the move. 339 340 @param other The object to move from. After the move, 341 the moved-from object will have zero capacity, zero readable 342 bytes, and zero writable bytes. 343 */ 344 basic_multi_buffer& 345 operator=(basic_multi_buffer&& other); 346 347 /** Copy Assignment 348 349 The container is assigned with the contents of `other` 350 using copy semantics. The maximum size will be the same 351 as the copied object. 352 353 After the copy, `this` will have zero writable bytes. 354 355 @param other The object to copy from. 356 357 @throws std::length_error if `other.size()` exceeds the 358 maximum allocation size of the allocator. 359 */ 360 basic_multi_buffer& operator=( 361 basic_multi_buffer const& other); 362 363 /** Copy Assignment 364 365 The container is assigned with the contents of `other` 366 using copy semantics. The maximum size will be the same 367 as the copied object. 368 369 After the copy, `this` will have zero writable bytes. 370 371 @param other The object to copy from. 372 373 @throws std::length_error if `other.size()` exceeds the 374 maximum allocation size of the allocator. 375 */ 376 template<class OtherAlloc> 377 basic_multi_buffer& operator=( 378 basic_multi_buffer<OtherAlloc> const& other); 379 380 /// Returns a copy of the allocator used. 381 allocator_type get_allocator() const382 get_allocator() const 383 { 384 return this->get(); 385 } 386 387 /** Set the maximum allowed capacity 388 389 This function changes the currently configured upper limit 390 on capacity to the specified value. 391 392 @param n The maximum number of bytes ever allowed for capacity. 393 394 @esafe 395 396 No-throw guarantee. 397 */ 398 void max_size(std::size_t n)399 max_size(std::size_t n) noexcept 400 { 401 max_ = n; 402 } 403 404 /** Guarantee a minimum capacity 405 406 This function adjusts the internal storage (if necessary) 407 to guarantee space for at least `n` bytes. 408 409 Buffer sequences previously obtained using @ref data remain 410 valid, while buffer sequences previously obtained using 411 @ref prepare become invalid. 412 413 @param n The minimum number of byte for the new capacity. 414 If this value is greater than the maximum size, then the 415 maximum size will be adjusted upwards to this value. 416 417 @throws std::length_error if n is larger than the maximum 418 allocation size of the allocator. 419 420 @esafe 421 422 Strong guarantee. 423 */ 424 void 425 reserve(std::size_t n); 426 427 /** Reallocate the buffer to fit the readable bytes exactly. 428 429 Buffer sequences previously obtained using @ref data or 430 @ref prepare become invalid. 431 432 @esafe 433 434 Strong guarantee. 435 */ 436 void 437 shrink_to_fit(); 438 439 /** Set the size of the readable and writable bytes to zero. 440 441 This clears the buffer without changing capacity. 442 Buffer sequences previously obtained using @ref data or 443 @ref prepare become invalid. 444 445 @esafe 446 447 No-throw guarantee. 448 */ 449 void 450 clear() noexcept; 451 452 /// Exchange two dynamic buffers 453 template<class Alloc> 454 friend 455 void 456 swap( 457 basic_multi_buffer<Alloc>& lhs, 458 basic_multi_buffer<Alloc>& rhs) noexcept; 459 460 //-------------------------------------------------------------------------- 461 462 /// Returns the number of readable bytes. 463 size_type size() const464 size() const noexcept 465 { 466 return in_size_; 467 } 468 469 /// Return the maximum number of bytes, both readable and writable, that can ever be held. 470 size_type max_size() const471 max_size() const noexcept 472 { 473 return max_; 474 } 475 476 /// Return the maximum number of bytes, both readable and writable, that can be held without requiring an allocation. 477 std::size_t 478 capacity() const noexcept; 479 480 /** Returns a constant buffer sequence representing the readable bytes 481 482 @note The sequence may contain multiple contiguous memory regions. 483 */ 484 const_buffers_type 485 data() const noexcept; 486 487 /** Returns a constant buffer sequence representing the readable bytes 488 489 @note The sequence may contain multiple contiguous memory regions. 490 */ 491 const_buffers_type cdata() const492 cdata() const noexcept 493 { 494 return data(); 495 } 496 497 /** Returns a mutable buffer sequence representing the readable bytes. 498 499 @note The sequence may contain multiple contiguous memory regions. 500 */ 501 mutable_buffers_type 502 data() noexcept; 503 504 /** Returns a mutable buffer sequence representing writable bytes. 505 506 Returns a mutable buffer sequence representing the writable 507 bytes containing exactly `n` bytes of storage. Memory may be 508 reallocated as needed. 509 510 All buffer sequences previously obtained using @ref prepare are 511 invalidated. Buffer sequences previously obtained using @ref data 512 remain valid. 513 514 @param n The desired number of bytes in the returned buffer 515 sequence. 516 517 @throws std::length_error if `size() + n` exceeds `max_size()`. 518 519 @esafe 520 521 Strong guarantee. 522 */ 523 mutable_buffers_type 524 prepare(size_type n); 525 526 /** Append writable bytes to the readable bytes. 527 528 Appends n bytes from the start of the writable bytes to the 529 end of the readable bytes. The remainder of the writable bytes 530 are discarded. If n is greater than the number of writable 531 bytes, all writable bytes are appended to the readable bytes. 532 533 All buffer sequences previously obtained using @ref prepare are 534 invalidated. Buffer sequences previously obtained using @ref data 535 remain valid. 536 537 @param n The number of bytes to append. If this number 538 is greater than the number of writable bytes, all 539 writable bytes are appended. 540 541 @esafe 542 543 No-throw guarantee. 544 */ 545 void 546 commit(size_type n) noexcept; 547 548 /** Remove bytes from beginning of the readable bytes. 549 550 Removes n bytes from the beginning of the readable bytes. 551 552 All buffers sequences previously obtained using 553 @ref data or @ref prepare are invalidated. 554 555 @param n The number of bytes to remove. If this number 556 is greater than the number of readable bytes, all 557 readable bytes are removed. 558 559 @esafe 560 561 No-throw guarantee. 562 */ 563 void 564 consume(size_type n) noexcept; 565 566 private: 567 template<class OtherAlloc> 568 friend class basic_multi_buffer; 569 570 template<class OtherAlloc> 571 void copy_from(basic_multi_buffer<OtherAlloc> const&); 572 void move_assign(basic_multi_buffer& other, std::false_type); 573 void move_assign(basic_multi_buffer& other, std::true_type) noexcept; 574 void copy_assign(basic_multi_buffer const& other, std::false_type); 575 void copy_assign(basic_multi_buffer const& other, std::true_type); 576 void swap(basic_multi_buffer&) noexcept; 577 void swap(basic_multi_buffer&, std::true_type) noexcept; 578 void swap(basic_multi_buffer&, std::false_type) noexcept; 579 void destroy(list_type& list) noexcept; 580 void destroy(const_iter it); 581 void destroy(element& e); 582 element& alloc(std::size_t size); 583 void debug_check() const; 584 }; 585 586 /// A typical multi buffer 587 using multi_buffer = basic_multi_buffer<std::allocator<char>>; 588 589 } // beast 590 } // boost 591 592 #include <boost/beast/core/impl/multi_buffer.hpp> 593 594 #endif