1 // Copyright 2023 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 #pragma once 15 16 #include <iterator> 17 #include <tuple> 18 19 #include "pw_multibuf/chunk.h" 20 #include "pw_preprocessor/compiler.h" 21 #include "pw_status/status_with_size.h" 22 23 namespace pw::multibuf { 24 25 class MultiBuf; 26 27 /// A `Chunk`-oriented view of a `MultiBuf`. 28 class MultiBufChunks { 29 public: 30 using element_type = Chunk; 31 using value_type = Chunk; 32 using pointer = Chunk*; 33 using reference = Chunk&; 34 using const_pointer = const Chunk*; 35 using difference_type = std::ptrdiff_t; 36 using const_reference = const Chunk&; 37 using size_type = std::size_t; 38 39 /// A `std::forward_iterator` over the `Chunk`s of a `MultiBuf`. 40 class iterator { 41 public: 42 using value_type = Chunk; 43 using difference_type = std::ptrdiff_t; 44 using reference = Chunk&; 45 using pointer = Chunk*; 46 using iterator_category = std::forward_iterator_tag; 47 48 constexpr iterator() = default; 49 50 constexpr reference operator*() const { return *chunk_; } 51 constexpr pointer operator->() const { return chunk_; } 52 53 constexpr iterator& operator++() { 54 chunk_ = chunk_->next_in_buf_; 55 return *this; 56 } 57 58 constexpr iterator operator++(int) { 59 iterator tmp = *this; 60 ++(*this); 61 return tmp; 62 } 63 64 constexpr bool operator==(const iterator& other) const { 65 return chunk_ == other.chunk_; 66 } 67 68 constexpr bool operator!=(const iterator& other) const { 69 return chunk_ != other.chunk_; 70 } 71 72 private: 73 friend class MultiBufChunks; 74 friend class MultiBuf; 75 iterator(Chunk * chunk)76 constexpr iterator(Chunk* chunk) : chunk_(chunk) {} end()77 static constexpr iterator end() { return iterator(nullptr); } 78 Chunk* chunk_ = nullptr; 79 }; 80 81 /// A const `std::forward_iterator` over the `Chunk`s of a `MultiBuf`. 82 class const_iterator { 83 public: 84 using value_type = const Chunk; 85 using difference_type = std::ptrdiff_t; 86 using reference = const Chunk&; 87 using pointer = const Chunk*; 88 using iterator_category = std::forward_iterator_tag; 89 90 constexpr const_iterator() = default; 91 92 constexpr reference operator*() const { return *chunk_; } 93 constexpr pointer operator->() const { return chunk_; } 94 95 constexpr const_iterator& operator++() { 96 chunk_ = chunk_->next_in_buf_; 97 return *this; 98 } 99 100 constexpr const_iterator operator++(int) { 101 const_iterator tmp = *this; 102 ++(*this); 103 return tmp; 104 } 105 106 constexpr bool operator==(const const_iterator& other) const { 107 return chunk_ == other.chunk_; 108 } 109 110 constexpr bool operator!=(const const_iterator& other) const { 111 return chunk_ != other.chunk_; 112 } 113 114 private: 115 friend class MultiBufChunks; 116 friend class MultiBuf; 117 const_iterator(const Chunk * chunk)118 constexpr const_iterator(const Chunk* chunk) : chunk_(chunk) {} end()119 static constexpr const_iterator end() { return const_iterator(nullptr); } 120 const Chunk* chunk_ = nullptr; 121 }; 122 123 MultiBufChunks(const MultiBufChunks&) = delete; 124 MultiBufChunks& operator=(const MultiBufChunks&) = delete; 125 126 /// Returns a reference to the first chunk. 127 /// 128 /// The behavior of this method is undefined when `size() == 0`. front()129 constexpr Chunk& front() { return *first_; } front()130 constexpr const Chunk& front() const { return *first_; } 131 132 /// Returns a reference to the final chunk. 133 /// 134 /// The behavior of this method is undefined when `size() == 0`. 135 /// 136 /// NOTE: this method is `O(size())`. back()137 Chunk& back() { return const_cast<Chunk&>(std::as_const(*this).back()); } 138 const Chunk& back() const; 139 begin()140 constexpr iterator begin() { return iterator(first_); } begin()141 constexpr const_iterator begin() const { return cbegin(); } cbegin()142 constexpr const_iterator cbegin() const { return const_iterator(first_); } 143 end()144 constexpr iterator end() { return iterator::end(); } end()145 constexpr const_iterator end() const { return cend(); } cend()146 constexpr const_iterator cend() const { return const_iterator::end(); } 147 148 /// Returns the number of `Chunk`s in this `MultiBuf`, including empty chunks. size()149 size_t size() const { 150 return static_cast<size_t>(std::distance(begin(), end())); 151 } 152 153 /// Returns the total number of bytes in all `Chunk`s. 154 size_t size_bytes() const; 155 156 /// Returns whether the `MultiBuf` contains any chunks (`size() == 0`). empty()157 [[nodiscard]] bool empty() const { return first_ == nullptr; } 158 159 /// Pushes `Chunk` onto the front of the `MultiBuf`. 160 /// 161 /// This operation does not move any data and is `O(1)`. 162 void push_front(OwnedChunk&& chunk); 163 164 /// Pushes `Chunk` onto the end of the `MultiBuf`. 165 /// 166 /// This operation does not move any data and is `O(Chunks().size())`. 167 void push_back(OwnedChunk&& chunk); 168 169 /// Removes the first `Chunk`. 170 /// 171 /// This operation does not move any data and is `O(1)`. 172 OwnedChunk take_front(); 173 174 /// Inserts `chunk` into the specified position in the `MultiBuf`. The `Chunk` 175 /// at `position` will be after the new chunk. 176 /// 177 /// This operation does not move any data and is `O(Chunks().size())`. 178 /// 179 /// Returns an iterator pointing to the newly-inserted `Chunk`. 180 // 181 // Implementation note: `Chunks().size()` should be remain relatively small, 182 // but this could be made `O(1)` in the future by adding a `prev` pointer to 183 // the `iterator`. 184 iterator insert(iterator position, OwnedChunk&& chunk); 185 186 /// Removes and returns `Chunk` from the specified position. 187 /// 188 /// This operation does not move any data and is `O(Chunks().size())`. 189 /// 190 /// Returns an iterator pointing to the `Chunk` after the removed `Chunk`, or 191 /// `Chunks().end()` if this was the last `Chunk` in the `MultiBuf`. 192 // 193 // Implementation note: `Chunks().size()` should be remain relatively small, 194 // but this could be made `O(1)` in the future by adding a `prev` pointer to 195 // the `iterator`. 196 std::tuple<iterator, OwnedChunk> take(iterator position); 197 198 protected: MultiBufChunks(Chunk * first_chunk)199 explicit constexpr MultiBufChunks(Chunk* first_chunk) : first_(first_chunk) {} 200 201 /// This destructor will acquire a mutex and is not IRQ safe. ~MultiBufChunks()202 ~MultiBufChunks() { Release(); } 203 204 // Disable maybe-uninitialized: this check fails erroneously on Windows GCC. 205 PW_MODIFY_DIAGNOSTICS_PUSH(); 206 PW_MODIFY_DIAGNOSTIC_GCC(ignored, "-Wmaybe-uninitialized"); MultiBufChunks(MultiBufChunks && other)207 constexpr MultiBufChunks(MultiBufChunks&& other) noexcept 208 : first_(other.first_) { 209 other.first_ = nullptr; 210 } 211 PW_MODIFY_DIAGNOSTICS_POP(); 212 213 MultiBufChunks& operator=(MultiBufChunks&& other) noexcept { 214 Release(); 215 first_ = other.first_; 216 other.first_ = nullptr; 217 return *this; 218 } 219 220 // Releases all chunks in the `MultiBuf`. 221 void Release() noexcept; 222 223 void PushSuffix(MultiBufChunks&& tail); 224 225 /// Returns the `Chunk` preceding `chunk` in this `MultiBuf`. 226 /// 227 /// Requires that this `MultiBuf` is not empty, and that `chunk` is either in 228 /// `MultiBuf` or is `nullptr`, in which case the last `Chunk` in `MultiBuf` 229 /// will be returned. 230 /// 231 /// This operation is `O(Chunks().size())`. 232 Chunk* Previous(Chunk* chunk) const; 233 234 private: 235 Chunk* first_; 236 }; 237 238 /// A byte buffer optimized for zero-copy data transfer. 239 /// 240 /// A `MultiBuf` consists of multiple `Chunk`s of data. 241 /// 242 /// `MultiBuf` inherits privately from `MultiBufChunks`. This allows one class 243 /// to provide either a byte-oriented or a `Chunk`-oriented interface, and keeps 244 /// those interfaces separate. 245 class MultiBuf : private MultiBufChunks { 246 public: 247 class iterator; 248 class const_iterator; 249 MultiBuf()250 constexpr MultiBuf() : MultiBufChunks(nullptr) {} 251 FromChunk(OwnedChunk && chunk)252 static MultiBuf FromChunk(OwnedChunk&& chunk) { 253 return MultiBuf(std::move(chunk).Take()); 254 } 255 256 MultiBuf(const MultiBuf&) = delete; 257 MultiBuf& operator=(const MultiBuf&) = delete; 258 259 constexpr MultiBuf(MultiBuf&& other) noexcept = default; 260 261 MultiBuf& operator=(MultiBuf&& other) noexcept = default; 262 263 /// Decrements the reference count on the underlying chunks of data and 264 /// empties this `MultiBuf` so that `size() == 0`. 265 /// 266 /// Does not modify the underlying data, but may cause it to be deallocated. 267 /// 268 /// This method is equivalent to `{ MultiBuf _unused = std::move(multibuf); }` 269 /// 270 /// This method will acquire a mutex and is not IRQ safe. Release()271 void Release() noexcept { MultiBufChunks::Release(); } 272 273 /// This destructor will acquire a mutex and is not IRQ safe. 274 ~MultiBuf() = default; 275 276 /// Returns the number of bytes in this container. 277 /// 278 /// This method's complexity is `O(Chunks().size())`. size()279 [[nodiscard]] size_t size() const { return MultiBufChunks::size_bytes(); } 280 281 /// Returns whether the `MultiBuf` contains any bytes (`size() == 0`). 282 /// 283 /// This method's complexity is `O(Chunks().size())`, but will be more 284 /// efficient than `size() == 0` in most cases. 285 [[nodiscard]] bool empty() const; 286 287 /// Returns if the `MultiBuf` is contiguous. A `MultiBuf` is contiguous if it 288 /// is comprised of either: 289 /// 290 /// - one non-empty chunk, 291 /// - only empty chunks, or 292 /// - no chunks at all. IsContiguous()293 [[nodiscard]] bool IsContiguous() const { 294 return ContiguousSpan().has_value(); 295 } 296 297 /// If the `MultiBuf` is contiguous, returns it as a span. The span will be 298 /// empty if the `MultiBuf` is empty. 299 /// 300 /// A `MultiBuf` is contiguous if it is comprised of either: 301 /// 302 /// - one non-empty chunk, 303 /// - only empty chunks, or 304 /// - no chunks at all. ContiguousSpan()305 std::optional<ByteSpan> ContiguousSpan() { 306 auto result = std::as_const(*this).ContiguousSpan(); 307 if (result.has_value()) { 308 return span(const_cast<std::byte*>(result->data()), result->size()); 309 } 310 return std::nullopt; 311 } 312 std::optional<ConstByteSpan> ContiguousSpan() const; 313 314 /// Returns an iterator pointing to the first byte of this `MultiBuf`. begin()315 iterator begin() { return iterator(Chunks().begin().chunk_); } 316 /// Returns a const iterator pointing to the first byte of this `MultiBuf`. begin()317 const_iterator begin() const { 318 return const_iterator(Chunks().begin().chunk_); 319 } 320 /// Returns a const iterator pointing to the first byte of this `MultiBuf`. cbegin()321 const_iterator cbegin() const { 322 return const_iterator(Chunks().begin().chunk_); 323 } 324 325 /// Returns an iterator pointing to the end of this `MultiBuf`. end()326 iterator end() { return iterator::end(); } 327 /// Returns a const iterator pointing to the end of this `MultiBuf`. end()328 const_iterator end() const { return const_iterator::end(); } 329 /// Returns a const iterator pointing to the end of this `MultiBuf`. cend()330 const_iterator cend() const { return const_iterator::end(); } 331 332 /// Attempts to add `bytes_to_claim` to the front of this buffer by advancing 333 /// its range backwards in memory. Returns `true` if the operation succeeded. 334 /// 335 /// This will only succeed if the first `Chunk` in this buffer points to a 336 /// section of a region that has unreferenced bytes preceding it. See also 337 /// `Chunk::ClaimPrefix`. 338 /// 339 /// This method will acquire a mutex and is not IRQ safe. 340 [[nodiscard]] bool ClaimPrefix(size_t bytes_to_claim); 341 342 /// Attempts to add `bytes_to_claim` to the front of this buffer by advancing 343 /// its range forwards in memory. Returns `true` if the operation succeeded. 344 /// 345 /// This will only succeed if the last `Chunk` in this buffer points to a 346 /// section of a region that has unreferenced bytes following it. See also 347 /// `Chunk::ClaimSuffix`. 348 /// 349 /// This method will acquire a mutex and is not IRQ safe. 350 [[nodiscard]] bool ClaimSuffix(size_t bytes_to_claim); 351 352 /// Shrinks this handle to refer to the data beginning at offset 353 /// `bytes_to_discard`. 354 /// 355 /// Does not modify the underlying data. The discarded memory continues to be 356 /// held by the underlying region as long as any `Chunk`s exist within it. 357 /// This allows the memory to be later reclaimed using `ClaimPrefix`. 358 /// 359 /// This method will acquire a mutex and is not IRQ safe. 360 void DiscardPrefix(size_t bytes_to_discard); 361 362 /// Shrinks this handle to refer to data in the range `begin..<end`. 363 /// 364 /// Does not modify the underlying data. The discarded memory continues to be 365 /// held by the underlying region as long as any `Chunk`s exist within it. 366 /// This allows the memory to be later reclaimed using `ClaimPrefix` or 367 /// `ClaimSuffix`. 368 /// 369 /// This method will acquire a mutex and is not IRQ safe. 370 void Slice(size_t begin, size_t end); 371 372 /// Shrinks this handle to refer to only the first `len` bytes. 373 /// 374 /// Does not modify the underlying data. The discarded memory continues to be 375 /// held by the underlying region as long as any `Chunk`s exist within it. 376 /// This allows the memory to be later reclaimed using `ClaimSuffix`. 377 /// 378 /// This method will acquire a mutex and is not IRQ safe. 379 void Truncate(size_t len); 380 381 /// Truncates the `MultiBuf` after the current iterator. All bytes following 382 /// the iterator are removed. 383 /// 384 /// Does not modify the underlying data. 385 /// 386 /// This method will acquire a mutex and is not IRQ safe. 387 void TruncateAfter(iterator pos); 388 389 /// Attempts to shrink this handle to refer to the data beginning at offset 390 /// `bytes_to_take`, returning the first `bytes_to_take` bytes as a new 391 /// `MultiBuf`. 392 /// 393 /// If the inner call to `AllocateChunkClass` fails, this function will return 394 /// `std::nullopt` and this handle's span will not change. 395 /// 396 /// This method will acquire a mutex and is not IRQ safe. 397 std::optional<MultiBuf> TakePrefix(size_t bytes_to_take); 398 399 /// Attempts to shrink this handle to refer only the first `len - 400 /// bytes_to_take` bytes, returning the last `bytes_to_take` bytes as a new 401 /// `MultiBuf`. 402 /// 403 /// If the inner call to `AllocateChunkClass` fails, this function will return 404 /// `std::nullopt` and this handle's span will not change. 405 /// 406 /// This method will acquire a mutex and is not IRQ safe. 407 std::optional<MultiBuf> TakeSuffix(size_t bytes_to_take); 408 409 /// Pushes `front` onto the front of this `MultiBuf`. 410 /// 411 /// This operation does not move any data and is `O(front.Chunks().size())`. 412 void PushPrefix(MultiBuf&& front); 413 414 /// Pushes `tail` onto the end of this `MultiBuf`. 415 /// 416 /// This operation does not move any data and is `O(Chunks().size())`. PushSuffix(MultiBuf && tail)417 void PushSuffix(MultiBuf&& tail) { 418 return MultiBufChunks::PushSuffix(std::move(tail.Chunks())); 419 } 420 421 /// Copies bytes from the multibuf into the provided buffer. 422 /// 423 /// @param[out] dest Destination into which to copy data from the `MultiBuf`. 424 /// @param[in] position Offset in the `MultiBuf` from which to start. 425 /// 426 /// @returns @rst 427 /// 428 /// .. pw-status-codes:: 429 /// 430 /// OK: All bytes were copied into the destination. The 431 /// :cpp:class:`pw::StatusWithSize` includes the number of bytes copied, 432 /// which is the size of the :cpp:class:`MultiBuf`. 433 /// 434 /// RESOURCE_EXHAUSTED: Some bytes were copied, but the 435 /// :cpp:class:`MultiBuf` was larger than the destination buffer. The 436 /// :cpp:class:`pw::StatusWithSize` includes the number of bytes copied. 437 /// 438 /// @endrst 439 StatusWithSize CopyTo(ByteSpan dest, size_t position = 0) const; 440 441 /// Copies bytes from the provided buffer into the multibuf. 442 /// 443 /// @param[in] source Data to copy into the `MultiBuf`. 444 /// @param[in] position Offset in the `MultiBuf` from which to start. 445 /// 446 /// @returns @rst 447 /// 448 /// .. pw-status-codes:: 449 /// 450 /// OK: All bytes were copied. The :cpp:class:`pw::StatusWithSize` includes 451 /// the number of bytes copied, which is the size of the `MultiBuf`. 452 /// 453 /// RESOURCE_EXHAUSTED: Some bytes were copied, but the source was larger 454 /// than the destination. The :cpp:class:`pw::StatusWithSize` includes the 455 /// number of bytes copied. 456 /// 457 /// @endrst 458 StatusWithSize CopyFrom(ConstByteSpan source, size_t position = 0) { 459 return CopyFromAndOptionallyTruncate(source, position, /*truncate=*/false); 460 } 461 462 /// Copies bytes from the provided buffer into this `MultiBuf` and truncates 463 /// it to the end of the copied data. This is a more efficient version of: 464 /// @code{.cpp} 465 /// 466 /// if (multibuf.CopyFrom(destination).ok()) { 467 /// multibuf.Truncate(destination.size()); 468 /// } 469 /// 470 /// @endcode 471 /// 472 /// @param[in] source Data to copy into the `MultiBuf`. 473 /// @param[in] position Offset in the `MultiBuf` from which to start. 474 /// 475 /// @returns @rst 476 /// 477 /// .. pw-status-codes:: 478 /// 479 /// OK: All bytes were copied and the :cpp:class:`MultiBuf` was truncated. 480 /// The :cpp:class:`pw::StatusWithSize` includes the new 481 /// :cpp:func:`MultiBuf::size`. 482 /// 483 /// RESOURCE_EXHAUSTED: Some bytes were copied, but the source buffer was 484 /// larger than the :cpp:class:`MultiBuf`. The returned 485 /// :cpp:class:`pw::StatusWithSize` includes the number of bytes copied, 486 /// which is the size of the :cpp:class:`MultiBuf`. 487 /// 488 /// @endrst 489 StatusWithSize CopyFromAndTruncate(ConstByteSpan source, 490 size_t position = 0) { 491 return CopyFromAndOptionallyTruncate(source, position, /*truncate=*/true); 492 } 493 494 /////////////////////////////////////////////////////////////////// 495 //--------------------- Chunk manipulation ----------------------// 496 /////////////////////////////////////////////////////////////////// 497 498 /// @copydoc MultiBufChunks::push_front PushFrontChunk(OwnedChunk && chunk)499 void PushFrontChunk(OwnedChunk&& chunk) { 500 MultiBufChunks::push_front(std::move(chunk)); 501 } 502 503 /// @copydoc MultiBufChunks::push_back PushBackChunk(OwnedChunk && chunk)504 void PushBackChunk(OwnedChunk&& chunk) { 505 MultiBufChunks::push_back(std::move(chunk)); 506 } 507 508 /// @copydoc MultiBufChunks::take_front TakeFrontChunk()509 OwnedChunk TakeFrontChunk() { return MultiBufChunks::take_front(); } 510 511 /// @copydoc MultiBufChunks::insert InsertChunk(MultiBufChunks::iterator position,OwnedChunk && chunk)512 MultiBufChunks::iterator InsertChunk(MultiBufChunks::iterator position, 513 OwnedChunk&& chunk) { 514 return MultiBufChunks::insert(position, std::move(chunk)); 515 } 516 517 /// @copydoc MultiBufChunks::take TakeChunk(MultiBufChunks::iterator position)518 std::tuple<MultiBufChunks::iterator, OwnedChunk> TakeChunk( 519 MultiBufChunks::iterator position) { 520 return MultiBufChunks::take(position); 521 } 522 523 /// Returns a `Chunk`-oriented view of this `MultiBuf`. Chunks()524 constexpr MultiBufChunks& Chunks() { return *this; } 525 526 /// Returns a `const Chunk`-oriented view of this `MultiBuf`. Chunks()527 constexpr const MultiBufChunks& Chunks() const { return *this; } 528 529 /// Returns a `const Chunk`-oriented view of this `MultiBuf`. ConstChunks()530 constexpr const MultiBufChunks& ConstChunks() const { return *this; } 531 532 /////////////////////////////////////////////////////////////////// 533 //--------------------- Iterator details ------------------------// 534 /////////////////////////////////////////////////////////////////// 535 536 using element_type = std::byte; 537 using value_type = std::byte; 538 using pointer = std::byte*; 539 using const_pointer = const std::byte*; 540 using reference = std::byte&; 541 using const_reference = const std::byte&; 542 using difference_type = std::ptrdiff_t; 543 using size_type = std::size_t; 544 545 /// A const `std::forward_iterator` over the bytes of a `MultiBuf`. 546 class const_iterator { 547 public: 548 using value_type = std::byte; 549 using difference_type = std::ptrdiff_t; 550 using reference = const std::byte&; 551 using pointer = const std::byte*; 552 using iterator_category = std::forward_iterator_tag; 553 const_iterator()554 constexpr const_iterator() : chunk_(nullptr), byte_index_(0) {} 555 556 reference operator*() const { return (*chunk_)[byte_index_]; } 557 pointer operator->() const { return &(*chunk_)[byte_index_]; } 558 559 const_iterator& operator++(); 560 const_iterator operator++(int) { 561 const_iterator tmp = *this; 562 ++(*this); 563 return tmp; 564 } 565 const_iterator operator+(size_t rhs) const { 566 const_iterator tmp = *this; 567 tmp += rhs; 568 return tmp; 569 } 570 const_iterator& operator+=(size_t advance); 571 572 constexpr bool operator==(const const_iterator& other) const { 573 return chunk_ == other.chunk_ && byte_index_ == other.byte_index_; 574 } 575 576 constexpr bool operator!=(const const_iterator& other) const { 577 return !(*this == other); 578 } 579 580 /// Returns the current `Chunk` pointed to by this `iterator`. chunk()581 constexpr const Chunk* chunk() const { return chunk_; } 582 583 /// Returns the index of the byte pointed to by this `iterator` within the 584 /// current `Chunk`. byte_index()585 constexpr size_t byte_index() const { return byte_index_; } 586 587 private: 588 friend class MultiBuf; 589 590 explicit constexpr const_iterator(const Chunk* chunk, size_t byte_index = 0) chunk_(chunk)591 : chunk_(chunk), byte_index_(byte_index) { 592 AdvanceToData(); 593 } 594 end()595 static const_iterator end() { return const_iterator(nullptr); } 596 AdvanceToData()597 constexpr void AdvanceToData() { 598 while (chunk_ != nullptr && chunk_->empty()) { 599 chunk_ = chunk_->next_in_buf_; 600 } 601 } 602 603 const Chunk* chunk_; 604 size_t byte_index_; 605 }; 606 607 /// An `std::forward_iterator` over the bytes of a `MultiBuf`. 608 class iterator { 609 public: 610 using value_type = std::byte; 611 using difference_type = std::ptrdiff_t; 612 using reference = std::byte&; 613 using pointer = std::byte*; 614 using iterator_category = std::forward_iterator_tag; 615 616 constexpr iterator() = default; 617 618 reference operator*() const { return const_cast<std::byte&>(*const_iter_); } 619 pointer operator->() const { return const_cast<std::byte*>(&*const_iter_); } 620 621 iterator& operator++() { 622 const_iter_++; 623 return *this; 624 } 625 iterator operator++(int) { 626 iterator tmp = *this; 627 ++(*this); 628 return tmp; 629 } 630 iterator operator+(size_t rhs) const { 631 iterator tmp = *this; 632 tmp += rhs; 633 return tmp; 634 } 635 iterator& operator+=(size_t rhs) { 636 const_iter_ += rhs; 637 return *this; 638 } 639 constexpr bool operator==(const iterator& other) const { 640 return const_iter_ == other.const_iter_; 641 } 642 constexpr bool operator!=(const iterator& other) const { 643 return const_iter_ != other.const_iter_; 644 } 645 646 /// Returns the current `Chunk` pointed to by this `iterator`. chunk()647 constexpr Chunk* chunk() const { 648 return const_cast<Chunk*>(const_iter_.chunk()); 649 } 650 651 /// Returns the index of the byte pointed to by this `iterator` within the 652 /// current `Chunk`. byte_index()653 constexpr size_t byte_index() const { return const_iter_.byte_index(); } 654 655 private: 656 friend class MultiBuf; 657 658 explicit constexpr iterator(Chunk* chunk, size_t byte_index = 0) const_iter_(chunk,byte_index)659 : const_iter_(chunk, byte_index) {} 660 end()661 static iterator end() { return iterator(nullptr); } 662 663 const_iterator const_iter_; 664 }; 665 666 private: MultiBuf(Chunk * first_chunk)667 explicit constexpr MultiBuf(Chunk* first_chunk) 668 : MultiBufChunks(first_chunk) {} 669 670 StatusWithSize CopyFromAndOptionallyTruncate(ConstByteSpan source, 671 size_t position, 672 bool truncate); 673 }; 674 675 } // namespace pw::multibuf 676