C/C++ API Reference
Loading...
Searching...
No Matches
multibuf.h
1// Copyright 2025 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 <cstddef>
17#include <optional>
18#include <tuple>
19
20#include "pw_bytes/span.h"
21#include "pw_multibuf/v1_adapter/chunk.h"
22#include "pw_multibuf/v1_adapter/internal/traits.h"
23#include "pw_multibuf/v2/chunks.h"
24#include "pw_multibuf/v2/multibuf.h"
25#include "pw_status/status_with_size.h"
26
27namespace pw::multibuf::v1_adapter {
28
30
40 private:
42 using Mutability = v2::internal::Mutability;
43 using Deque = Entry::Deque;
44
47 template <Mutability kMutability>
48 class Iterator {
49 public:
50 using size_type = Deque::size_type;
51 using difference_type = Deque::difference_type;
52 using value_type = std::
53 conditional_t<kMutability == Mutability::kConst, const Chunk, Chunk>;
54 using reference = value_type&;
55 using pointer = value_type*;
56 using iterator_category = std::forward_iterator_tag;
57
58 constexpr Iterator() = default;
59
60 // Support converting non-const iterators to const_iterators.
61 operator Iterator<Mutability::kConst>() const {
62 return mbv2_ == nullptr ? Iterator<Mutability::kConst>()
63 : Iterator<Mutability::kConst>(*mbv2_, index_);
64 }
65
66 constexpr reference operator*() const {
67 PW_ASSERT(chunk_.has_value());
68 return const_cast<reference>(*chunk_);
69 }
70 constexpr pointer operator->() const {
71 PW_ASSERT(chunk_.has_value());
72 return const_cast<pointer>(&(*chunk_));
73 }
74
75 constexpr Iterator& operator++() {
76 ++index_;
77 Update();
78 return *this;
79 }
80
81 constexpr Iterator operator++(int) {
82 Iterator tmp = *this;
83 ++(*this);
84 return tmp;
85 }
86
87 constexpr bool operator==(const Iterator& other) const {
88 return mbv2_ == other.mbv2_ && index_ == other.index_;
89 }
90
91 constexpr bool operator!=(const Iterator& other) const {
92 return !(*this == other);
93 }
94
95 private:
96 friend class MultiBufChunks;
97
98 constexpr Iterator(const v2::MultiBuf& mbv2, size_type chunk)
99 : mbv2_(&(mbv2.generic())), index_(chunk) {
100 PW_ASSERT(chunk <= mbv2_->num_chunks());
101 Update();
102 }
103
104 // Updates the chunk to match the current index.
105 constexpr void Update();
106
107 const v2::internal::GenericMultiBuf* mbv2_ = nullptr;
108 size_type index_;
109 std::optional<Chunk> chunk_;
110 };
111
112 public:
113 using iterator = Iterator<Mutability::kMutable>;
114 using const_iterator = Iterator<Mutability::kConst>;
115
116 ~MultiBufChunks() { Release(); }
117
118 MultiBufChunks(MultiBufChunks&& other) = default;
119 MultiBufChunks& operator=(MultiBufChunks&& other) = default;
120
134 constexpr v2::TrackedMultiBuf* v2() {
135 return mbv2_.has_value() ? &(**mbv2_) : nullptr;
136 }
137 constexpr const v2::TrackedMultiBuf* v2() const {
138 return mbv2_.has_value() ? &(**mbv2_) : nullptr;
139 }
140
142 constexpr size_t size() const;
143
144 Chunk& front() {
145 first_ = *begin();
146 return *first_;
147 }
148
149 Chunk& back() {
150 const auto* mbv2 = v2();
151 last_ = *iterator(*mbv2, mbv2->generic().num_chunks() - 1);
152 return *last_;
153 }
154
155 iterator begin() const {
156 const auto* mbv2 = v2();
157 return mbv2 == nullptr ? iterator() : iterator(*mbv2, 0);
158 }
159 iterator end() const {
160 const auto* mbv2 = v2();
161 return mbv2 == nullptr ? iterator()
162 : iterator(*mbv2, mbv2->generic().num_chunks());
163 }
164
165 const_iterator cbegin() const { return begin(); }
166 const_iterator cend() const { return end(); }
167
168 protected:
169 constexpr MultiBufChunks() = default;
170
171 v2::TrackedMultiBuf* Assign(Allocator& allocator) {
172 mbv2_ = v2::TrackedMultiBuf::Instance(allocator);
173 return &(**mbv2_);
174 }
175
176 template <v2::Property... kProperties>
177 constexpr v2::TrackedMultiBuf* Assign(
178 v2::BasicMultiBuf<kProperties...>&& mb) {
179 mbv2_ = std::move(mb);
180 return &(**mbv2_);
181 }
182
183 template <typename MultiBufType>
184 constexpr v2::TrackedMultiBuf* Assign(
185 v2::internal::Instance<MultiBufType>&& mbi) {
186 mbv2_ = std::move(*mbi);
187 return &(**mbv2_);
188 }
189
190 void Release() noexcept { mbv2_.reset(); }
191
192 private:
193 std::optional<v2::TrackedMultiBuf::Instance> mbv2_;
194 std::optional<Chunk> first_;
195 std::optional<Chunk> last_;
196};
197
213class MultiBuf final : private MultiBufChunks {
214 private:
216
217 public:
218 using size_type = v2::MultiBuf::size_type;
219 using difference_type = v2::MultiBuf::difference_type;
220 using iterator = v2::MultiBuf::iterator;
222 using pointer = v2::MultiBuf::pointer;
223 using const_pointer = v2::MultiBuf::const_pointer;
224 using reference = v2::MultiBuf::reference;
225 using const_reference = v2::MultiBuf::const_reference;
226 using value_type = v2::MultiBuf::value_type;
227
228 MultiBuf(const MultiBuf& other) = delete;
229 MultiBuf& operator=(const MultiBuf& other) = delete;
230
231 MultiBuf(MultiBuf&& other) noexcept = default;
232 MultiBuf& operator=(MultiBuf&& other) noexcept = default;
233
234 ~MultiBuf() = default;
235
236 // v1 API ////////////////////////////////////////////////////////////////////
237
240
241 constexpr MultiBuf() = default;
242
244 void Release() noexcept { MultiBufChunks::Release(); }
245
247 [[nodiscard]] constexpr size_t size() const {
248 const auto* mbv2 = v2();
249 return mbv2 != nullptr ? mbv2->size() : 0;
250 }
251
253 [[nodiscard]] constexpr bool empty() const {
254 // `v2::MultiBuf::empty()` returns true for multibufs that only have one or
255 // more empty chunks, so use `size()` instead.
256 // NOLINTNEXTLINE(readability-container-size-empty)
257 return size() == 0;
258 }
259
261 [[nodiscard]] bool IsContiguous() const {
262 return ContiguousSpan().has_value();
263 }
264
266 std::optional<ByteSpan> ContiguousSpan();
267 std::optional<ConstByteSpan> ContiguousSpan() const;
268
270 constexpr iterator begin() {
271 auto* mbv2 = v2();
272 return mbv2 != nullptr ? mbv2->begin() : iterator();
273 }
274 constexpr const_iterator begin() const {
275 const auto* mbv2 = v2();
276 return mbv2 != nullptr ? mbv2->begin() : const_iterator();
277 }
278 constexpr const_iterator cbegin() const {
279 const auto* mbv2 = v2();
280 return mbv2 != nullptr ? mbv2->cbegin() : const_iterator();
281 }
282
284 constexpr iterator end() {
285 auto* mbv2 = v2();
286 return mbv2 != nullptr ? mbv2->end() : iterator();
287 }
288 constexpr const_iterator end() const {
289 const auto* mbv2 = v2();
290 return mbv2 != nullptr ? mbv2->end() : const_iterator();
291 }
292 constexpr const_iterator cend() const {
293 const auto* mbv2 = v2();
294 return mbv2 != nullptr ? mbv2->end() : const_iterator();
295 }
296
298 [[nodiscard]] bool ClaimPrefix(size_t bytes_to_claim);
299
301 [[nodiscard]] bool ClaimSuffix(size_t bytes_to_claim);
302
304 void DiscardPrefix(size_t bytes_to_discard);
305
307 void Slice(size_t begin, size_t end);
308
310 void Truncate(size_t len);
311
313 void TruncateAfter(iterator pos);
314
316 std::optional<MultiBuf> TakePrefix(size_t bytes_to_take);
317
319 std::optional<MultiBuf> TakeSuffix(size_t bytes_to_take);
320
322 void PushPrefix(MultiBuf&& front);
323
325 void PushSuffix(MultiBuf&& tail);
326
328 StatusWithSize CopyTo(ByteSpan dest, size_t position = 0) const;
329
331 StatusWithSize CopyFrom(ConstByteSpan source, size_t position = 0);
332
335
338
341
344 return std::get<OwnedChunk>(TakeChunk(Chunks().begin()));
345 }
346
348 MultiBufChunks::iterator InsertChunk(MultiBufChunks::iterator position,
349 OwnedChunk&& chunk);
350
352 std::tuple<MultiBufChunks::iterator, OwnedChunk> TakeChunk(
353 MultiBufChunks::iterator position);
354
356 constexpr MultiBufChunks& Chunks() { return *this; }
357
359 constexpr const MultiBufChunks& Chunks() const { return *this; }
360
362 constexpr const MultiBufChunks& ConstChunks() const { return *this; }
363
364 // v2 API ////////////////////////////////////////////////////////////////////
365
366 explicit MultiBuf(Allocator& allocator) { Assign(allocator); }
367
368 template <v2::Property... kProperties>
369 constexpr MultiBuf(v2::BasicMultiBuf<kProperties...>&& mb) {
370 *this = std::move(mb);
371 }
372
373 template <v2::Property... kProperties>
374 constexpr MultiBuf& operator=(v2::BasicMultiBuf<kProperties...>&& mb) {
375 Assign(std::move(mb));
376 return *this;
377 }
378
379 template <typename MultiBufType>
380 constexpr MultiBuf(v2::internal::Instance<MultiBufType>&& mbi) {
381 *this = std::move(mbi);
382 }
383
384 template <typename MultiBufType>
385 constexpr MultiBuf& operator=(v2::internal::Instance<MultiBufType>&& mbi) {
386 Assign(std::move(mbi));
387 return *this;
388 }
389
390 constexpr v2::TrackedMultiBuf* operator->() {
391 auto* mbv2 = v2();
392 PW_ASSERT(mbv2 != nullptr);
393 return mbv2;
394 }
395 constexpr const v2::TrackedMultiBuf* operator->() const {
396 const auto* mbv2 = v2();
397 PW_ASSERT(mbv2 != nullptr);
398 return mbv2;
399 }
400
401 constexpr v2::TrackedMultiBuf& operator*() & {
402 auto* mbv2 = v2();
403 PW_ASSERT(mbv2 != nullptr);
404 return *mbv2;
405 }
406 constexpr const v2::TrackedMultiBuf& operator*() const& {
407 const auto* mbv2 = v2();
408 PW_ASSERT(mbv2 != nullptr);
409 return *mbv2;
410 }
411
412 constexpr v2::TrackedMultiBuf&& operator*() && {
413 auto* mbv2 = v2();
414 PW_ASSERT(mbv2 != nullptr);
415 return std::move(*mbv2);
416 }
417 constexpr const v2::TrackedMultiBuf&& operator*() const&& {
418 const auto* mbv2 = v2();
419 PW_ASSERT(mbv2 != nullptr);
420 return std::move(*mbv2);
421 }
422
423 template <typename OtherMultiBuf,
424 typename = v2::internal::EnableIfConvertible<v2::TrackedMultiBuf,
425 OtherMultiBuf>>
426 constexpr operator OtherMultiBuf&() & {
427 auto* mbv2 = v2();
428 PW_ASSERT(mbv2 != nullptr);
429 return *mbv2;
430 }
431
432 template <typename OtherMultiBuf,
433 typename = v2::internal::EnableIfConvertible<v2::TrackedMultiBuf,
434 OtherMultiBuf>>
435 constexpr operator const OtherMultiBuf&() const& {
436 auto* mbv2 = v2();
437 PW_ASSERT(mbv2 != nullptr);
438 return *mbv2;
439 }
440
441 template <typename OtherMultiBuf,
442 typename = v2::internal::EnableIfConvertible<v2::TrackedMultiBuf,
443 OtherMultiBuf>>
444 constexpr operator OtherMultiBuf&&() && {
445 auto* mbv2 = v2();
446 PW_ASSERT(mbv2 != nullptr);
447 return std::move(*mbv2);
448 }
449
450 template <typename OtherMultiBuf,
451 typename = v2::internal::EnableIfConvertible<v2::TrackedMultiBuf,
452 OtherMultiBuf>>
453 constexpr operator const OtherMultiBuf&&() const&& {
454 auto* mbv2 = v2();
455 PW_ASSERT(mbv2 != nullptr);
456 return std::move(*mbv2);
457 }
458
459 private:
461 iterator ToByteIterator(const MultiBufChunks::iterator& position);
462
464 MultiBufChunks::iterator ToChunksIterator(const const_iterator& position);
465
466 size_t offset_ = 0;
467};
468
470
471// Template and constexpr method implementations.
472
473template <v2::internal::Mutability kMutability>
474constexpr void MultiBufChunks::Iterator<kMutability>::Update() {
475 // Check if this iterator is valid.
476 if (mbv2_ == nullptr || index_ >= mbv2_->num_chunks()) {
477 chunk_ = std::nullopt;
478 return;
479 }
480
481 // Check if the last chunk of the v2 multibuf is empty.
482 auto pos = mbv2_->MakeIterator(index_);
483 if (pos == mbv2_->cend()) {
484 chunk_ = Chunk(mbv2_->get_allocator(), SharedPtr<std::byte[]>());
485 return;
486 }
487
488 // Make a Chunk that corresponds to the v2 chunk.
489 chunk_ = Chunk(mbv2_->get_allocator(), mbv2_->Share(pos));
490 size_type start = mbv2_->GetOffset(index_);
491 size_type end = start + mbv2_->GetLength(index_);
492 chunk_->Slice(start, end);
493}
494
495constexpr size_t MultiBufChunks::size() const {
496 const auto* mbv2 = v2();
497 if (mbv2 == nullptr) {
498 return 0;
499 }
500 size_t size = 0;
501 for (Entry::size_type i = 0; i < mbv2->generic().num_chunks(); ++i) {
502 if (mbv2->generic().GetLength(i) != 0) {
503 ++size;
504 }
505 }
506 return size;
507}
508
509} // namespace pw::multibuf::v1_adapter
Definition: allocator.h:42
Definition: dynamic_deque.h:73
Definition: status_with_size.h:51
constexpr size_t size() const
Returns the number of Chunks in this MultiBuf, including empty chunks.
Definition: multibuf.h:495
constexpr v2::TrackedMultiBuf * v2()
Definition: multibuf.h:134
Definition: multibuf.h:213
void PushBackChunk(OwnedChunk &&chunk)
void Release() noexcept
Definition: multibuf.h:244
constexpr const MultiBufChunks & ConstChunks() const
Returns a const Chunk-oriented view of this MultiBuf.
Definition: multibuf.h:362
bool ClaimPrefix(size_t bytes_to_claim)
std::optional< ByteSpan > ContiguousSpan()
constexpr MultiBufChunks & Chunks()
Returns a Chunk-oriented view of this MultiBuf.
Definition: multibuf.h:356
void DiscardPrefix(size_t bytes_to_discard)
constexpr const MultiBufChunks & Chunks() const
Returns a Chunk-oriented view of this MultiBuf.
Definition: multibuf.h:359
StatusWithSize CopyTo(ByteSpan dest, size_t position=0) const
std::optional< MultiBuf > TakeSuffix(size_t bytes_to_take)
OwnedChunk TakeFrontChunk()
Definition: multibuf.h:343
constexpr iterator begin()
Returns an iterator pointing to the first byte of this MultiBuf.
Definition: multibuf.h:270
StatusWithSize CopyFromAndTruncate(ConstByteSpan source, size_t position=0)
MultiBufChunks::iterator InsertChunk(MultiBufChunks::iterator position, OwnedChunk &&chunk)
void Slice(size_t begin, size_t end)
constexpr bool empty() const
Definition: multibuf.h:253
std::tuple< MultiBufChunks::iterator, OwnedChunk > TakeChunk(MultiBufChunks::iterator position)
std::optional< MultiBuf > TakePrefix(size_t bytes_to_take)
constexpr size_t size() const
Definition: multibuf.h:247
void PushSuffix(MultiBuf &&tail)
StatusWithSize CopyFrom(ConstByteSpan source, size_t position=0)
void PushFrontChunk(OwnedChunk &&chunk)
static MultiBuf FromChunk(OwnedChunk &&chunk)
bool ClaimSuffix(size_t bytes_to_claim)
bool IsContiguous() const
Definition: multibuf.h:261
void PushPrefix(MultiBuf &&front)
constexpr iterator end()
Returns an iterator pointing to the end of this MultiBuf.
Definition: multibuf.h:284
internal::Instance< BasicMultiBuf > Instance
Definition: multibuf.h:249
Byte iterator templated on the const-ness of the bytes it references.
Definition: byte_iterator.h:85
Property
Basic properties of a MultiBuf.
Definition: properties.h:24
BasicMultiBuf< Property::kLayerable, Property::kObservable > TrackedMultiBuf
Definition: multibuf.h:80
Status Assign(InlineString<> &string, std::string_view view)
Definition: util.h:142
Definition: entry.h:47