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// __ ___ ___ _ _ ___ _ _ ___
17// \ \ / /_\ | _ \ \| |_ _| \| |/ __|
18// \ \/\/ / _ \| / .` || || .` | (_ |
19// \_/\_/_/ \_\_|_\_|\_|___|_|\_|\___|
20// _____ _____ ___ ___ ___ __ __ ___ _ _ _____ _ _
21// | __\ \/ / _ \ __| _ \_ _| \/ | __| \| |_ _/_\ | |
22// | _| > <| _/ _|| /| || |\/| | _|| .` | | |/ _ \| |__
23// |___/_/\_\_| |___|_|_\___|_| |_|___|_|\_| |_/_/ \_\____|
24//
25// MultiBuf version 2 is in an early, experimental state. The APIs are in flux
26// and may change without notice. Please do not rely on it in production code,
27// but feel free to explore and share feedback with the Pigweed team!
28
29#include <cstddef>
30#include <cstdint>
31#include <numeric>
32#include <type_traits>
33
34#include "pw_allocator/allocator.h"
35#include "pw_bytes/span.h"
36#include "pw_containers/dynamic_deque.h"
37#include "pw_multibuf/internal/byte_iterator.h"
38#include "pw_multibuf/internal/chunk_iterator.h"
39#include "pw_multibuf/internal/entry.h"
40#include "pw_multibuf/observer.h"
41#include "pw_multibuf/properties.h"
42#include "pw_span/span.h"
43
44namespace pw {
45
46// Forward declarations.
47template <MultiBufProperty...>
48class BasicMultiBuf;
49
50namespace multibuf_impl {
51
52template <typename>
53class Instance;
54
55class GenericMultiBuf;
56
57} // namespace multibuf_impl
58
60
61// Type aliases for convenience, listed here for easier discoverability.
62
65
68
72
77
81
86
91
97
191template <MultiBufProperty... kProperties>
193 protected:
195 using ChunksType = multibuf_impl::Chunks<typename Deque::size_type>;
196 using ConstChunksType = multibuf_impl::ConstChunks<typename Deque::size_type>;
198
200
201 public:
203 [[nodiscard]] static constexpr bool is_const() {
204 return ((kProperties == Property::kConst) || ...);
205 }
206
208 [[nodiscard]] static constexpr bool is_layerable() {
209 return ((kProperties == Property::kLayerable) || ...);
210 }
211
213 [[nodiscard]] static constexpr bool is_observable() {
214 return ((kProperties == Property::kObservable) || ...);
215 }
216
217 using size_type = typename Deque::size_type;
218 using difference_type = typename Deque::difference_type;
219 using iterator = multibuf_impl::ByteIterator<size_type, /*kIsConst=*/false>;
220 using const_iterator =
221 multibuf_impl::ByteIterator<size_type, /*kIsConst=*/true>;
222 using pointer = iterator::pointer;
223 using const_pointer = const_iterator::pointer;
224 using reference = iterator::reference;
225 using const_reference = const_iterator::reference;
226 using value_type = std::conditional_t<is_const(),
227 const_iterator::value_type,
228 iterator::value_type>;
229
250
251 // Interfaces are not copyable or movable; copy and move `Instance`s instead.
252
253 BasicMultiBuf(const BasicMultiBuf&) = delete;
254
255 template <Property... kOtherProperties>
257
258 BasicMultiBuf& operator=(const BasicMultiBuf&) = delete;
259
260 template <Property... kOtherProperties>
261 BasicMultiBuf& operator=(const BasicMultiBuf<kOtherProperties...>&) = delete;
262
263 BasicMultiBuf(BasicMultiBuf&&) = delete;
264
265 BasicMultiBuf& operator=(BasicMultiBuf&&) = delete;
266
267 // Conversions
268
269 template <typename OtherMultiBuf>
270 OtherMultiBuf& as() & {
271 multibuf_impl::AssertIsConvertible<BasicMultiBuf, OtherMultiBuf>();
272 return generic().template as<OtherMultiBuf>();
273 }
274
275 template <typename OtherMultiBuf>
276 const OtherMultiBuf& as() const& {
277 multibuf_impl::AssertIsConvertible<BasicMultiBuf, OtherMultiBuf>();
278 return generic().template as<OtherMultiBuf>();
279 }
280
281 template <typename OtherMultiBuf>
282 OtherMultiBuf&& as() && {
283 multibuf_impl::AssertIsConvertible<BasicMultiBuf, OtherMultiBuf>();
284 return std::move(generic().template as<OtherMultiBuf>());
285 }
286
287 template <typename OtherMultiBuf>
288 const OtherMultiBuf&& as() const&& {
289 multibuf_impl::AssertIsConvertible<BasicMultiBuf, OtherMultiBuf>();
290 return std::move(generic().template as<OtherMultiBuf>());
291 }
292
293 template <typename OtherMultiBuf,
294 typename = multibuf_impl::EnableIfConvertible<BasicMultiBuf,
295 OtherMultiBuf>>
296 operator OtherMultiBuf&() & {
297 return as<OtherMultiBuf>();
298 }
299
300 template <typename OtherMultiBuf,
301 typename = multibuf_impl::EnableIfConvertible<BasicMultiBuf,
302 OtherMultiBuf>>
303 operator const OtherMultiBuf&() const& {
304 return as<OtherMultiBuf>();
305 }
306
307 template <typename OtherMultiBuf,
308 typename = multibuf_impl::EnableIfConvertible<BasicMultiBuf,
309 OtherMultiBuf>>
310 operator OtherMultiBuf&&() && {
311 return std::move(as<OtherMultiBuf>());
312 }
313
314 template <typename OtherMultiBuf,
315 typename = multibuf_impl::EnableIfConvertible<BasicMultiBuf,
316 OtherMultiBuf>>
317 operator const OtherMultiBuf&&() const&& {
318 return std::move(as<OtherMultiBuf>());
319 }
320
321 // Accessors
322
325 constexpr bool empty() const { return generic().empty(); }
326
329 constexpr size_t size() const { return generic().size(); }
330
342 template <bool kMutable = !is_const()>
343 std::enable_if_t<kMutable, reference> at(size_t index) {
344 return *(begin() + static_cast<int>(index));
345 }
346 const_reference at(size_t index) const {
347 return *(begin() + static_cast<int>(index));
348 }
349
350 template <bool kMutable = !is_const()>
351 std::enable_if_t<kMutable, reference> operator[](size_t index) {
352 return at(index);
353 }
354 const_reference operator[](size_t index) const { return at(index); }
356
368 template <bool kMutable = !is_const()>
369 constexpr std::enable_if_t<kMutable, ChunksType> Chunks() {
370 return generic().Chunks();
371 }
372 ConstChunksType Chunks() const { return generic().ConstChunks(); }
373 ConstChunksType ConstChunks() const { return generic().ConstChunks(); }
375
376 // Iterators.
377
388 template <bool kMutable = !is_const()>
389 constexpr std::enable_if_t<kMutable, iterator> begin() {
390 return generic().begin();
391 }
392 constexpr const_iterator begin() const { return cbegin(); }
393 constexpr const_iterator cbegin() const { return generic().cbegin(); }
395
406 template <bool kMutable = !is_const()>
407 constexpr std::enable_if_t<kMutable, iterator> end() {
408 return generic().end();
409 }
410 constexpr const_iterator end() const { return cend(); }
411 constexpr const_iterator cend() const { return generic().cend(); }
413
414 // Other methods
415
425 bool IsCompatible(const BasicMultiBuf& mb) const {
426 return generic().IsCompatible(mb.generic());
427 }
428
438 bool IsCompatible(const UniquePtr<std::byte[]>& bytes) const {
439 return generic().IsCompatible(bytes.deallocator());
440 }
441 bool IsCompatible(const UniquePtr<const std::byte[]>& bytes) const {
442 return generic().IsCompatible(bytes.deallocator());
443 }
445
454 bool IsCompatible(const SharedPtr<std::byte[]>& bytes) const {
455 return generic().IsCompatible(bytes.control_block());
456 }
457 bool IsCompatible(const SharedPtr<const std::byte[]>& bytes) const {
458 return generic().IsCompatible(bytes.control_block());
459 }
461
464 [[nodiscard]] bool TryReserveChunks(size_t num_chunks) {
465 return generic().TryReserveChunks(num_chunks);
466 }
467
468 // Mutators
469
481 template <Property... kOtherProperties>
482 [[nodiscard]] bool TryReserveForInsert(
483 const_iterator pos, const BasicMultiBuf<kOtherProperties...>& mb);
484
496 template <
497 int&... kExplicitGuard,
498 typename T,
499 typename =
500 std::enable_if_t<std::is_constructible_v<ConstByteSpan, T>, int>>
501 [[nodiscard]] bool TryReserveForInsert(const_iterator pos, const T& bytes);
502
516 [[nodiscard]] bool TryReserveForInsert(const_iterator pos,
517 const UniquePtr<std::byte[]>& bytes);
518 [[nodiscard]] bool TryReserveForInsert(
519 const_iterator pos, const UniquePtr<const std::byte[]>& bytes);
521
535 [[nodiscard]] bool TryReserveForInsert(const_iterator pos,
536 const SharedPtr<std::byte[]>& bytes);
537 [[nodiscard]] bool TryReserveForInsert(
538 const_iterator pos, const SharedPtr<const std::byte[]>& bytes);
540
549 template <Property... kOtherProperties>
550 void Insert(const_iterator pos, BasicMultiBuf<kOtherProperties...>&& mb);
551
560 template <
561 int&... kExplicitGuard,
562 typename T,
563 typename =
564 std::enable_if_t<std::is_constructible_v<ConstByteSpan, T>, int>>
565 void Insert(const_iterator pos, const T& bytes);
566
577 void Insert(const_iterator pos, UniquePtr<std::byte[]>&& bytes) {
578 Insert(pos, std::move(bytes), 0);
579 }
580 void Insert(const_iterator pos, UniquePtr<const std::byte[]>&& bytes) {
581 Insert(pos, std::move(bytes), 0);
582 }
584
597 void Insert(const_iterator pos,
598 UniquePtr<std::byte[]>&& bytes,
599 size_t offset,
600 size_t length = dynamic_extent);
601 void Insert(const_iterator pos,
602 UniquePtr<const std::byte[]>&& bytes,
603 size_t offset,
604 size_t length = dynamic_extent);
606
617 void Insert(const_iterator pos, const SharedPtr<std::byte[]>& bytes) {
618 Insert(pos, bytes, 0);
619 }
620 void Insert(const_iterator pos, const SharedPtr<const std::byte[]>& bytes) {
621 Insert(pos, bytes, 0);
622 }
624
637 void Insert(const_iterator pos,
638 const SharedPtr<std::byte[]>& bytes,
639 size_t offset,
640 size_t length = dynamic_extent);
641 void Insert(const_iterator pos,
642 const SharedPtr<const std::byte[]>& bytes,
643 size_t offset,
644 size_t length = dynamic_extent);
646
654 template <Property... kOtherProperties>
655 [[nodiscard]] bool TryReserveForPushBack(
657
665 template <
666 int&... kExplicitGuard,
667 typename T,
668 typename =
669 std::enable_if_t<std::is_constructible_v<ConstByteSpan, T>, int>>
670 [[nodiscard]] bool TryReserveForPushBack(const T& bytes);
671
681 [[nodiscard]] bool TryReserveForPushBack(const UniquePtr<std::byte[]>& bytes);
682 [[nodiscard]] bool TryReserveForPushBack(
683 const UniquePtr<const std::byte[]>& bytes);
685
695 [[nodiscard]] bool TryReserveForPushBack(const SharedPtr<std::byte[]>& bytes);
696 [[nodiscard]] bool TryReserveForPushBack(
697 const SharedPtr<const std::byte[]>& bytes);
699
707 template <Property... kOtherProperties>
709
717 template <
718 int&... kExplicitGuard,
719 typename T,
720 typename =
721 std::enable_if_t<std::is_constructible_v<ConstByteSpan, T>, int>>
722 void PushBack(const T& bytes);
723
733 void PushBack(UniquePtr<std::byte[]>&& bytes) {
734 PushBack(std::move(bytes), 0);
735 }
736 void PushBack(UniquePtr<const std::byte[]>&& bytes) {
737 PushBack(std::move(bytes), 0);
738 }
740
752 void PushBack(UniquePtr<std::byte[]>&& bytes,
753 size_t offset,
754 size_t length = dynamic_extent);
755
756 void PushBack(UniquePtr<const std::byte[]>&& bytes,
757 size_t offset,
758 size_t length = dynamic_extent);
760
770 void PushBack(const SharedPtr<std::byte[]>& bytes) { PushBack(bytes, 0); }
771 void PushBack(const SharedPtr<const std::byte[]>& bytes) {
772 PushBack(bytes, 0);
773 }
775
787 void PushBack(const SharedPtr<std::byte[]>& bytes,
788 size_t offset,
789 size_t length = dynamic_extent);
790 void PushBack(const SharedPtr<const std::byte[]>& bytes,
791 size_t offset,
792 size_t length = dynamic_extent);
794
804 [[nodiscard]] bool IsRemovable(const_iterator pos, size_t size) const {
805 return generic().IsRemovable(pos, size);
806 }
807
833 Result<Instance> Remove(const_iterator pos, size_t size);
834
853
881 Result<const_iterator> Discard(const_iterator pos, size_t size) {
882 return generic().Discard(pos, size);
883 }
884
889 [[nodiscard]] bool IsReleasable(const_iterator pos) const {
890 return generic().IsReleasable(pos);
891 }
892
905 UniquePtr<value_type[]> Release(const_iterator pos);
906
911 [[nodiscard]] bool IsShareable(const_iterator pos) const {
912 return generic().IsShareable(pos);
913 }
914
929 SharedPtr<value_type[]> Share(const_iterator pos);
930
939 size_t CopyTo(ByteSpan dst, size_t offset = 0) const {
940 return generic().CopyTo(dst, offset);
941 }
942
951 size_t CopyFrom(ConstByteSpan src, size_t offset = 0) {
952 static_assert(!is_const(),
953 "`CopyFrom` may only be called on mutable MultiBufs");
954 return generic().CopyFrom(src, offset);
955 }
956
973 ConstByteSpan Get(ByteSpan copy, size_t offset = 0) const {
974 return generic().Get(copy, offset);
975 }
976
992 template <int&... kExplicitGuard, typename Visitor>
993 auto Visit(Visitor visitor, ByteSpan copy, size_t offset) {
994 return visitor(Get(copy, offset));
995 }
996
1002 void Clear() { generic().Clear(); }
1003
1004 // Observable methods.
1005
1010 constexpr MultiBufObserver* observer() const {
1011 static_assert(is_observable(),
1012 "`observer` may only be called on observable MultiBufs");
1013 return generic().observer_;
1014 }
1015
1023 static_assert(is_observable(),
1024 "`set_observer` may only be called on observable MultiBufs");
1025 generic().observer_ = observer;
1026 }
1027
1028 // Layerable methods.
1029
1036 size_type NumFragments() const {
1037 static_assert(is_layerable(),
1038 "`NumFragments` may only be called on layerable MultiBufs");
1039 return generic().NumFragments();
1040 }
1041
1045 constexpr size_type NumLayers() const {
1046 static_assert(is_layerable(),
1047 "`NumLayers` may only be called on layerable MultiBufs");
1048 return generic().NumLayers();
1049 }
1050
1058 [[nodiscard]] bool TryReserveLayers(size_t num_layers,
1059 size_t num_chunks = 1) {
1060 return generic().TryReserveLayers(num_layers, num_chunks);
1061 }
1062
1082 [[nodiscard]] bool AddLayer(size_t offset, size_t length = dynamic_extent) {
1083 static_assert(is_layerable(),
1084 "`AddLayer` may only be called on layerable MultiBufs");
1085 return generic().AddLayer(offset, length);
1086 }
1087
1091 static_assert(is_layerable(),
1092 "`SealTopLayer` may only be called on layerable MultiBufs");
1093 return generic().SealTopLayer();
1094 }
1095
1099 static_assert(is_layerable(),
1100 "`UnsealTopLayer` may only be called on layerable MultiBufs");
1101 return generic().UnsealTopLayer();
1102 }
1103
1105 [[nodiscard]] bool IsTopLayerSealed() {
1106 static_assert(
1107 is_layerable(),
1108 "`IsTopLayerSealed` may only be called on layerable MultiBufs");
1109 return generic().IsTopLayerSealed();
1110 }
1111
1120 void TruncateTopLayer(size_t length) {
1121 static_assert(
1122 is_layerable(),
1123 "`TruncateTopLayer` may only be called on layerable MultiBufs");
1124 generic().TruncateTopLayer(length);
1125 }
1126
1134 static_assert(!is_const() && is_layerable(),
1135 "`SetTopLayer` may only be called on mutable, layerable "
1136 "MultiBufs");
1137 PW_ASSERT(src.size() <= size());
1138 CopyFrom(src);
1139 TruncateTopLayer(src.size());
1140 }
1141
1150 void PopLayer() {
1151 static_assert(is_layerable(),
1152 "`PopLayer` may only be called on layerable MultiBufs");
1153 generic().PopLayer();
1154 }
1155
1156 protected:
1157 constexpr BasicMultiBuf() { multibuf_impl::PropertiesAreValid(); }
1158
1159 private:
1160 template <Property...>
1161 friend class BasicMultiBuf;
1162
1163 constexpr GenericMultiBuf& generic() {
1164 return static_cast<GenericMultiBuf&>(*this);
1165 }
1166 constexpr const GenericMultiBuf& generic() const {
1167 return static_cast<const GenericMultiBuf&>(*this);
1168 }
1169};
1170
1171namespace multibuf_impl {
1172
1173// A generic MultiBuf implementation that provides the functionality of any
1174// `BasicMultiBuf<kProperties>` type.
1175//
1176// This class should not be instantiated directly. Instead, use `Instance` as
1177// described below. This is the base class for all `Instance` types, and derives
1178// from every supported `BasicMultiBuf` type. It implements the MultiBuf
1179// behavior in one type, and allows for performing conversions from `Instance`s
1180// and `BasicMultiBuf`s to `BasicMultiBuf`s with different yet compatible
1181// propreties.
1183 : private BasicMultiBuf<>,
1196 private:
1197 using typename BasicMultiBuf<>::ChunksType;
1198 using typename BasicMultiBuf<>::ConstChunksType;
1199 using ControlBlock = allocator::internal::ControlBlock;
1200 using typename BasicMultiBuf<>::Deque;
1201
1202 public:
1203 using typename BasicMultiBuf<>::const_iterator;
1204 using typename BasicMultiBuf<>::const_pointer;
1205 using typename BasicMultiBuf<>::const_reference;
1206 using typename BasicMultiBuf<>::difference_type;
1207 using typename BasicMultiBuf<>::iterator;
1208 using typename BasicMultiBuf<>::pointer;
1209 using typename BasicMultiBuf<>::reference;
1210 using typename BasicMultiBuf<>::size_type;
1211
1212 ~GenericMultiBuf() { Clear(); }
1213
1214 // Not copyable.
1215 GenericMultiBuf(const GenericMultiBuf&) = delete;
1216 GenericMultiBuf& operator=(const GenericMultiBuf&) = delete;
1217
1218 // Movable.
1220 : GenericMultiBuf(other.deque_.get_allocator()) {
1221 *this = std::move(other);
1222 }
1223 GenericMultiBuf& operator=(GenericMultiBuf&& other);
1224
1225 private:
1226 template <MultiBufProperty...>
1227 friend class ::pw::BasicMultiBuf;
1228
1229 template <typename>
1230 friend class ::pw::multibuf_impl::Instance;
1231
1233 constexpr explicit GenericMultiBuf(Allocator& allocator)
1234 : deque_(allocator) {}
1235
1236 template <typename MultiBufType>
1237 MultiBufType& as() {
1238 return *this;
1239 }
1240
1241 template <typename MultiBufType>
1242 const MultiBufType& as() const {
1243 return *this;
1244 }
1245
1246 // Accessors.
1247
1249 constexpr bool empty() const { return deque_.empty(); }
1250
1252 constexpr size_t size() const {
1253 return static_cast<size_t>(cend() - cbegin());
1254 }
1255
1257 constexpr bool has_deallocator() const {
1258 return memory_tag_ == MemoryTag::kDeallocator || has_control_block();
1259 }
1260
1262 constexpr bool has_control_block() const {
1263 return memory_tag_ == MemoryTag::kControlBlock;
1264 }
1265
1266 // Iterators.
1267
1268 constexpr ChunksType Chunks() { return ChunksType(deque_, depth_); }
1269 constexpr ConstChunksType ConstChunks() const {
1270 return ConstChunksType(deque_, depth_);
1271 }
1272
1273 constexpr iterator begin() { return iterator(Chunks().begin(), 0); }
1274 constexpr const_iterator cbegin() const {
1275 return const_iterator(ConstChunks().cbegin(), 0);
1276 }
1277
1278 constexpr iterator end() { return iterator(Chunks().end(), 0); }
1279 constexpr const_iterator cend() const {
1280 return const_iterator(ConstChunks().cend(), 0);
1281 }
1282
1283 // Mutators.
1284
1286 bool IsCompatible(const GenericMultiBuf& other) const;
1287 bool IsCompatible(const Deallocator* other) const;
1288 bool IsCompatible(const ControlBlock* other) const;
1289
1291 [[nodiscard]] bool TryReserveChunks(size_t num_chunks);
1292
1295 [[nodiscard]] bool TryReserveForInsert(const_iterator pos,
1296 const GenericMultiBuf& mb);
1297 [[nodiscard]] bool TryReserveForInsert(const_iterator pos, size_t size);
1298 [[nodiscard]] bool TryReserveForInsert(const_iterator pos,
1299 size_t size,
1300 const Deallocator* deallocator);
1301 [[nodiscard]] bool TryReserveForInsert(const_iterator pos,
1302 size_t size,
1303 const ControlBlock* control_block);
1305
1308 void Insert(const_iterator pos, GenericMultiBuf&& mb);
1309 void Insert(const_iterator pos, ConstByteSpan bytes);
1310 void Insert(const_iterator pos,
1311 ConstByteSpan bytes,
1312 size_t offset,
1313 size_t length,
1314 Deallocator* deallocator);
1315 void Insert(const_iterator pos,
1316 ConstByteSpan bytes,
1317 size_t offset,
1318 size_t length,
1319 ControlBlock* control_block);
1321
1323 [[nodiscard]] bool IsRemovable(const_iterator pos, size_t size) const;
1324
1326 Result<GenericMultiBuf> Remove(const_iterator pos, size_t size);
1327
1329 Result<GenericMultiBuf> PopFrontFragment();
1330
1332 Result<const_iterator> Discard(const_iterator pos, size_t size);
1333
1335 [[nodiscard]] bool IsReleasable(const_iterator pos) const;
1336
1338 UniquePtr<std::byte[]> Release(const_iterator pos);
1339
1341 [[nodiscard]] bool IsShareable(const_iterator pos) const;
1342
1344 std::byte* Share(const_iterator pos);
1345
1347 size_t CopyTo(ByteSpan dst, size_t offset) const;
1348
1350 size_t CopyFrom(ConstByteSpan src, size_t offset);
1351
1353 ConstByteSpan Get(ByteSpan copy, size_t offset) const;
1354
1356 void Clear();
1357
1358 // Layerable methods.
1359
1361 size_type NumFragments() const;
1362
1364 constexpr size_type NumLayers() const { return depth_ - 1; }
1365
1367 [[nodiscard]] bool TryReserveLayers(size_t num_layers, size_t num_chunks = 1);
1368
1370 [[nodiscard]] bool AddLayer(size_t offset, size_t length = dynamic_extent);
1371
1373 void SealTopLayer();
1374
1376 void UnsealTopLayer();
1377
1379 void TruncateTopLayer(size_t length);
1380
1382 void PopLayer();
1383
1384 // Implementation methods.
1385 //
1386 // These methods are used to implement the methods above, and should not be
1387 // called directly by BasicMultiBuf<>.
1388
1393 static size_t CheckRange(size_t offset, size_t length, size_t size);
1394
1396 constexpr std::byte* GetData(size_type index) const {
1397 return deque_[index].data;
1398 }
1399
1402 constexpr bool IsOwned(size_type index) const {
1403 return deque_[index + 1].base_view.owned;
1404 }
1405
1408 constexpr bool IsShared(size_type index) const {
1409 return deque_[index + 1].base_view.shared;
1410 }
1411
1413 constexpr bool IsSealed(size_type index) const {
1414 return depth_ == 2 ? false : deque_[index + depth_ - 1].view.sealed;
1415 }
1416
1419 constexpr bool IsBoundary(size_type index) const {
1420 return depth_ == 2 ? true : deque_[index + depth_ - 1].view.boundary;
1421 }
1422
1426 constexpr size_type GetOffset(size_type index, uint16_t layer) const {
1427 return layer == 1 ? deque_[index + 1].base_view.offset
1428 : deque_[index + layer].view.offset;
1429 }
1430
1432 constexpr size_type GetOffset(size_type index) const {
1433 return GetOffset(index, NumLayers());
1434 }
1435
1438 constexpr size_type GetRelativeOffset(size_type index) const {
1439 uint16_t layer = NumLayers();
1440 if (layer == 1) {
1441 return GetOffset(index, layer);
1442 }
1443 return GetOffset(index, layer) - GetOffset(index, layer - 1);
1444 }
1445
1447 constexpr size_type GetLength(size_type index) const {
1448 return depth_ == 2 ? deque_[index + 1].base_view.length
1449 : deque_[index + depth_ - 1].view.length;
1450 }
1451
1453 constexpr ByteSpan GetView(size_type index) const {
1454 return ByteSpan(GetData(index) + GetOffset(index), GetLength(index));
1455 }
1456
1458 Deallocator* GetDeallocator() const;
1459
1461 void SetDeallocator(Deallocator* deallocator);
1462
1464 ControlBlock* GetControlBlock() const;
1465
1467 void SetControlBlock(ControlBlock* control_block);
1468
1470 void CopyMemoryContext(const GenericMultiBuf& other);
1471
1473 void ClearMemoryContext();
1474
1478 std::pair<size_type, size_type> GetIndexAndOffset(const_iterator pos) const;
1479
1485 bool TryReserveEntries(const_iterator pos, size_type num_entries);
1486 bool TryReserveEntries(size_type num_entries, bool split = false);
1488
1491 size_type InsertEntries(const_iterator pos, size_type num_entries);
1492
1495 size_type Insert(const_iterator pos,
1496 ConstByteSpan bytes,
1497 size_t offset,
1498 size_t length);
1499
1509 void SplitBase(size_type index, Deque& out_deque, size_type out_index);
1510
1516 void SplitBefore(size_type index,
1517 size_type split,
1518 Deque& out_deque,
1519 size_type out_index);
1520
1523 void SplitBefore(size_type index, size_type split);
1524
1530 void SplitAfter(size_type index,
1531 size_type split,
1532 Deque& out_deque,
1533 size_type out_index);
1534
1537 void SplitAfter(size_type index, size_type split);
1538
1549 [[nodiscard]] bool TryReserveForRemove(const_iterator pos,
1550 size_t size,
1551 GenericMultiBuf* out);
1552
1557 void CopyRange(const_iterator pos, size_t size, GenericMultiBuf& out);
1558
1562 void ClearRange(const_iterator pos, size_t size);
1563
1568 void EraseRange(const_iterator pos, size_t size);
1569
1572 size_type FindShared(size_type index, size_type start);
1573
1576 size_t CopyToImpl(ByteSpan dst, size_t offset, size_type start) const;
1577
1579 [[nodiscard]] bool IsTopLayerSealed() const;
1580
1583 void SetLayer(size_t offset, size_t length);
1584
1585 // Describes the memory and views to that in a one-dimensional sequence.
1586 // Every `depth_`-th entry holds a pointer to memory, each entry for a given
1587 // offset less than `depth_` after that entry is a view that is part of the
1588 // same "layer" in the MultiBuf.
1589 //
1590 // This base type will always have 0 or 2 layers, but derived classes may add
1591 // more.
1592 //
1593 // For example, a MultiBuf that has had 3 `Chunk`s and two additional layers
1594 // added would have a `deque_` resembling the following:
1595 //
1596 // buffer 0: buffer 1: buffer 2:
1597 // layer 3: deque_[0x3].view deque_[0x7].view deque_[0xB].view
1598 // layer 2: deque_[0x2].view deque_[0x6].view deque_[0xA].view
1599 // layer 1: deque_[0x1].view deque_[0x5].view deque_[0x9].view
1600 // layer 0: deque_[0x0].data deque_[0x4].data deque_[0x8].data
1601 Deque deque_;
1602
1603 // Number of entries per chunk in this MultiBuf.
1604 size_type depth_ = 2;
1605
1622 enum class MemoryTag : uint8_t {
1623 kEmpty,
1624 kDeallocator,
1625 kControlBlock,
1626 } memory_tag_ = MemoryTag::kEmpty;
1627
1628 union MemoryContext {
1629 Deallocator* deallocator;
1630 ControlBlock* control_block;
1631 } memory_context_ = {.deallocator = nullptr};
1633
1636 MultiBufObserver* observer_ = nullptr;
1637};
1638
1657template <typename MultiBufType>
1659 public:
1660 constexpr explicit Instance(Allocator& allocator) : base_(allocator) {}
1661
1662 constexpr Instance(Instance&&) = default;
1663 constexpr Instance& operator=(Instance&&) = default;
1664
1665 constexpr Instance(MultiBufType&& mb)
1666 : base_(std::move(static_cast<GenericMultiBuf&>(mb))) {}
1667
1668 constexpr Instance& operator=(MultiBufType&& mb) {
1669 base_ = std::move(static_cast<GenericMultiBuf&>(mb));
1670 return *this;
1671 }
1672
1673 MultiBufType* operator->() { return &base_.as<MultiBufType>(); }
1674 const MultiBufType* operator->() const { return &base_.as<MultiBufType>(); }
1675
1676 MultiBufType& operator*() & { return base_.as<MultiBufType>(); }
1677 const MultiBufType& operator*() const& { return base_.as<MultiBufType>(); }
1678
1679 MultiBufType&& operator*() && { return std::move(base_.as<MultiBufType>()); }
1680 const MultiBufType&& operator*() const&& {
1681 return std::move(base_.as<MultiBufType>());
1682 }
1683
1684 operator MultiBufType&() & { return base_.as<MultiBufType>(); }
1685 operator const MultiBufType&() const& { return base_.as<MultiBufType>(); }
1686
1687 operator MultiBufType&&() && { return std::move(base_.as<MultiBufType>()); }
1688 operator const MultiBufType&&() const&& {
1689 return std::move(base_.as<MultiBufType>());
1690 }
1691
1692 private:
1693 GenericMultiBuf base_;
1694};
1695
1696} // namespace multibuf_impl
1697
1699
1701
1702// Template method implementations.
1703
1704template <MultiBufProperty... kProperties>
1705template <MultiBufProperty... kOtherProperties>
1707 const_iterator pos, const BasicMultiBuf<kOtherProperties...>& mb) {
1708 multibuf_impl::AssertIsConvertible<BasicMultiBuf<kOtherProperties...>,
1709 BasicMultiBuf>();
1710 return generic().TryReserveForInsert(pos,
1711 static_cast<const GenericMultiBuf&>(mb));
1712}
1713
1714template <MultiBufProperty... kProperties>
1715template <int&... kExplicitGuard, typename T, typename>
1717 const T& bytes) {
1718 using data_ptr_type = decltype(std::data(std::declval<T&>()));
1719 static_assert(std::is_same_v<data_ptr_type, std::byte*> || is_const(),
1720 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1721 return generic().TryReserveForInsert(pos, bytes.size());
1722}
1723
1724template <MultiBufProperty... kProperties>
1726 const_iterator pos, const UniquePtr<std::byte[]>& bytes) {
1727 return generic().TryReserveForInsert(pos, bytes.size(), bytes.deallocator());
1728}
1729
1730template <MultiBufProperty... kProperties>
1732 const_iterator pos, const UniquePtr<const std::byte[]>& bytes) {
1733 static_assert(is_const(),
1734 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1735 return generic().TryReserveForInsert(pos, bytes.size(), bytes.deallocator());
1736}
1737
1738template <MultiBufProperty... kProperties>
1740 const_iterator pos, const SharedPtr<std::byte[]>& bytes) {
1741 return generic().TryReserveForInsert(
1742 pos, bytes.size(), bytes.control_block());
1743}
1744
1745template <MultiBufProperty... kProperties>
1747 const_iterator pos, const SharedPtr<const std::byte[]>& bytes) {
1748 static_assert(is_const(),
1749 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1750 return generic().TryReserveForInsert(
1751 pos, bytes.size(), bytes.control_block());
1752}
1753
1754template <MultiBufProperty... kProperties>
1755template <MultiBufProperty... kOtherProperties>
1757 const_iterator pos, BasicMultiBuf<kOtherProperties...>&& mb) {
1758 multibuf_impl::AssertIsConvertible<BasicMultiBuf<kOtherProperties...>,
1759 BasicMultiBuf>();
1760 generic().Insert(pos, std::move(mb.generic()));
1761}
1762
1763template <MultiBufProperty... kProperties>
1764template <int&... kExplicitGuard, typename T, typename>
1765void BasicMultiBuf<kProperties...>::Insert(const_iterator pos, const T& bytes) {
1766 using data_ptr_type = decltype(std::data(std::declval<T&>()));
1767 static_assert(std::is_same_v<data_ptr_type, std::byte*> || is_const(),
1768 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1769 generic().Insert(pos, bytes);
1770}
1771
1772template <MultiBufProperty... kProperties>
1773void BasicMultiBuf<kProperties...>::Insert(const_iterator pos,
1774 UniquePtr<std::byte[]>&& bytes,
1775 size_t offset,
1776 size_t length) {
1777 ConstByteSpan chunk(bytes.get(), bytes.size());
1778 generic().Insert(pos, chunk, offset, length, bytes.deallocator());
1779 bytes.Release();
1780}
1781
1782template <MultiBufProperty... kProperties>
1783void BasicMultiBuf<kProperties...>::Insert(const_iterator pos,
1784 UniquePtr<const std::byte[]>&& bytes,
1785 size_t offset,
1786 size_t length) {
1787 static_assert(is_const(),
1788 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1789 ConstByteSpan chunk(bytes.get(), bytes.size());
1790 generic().Insert(pos, chunk, offset, length, bytes.deallocator());
1791 bytes.Release();
1792}
1793
1794template <MultiBufProperty... kProperties>
1795void BasicMultiBuf<kProperties...>::Insert(const_iterator pos,
1796 const SharedPtr<std::byte[]>& bytes,
1797 size_t offset,
1798 size_t length) {
1799 ConstByteSpan chunk(bytes.get(), bytes.size());
1800 generic().Insert(pos, chunk, offset, length, bytes.control_block());
1801}
1802
1803template <MultiBufProperty... kProperties>
1805 const_iterator pos,
1806 const SharedPtr<const std::byte[]>& bytes,
1807 size_t offset,
1808 size_t length) {
1809 static_assert(is_const(),
1810 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1811 ConstByteSpan chunk(bytes.get(), bytes.size());
1812 generic().Insert(pos, chunk, offset, length, bytes.control_block());
1813}
1814
1815template <MultiBufProperty... kProperties>
1816template <MultiBufProperty... kOtherProperties>
1819 return TryReserveForInsert(end(), mb);
1820}
1821
1822template <MultiBufProperty... kProperties>
1823template <int&... kExplicitGuard, typename T, typename>
1825 using data_ptr_type = decltype(std::data(std::declval<T&>()));
1826 static_assert(std::is_same_v<data_ptr_type, std::byte*> || is_const(),
1827 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1828 return TryReserveForInsert(end(), bytes);
1829}
1830
1831template <MultiBufProperty... kProperties>
1833 const UniquePtr<std::byte[]>& bytes) {
1834 return TryReserveForInsert(end(), std::move(bytes));
1835}
1836
1837template <MultiBufProperty... kProperties>
1839 const UniquePtr<const std::byte[]>& bytes) {
1840 static_assert(is_const(),
1841 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1842 return TryReserveForInsert(end(), std::move(bytes));
1843}
1844
1845template <MultiBufProperty... kProperties>
1847 const SharedPtr<std::byte[]>& bytes) {
1848 return TryReserveForInsert(end(), bytes);
1849}
1850
1851template <MultiBufProperty... kProperties>
1853 const SharedPtr<const std::byte[]>& bytes) {
1854 static_assert(is_const(),
1855 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1856 return TryReserveForInsert(end(), bytes);
1857}
1858
1859template <MultiBufProperty... kProperties>
1860template <MultiBufProperty... kOtherProperties>
1863 Insert(end(), std::move(mb));
1864}
1865
1866template <MultiBufProperty... kProperties>
1867template <int&... kExplicitGuard, typename T, typename>
1869 using data_ptr_type = decltype(std::data(std::declval<T&>()));
1870 static_assert(std::is_same_v<data_ptr_type, std::byte*> || is_const(),
1871 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1872 Insert(end(), bytes);
1873}
1874
1875template <MultiBufProperty... kProperties>
1876void BasicMultiBuf<kProperties...>::PushBack(UniquePtr<std::byte[]>&& bytes,
1877 size_t offset,
1878 size_t length) {
1879 Insert(end(), std::move(bytes), offset, length);
1880}
1881
1882template <MultiBufProperty... kProperties>
1884 UniquePtr<const std::byte[]>&& bytes, size_t offset, size_t length) {
1885 static_assert(is_const(),
1886 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1887 Insert(end(), std::move(bytes), offset, length);
1888}
1889
1890template <MultiBufProperty... kProperties>
1892 const SharedPtr<std::byte[]>& bytes, size_t offset, size_t length) {
1893 Insert(end(), bytes, offset, length);
1894}
1895
1896template <MultiBufProperty... kProperties>
1898 const SharedPtr<const std::byte[]>& bytes, size_t offset, size_t length) {
1899 static_assert(is_const(),
1900 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1901 Insert(end(), bytes, offset, length);
1902}
1903
1904template <MultiBufProperty... kProperties>
1905Result<multibuf_impl::Instance<BasicMultiBuf<kProperties...>>>
1906BasicMultiBuf<kProperties...>::Remove(const_iterator pos, size_t size) {
1907 auto result = generic().Remove(pos, size);
1908 if (!result.ok()) {
1909 return result.status();
1910 }
1911 return Instance(std::move(*result));
1912}
1913
1914template <MultiBufProperty... kProperties>
1917 Result<GenericMultiBuf> result = generic().PopFrontFragment();
1918 if (!result.ok()) {
1919 return result.status();
1920 }
1921 return Instance(std::move(*result));
1922}
1923
1924template <MultiBufProperty... kProperties>
1925UniquePtr<typename BasicMultiBuf<kProperties...>::value_type[]>
1927 UniquePtr<std::byte[]> bytes = generic().Release(pos);
1928 if constexpr (is_const()) {
1929 UniquePtr<const std::byte[]> const_bytes(
1930 bytes.get(), bytes.size(), *(bytes.deallocator()));
1931 bytes.Release();
1932 return const_bytes;
1933 } else {
1934 return bytes;
1935 }
1936}
1937
1938template <MultiBufProperty... kProperties>
1939SharedPtr<typename BasicMultiBuf<kProperties...>::value_type[]>
1941 return SharedPtr<value_type[]>(generic().Share(pos),
1942 generic().GetControlBlock());
1943}
1944
1945} // namespace pw
Definition: allocator.h:36
bool IsTopLayerSealed()
Returns whether the "sealed" flag is set in the top layer.
Definition: multibuf_v2.h:1105
bool AddLayer(size_t offset, size_t length=dynamic_extent)
Definition: multibuf_v2.h:1082
Result< Instance > Remove(const_iterator pos, size_t size)
Definition: multibuf_v2.h:1906
void Insert(const_iterator pos, BasicMultiBuf< kOtherProperties... > &&mb)
Definition: multibuf_v2.h:1756
bool TryReserveForPushBack(const T &bytes)
Definition: multibuf_v2.h:1824
void SealTopLayer()
Definition: multibuf_v2.h:1090
static constexpr bool is_layerable()
Returns whether additional views can be layered on the MultiBuf.
Definition: multibuf_v2.h:208
void UnsealTopLayer()
Definition: multibuf_v2.h:1098
SharedPtr< value_type[]> Share(const_iterator pos)
Definition: multibuf_v2.h:1940
void Insert(const_iterator pos, const T &bytes)
Definition: multibuf_v2.h:1765
void TruncateTopLayer(size_t length)
Definition: multibuf_v2.h:1120
void set_observer(MultiBufObserver *observer)
Definition: multibuf_v2.h:1022
void SetTopLayer(ConstByteSpan src)
Definition: multibuf_v2.h:1133
void PushBack(const T &bytes)
Definition: multibuf_v2.h:1868
static constexpr bool is_const()
Returns whether the MultiBuf data is immutable.
Definition: multibuf_v2.h:203
void PopLayer()
Definition: multibuf_v2.h:1150
void Clear()
Definition: multibuf_v2.h:1002
ConstByteSpan Get(ByteSpan copy, size_t offset=0) const
Definition: multibuf_v2.h:973
Result< Instance > PopFrontFragment()
Definition: multibuf_v2.h:1916
UniquePtr< value_type[]> Release(const_iterator pos)
Definition: multibuf_v2.h:1926
bool TryReserveChunks(size_t num_chunks)
Definition: multibuf_v2.h:464
size_t CopyTo(ByteSpan dst, size_t offset=0) const
Definition: multibuf_v2.h:939
size_t CopyFrom(ConstByteSpan src, size_t offset=0)
Definition: multibuf_v2.h:951
bool TryReserveForPushBack(const BasicMultiBuf< kOtherProperties... > &mb)
Definition: multibuf_v2.h:1817
static constexpr bool is_observable()
Returns whether an observer can be registered on the MultiBuf.
Definition: multibuf_v2.h:213
constexpr size_t size() const
Definition: multibuf_v2.h:329
bool TryReserveLayers(size_t num_layers, size_t num_chunks=1)
Definition: multibuf_v2.h:1058
bool IsShareable(const_iterator pos) const
Definition: multibuf_v2.h:911
constexpr size_type NumLayers() const
Definition: multibuf_v2.h:1045
constexpr bool empty() const
Definition: multibuf_v2.h:325
Result< const_iterator > Discard(const_iterator pos, size_t size)
Definition: multibuf_v2.h:881
bool IsReleasable(const_iterator pos) const
Definition: multibuf_v2.h:889
bool IsRemovable(const_iterator pos, size_t size) const
Definition: multibuf_v2.h:804
bool TryReserveForInsert(const_iterator pos, const BasicMultiBuf< kOtherProperties... > &mb)
Definition: multibuf_v2.h:1706
size_type NumFragments() const
Definition: multibuf_v2.h:1036
void PushBack(BasicMultiBuf< kOtherProperties... > &&mb)
Definition: multibuf_v2.h:1861
constexpr MultiBufObserver * observer() const
Definition: multibuf_v2.h:1010
auto Visit(Visitor visitor, ByteSpan copy, size_t offset)
Definition: multibuf_v2.h:993
bool TryReserveForInsert(const_iterator pos, const T &bytes)
Definition: multibuf_v2.h:1716
bool IsCompatible(const BasicMultiBuf &mb) const
Definition: multibuf_v2.h:425
Abstract interface for releasing memory.
Definition: deallocator.h:29
Definition: dynamic_deque.h:60
Definition: observer.h:29
Definition: poll.h:25
Definition: shared_ptr.h:59
Definition: unique_ptr.h:43
element_type * Release() noexcept
Definition: unique_ptr.h:205
Deallocator * deallocator() const
Returns a pointer to the object that can destroy the value.
Definition: unique_ptr.h:137
size_t size() const
Definition: unique_ptr.h:130
Definition: multibuf_v2.h:1195
Definition: multibuf_v2.h:1658
MultiBufProperty
Basic properties of a MultiBuf.
Definition: properties.h:24
The Pigweed namespace.
Definition: alignment.h:27