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_allocator/null_allocator.h"
28#include "pw_bytes/span.h"
29#include "pw_containers/dynamic_deque.h"
30#include "pw_multibuf/chunks.h"
31#include "pw_multibuf/internal/byte_iterator.h"
32#include "pw_multibuf/internal/entry.h"
33#include "pw_multibuf/observer.h"
34#include "pw_multibuf/properties.h"
35
36namespace pw {
37
38// Forward declarations.
39template <multibuf::Property...>
40class BasicMultiBuf;
41
43
44// Type aliases of the BasicMultiBuf interface types for each different
45// combination of propeties, listed here for easier discoverability.
46
49
52
56
61
65
70
75
81
82namespace multibuf::internal {
83
84class GenericMultiBuf;
85
86template <typename>
87class Instance;
88
89} // namespace multibuf::internal
90
184template <multibuf::Property... kProperties>
186 protected:
189
190 public:
192 [[nodiscard]] static constexpr bool is_const() {
193 return ((kProperties == multibuf::Property::kConst) || ...);
194 }
195
197 [[nodiscard]] static constexpr bool is_layerable() {
198 return ((kProperties == multibuf::Property::kLayerable) || ...);
199 }
200
202 [[nodiscard]] static constexpr bool is_observable() {
203 return ((kProperties == multibuf::Property::kObservable) || ...);
204 }
205
206 using size_type = typename Deque::size_type;
207 using difference_type = typename Deque::difference_type;
208 using iterator =
209 multibuf::internal::ByteIterator<size_type, /*kIsConst=*/false>;
210 using const_iterator =
211 multibuf::internal::ByteIterator<size_type, /*kIsConst=*/true>;
212 using pointer = iterator::pointer;
213 using const_pointer = const_iterator::pointer;
214 using reference = iterator::reference;
215 using const_reference = const_iterator::reference;
216 using value_type = std::conditional_t<is_const(),
217 const_iterator::value_type,
218 iterator::value_type>;
219
240
241 // Interfaces are not copyable or movable; copy and move `Instance`s instead.
242
243 BasicMultiBuf(const BasicMultiBuf&) { InvalidCopyOrMove<>(); }
244
245 template <multibuf::Property... kOtherProperties>
247 InvalidCopyOrMove<>();
248 }
249
250 BasicMultiBuf& operator=(const BasicMultiBuf&) { InvalidCopyOrMove<>(); }
251
252 template <multibuf::Property... kOtherProperties>
253 BasicMultiBuf& operator=(const BasicMultiBuf<kOtherProperties...>&) {
254 InvalidCopyOrMove<>();
255 }
256
257 BasicMultiBuf(BasicMultiBuf&&) { InvalidCopyOrMove<>(); }
258
259 BasicMultiBuf& operator=(BasicMultiBuf&&) { InvalidCopyOrMove<>(); }
260
261 // Conversions
262
263 template <typename OtherMultiBuf>
264 constexpr OtherMultiBuf& as() & {
265 multibuf::internal::AssertIsConvertible<BasicMultiBuf, OtherMultiBuf>();
266 return generic().template as<OtherMultiBuf>();
267 }
268
269 template <typename OtherMultiBuf>
270 constexpr const OtherMultiBuf& as() const& {
271 multibuf::internal::AssertIsConvertible<BasicMultiBuf, OtherMultiBuf>();
272 return generic().template as<OtherMultiBuf>();
273 }
274
275 template <typename OtherMultiBuf>
276 constexpr OtherMultiBuf&& as() && {
277 multibuf::internal::AssertIsConvertible<BasicMultiBuf, OtherMultiBuf>();
278 return std::move(generic().template as<OtherMultiBuf>());
279 }
280
281 template <typename OtherMultiBuf>
282 constexpr const OtherMultiBuf&& as() const&& {
283 multibuf::internal::AssertIsConvertible<BasicMultiBuf, OtherMultiBuf>();
284 return std::move(generic().template as<OtherMultiBuf>());
285 }
286
287 template <typename OtherMultiBuf,
288 typename = multibuf::internal::EnableIfConvertible<BasicMultiBuf,
289 OtherMultiBuf>>
290 constexpr operator OtherMultiBuf&() & {
291 return as<OtherMultiBuf>();
292 }
293
294 template <typename OtherMultiBuf,
295 typename = multibuf::internal::EnableIfConvertible<BasicMultiBuf,
296 OtherMultiBuf>>
297 constexpr operator const OtherMultiBuf&() const& {
298 return as<OtherMultiBuf>();
299 }
300
301 template <typename OtherMultiBuf,
302 typename = multibuf::internal::EnableIfConvertible<BasicMultiBuf,
303 OtherMultiBuf>>
304 constexpr operator OtherMultiBuf&&() && {
305 return std::move(as<OtherMultiBuf>());
306 }
307
308 template <typename OtherMultiBuf,
309 typename = multibuf::internal::EnableIfConvertible<BasicMultiBuf,
310 OtherMultiBuf>>
311 constexpr operator const OtherMultiBuf&&() const&& {
312 return std::move(as<OtherMultiBuf>());
313 }
314
315 // Accessors
316
319 constexpr bool empty() const { return generic().empty(); }
320
323 constexpr size_t size() const { return generic().size(); }
324
336 template <bool kMutable = !is_const()>
337 std::enable_if_t<kMutable, reference> at(size_t index) {
338 return *(begin() + static_cast<int>(index));
339 }
340 const_reference at(size_t index) const {
341 return *(begin() + static_cast<int>(index));
342 }
343
344 template <bool kMutable = !is_const()>
345 std::enable_if_t<kMutable, reference> operator[](size_t index) {
346 return at(index);
347 }
348 const_reference operator[](size_t index) const { return at(index); }
350
362 template <bool kMutable = !is_const()>
363 constexpr std::enable_if_t<kMutable, multibuf::Chunks<Deque>> Chunks() {
364 return generic().Chunks();
365 }
366 constexpr multibuf::ConstChunks<Deque> Chunks() const {
367 return generic().ConstChunks();
368 }
369 constexpr multibuf::ConstChunks<Deque> ConstChunks() const {
370 return generic().ConstChunks();
371 }
373
374 // Iterators.
375
386 template <bool kMutable = !is_const()>
387 constexpr std::enable_if_t<kMutable, iterator> begin() {
388 return generic().begin();
389 }
390 constexpr const_iterator begin() const { return cbegin(); }
391 constexpr const_iterator cbegin() const { return generic().cbegin(); }
393
404 template <bool kMutable = !is_const()>
405 constexpr std::enable_if_t<kMutable, iterator> end() {
406 return generic().end();
407 }
408 constexpr const_iterator end() const { return cend(); }
409 constexpr const_iterator cend() const { return generic().cend(); }
411
412 // Other methods
413
423 bool IsCompatible(const BasicMultiBuf& mb) const {
424 return generic().IsCompatible(mb.generic());
425 }
426
436 bool IsCompatible(const UniquePtr<std::byte[]>& bytes) const {
437 return generic().IsCompatible(bytes.deallocator());
438 }
439 bool IsCompatible(const UniquePtr<const std::byte[]>& bytes) const {
440 return generic().IsCompatible(bytes.deallocator());
441 }
443
452 bool IsCompatible(const SharedPtr<std::byte[]>& bytes) const {
453 return generic().IsCompatible(bytes.control_block());
454 }
455 bool IsCompatible(const SharedPtr<const std::byte[]>& bytes) const {
456 return generic().IsCompatible(bytes.control_block());
457 }
459
462 [[nodiscard]] bool TryReserveChunks(size_t num_chunks) {
463 return generic().TryReserveChunks(num_chunks);
464 }
465
466 // Mutators
467
479 template <multibuf::Property... kOtherProperties>
480 [[nodiscard]] bool TryReserveForInsert(
482
494 template <
495 int&... kExplicitGuard,
496 typename T,
497 typename =
498 std::enable_if_t<std::is_constructible_v<ConstByteSpan, T>, int>>
499 [[nodiscard]] bool TryReserveForInsert(const_iterator pos, const T& bytes);
500
514 [[nodiscard]] bool TryReserveForInsert(const_iterator pos,
515 const UniquePtr<std::byte[]>& bytes);
516 [[nodiscard]] bool TryReserveForInsert(
517 const_iterator pos, const UniquePtr<const std::byte[]>& bytes);
519
533 [[nodiscard]] bool TryReserveForInsert(const_iterator pos,
534 const SharedPtr<std::byte[]>& bytes);
535 [[nodiscard]] bool TryReserveForInsert(
536 const_iterator pos, const SharedPtr<const std::byte[]>& bytes);
538
547 template <multibuf::Property... kOtherProperties>
549
558 template <
559 int&... kExplicitGuard,
560 typename T,
561 typename =
562 std::enable_if_t<std::is_constructible_v<ConstByteSpan, T>, int>>
563 void Insert(const_iterator pos, const T& bytes);
564
575 void Insert(const_iterator pos, UniquePtr<std::byte[]>&& bytes) {
576 Insert(pos, std::move(bytes), 0);
577 }
578 void Insert(const_iterator pos, UniquePtr<const std::byte[]>&& bytes) {
579 Insert(pos, std::move(bytes), 0);
580 }
582
595 void Insert(const_iterator pos,
596 UniquePtr<std::byte[]>&& bytes,
597 size_t offset,
598 size_t length = dynamic_extent);
599 void Insert(const_iterator pos,
600 UniquePtr<const std::byte[]>&& bytes,
601 size_t offset,
602 size_t length = dynamic_extent);
604
615 void Insert(const_iterator pos, const SharedPtr<std::byte[]>& bytes) {
616 Insert(pos, bytes, 0);
617 }
618 void Insert(const_iterator pos, const SharedPtr<const std::byte[]>& bytes) {
619 Insert(pos, bytes, 0);
620 }
622
635 void Insert(const_iterator pos,
636 const SharedPtr<std::byte[]>& bytes,
637 size_t offset,
638 size_t length = dynamic_extent);
639 void Insert(const_iterator pos,
640 const SharedPtr<const std::byte[]>& bytes,
641 size_t offset,
642 size_t length = dynamic_extent);
644
652 template <multibuf::Property... kOtherProperties>
653 [[nodiscard]] bool TryReserveForPushBack(
655
663 template <
664 int&... kExplicitGuard,
665 typename T,
666 typename =
667 std::enable_if_t<std::is_constructible_v<ConstByteSpan, T>, int>>
668 [[nodiscard]] bool TryReserveForPushBack(const T& bytes);
669
679 [[nodiscard]] bool TryReserveForPushBack(const UniquePtr<std::byte[]>& bytes);
680 [[nodiscard]] bool TryReserveForPushBack(
681 const UniquePtr<const std::byte[]>& bytes);
683
693 [[nodiscard]] bool TryReserveForPushBack(const SharedPtr<std::byte[]>& bytes);
694 [[nodiscard]] bool TryReserveForPushBack(
695 const SharedPtr<const std::byte[]>& bytes);
697
705 template <multibuf::Property... kOtherProperties>
707
715 template <
716 int&... kExplicitGuard,
717 typename T,
718 typename =
719 std::enable_if_t<std::is_constructible_v<ConstByteSpan, T>, int>>
720 void PushBack(const T& bytes);
721
731 void PushBack(UniquePtr<std::byte[]>&& bytes) {
732 PushBack(std::move(bytes), 0);
733 }
734 void PushBack(UniquePtr<const std::byte[]>&& bytes) {
735 PushBack(std::move(bytes), 0);
736 }
738
750 void PushBack(UniquePtr<std::byte[]>&& bytes,
751 size_t offset,
752 size_t length = dynamic_extent);
753
754 void PushBack(UniquePtr<const std::byte[]>&& bytes,
755 size_t offset,
756 size_t length = dynamic_extent);
758
768 void PushBack(const SharedPtr<std::byte[]>& bytes) { PushBack(bytes, 0); }
769 void PushBack(const SharedPtr<const std::byte[]>& bytes) {
770 PushBack(bytes, 0);
771 }
773
785 void PushBack(const SharedPtr<std::byte[]>& bytes,
786 size_t offset,
787 size_t length = dynamic_extent);
788 void PushBack(const SharedPtr<const std::byte[]>& bytes,
789 size_t offset,
790 size_t length = dynamic_extent);
792
802 [[nodiscard]] bool IsRemovable(const_iterator pos, size_t size) const {
803 return generic().IsRemovable(pos, size);
804 }
805
826
838
839 // clang-format off
861 // clang-format on
863 return generic().Discard(pos, size);
864 }
865
870 [[nodiscard]] bool IsReleasable(const_iterator pos) const {
871 return generic().IsReleasable(pos);
872 }
873
887
892 [[nodiscard]] bool IsShareable(const_iterator pos) const {
893 return generic().IsShareable(pos);
894 }
895
911
920 size_t CopyTo(ByteSpan dst, size_t offset = 0) const {
921 return generic().CopyTo(dst, offset);
922 }
923
932 size_t CopyFrom(ConstByteSpan src, size_t offset = 0) {
933 static_assert(!is_const(),
934 "`CopyFrom` may only be called on mutable MultiBufs");
935 return generic().CopyFrom(src, offset);
936 }
937
954 ConstByteSpan Get(ByteSpan copy, size_t offset = 0) const {
955 return generic().Get(copy, offset);
956 }
957
973 template <int&... kExplicitGuard, typename Visitor>
974 auto Visit(Visitor visitor, ByteSpan copy, size_t offset) {
975 return visitor(Get(copy, offset));
976 }
977
983 void Clear() { generic().Clear(); }
984
991 void ShrinkToFit() { generic().ShrinkToFit(); }
992
993 // Observable methods.
994
999 constexpr multibuf::Observer* observer() const {
1000 static_assert(is_observable(),
1001 "`observer` may only be called on observable MultiBufs");
1002 return generic().observer_;
1003 }
1004
1012 static_assert(is_observable(),
1013 "`set_observer` may only be called on observable MultiBufs");
1014 generic().observer_ = observer;
1015 }
1016
1017 // Layerable methods.
1018
1025 size_type NumFragments() const {
1026 static_assert(is_layerable(),
1027 "`NumFragments` may only be called on layerable MultiBufs");
1028 return generic().NumFragments();
1029 }
1030
1034 constexpr size_type NumLayers() const {
1035 static_assert(is_layerable(),
1036 "`NumLayers` may only be called on layerable MultiBufs");
1037 return generic().NumLayers();
1038 }
1039
1047 [[nodiscard]] bool TryReserveLayers(size_t num_layers,
1048 size_t num_chunks = 1) {
1049 return generic().TryReserveLayers(num_layers, num_chunks);
1050 }
1051
1071 [[nodiscard]] bool AddLayer(size_t offset, size_t length = dynamic_extent) {
1072 static_assert(is_layerable(),
1073 "`AddLayer` may only be called on layerable MultiBufs");
1074 return generic().AddLayer(offset, length);
1075 }
1076
1080 static_assert(is_layerable(),
1081 "`SealTopLayer` may only be called on layerable MultiBufs");
1082 return generic().SealTopLayer();
1083 }
1084
1088 static_assert(is_layerable(),
1089 "`UnsealTopLayer` may only be called on layerable MultiBufs");
1090 return generic().UnsealTopLayer();
1091 }
1092
1094 [[nodiscard]] bool IsTopLayerSealed() {
1095 static_assert(
1096 is_layerable(),
1097 "`IsTopLayerSealed` may only be called on layerable MultiBufs");
1098 return generic().IsTopLayerSealed();
1099 }
1100
1109 void TruncateTopLayer(size_t length) {
1110 static_assert(
1111 is_layerable(),
1112 "`TruncateTopLayer` may only be called on layerable MultiBufs");
1113 generic().TruncateTopLayer(length);
1114 }
1115
1123 static_assert(!is_const() && is_layerable(),
1124 "`SetTopLayer` may only be called on mutable, layerable "
1125 "MultiBufs");
1126 PW_ASSERT(src.size() <= size());
1127 CopyFrom(src);
1128 TruncateTopLayer(src.size());
1129 }
1130
1139 void PopLayer() {
1140 static_assert(is_layerable(),
1141 "`PopLayer` may only be called on layerable MultiBufs");
1142 generic().PopLayer();
1143 }
1144
1145 protected:
1146 constexpr BasicMultiBuf() { multibuf::internal::PropertiesAreValid(); }
1147
1148 private:
1149 template <bool kValidCopyOrMove = false>
1150 static constexpr void InvalidCopyOrMove() {
1151 static_assert(kValidCopyOrMove,
1152 "Only copies and moves from `BasicMultiBuf<...>::Instance`"
1153 "to `BasicMultiBuf<...>&` or another "
1154 "`BasicMultiBuf<...>::Instance` are valid.");
1155 }
1156
1157 template <multibuf::Property...>
1158 friend class BasicMultiBuf;
1159
1160 constexpr GenericMultiBuf& generic() {
1161 return static_cast<GenericMultiBuf&>(*this);
1162 }
1163 constexpr const GenericMultiBuf& generic() const {
1164 return static_cast<const GenericMultiBuf&>(*this);
1165 }
1166};
1167
1169
1171
1172namespace multibuf::internal {
1173
1174// A generic MultiBuf implementation that provides the functionality of any
1175// `BasicMultiBuf<kProperties>` type.
1176//
1177// This class should not be instantiated directly. Instead, use `Instance` as
1178// described below. This is the base class for all `Instance` types, and derives
1179// from every supported `BasicMultiBuf` type. It implements the MultiBuf
1180// behavior in one type, and allows for performing conversions from `Instance`s
1181// and `BasicMultiBuf`s to `BasicMultiBuf`s with different yet compatible
1182// propreties.
1184 : private BasicMultiBuf<>,
1191 private BasicMultiBuf<Property::kConst,
1192 Property::kLayerable,
1193 Property::kObservable> {
1194 private:
1195 using ControlBlock = allocator::internal::ControlBlock;
1196 using typename BasicMultiBuf<>::Deque;
1197
1198 public:
1199 using typename BasicMultiBuf<>::const_iterator;
1200 using typename BasicMultiBuf<>::const_pointer;
1201 using typename BasicMultiBuf<>::const_reference;
1202 using typename BasicMultiBuf<>::difference_type;
1203 using typename BasicMultiBuf<>::iterator;
1204 using typename BasicMultiBuf<>::pointer;
1205 using typename BasicMultiBuf<>::reference;
1206 using typename BasicMultiBuf<>::size_type;
1207
1208 ~GenericMultiBuf() { Clear(); }
1209
1210 // Not copyable.
1211 GenericMultiBuf(const GenericMultiBuf&) = delete;
1212 GenericMultiBuf& operator=(const GenericMultiBuf&) = delete;
1213
1214 // Movable.
1216 : GenericMultiBuf(other.deque_.get_allocator()) {
1217 *this = std::move(other);
1218 }
1219 GenericMultiBuf& operator=(GenericMultiBuf&& other);
1220
1221 private:
1222 template <::pw::multibuf::Property...>
1223 friend class ::pw::BasicMultiBuf;
1224
1225 template <typename>
1226 friend class Instance;
1227
1229 constexpr explicit GenericMultiBuf(Allocator& allocator)
1230 : deque_(allocator) {}
1231
1232 template <typename MultiBufType>
1233 constexpr MultiBufType& as() {
1234 return *this;
1235 }
1236
1237 template <typename MultiBufType>
1238 constexpr const MultiBufType& as() const {
1239 return *this;
1240 }
1241
1242 // Accessors.
1243
1245 constexpr bool empty() const { return deque_.empty(); }
1246
1248 constexpr size_t size() const {
1249 return static_cast<size_t>(cend() - cbegin());
1250 }
1251
1253 constexpr bool has_deallocator() const {
1254 return memory_tag_ == MemoryTag::kDeallocator || has_control_block();
1255 }
1256
1258 constexpr bool has_control_block() const {
1259 return memory_tag_ == MemoryTag::kControlBlock;
1260 }
1261
1262 // Iterators.
1263
1264 constexpr multibuf::Chunks<Deque> Chunks() {
1265 return multibuf::Chunks<Deque>(deque_, depth_);
1266 }
1267 constexpr multibuf::ConstChunks<Deque> ConstChunks() const {
1268 return multibuf::ConstChunks<Deque>(deque_, depth_);
1269 }
1270
1271 constexpr iterator begin() { return iterator(Chunks().begin(), 0); }
1272 constexpr const_iterator cbegin() const {
1273 return const_iterator(ConstChunks().cbegin(), 0);
1274 }
1275
1276 constexpr iterator end() { return iterator(Chunks().end(), 0); }
1277 constexpr const_iterator cend() const {
1278 return const_iterator(ConstChunks().cend(), 0);
1279 }
1280
1281 // Mutators.
1282
1284 bool IsCompatible(const GenericMultiBuf& other) const;
1285 bool IsCompatible(const Deallocator* other) const;
1286 bool IsCompatible(const ControlBlock* other) const;
1287
1289 [[nodiscard]] bool TryReserveChunks(size_t num_chunks);
1290
1293 [[nodiscard]] bool TryReserveForInsert(const_iterator pos,
1294 const GenericMultiBuf& mb);
1295 [[nodiscard]] bool TryReserveForInsert(const_iterator pos, size_t size);
1296 [[nodiscard]] bool TryReserveForInsert(const_iterator pos,
1297 size_t size,
1298 const Deallocator* deallocator);
1299 [[nodiscard]] bool TryReserveForInsert(const_iterator pos,
1300 size_t size,
1301 const ControlBlock* control_block);
1303
1306 void Insert(const_iterator pos, GenericMultiBuf&& mb);
1307 void Insert(const_iterator pos, ConstByteSpan bytes);
1308 void Insert(const_iterator pos,
1309 ConstByteSpan bytes,
1310 size_t offset,
1311 size_t length,
1312 Deallocator* deallocator);
1313 void Insert(const_iterator pos,
1314 ConstByteSpan bytes,
1315 size_t offset,
1316 size_t length,
1317 ControlBlock* control_block);
1319
1321 [[nodiscard]] bool IsRemovable(const_iterator pos, size_t size) const;
1322
1324 Result<GenericMultiBuf> Remove(const_iterator pos, size_t size);
1325
1327 Result<GenericMultiBuf> PopFrontFragment();
1328
1330 Result<const_iterator> Discard(const_iterator pos, size_t size);
1331
1333 [[nodiscard]] bool IsReleasable(const_iterator pos) const;
1334
1337
1339 [[nodiscard]] bool IsShareable(const_iterator pos) const;
1340
1342 std::byte* Share(const_iterator pos);
1343
1345 size_t CopyTo(ByteSpan dst, size_t offset) const;
1346
1348 size_t CopyFrom(ConstByteSpan src, size_t offset);
1349
1351 ConstByteSpan Get(ByteSpan copy, size_t offset) const;
1352
1354 void Clear();
1355
1357 void ShrinkToFit();
1358
1359 // Layerable methods.
1360
1362 size_type NumFragments() const;
1363
1365 constexpr size_type NumLayers() const { return depth_ - 1; }
1366
1368 [[nodiscard]] bool TryReserveLayers(size_t num_layers, size_t num_chunks = 1);
1369
1371 [[nodiscard]] bool AddLayer(size_t offset, size_t length = dynamic_extent);
1372
1374 void SealTopLayer();
1375
1377 void UnsealTopLayer();
1378
1380 void TruncateTopLayer(size_t length);
1381
1383 void PopLayer();
1384
1385 // Implementation methods.
1386 //
1387 // These methods are used to implement the methods above, and should not be
1388 // called directly by BasicMultiBuf<>.
1389
1394 static size_t CheckRange(size_t offset, size_t length, size_t size);
1395
1397 constexpr std::byte* GetData(size_type index) const {
1398 return deque_[index].data;
1399 }
1400
1403 constexpr bool IsOwned(size_type index) const {
1404 return deque_[index + 1].base_view.owned;
1405 }
1406
1409 constexpr bool IsShared(size_type index) const {
1410 return deque_[index + 1].base_view.shared;
1411 }
1412
1414 constexpr bool IsSealed(size_type index) const {
1415 return depth_ == 2 ? false : deque_[index + depth_ - 1].view.sealed;
1416 }
1417
1420 constexpr bool IsBoundary(size_type index) const {
1421 return depth_ == 2 ? true : deque_[index + depth_ - 1].view.boundary;
1422 }
1423
1427 constexpr size_type GetOffset(size_type index, uint16_t layer) const {
1428 return layer == 1 ? deque_[index + 1].base_view.offset
1429 : deque_[index + layer].view.offset;
1430 }
1431
1433 constexpr size_type GetOffset(size_type index) const {
1434 return GetOffset(index, NumLayers());
1435 }
1436
1439 constexpr size_type GetRelativeOffset(size_type index) const {
1440 uint16_t layer = NumLayers();
1441 if (layer == 1) {
1442 return GetOffset(index, layer);
1443 }
1444 return GetOffset(index, layer) - GetOffset(index, layer - 1);
1445 }
1446
1448 constexpr size_type GetLength(size_type index) const {
1449 return depth_ == 2 ? deque_[index + 1].base_view.length
1450 : deque_[index + depth_ - 1].view.length;
1451 }
1452
1454 constexpr ByteSpan GetView(size_type index) const {
1455 return ByteSpan(GetData(index) + GetOffset(index), GetLength(index));
1456 }
1457
1459 Deallocator* GetDeallocator() const;
1460
1462 void SetDeallocator(Deallocator* deallocator);
1463
1465 ControlBlock* GetControlBlock() const;
1466
1468 void SetControlBlock(ControlBlock* control_block);
1469
1471 void CopyMemoryContext(const GenericMultiBuf& other);
1472
1474 void ClearMemoryContext();
1475
1479 std::pair<size_type, size_type> GetIndexAndOffset(const_iterator pos) const;
1480
1486 bool TryReserveEntries(const_iterator pos, size_type num_entries);
1487 bool TryReserveEntries(size_type num_entries, bool split = false);
1489
1492 size_type InsertEntries(const_iterator pos, size_type num_entries);
1493
1496 size_type Insert(const_iterator pos,
1497 ConstByteSpan bytes,
1498 size_t offset,
1499 size_t length);
1500
1510 void SplitBase(size_type index, Deque& out_deque, size_type out_index);
1511
1517 void SplitBefore(size_type index,
1518 size_type split,
1519 Deque& out_deque,
1520 size_type out_index);
1521
1524 void SplitBefore(size_type index, size_type split);
1525
1531 void SplitAfter(size_type index,
1532 size_type split,
1533 Deque& out_deque,
1534 size_type out_index);
1535
1538 void SplitAfter(size_type index, size_type split);
1539
1550 [[nodiscard]] bool TryReserveForRemove(const_iterator pos,
1551 size_t size,
1552 GenericMultiBuf* out);
1553
1558 void CopyRange(const_iterator pos, size_t size, GenericMultiBuf& out);
1559
1563 void ClearRange(const_iterator pos, size_t size);
1564
1569 void EraseRange(const_iterator pos, size_t size);
1570
1573 size_type FindShared(size_type index, size_type start);
1574
1577 size_t CopyToImpl(ByteSpan dst, size_t offset, size_type start) const;
1578
1580 [[nodiscard]] bool IsTopLayerSealed() const;
1581
1584 void SetLayer(size_t offset, size_t length);
1585
1586 // Describes the memory and views to that in a one-dimensional sequence.
1587 // Every `depth_`-th entry holds a pointer to memory, each entry for a given
1588 // offset less than `depth_` after that entry is a view that is part of the
1589 // same "layer" in the MultiBuf.
1590 //
1591 // This base type will always have 0 or 2 layers, but derived classes may add
1592 // more.
1593 //
1594 // For example, a MultiBuf that has had 3 `Chunk`s and two additional layers
1595 // added would have a `deque_` resembling the following:
1596 //
1597 // buffer 0: buffer 1: buffer 2:
1598 // layer 3: deque_[0x3].view deque_[0x7].view deque_[0xB].view
1599 // layer 2: deque_[0x2].view deque_[0x6].view deque_[0xA].view
1600 // layer 1: deque_[0x1].view deque_[0x5].view deque_[0x9].view
1601 // layer 0: deque_[0x0].data deque_[0x4].data deque_[0x8].data
1602 Deque deque_;
1603
1604 // Number of entries per chunk in this MultiBuf.
1605 size_type depth_ = 2;
1606
1623 enum class MemoryTag : uint8_t {
1624 kEmpty,
1625 kDeallocator,
1626 kControlBlock,
1627 } memory_tag_ = MemoryTag::kEmpty;
1628
1629 union MemoryContext {
1630 Deallocator* deallocator;
1631 ControlBlock* control_block;
1632 } memory_context_ = {.deallocator = nullptr};
1634
1637 Observer* observer_ = nullptr;
1638};
1639
1658template <typename MultiBufType>
1660 public:
1661 constexpr explicit Instance(Allocator& allocator) : base_(allocator) {}
1662
1663 constexpr Instance(Instance&&) = default;
1664 constexpr Instance& operator=(Instance&&) = default;
1665
1666 // Provide a more helpful compile-time error when a user tries to
1667 // copy-construct an interface type.
1668 template <Property... kProperties>
1669 constexpr Instance(const BasicMultiBuf<kProperties...>&)
1670 : base_(allocator::GetNullAllocator()) {
1671 MoveOnly<>();
1672 }
1673
1674 // Provide a more helpful compile-time error when a user tries to copy-assign
1675 // an interface type.
1676 template <Property... kProperties>
1677 constexpr Instance& operator=(const BasicMultiBuf<kProperties...>&) {
1678 MoveOnly<>();
1679 }
1680
1681 constexpr Instance(MultiBufType&& mb)
1682 : base_(std::move(static_cast<GenericMultiBuf&>(mb))) {}
1683
1684 constexpr Instance& operator=(MultiBufType&& mb) {
1685 base_ = std::move(static_cast<GenericMultiBuf&>(mb));
1686 return *this;
1687 }
1688
1689 template <Property... kProperties>
1691 : base_(std::move(static_cast<internal::GenericMultiBuf&>(mb))) {
1692 internal::AssertIsConvertible<BasicMultiBuf<kProperties...>,
1693 MultiBufType>();
1694 }
1695
1696 template <Property... kProperties>
1697 constexpr Instance& operator=(BasicMultiBuf<kProperties...>&& mb) {
1698 internal::AssertIsConvertible<BasicMultiBuf<kProperties...>,
1699 MultiBufType>();
1700 base_ = std::move(static_cast<internal::GenericMultiBuf&>(mb));
1701 return *this;
1702 }
1703
1704 constexpr MultiBufType* operator->() { return &base_.as<MultiBufType>(); }
1705 constexpr const MultiBufType* operator->() const {
1706 return &base_.as<MultiBufType>();
1707 }
1708
1709 constexpr MultiBufType& operator*() & { return base_.as<MultiBufType>(); }
1710 constexpr const MultiBufType& operator*() const& {
1711 return base_.as<MultiBufType>();
1712 }
1713
1714 constexpr MultiBufType&& operator*() && {
1715 return std::move(base_.as<MultiBufType>());
1716 }
1717 constexpr const MultiBufType&& operator*() const&& {
1718 return std::move(base_.as<MultiBufType>());
1719 }
1720
1721 constexpr operator MultiBufType&() & { return base_.as<MultiBufType>(); }
1722 constexpr operator const MultiBufType&() const& {
1723 return base_.as<MultiBufType>();
1724 }
1725
1726 constexpr operator MultiBufType&&() && {
1727 return std::move(base_.as<MultiBufType>());
1728 }
1729 constexpr operator const MultiBufType&&() const&& {
1730 return std::move(base_.as<MultiBufType>());
1731 }
1732
1733 private:
1734 // Helper functions to provide a more helpful compile-time error when a this
1735 // type is used incorrectly.
1736 template <bool kMoveOnly = false>
1737 static constexpr void MoveOnly() {
1738 static_assert(kMoveOnly,
1739 "Instances can only be created from existing MultiBufs using "
1740 "move-construction or move-assignment.");
1741 }
1742
1743 GenericMultiBuf base_;
1744};
1745
1746} // namespace multibuf::internal
1747
1748// Template method implementations.
1749
1750template <multibuf::Property... kProperties>
1751template <multibuf::Property... kOtherProperties>
1754 multibuf::internal::AssertIsConvertible<BasicMultiBuf<kOtherProperties...>,
1755 BasicMultiBuf>();
1756 return generic().TryReserveForInsert(pos,
1757 static_cast<const GenericMultiBuf&>(mb));
1758}
1759
1760template <multibuf::Property... kProperties>
1761template <int&... kExplicitGuard, typename T, typename>
1763 const T& bytes) {
1764 using data_ptr_type = decltype(std::data(std::declval<T&>()));
1765 static_assert(std::is_same_v<data_ptr_type, std::byte*> || is_const(),
1766 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1767 return generic().TryReserveForInsert(pos, bytes.size());
1768}
1769
1770template <multibuf::Property... kProperties>
1772 const_iterator pos, const UniquePtr<std::byte[]>& bytes) {
1773 return generic().TryReserveForInsert(pos, bytes.size(), bytes.deallocator());
1774}
1775
1776template <multibuf::Property... kProperties>
1778 const_iterator pos, const UniquePtr<const std::byte[]>& bytes) {
1779 static_assert(is_const(),
1780 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1781 return generic().TryReserveForInsert(pos, bytes.size(), bytes.deallocator());
1782}
1783
1784template <multibuf::Property... kProperties>
1786 const_iterator pos, const SharedPtr<std::byte[]>& bytes) {
1787 return generic().TryReserveForInsert(
1788 pos, bytes.size(), bytes.control_block());
1789}
1790
1791template <multibuf::Property... kProperties>
1793 const_iterator pos, const SharedPtr<const std::byte[]>& bytes) {
1794 static_assert(is_const(),
1795 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1796 return generic().TryReserveForInsert(
1797 pos, bytes.size(), bytes.control_block());
1798}
1799
1800template <multibuf::Property... kProperties>
1801template <multibuf::Property... kOtherProperties>
1804 multibuf::internal::AssertIsConvertible<BasicMultiBuf<kOtherProperties...>,
1805 BasicMultiBuf>();
1806 generic().Insert(pos, std::move(mb.generic()));
1807}
1808
1809template <multibuf::Property... 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 `Insert` read-only bytes into mutable MultiBuf");
1815 generic().Insert(pos, bytes);
1816}
1817
1818template <multibuf::Property... kProperties>
1819void BasicMultiBuf<kProperties...>::Insert(const_iterator pos,
1820 UniquePtr<std::byte[]>&& bytes,
1821 size_t offset,
1822 size_t length) {
1823 ConstByteSpan chunk(bytes.get(), bytes.size());
1824 generic().Insert(pos, chunk, offset, length, bytes.deallocator());
1825 bytes.Release();
1826}
1827
1828template <multibuf::Property... kProperties>
1829void BasicMultiBuf<kProperties...>::Insert(const_iterator pos,
1830 UniquePtr<const std::byte[]>&& bytes,
1831 size_t offset,
1832 size_t length) {
1833 static_assert(is_const(),
1834 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1835 ConstByteSpan chunk(bytes.get(), bytes.size());
1836 generic().Insert(pos, chunk, offset, length, bytes.deallocator());
1837 bytes.Release();
1838}
1839
1840template <multibuf::Property... kProperties>
1841void BasicMultiBuf<kProperties...>::Insert(const_iterator pos,
1842 const SharedPtr<std::byte[]>& bytes,
1843 size_t offset,
1844 size_t length) {
1845 ConstByteSpan chunk(bytes.get(), bytes.size());
1846 generic().Insert(pos, chunk, offset, length, bytes.control_block());
1847}
1848
1849template <multibuf::Property... kProperties>
1851 const_iterator pos,
1852 const SharedPtr<const std::byte[]>& bytes,
1853 size_t offset,
1854 size_t length) {
1855 static_assert(is_const(),
1856 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1857 ConstByteSpan chunk(bytes.get(), bytes.size());
1858 generic().Insert(pos, chunk, offset, length, bytes.control_block());
1859}
1860
1861template <multibuf::Property... kProperties>
1862template <multibuf::Property... kOtherProperties>
1865 return TryReserveForInsert(end(), mb);
1866}
1867
1868template <multibuf::Property... kProperties>
1869template <int&... kExplicitGuard, typename T, typename>
1871 using data_ptr_type = decltype(std::data(std::declval<T&>()));
1872 static_assert(std::is_same_v<data_ptr_type, std::byte*> || is_const(),
1873 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1874 return TryReserveForInsert(end(), bytes);
1875}
1876
1877template <multibuf::Property... kProperties>
1879 const UniquePtr<std::byte[]>& bytes) {
1880 return TryReserveForInsert(end(), std::move(bytes));
1881}
1882
1883template <multibuf::Property... kProperties>
1885 const UniquePtr<const std::byte[]>& bytes) {
1886 static_assert(is_const(),
1887 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1888 return TryReserveForInsert(end(), std::move(bytes));
1889}
1890
1891template <multibuf::Property... kProperties>
1893 const SharedPtr<std::byte[]>& bytes) {
1894 return TryReserveForInsert(end(), bytes);
1895}
1896
1897template <multibuf::Property... kProperties>
1899 const SharedPtr<const std::byte[]>& bytes) {
1900 static_assert(is_const(),
1901 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1902 return TryReserveForInsert(end(), bytes);
1903}
1904
1905template <multibuf::Property... kProperties>
1906template <multibuf::Property... kOtherProperties>
1909 Insert(end(), std::move(mb));
1910}
1911
1912template <multibuf::Property... kProperties>
1913template <int&... kExplicitGuard, typename T, typename>
1915 using data_ptr_type = decltype(std::data(std::declval<T&>()));
1916 static_assert(std::is_same_v<data_ptr_type, std::byte*> || is_const(),
1917 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1918 Insert(end(), bytes);
1919}
1920
1921template <multibuf::Property... kProperties>
1922void BasicMultiBuf<kProperties...>::PushBack(UniquePtr<std::byte[]>&& bytes,
1923 size_t offset,
1924 size_t length) {
1925 Insert(end(), std::move(bytes), offset, length);
1926}
1927
1928template <multibuf::Property... kProperties>
1930 UniquePtr<const std::byte[]>&& bytes, size_t offset, size_t length) {
1931 static_assert(is_const(),
1932 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1933 Insert(end(), std::move(bytes), offset, length);
1934}
1935
1936template <multibuf::Property... kProperties>
1938 const SharedPtr<std::byte[]>& bytes, size_t offset, size_t length) {
1939 Insert(end(), bytes, offset, length);
1940}
1941
1942template <multibuf::Property... kProperties>
1944 const SharedPtr<const std::byte[]>& bytes, size_t offset, size_t length) {
1945 static_assert(is_const(),
1946 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1947 Insert(end(), bytes, offset, length);
1948}
1949
1950template <multibuf::Property... kProperties>
1951Result<multibuf::internal::Instance<BasicMultiBuf<kProperties...>>>
1953 auto result = generic().Remove(pos, size);
1954 if (!result.ok()) {
1955 return result.status();
1956 }
1957 return Instance(std::move(*result));
1958}
1959
1960template <multibuf::Property... kProperties>
1963 auto result = generic().PopFrontFragment();
1964 if (!result.ok()) {
1965 return result.status();
1966 }
1967 return Instance(std::move(*result));
1968}
1969
1970template <multibuf::Property... kProperties>
1971UniquePtr<typename BasicMultiBuf<kProperties...>::value_type[]>
1973 UniquePtr<std::byte[]> bytes = generic().Release(pos);
1974 if constexpr (is_const()) {
1975 UniquePtr<const std::byte[]> const_bytes(
1976 bytes.get(), bytes.size(), *(bytes.deallocator()));
1977 bytes.Release();
1978 return const_bytes;
1979 } else {
1980 return bytes;
1981 }
1982}
1983
1984template <multibuf::Property... kProperties>
1985SharedPtr<typename BasicMultiBuf<kProperties...>::value_type[]>
1987 return SharedPtr<value_type[]>(generic().Share(pos),
1988 generic().GetControlBlock());
1989}
1990
1991} // namespace pw
Definition: allocator.h:43
bool IsTopLayerSealed()
Returns whether the "sealed" flag is set in the top layer.
Definition: multibuf_v2.h:1094
bool AddLayer(size_t offset, size_t length=dynamic_extent)
Definition: multibuf_v2.h:1071
void Insert(const_iterator pos, BasicMultiBuf< kOtherProperties... > &&mb)
Definition: multibuf_v2.h:1802
bool TryReserveForPushBack(const T &bytes)
Definition: multibuf_v2.h:1870
void SealTopLayer()
Definition: multibuf_v2.h:1079
static constexpr bool is_layerable()
Returns whether additional views can be layered on the MultiBuf.
Definition: multibuf_v2.h:197
void UnsealTopLayer()
Definition: multibuf_v2.h:1087
SharedPtr< value_type[]> Share(const_iterator pos)
Definition: multibuf_v2.h:1986
void Insert(const_iterator pos, const T &bytes)
Definition: multibuf_v2.h:1811
void TruncateTopLayer(size_t length)
Definition: multibuf_v2.h:1109
void SetTopLayer(ConstByteSpan src)
Definition: multibuf_v2.h:1122
void PushBack(const T &bytes)
Definition: multibuf_v2.h:1914
static constexpr bool is_const()
Returns whether the MultiBuf data is immutable.
Definition: multibuf_v2.h:192
void PopLayer()
Definition: multibuf_v2.h:1139
void Clear()
Definition: multibuf_v2.h:983
ConstByteSpan Get(ByteSpan copy, size_t offset=0) const
Definition: multibuf_v2.h:954
void set_observer(multibuf::Observer *observer)
Definition: multibuf_v2.h:1011
UniquePtr< value_type[]> Release(const_iterator pos)
Definition: multibuf_v2.h:1972
bool TryReserveChunks(size_t num_chunks)
Definition: multibuf_v2.h:462
size_t CopyTo(ByteSpan dst, size_t offset=0) const
Definition: multibuf_v2.h:920
size_t CopyFrom(ConstByteSpan src, size_t offset=0)
Definition: multibuf_v2.h:932
void ShrinkToFit()
Definition: multibuf_v2.h:991
constexpr multibuf::Observer * observer() const
Definition: multibuf_v2.h:999
bool TryReserveForPushBack(const BasicMultiBuf< kOtherProperties... > &mb)
Definition: multibuf_v2.h:1863
Result< Instance > PopFrontFragment()
Definition: multibuf_v2.h:1962
Result< Instance > Remove(const_iterator pos, size_t size)
Definition: multibuf_v2.h:1952
static constexpr bool is_observable()
Returns whether an observer can be registered on the MultiBuf.
Definition: multibuf_v2.h:202
constexpr size_t size() const
Definition: multibuf_v2.h:323
bool TryReserveLayers(size_t num_layers, size_t num_chunks=1)
Definition: multibuf_v2.h:1047
bool IsShareable(const_iterator pos) const
Definition: multibuf_v2.h:892
constexpr size_type NumLayers() const
Definition: multibuf_v2.h:1034
constexpr bool empty() const
Definition: multibuf_v2.h:319
Result< const_iterator > Discard(const_iterator pos, size_t size)
Definition: multibuf_v2.h:862
bool IsReleasable(const_iterator pos) const
Definition: multibuf_v2.h:870
bool IsRemovable(const_iterator pos, size_t size) const
Definition: multibuf_v2.h:802
bool TryReserveForInsert(const_iterator pos, const BasicMultiBuf< kOtherProperties... > &mb)
Definition: multibuf_v2.h:1752
size_type NumFragments() const
Definition: multibuf_v2.h:1025
void PushBack(BasicMultiBuf< kOtherProperties... > &&mb)
Definition: multibuf_v2.h:1907
auto Visit(Visitor visitor, ByteSpan copy, size_t offset)
Definition: multibuf_v2.h:974
bool TryReserveForInsert(const_iterator pos, const T &bytes)
Definition: multibuf_v2.h:1762
bool IsCompatible(const BasicMultiBuf &mb) const
Definition: multibuf_v2.h:423
Abstract interface for releasing memory.
Definition: deallocator.h:29
Definition: dynamic_deque.h:60
Definition: result.h:143
Definition: shared_ptr.h:63
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: chunks.h:79
Definition: chunks.h:123
Definition: observer.h:29
Definition: byte_iterator.h:44
Definition: multibuf_v2.h:1193
Definition: multibuf_v2.h:1659
NullAllocator & GetNullAllocator()
Returns a reference to the NullAllocator singleton.
constexpr OutputIt copy(InputIt first, InputIt last, OutputIt d_first)
constexpr backport of <algorithm>'s std::copy for C++17.
Definition: algorithm.h:355
Property
Basic properties of a MultiBuf.
Definition: properties.h:25
Result(T value) -> Result< T >
Deduction guide to allow Result(v) rather than Result<T>(v).
The Pigweed namespace.
Definition: alignment.h:27