C/C++ API Reference
Loading...
Searching...
No Matches
multibuf_v2.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// NOTE: MultiBuf version 2 is in a nearly stable state, but may still change.
17// The Pigweed team will announce breaking changes in the API. It is strongly
18// recommended to coordinate with the Pigweed team if you want to start using
19// this code.
20
21#include <cstddef>
22#include <cstdint>
23#include <numeric>
24#include <type_traits>
25
26#include "pw_allocator/allocator.h"
27#include "pw_bytes/span.h"
28#include "pw_containers/dynamic_deque.h"
29#include "pw_multibuf/internal/byte_iterator.h"
30#include "pw_multibuf/internal/chunk_iterator.h"
31#include "pw_multibuf/internal/entry.h"
32#include "pw_multibuf/observer.h"
33#include "pw_multibuf/properties.h"
34#include "pw_span/span.h"
35
36namespace pw {
37
38// Forward declarations.
39template <MultiBufProperty...>
40class BasicMultiBuf;
41
42namespace multibuf_impl {
43
44template <typename>
45class Instance;
46
47class GenericMultiBuf;
48
49} // namespace multibuf_impl
50
52
53// Type aliases for convenience, listed here for easier discoverability.
54
57
60
64
69
73
78
83
89
183template <MultiBufProperty... kProperties>
185 protected:
190
192
193 public:
195 [[nodiscard]] static constexpr bool is_const() {
196 return ((kProperties == Property::kConst) || ...);
197 }
198
200 [[nodiscard]] static constexpr bool is_layerable() {
201 return ((kProperties == Property::kLayerable) || ...);
202 }
203
205 [[nodiscard]] static constexpr bool is_observable() {
206 return ((kProperties == Property::kObservable) || ...);
207 }
208
209 using size_type = typename Deque::size_type;
210 using difference_type = typename Deque::difference_type;
211 using iterator = multibuf_impl::ByteIterator<size_type, /*kIsConst=*/false>;
212 using const_iterator =
213 multibuf_impl::ByteIterator<size_type, /*kIsConst=*/true>;
214 using pointer = iterator::pointer;
215 using const_pointer = const_iterator::pointer;
216 using reference = iterator::reference;
217 using const_reference = const_iterator::reference;
218 using value_type = std::conditional_t<is_const(),
219 const_iterator::value_type,
220 iterator::value_type>;
221
242
243 // Interfaces are not copyable or movable; copy and move `Instance`s instead.
244
245 BasicMultiBuf(const BasicMultiBuf&) = delete;
246
247 template <Property... kOtherProperties>
249
250 BasicMultiBuf& operator=(const BasicMultiBuf&) = delete;
251
252 template <Property... kOtherProperties>
253 BasicMultiBuf& operator=(const BasicMultiBuf<kOtherProperties...>&) = delete;
254
255 BasicMultiBuf(BasicMultiBuf&&) = delete;
256
257 BasicMultiBuf& operator=(BasicMultiBuf&&) = delete;
258
259 // Conversions
260
261 template <typename OtherMultiBuf>
262 constexpr OtherMultiBuf& as() & {
263 multibuf_impl::AssertIsConvertible<BasicMultiBuf, OtherMultiBuf>();
264 return generic().template as<OtherMultiBuf>();
265 }
266
267 template <typename OtherMultiBuf>
268 constexpr const OtherMultiBuf& as() const& {
269 multibuf_impl::AssertIsConvertible<BasicMultiBuf, OtherMultiBuf>();
270 return generic().template as<OtherMultiBuf>();
271 }
272
273 template <typename OtherMultiBuf>
274 constexpr OtherMultiBuf&& as() && {
275 multibuf_impl::AssertIsConvertible<BasicMultiBuf, OtherMultiBuf>();
276 return std::move(generic().template as<OtherMultiBuf>());
277 }
278
279 template <typename OtherMultiBuf>
280 constexpr const OtherMultiBuf&& as() const&& {
281 multibuf_impl::AssertIsConvertible<BasicMultiBuf, OtherMultiBuf>();
282 return std::move(generic().template as<OtherMultiBuf>());
283 }
284
285 template <typename OtherMultiBuf,
286 typename = multibuf_impl::EnableIfConvertible<BasicMultiBuf,
287 OtherMultiBuf>>
288 constexpr operator OtherMultiBuf&() & {
289 return as<OtherMultiBuf>();
290 }
291
292 template <typename OtherMultiBuf,
293 typename = multibuf_impl::EnableIfConvertible<BasicMultiBuf,
294 OtherMultiBuf>>
295 constexpr operator const OtherMultiBuf&() const& {
296 return as<OtherMultiBuf>();
297 }
298
299 template <typename OtherMultiBuf,
300 typename = multibuf_impl::EnableIfConvertible<BasicMultiBuf,
301 OtherMultiBuf>>
302 constexpr operator OtherMultiBuf&&() && {
303 return std::move(as<OtherMultiBuf>());
304 }
305
306 template <typename OtherMultiBuf,
307 typename = multibuf_impl::EnableIfConvertible<BasicMultiBuf,
308 OtherMultiBuf>>
309 constexpr operator const OtherMultiBuf&&() const&& {
310 return std::move(as<OtherMultiBuf>());
311 }
312
313 // Accessors
314
317 constexpr bool empty() const { return generic().empty(); }
318
321 constexpr size_t size() const { return generic().size(); }
322
334 template <bool kMutable = !is_const()>
335 std::enable_if_t<kMutable, reference> at(size_t index) {
336 return *(begin() + static_cast<int>(index));
337 }
338 const_reference at(size_t index) const {
339 return *(begin() + static_cast<int>(index));
340 }
341
342 template <bool kMutable = !is_const()>
343 std::enable_if_t<kMutable, reference> operator[](size_t index) {
344 return at(index);
345 }
346 const_reference operator[](size_t index) const { return at(index); }
348
360 template <bool kMutable = !is_const()>
361 constexpr std::enable_if_t<kMutable, ChunksType> Chunks() {
362 return generic().Chunks();
363 }
364 constexpr ConstChunksType Chunks() const { return generic().ConstChunks(); }
365 constexpr ConstChunksType ConstChunks() const {
366 return generic().ConstChunks();
367 }
369
370 // Iterators.
371
382 template <bool kMutable = !is_const()>
383 constexpr std::enable_if_t<kMutable, iterator> begin() {
384 return generic().begin();
385 }
386 constexpr const_iterator begin() const { return cbegin(); }
387 constexpr const_iterator cbegin() const { return generic().cbegin(); }
389
400 template <bool kMutable = !is_const()>
401 constexpr std::enable_if_t<kMutable, iterator> end() {
402 return generic().end();
403 }
404 constexpr const_iterator end() const { return cend(); }
405 constexpr const_iterator cend() const { return generic().cend(); }
407
408 // Other methods
409
419 bool IsCompatible(const BasicMultiBuf& mb) const {
420 return generic().IsCompatible(mb.generic());
421 }
422
432 bool IsCompatible(const UniquePtr<std::byte[]>& bytes) const {
433 return generic().IsCompatible(bytes.deallocator());
434 }
435 bool IsCompatible(const UniquePtr<const std::byte[]>& bytes) const {
436 return generic().IsCompatible(bytes.deallocator());
437 }
439
448 bool IsCompatible(const SharedPtr<std::byte[]>& bytes) const {
449 return generic().IsCompatible(bytes.control_block());
450 }
451 bool IsCompatible(const SharedPtr<const std::byte[]>& bytes) const {
452 return generic().IsCompatible(bytes.control_block());
453 }
455
458 [[nodiscard]] bool TryReserveChunks(size_t num_chunks) {
459 return generic().TryReserveChunks(num_chunks);
460 }
461
462 // Mutators
463
475 template <Property... kOtherProperties>
476 [[nodiscard]] bool TryReserveForInsert(
478
490 template <
491 int&... kExplicitGuard,
492 typename T,
493 typename =
494 std::enable_if_t<std::is_constructible_v<ConstByteSpan, T>, int>>
495 [[nodiscard]] bool TryReserveForInsert(const_iterator pos, const T& bytes);
496
510 [[nodiscard]] bool TryReserveForInsert(const_iterator pos,
511 const UniquePtr<std::byte[]>& bytes);
512 [[nodiscard]] bool TryReserveForInsert(
513 const_iterator pos, const UniquePtr<const std::byte[]>& bytes);
515
529 [[nodiscard]] bool TryReserveForInsert(const_iterator pos,
530 const SharedPtr<std::byte[]>& bytes);
531 [[nodiscard]] bool TryReserveForInsert(
532 const_iterator pos, const SharedPtr<const std::byte[]>& bytes);
534
543 template <Property... kOtherProperties>
545
554 template <
555 int&... kExplicitGuard,
556 typename T,
557 typename =
558 std::enable_if_t<std::is_constructible_v<ConstByteSpan, T>, int>>
559 void Insert(const_iterator pos, const T& bytes);
560
571 void Insert(const_iterator pos, UniquePtr<std::byte[]>&& bytes) {
572 Insert(pos, std::move(bytes), 0);
573 }
574 void Insert(const_iterator pos, UniquePtr<const std::byte[]>&& bytes) {
575 Insert(pos, std::move(bytes), 0);
576 }
578
591 void Insert(const_iterator pos,
592 UniquePtr<std::byte[]>&& bytes,
593 size_t offset,
594 size_t length = dynamic_extent);
595 void Insert(const_iterator pos,
596 UniquePtr<const std::byte[]>&& bytes,
597 size_t offset,
598 size_t length = dynamic_extent);
600
611 void Insert(const_iterator pos, const SharedPtr<std::byte[]>& bytes) {
612 Insert(pos, bytes, 0);
613 }
614 void Insert(const_iterator pos, const SharedPtr<const std::byte[]>& bytes) {
615 Insert(pos, bytes, 0);
616 }
618
631 void Insert(const_iterator pos,
632 const SharedPtr<std::byte[]>& bytes,
633 size_t offset,
634 size_t length = dynamic_extent);
635 void Insert(const_iterator pos,
636 const SharedPtr<const std::byte[]>& bytes,
637 size_t offset,
638 size_t length = dynamic_extent);
640
648 template <Property... kOtherProperties>
649 [[nodiscard]] bool TryReserveForPushBack(
651
659 template <
660 int&... kExplicitGuard,
661 typename T,
662 typename =
663 std::enable_if_t<std::is_constructible_v<ConstByteSpan, T>, int>>
664 [[nodiscard]] bool TryReserveForPushBack(const T& bytes);
665
675 [[nodiscard]] bool TryReserveForPushBack(const UniquePtr<std::byte[]>& bytes);
676 [[nodiscard]] bool TryReserveForPushBack(
677 const UniquePtr<const std::byte[]>& bytes);
679
689 [[nodiscard]] bool TryReserveForPushBack(const SharedPtr<std::byte[]>& bytes);
690 [[nodiscard]] bool TryReserveForPushBack(
691 const SharedPtr<const std::byte[]>& bytes);
693
701 template <Property... kOtherProperties>
703
711 template <
712 int&... kExplicitGuard,
713 typename T,
714 typename =
715 std::enable_if_t<std::is_constructible_v<ConstByteSpan, T>, int>>
716 void PushBack(const T& bytes);
717
727 void PushBack(UniquePtr<std::byte[]>&& bytes) {
728 PushBack(std::move(bytes), 0);
729 }
730 void PushBack(UniquePtr<const std::byte[]>&& bytes) {
731 PushBack(std::move(bytes), 0);
732 }
734
746 void PushBack(UniquePtr<std::byte[]>&& bytes,
747 size_t offset,
748 size_t length = dynamic_extent);
749
750 void PushBack(UniquePtr<const std::byte[]>&& bytes,
751 size_t offset,
752 size_t length = dynamic_extent);
754
764 void PushBack(const SharedPtr<std::byte[]>& bytes) { PushBack(bytes, 0); }
765 void PushBack(const SharedPtr<const std::byte[]>& bytes) {
766 PushBack(bytes, 0);
767 }
769
781 void PushBack(const SharedPtr<std::byte[]>& bytes,
782 size_t offset,
783 size_t length = dynamic_extent);
784 void PushBack(const SharedPtr<const std::byte[]>& bytes,
785 size_t offset,
786 size_t length = dynamic_extent);
788
798 [[nodiscard]] bool IsRemovable(const_iterator pos, size_t size) const {
799 return generic().IsRemovable(pos, size);
800 }
801
822
834
835 // clang-format off
857 // clang-format on
859 return generic().Discard(pos, size);
860 }
861
866 [[nodiscard]] bool IsReleasable(const_iterator pos) const {
867 return generic().IsReleasable(pos);
868 }
869
883
888 [[nodiscard]] bool IsShareable(const_iterator pos) const {
889 return generic().IsShareable(pos);
890 }
891
907
916 size_t CopyTo(ByteSpan dst, size_t offset = 0) const {
917 return generic().CopyTo(dst, offset);
918 }
919
928 size_t CopyFrom(ConstByteSpan src, size_t offset = 0) {
929 static_assert(!is_const(),
930 "`CopyFrom` may only be called on mutable MultiBufs");
931 return generic().CopyFrom(src, offset);
932 }
933
950 ConstByteSpan Get(ByteSpan copy, size_t offset = 0) const {
951 return generic().Get(copy, offset);
952 }
953
969 template <int&... kExplicitGuard, typename Visitor>
970 auto Visit(Visitor visitor, ByteSpan copy, size_t offset) {
971 return visitor(Get(copy, offset));
972 }
973
979 void Clear() { generic().Clear(); }
980
981 // Observable methods.
982
987 constexpr MultiBufObserver* observer() const {
988 static_assert(is_observable(),
989 "`observer` may only be called on observable MultiBufs");
990 return generic().observer_;
991 }
992
1000 static_assert(is_observable(),
1001 "`set_observer` may only be called on observable MultiBufs");
1002 generic().observer_ = observer;
1003 }
1004
1005 // Layerable methods.
1006
1013 size_type NumFragments() const {
1014 static_assert(is_layerable(),
1015 "`NumFragments` may only be called on layerable MultiBufs");
1016 return generic().NumFragments();
1017 }
1018
1022 constexpr size_type NumLayers() const {
1023 static_assert(is_layerable(),
1024 "`NumLayers` may only be called on layerable MultiBufs");
1025 return generic().NumLayers();
1026 }
1027
1035 [[nodiscard]] bool TryReserveLayers(size_t num_layers,
1036 size_t num_chunks = 1) {
1037 return generic().TryReserveLayers(num_layers, num_chunks);
1038 }
1039
1059 [[nodiscard]] bool AddLayer(size_t offset, size_t length = dynamic_extent) {
1060 static_assert(is_layerable(),
1061 "`AddLayer` may only be called on layerable MultiBufs");
1062 return generic().AddLayer(offset, length);
1063 }
1064
1068 static_assert(is_layerable(),
1069 "`SealTopLayer` may only be called on layerable MultiBufs");
1070 return generic().SealTopLayer();
1071 }
1072
1076 static_assert(is_layerable(),
1077 "`UnsealTopLayer` may only be called on layerable MultiBufs");
1078 return generic().UnsealTopLayer();
1079 }
1080
1082 [[nodiscard]] bool IsTopLayerSealed() {
1083 static_assert(
1084 is_layerable(),
1085 "`IsTopLayerSealed` may only be called on layerable MultiBufs");
1086 return generic().IsTopLayerSealed();
1087 }
1088
1097 void TruncateTopLayer(size_t length) {
1098 static_assert(
1099 is_layerable(),
1100 "`TruncateTopLayer` may only be called on layerable MultiBufs");
1101 generic().TruncateTopLayer(length);
1102 }
1103
1111 static_assert(!is_const() && is_layerable(),
1112 "`SetTopLayer` may only be called on mutable, layerable "
1113 "MultiBufs");
1114 PW_ASSERT(src.size() <= size());
1115 CopyFrom(src);
1116 TruncateTopLayer(src.size());
1117 }
1118
1127 void PopLayer() {
1128 static_assert(is_layerable(),
1129 "`PopLayer` may only be called on layerable MultiBufs");
1130 generic().PopLayer();
1131 }
1132
1133 protected:
1134 constexpr BasicMultiBuf() { multibuf_impl::PropertiesAreValid(); }
1135
1136 private:
1137 template <Property...>
1138 friend class BasicMultiBuf;
1139
1140 constexpr GenericMultiBuf& generic() {
1141 return static_cast<GenericMultiBuf&>(*this);
1142 }
1143 constexpr const GenericMultiBuf& generic() const {
1144 return static_cast<const GenericMultiBuf&>(*this);
1145 }
1146};
1147
1148namespace multibuf_impl {
1149
1150// A generic MultiBuf implementation that provides the functionality of any
1151// `BasicMultiBuf<kProperties>` type.
1152//
1153// This class should not be instantiated directly. Instead, use `Instance` as
1154// described below. This is the base class for all `Instance` types, and derives
1155// from every supported `BasicMultiBuf` type. It implements the MultiBuf
1156// behavior in one type, and allows for performing conversions from `Instance`s
1157// and `BasicMultiBuf`s to `BasicMultiBuf`s with different yet compatible
1158// propreties.
1160 : private BasicMultiBuf<>,
1173 private:
1174 using typename BasicMultiBuf<>::ChunksType;
1175 using typename BasicMultiBuf<>::ConstChunksType;
1176 using ControlBlock = allocator::internal::ControlBlock;
1177 using typename BasicMultiBuf<>::Deque;
1178
1179 public:
1180 using typename BasicMultiBuf<>::const_iterator;
1181 using typename BasicMultiBuf<>::const_pointer;
1182 using typename BasicMultiBuf<>::const_reference;
1183 using typename BasicMultiBuf<>::difference_type;
1184 using typename BasicMultiBuf<>::iterator;
1185 using typename BasicMultiBuf<>::pointer;
1186 using typename BasicMultiBuf<>::reference;
1187 using typename BasicMultiBuf<>::size_type;
1188
1189 ~GenericMultiBuf() { Clear(); }
1190
1191 // Not copyable.
1192 GenericMultiBuf(const GenericMultiBuf&) = delete;
1193 GenericMultiBuf& operator=(const GenericMultiBuf&) = delete;
1194
1195 // Movable.
1197 : GenericMultiBuf(other.deque_.get_allocator()) {
1198 *this = std::move(other);
1199 }
1200 GenericMultiBuf& operator=(GenericMultiBuf&& other);
1201
1202 private:
1203 template <MultiBufProperty...>
1204 friend class ::pw::BasicMultiBuf;
1205
1206 template <typename>
1207 friend class ::pw::multibuf_impl::Instance;
1208
1210 constexpr explicit GenericMultiBuf(Allocator& allocator)
1211 : deque_(allocator) {}
1212
1213 template <typename MultiBufType>
1214 constexpr MultiBufType& as() {
1215 return *this;
1216 }
1217
1218 template <typename MultiBufType>
1219 constexpr const MultiBufType& as() const {
1220 return *this;
1221 }
1222
1223 // Accessors.
1224
1226 constexpr bool empty() const { return deque_.empty(); }
1227
1229 constexpr size_t size() const {
1230 return static_cast<size_t>(cend() - cbegin());
1231 }
1232
1234 constexpr bool has_deallocator() const {
1235 return memory_tag_ == MemoryTag::kDeallocator || has_control_block();
1236 }
1237
1239 constexpr bool has_control_block() const {
1240 return memory_tag_ == MemoryTag::kControlBlock;
1241 }
1242
1243 // Iterators.
1244
1245 constexpr ChunksType Chunks() { return ChunksType(deque_, depth_); }
1246 constexpr ConstChunksType ConstChunks() const {
1247 return ConstChunksType(deque_, depth_);
1248 }
1249
1250 constexpr iterator begin() { return iterator(Chunks().begin(), 0); }
1251 constexpr const_iterator cbegin() const {
1252 return const_iterator(ConstChunks().cbegin(), 0);
1253 }
1254
1255 constexpr iterator end() { return iterator(Chunks().end(), 0); }
1256 constexpr const_iterator cend() const {
1257 return const_iterator(ConstChunks().cend(), 0);
1258 }
1259
1260 // Mutators.
1261
1263 bool IsCompatible(const GenericMultiBuf& other) const;
1264 bool IsCompatible(const Deallocator* other) const;
1265 bool IsCompatible(const ControlBlock* other) const;
1266
1268 [[nodiscard]] bool TryReserveChunks(size_t num_chunks);
1269
1272 [[nodiscard]] bool TryReserveForInsert(const_iterator pos,
1273 const GenericMultiBuf& mb);
1274 [[nodiscard]] bool TryReserveForInsert(const_iterator pos, size_t size);
1275 [[nodiscard]] bool TryReserveForInsert(const_iterator pos,
1276 size_t size,
1277 const Deallocator* deallocator);
1278 [[nodiscard]] bool TryReserveForInsert(const_iterator pos,
1279 size_t size,
1280 const ControlBlock* control_block);
1282
1285 void Insert(const_iterator pos, GenericMultiBuf&& mb);
1286 void Insert(const_iterator pos, ConstByteSpan bytes);
1287 void Insert(const_iterator pos,
1288 ConstByteSpan bytes,
1289 size_t offset,
1290 size_t length,
1291 Deallocator* deallocator);
1292 void Insert(const_iterator pos,
1293 ConstByteSpan bytes,
1294 size_t offset,
1295 size_t length,
1296 ControlBlock* control_block);
1298
1300 [[nodiscard]] bool IsRemovable(const_iterator pos, size_t size) const;
1301
1303 Result<GenericMultiBuf> Remove(const_iterator pos, size_t size);
1304
1306 Result<GenericMultiBuf> PopFrontFragment();
1307
1309 Result<const_iterator> Discard(const_iterator pos, size_t size);
1310
1312 [[nodiscard]] bool IsReleasable(const_iterator pos) const;
1313
1316
1318 [[nodiscard]] bool IsShareable(const_iterator pos) const;
1319
1321 std::byte* Share(const_iterator pos);
1322
1324 size_t CopyTo(ByteSpan dst, size_t offset) const;
1325
1327 size_t CopyFrom(ConstByteSpan src, size_t offset);
1328
1330 ConstByteSpan Get(ByteSpan copy, size_t offset) const;
1331
1333 void Clear();
1334
1335 // Layerable methods.
1336
1338 size_type NumFragments() const;
1339
1341 constexpr size_type NumLayers() const { return depth_ - 1; }
1342
1344 [[nodiscard]] bool TryReserveLayers(size_t num_layers, size_t num_chunks = 1);
1345
1347 [[nodiscard]] bool AddLayer(size_t offset, size_t length = dynamic_extent);
1348
1350 void SealTopLayer();
1351
1353 void UnsealTopLayer();
1354
1356 void TruncateTopLayer(size_t length);
1357
1359 void PopLayer();
1360
1361 // Implementation methods.
1362 //
1363 // These methods are used to implement the methods above, and should not be
1364 // called directly by BasicMultiBuf<>.
1365
1370 static size_t CheckRange(size_t offset, size_t length, size_t size);
1371
1373 constexpr std::byte* GetData(size_type index) const {
1374 return deque_[index].data;
1375 }
1376
1379 constexpr bool IsOwned(size_type index) const {
1380 return deque_[index + 1].base_view.owned;
1381 }
1382
1385 constexpr bool IsShared(size_type index) const {
1386 return deque_[index + 1].base_view.shared;
1387 }
1388
1390 constexpr bool IsSealed(size_type index) const {
1391 return depth_ == 2 ? false : deque_[index + depth_ - 1].view.sealed;
1392 }
1393
1396 constexpr bool IsBoundary(size_type index) const {
1397 return depth_ == 2 ? true : deque_[index + depth_ - 1].view.boundary;
1398 }
1399
1403 constexpr size_type GetOffset(size_type index, uint16_t layer) const {
1404 return layer == 1 ? deque_[index + 1].base_view.offset
1405 : deque_[index + layer].view.offset;
1406 }
1407
1409 constexpr size_type GetOffset(size_type index) const {
1410 return GetOffset(index, NumLayers());
1411 }
1412
1415 constexpr size_type GetRelativeOffset(size_type index) const {
1416 uint16_t layer = NumLayers();
1417 if (layer == 1) {
1418 return GetOffset(index, layer);
1419 }
1420 return GetOffset(index, layer) - GetOffset(index, layer - 1);
1421 }
1422
1424 constexpr size_type GetLength(size_type index) const {
1425 return depth_ == 2 ? deque_[index + 1].base_view.length
1426 : deque_[index + depth_ - 1].view.length;
1427 }
1428
1430 constexpr ByteSpan GetView(size_type index) const {
1431 return ByteSpan(GetData(index) + GetOffset(index), GetLength(index));
1432 }
1433
1435 Deallocator* GetDeallocator() const;
1436
1438 void SetDeallocator(Deallocator* deallocator);
1439
1441 ControlBlock* GetControlBlock() const;
1442
1444 void SetControlBlock(ControlBlock* control_block);
1445
1447 void CopyMemoryContext(const GenericMultiBuf& other);
1448
1450 void ClearMemoryContext();
1451
1455 std::pair<size_type, size_type> GetIndexAndOffset(const_iterator pos) const;
1456
1462 bool TryReserveEntries(const_iterator pos, size_type num_entries);
1463 bool TryReserveEntries(size_type num_entries, bool split = false);
1465
1468 size_type InsertEntries(const_iterator pos, size_type num_entries);
1469
1472 size_type Insert(const_iterator pos,
1473 ConstByteSpan bytes,
1474 size_t offset,
1475 size_t length);
1476
1486 void SplitBase(size_type index, Deque& out_deque, size_type out_index);
1487
1493 void SplitBefore(size_type index,
1494 size_type split,
1495 Deque& out_deque,
1496 size_type out_index);
1497
1500 void SplitBefore(size_type index, size_type split);
1501
1507 void SplitAfter(size_type index,
1508 size_type split,
1509 Deque& out_deque,
1510 size_type out_index);
1511
1514 void SplitAfter(size_type index, size_type split);
1515
1526 [[nodiscard]] bool TryReserveForRemove(const_iterator pos,
1527 size_t size,
1528 GenericMultiBuf* out);
1529
1534 void CopyRange(const_iterator pos, size_t size, GenericMultiBuf& out);
1535
1539 void ClearRange(const_iterator pos, size_t size);
1540
1545 void EraseRange(const_iterator pos, size_t size);
1546
1549 size_type FindShared(size_type index, size_type start);
1550
1553 size_t CopyToImpl(ByteSpan dst, size_t offset, size_type start) const;
1554
1556 [[nodiscard]] bool IsTopLayerSealed() const;
1557
1560 void SetLayer(size_t offset, size_t length);
1561
1562 // Describes the memory and views to that in a one-dimensional sequence.
1563 // Every `depth_`-th entry holds a pointer to memory, each entry for a given
1564 // offset less than `depth_` after that entry is a view that is part of the
1565 // same "layer" in the MultiBuf.
1566 //
1567 // This base type will always have 0 or 2 layers, but derived classes may add
1568 // more.
1569 //
1570 // For example, a MultiBuf that has had 3 `Chunk`s and two additional layers
1571 // added would have a `deque_` resembling the following:
1572 //
1573 // buffer 0: buffer 1: buffer 2:
1574 // layer 3: deque_[0x3].view deque_[0x7].view deque_[0xB].view
1575 // layer 2: deque_[0x2].view deque_[0x6].view deque_[0xA].view
1576 // layer 1: deque_[0x1].view deque_[0x5].view deque_[0x9].view
1577 // layer 0: deque_[0x0].data deque_[0x4].data deque_[0x8].data
1578 Deque deque_;
1579
1580 // Number of entries per chunk in this MultiBuf.
1581 size_type depth_ = 2;
1582
1599 enum class MemoryTag : uint8_t {
1600 kEmpty,
1601 kDeallocator,
1602 kControlBlock,
1603 } memory_tag_ = MemoryTag::kEmpty;
1604
1605 union MemoryContext {
1606 Deallocator* deallocator;
1607 ControlBlock* control_block;
1608 } memory_context_ = {.deallocator = nullptr};
1610
1613 MultiBufObserver* observer_ = nullptr;
1614};
1615
1634template <typename MultiBufType>
1636 public:
1637 constexpr explicit Instance(Allocator& allocator) : base_(allocator) {}
1638
1639 constexpr Instance(Instance&&) = default;
1640 constexpr Instance& operator=(Instance&&) = default;
1641
1642 constexpr Instance(MultiBufType&& mb)
1643 : base_(std::move(static_cast<GenericMultiBuf&>(mb))) {}
1644
1645 constexpr Instance& operator=(MultiBufType&& mb) {
1646 base_ = std::move(static_cast<GenericMultiBuf&>(mb));
1647 return *this;
1648 }
1649
1650 constexpr MultiBufType* operator->() { return &base_.as<MultiBufType>(); }
1651 constexpr const MultiBufType* operator->() const {
1652 return &base_.as<MultiBufType>();
1653 }
1654
1655 constexpr MultiBufType& operator*() & { return base_.as<MultiBufType>(); }
1656 constexpr const MultiBufType& operator*() const& {
1657 return base_.as<MultiBufType>();
1658 }
1659
1660 constexpr MultiBufType&& operator*() && {
1661 return std::move(base_.as<MultiBufType>());
1662 }
1663 constexpr const MultiBufType&& operator*() const&& {
1664 return std::move(base_.as<MultiBufType>());
1665 }
1666
1667 constexpr operator MultiBufType&() & { return base_.as<MultiBufType>(); }
1668 constexpr operator const MultiBufType&() const& {
1669 return base_.as<MultiBufType>();
1670 }
1671
1672 constexpr operator MultiBufType&&() && {
1673 return std::move(base_.as<MultiBufType>());
1674 }
1675 constexpr operator const MultiBufType&&() const&& {
1676 return std::move(base_.as<MultiBufType>());
1677 }
1678
1679 private:
1680 GenericMultiBuf base_;
1681};
1682
1683} // namespace multibuf_impl
1684
1686
1688
1689// Template method implementations.
1690
1691template <MultiBufProperty... kProperties>
1692template <MultiBufProperty... kOtherProperties>
1695 multibuf_impl::AssertIsConvertible<BasicMultiBuf<kOtherProperties...>,
1696 BasicMultiBuf>();
1697 return generic().TryReserveForInsert(pos,
1698 static_cast<const GenericMultiBuf&>(mb));
1699}
1700
1701template <MultiBufProperty... kProperties>
1702template <int&... kExplicitGuard, typename T, typename>
1704 const T& bytes) {
1705 using data_ptr_type = decltype(std::data(std::declval<T&>()));
1706 static_assert(std::is_same_v<data_ptr_type, std::byte*> || is_const(),
1707 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1708 return generic().TryReserveForInsert(pos, bytes.size());
1709}
1710
1711template <MultiBufProperty... kProperties>
1713 const_iterator pos, const UniquePtr<std::byte[]>& bytes) {
1714 return generic().TryReserveForInsert(pos, bytes.size(), bytes.deallocator());
1715}
1716
1717template <MultiBufProperty... kProperties>
1719 const_iterator pos, const UniquePtr<const std::byte[]>& bytes) {
1720 static_assert(is_const(),
1721 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1722 return generic().TryReserveForInsert(pos, bytes.size(), bytes.deallocator());
1723}
1724
1725template <MultiBufProperty... kProperties>
1727 const_iterator pos, const SharedPtr<std::byte[]>& bytes) {
1728 return generic().TryReserveForInsert(
1729 pos, bytes.size(), bytes.control_block());
1730}
1731
1732template <MultiBufProperty... kProperties>
1734 const_iterator pos, const SharedPtr<const std::byte[]>& bytes) {
1735 static_assert(is_const(),
1736 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1737 return generic().TryReserveForInsert(
1738 pos, bytes.size(), bytes.control_block());
1739}
1740
1741template <MultiBufProperty... kProperties>
1742template <MultiBufProperty... kOtherProperties>
1745 multibuf_impl::AssertIsConvertible<BasicMultiBuf<kOtherProperties...>,
1746 BasicMultiBuf>();
1747 generic().Insert(pos, std::move(mb.generic()));
1748}
1749
1750template <MultiBufProperty... kProperties>
1751template <int&... kExplicitGuard, typename T, typename>
1753 using data_ptr_type = decltype(std::data(std::declval<T&>()));
1754 static_assert(std::is_same_v<data_ptr_type, std::byte*> || is_const(),
1755 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1756 generic().Insert(pos, bytes);
1757}
1758
1759template <MultiBufProperty... kProperties>
1760void BasicMultiBuf<kProperties...>::Insert(const_iterator pos,
1761 UniquePtr<std::byte[]>&& bytes,
1762 size_t offset,
1763 size_t length) {
1764 ConstByteSpan chunk(bytes.get(), bytes.size());
1765 generic().Insert(pos, chunk, offset, length, bytes.deallocator());
1766 bytes.Release();
1767}
1768
1769template <MultiBufProperty... kProperties>
1770void BasicMultiBuf<kProperties...>::Insert(const_iterator pos,
1771 UniquePtr<const std::byte[]>&& bytes,
1772 size_t offset,
1773 size_t length) {
1774 static_assert(is_const(),
1775 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1776 ConstByteSpan chunk(bytes.get(), bytes.size());
1777 generic().Insert(pos, chunk, offset, length, bytes.deallocator());
1778 bytes.Release();
1779}
1780
1781template <MultiBufProperty... kProperties>
1782void BasicMultiBuf<kProperties...>::Insert(const_iterator pos,
1783 const SharedPtr<std::byte[]>& bytes,
1784 size_t offset,
1785 size_t length) {
1786 ConstByteSpan chunk(bytes.get(), bytes.size());
1787 generic().Insert(pos, chunk, offset, length, bytes.control_block());
1788}
1789
1790template <MultiBufProperty... kProperties>
1792 const_iterator pos,
1793 const SharedPtr<const std::byte[]>& bytes,
1794 size_t offset,
1795 size_t length) {
1796 static_assert(is_const(),
1797 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1798 ConstByteSpan chunk(bytes.get(), bytes.size());
1799 generic().Insert(pos, chunk, offset, length, bytes.control_block());
1800}
1801
1802template <MultiBufProperty... kProperties>
1803template <MultiBufProperty... kOtherProperties>
1806 return TryReserveForInsert(end(), mb);
1807}
1808
1809template <MultiBufProperty... kProperties>
1810template <int&... kExplicitGuard, typename T, typename>
1812 using data_ptr_type = decltype(std::data(std::declval<T&>()));
1813 static_assert(std::is_same_v<data_ptr_type, std::byte*> || is_const(),
1814 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1815 return TryReserveForInsert(end(), bytes);
1816}
1817
1818template <MultiBufProperty... kProperties>
1820 const UniquePtr<std::byte[]>& bytes) {
1821 return TryReserveForInsert(end(), std::move(bytes));
1822}
1823
1824template <MultiBufProperty... kProperties>
1826 const UniquePtr<const std::byte[]>& bytes) {
1827 static_assert(is_const(),
1828 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1829 return TryReserveForInsert(end(), std::move(bytes));
1830}
1831
1832template <MultiBufProperty... kProperties>
1834 const SharedPtr<std::byte[]>& bytes) {
1835 return TryReserveForInsert(end(), bytes);
1836}
1837
1838template <MultiBufProperty... kProperties>
1840 const SharedPtr<const std::byte[]>& bytes) {
1841 static_assert(is_const(),
1842 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1843 return TryReserveForInsert(end(), bytes);
1844}
1845
1846template <MultiBufProperty... kProperties>
1847template <MultiBufProperty... kOtherProperties>
1850 Insert(end(), std::move(mb));
1851}
1852
1853template <MultiBufProperty... kProperties>
1854template <int&... kExplicitGuard, typename T, typename>
1856 using data_ptr_type = decltype(std::data(std::declval<T&>()));
1857 static_assert(std::is_same_v<data_ptr_type, std::byte*> || is_const(),
1858 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1859 Insert(end(), bytes);
1860}
1861
1862template <MultiBufProperty... kProperties>
1863void BasicMultiBuf<kProperties...>::PushBack(UniquePtr<std::byte[]>&& bytes,
1864 size_t offset,
1865 size_t length) {
1866 Insert(end(), std::move(bytes), offset, length);
1867}
1868
1869template <MultiBufProperty... kProperties>
1871 UniquePtr<const std::byte[]>&& bytes, size_t offset, size_t length) {
1872 static_assert(is_const(),
1873 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1874 Insert(end(), std::move(bytes), offset, length);
1875}
1876
1877template <MultiBufProperty... kProperties>
1879 const SharedPtr<std::byte[]>& bytes, size_t offset, size_t length) {
1880 Insert(end(), bytes, offset, length);
1881}
1882
1883template <MultiBufProperty... kProperties>
1885 const SharedPtr<const std::byte[]>& bytes, size_t offset, size_t length) {
1886 static_assert(is_const(),
1887 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1888 Insert(end(), bytes, offset, length);
1889}
1890
1891template <MultiBufProperty... kProperties>
1892Result<multibuf_impl::Instance<BasicMultiBuf<kProperties...>>>
1894 auto result = generic().Remove(pos, size);
1895 if (!result.ok()) {
1896 return result.status();
1897 }
1898 return Instance(std::move(*result));
1899}
1900
1901template <MultiBufProperty... kProperties>
1904 Result<GenericMultiBuf> result = generic().PopFrontFragment();
1905 if (!result.ok()) {
1906 return result.status();
1907 }
1908 return Instance(std::move(*result));
1909}
1910
1911template <MultiBufProperty... kProperties>
1912UniquePtr<typename BasicMultiBuf<kProperties...>::value_type[]>
1914 UniquePtr<std::byte[]> bytes = generic().Release(pos);
1915 if constexpr (is_const()) {
1916 UniquePtr<const std::byte[]> const_bytes(
1917 bytes.get(), bytes.size(), *(bytes.deallocator()));
1918 bytes.Release();
1919 return const_bytes;
1920 } else {
1921 return bytes;
1922 }
1923}
1924
1925template <MultiBufProperty... kProperties>
1926SharedPtr<typename BasicMultiBuf<kProperties...>::value_type[]>
1928 return SharedPtr<value_type[]>(generic().Share(pos),
1929 generic().GetControlBlock());
1930}
1931
1932} // namespace pw
Definition: allocator.h:36
bool IsTopLayerSealed()
Returns whether the "sealed" flag is set in the top layer.
Definition: multibuf_v2.h:1082
bool AddLayer(size_t offset, size_t length=dynamic_extent)
Definition: multibuf_v2.h:1059
Result< Instance > Remove(const_iterator pos, size_t size)
Definition: multibuf_v2.h:1893
void Insert(const_iterator pos, BasicMultiBuf< kOtherProperties... > &&mb)
Definition: multibuf_v2.h:1743
bool TryReserveForPushBack(const T &bytes)
Definition: multibuf_v2.h:1811
void SealTopLayer()
Definition: multibuf_v2.h:1067
static constexpr bool is_layerable()
Returns whether additional views can be layered on the MultiBuf.
Definition: multibuf_v2.h:200
void UnsealTopLayer()
Definition: multibuf_v2.h:1075
SharedPtr< value_type[]> Share(const_iterator pos)
Definition: multibuf_v2.h:1927
void Insert(const_iterator pos, const T &bytes)
Definition: multibuf_v2.h:1752
void TruncateTopLayer(size_t length)
Definition: multibuf_v2.h:1097
void set_observer(MultiBufObserver *observer)
Definition: multibuf_v2.h:999
void SetTopLayer(ConstByteSpan src)
Definition: multibuf_v2.h:1110
void PushBack(const T &bytes)
Definition: multibuf_v2.h:1855
static constexpr bool is_const()
Returns whether the MultiBuf data is immutable.
Definition: multibuf_v2.h:195
void PopLayer()
Definition: multibuf_v2.h:1127
void Clear()
Definition: multibuf_v2.h:979
ConstByteSpan Get(ByteSpan copy, size_t offset=0) const
Definition: multibuf_v2.h:950
Result< Instance > PopFrontFragment()
Definition: multibuf_v2.h:1903
UniquePtr< value_type[]> Release(const_iterator pos)
Definition: multibuf_v2.h:1913
bool TryReserveChunks(size_t num_chunks)
Definition: multibuf_v2.h:458
size_t CopyTo(ByteSpan dst, size_t offset=0) const
Definition: multibuf_v2.h:916
size_t CopyFrom(ConstByteSpan src, size_t offset=0)
Definition: multibuf_v2.h:928
bool TryReserveForPushBack(const BasicMultiBuf< kOtherProperties... > &mb)
Definition: multibuf_v2.h:1804
static constexpr bool is_observable()
Returns whether an observer can be registered on the MultiBuf.
Definition: multibuf_v2.h:205
constexpr size_t size() const
Definition: multibuf_v2.h:321
bool TryReserveLayers(size_t num_layers, size_t num_chunks=1)
Definition: multibuf_v2.h:1035
bool IsShareable(const_iterator pos) const
Definition: multibuf_v2.h:888
constexpr size_type NumLayers() const
Definition: multibuf_v2.h:1022
constexpr bool empty() const
Definition: multibuf_v2.h:317
Result< const_iterator > Discard(const_iterator pos, size_t size)
Definition: multibuf_v2.h:858
bool IsReleasable(const_iterator pos) const
Definition: multibuf_v2.h:866
bool IsRemovable(const_iterator pos, size_t size) const
Definition: multibuf_v2.h:798
bool TryReserveForInsert(const_iterator pos, const BasicMultiBuf< kOtherProperties... > &mb)
Definition: multibuf_v2.h:1693
size_type NumFragments() const
Definition: multibuf_v2.h:1013
void PushBack(BasicMultiBuf< kOtherProperties... > &&mb)
Definition: multibuf_v2.h:1848
constexpr MultiBufObserver * observer() const
Definition: multibuf_v2.h:987
auto Visit(Visitor visitor, ByteSpan copy, size_t offset)
Definition: multibuf_v2.h:970
bool TryReserveForInsert(const_iterator pos, const T &bytes)
Definition: multibuf_v2.h:1703
bool IsCompatible(const BasicMultiBuf &mb) const
Definition: multibuf_v2.h:419
Abstract interface for releasing memory.
Definition: deallocator.h:29
Definition: dynamic_deque.h:60
Definition: observer.h:29
Definition: result.h:143
constexpr bool ok() const
Definition: result.h:447
constexpr Status status() const
Definition: result.h:803
Definition: shared_ptr.h:60
Definition: unique_ptr.h:43
element_type * Release() noexcept
Definition: unique_ptr.h:252
Deallocator * deallocator() const
Returns a pointer to the object that can destroy the value.
Definition: unique_ptr.h:183
size_t size() const
Definition: unique_ptr.h:176
Definition: byte_iterator.h:38
Definition: chunk_iterator.h:203
Definition: chunk_iterator.h:248
Definition: multibuf_v2.h:1172
Definition: multibuf_v2.h:1635
constexpr OutputIt copy(InputIt first, InputIt last, OutputIt d_first)
constexpr backport of <algorithm>'s std::copy for C++17.
Definition: algorithm.h:348
MultiBufProperty
Basic properties of a MultiBuf.
Definition: properties.h:24
Result(T value) -> Result< T >
Deduction guide to allow Result(v) rather than Result<T>(v).
The Pigweed namespace.
Definition: alignment.h:27