C/C++ API Reference
Loading...
Searching...
No Matches
multibuf.h
1// Copyright 2025 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14#pragma once
15
16// 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_multibuf/v2/chunks.h"
30#include "pw_multibuf/v2/internal/byte_iterator.h"
31#include "pw_multibuf/v2/internal/entry.h"
32#include "pw_multibuf/v2/observer.h"
33#include "pw_multibuf/v2/properties.h"
34
35namespace pw::multibuf {
36
37// Forward declarations.
38
39namespace v1_adapter {
40class MultiBuf;
41class MultiBufChunks;
42} // namespace v1_adapter
43
44namespace v2 {
45
46template <Property...>
47class BasicMultiBuf;
48
50
51// Type aliases of the BasicMultiBuf interface types for each different
52// combination of propeties, listed here for easier discoverability.
53
56
59
63
67
71
76
81
87
88namespace internal {
89
90class GenericMultiBuf;
91
92template <typename>
93class Instance;
94
95template <typename OtherMultiBuf, typename MultiBufType>
96constexpr OtherMultiBuf& ConstCast(const MultiBufType& mb);
97
98} // namespace internal
99
193template <Property... kProperties>
195 protected:
198
199 public:
201 [[nodiscard]] static constexpr bool is_const() {
202 return ((kProperties == Property::kConst) || ...);
203 }
204
206 [[nodiscard]] static constexpr bool is_layerable() {
207 return ((kProperties == Property::kLayerable) || ...);
208 }
209
211 [[nodiscard]] static constexpr bool is_observable() {
212 return ((kProperties == Property::kObservable) || ...);
213 }
214
215 using size_type = typename Deque::size_type;
216 using difference_type = typename Deque::difference_type;
218 using iterator = std::conditional_t<
219 is_const(),
220 const_iterator,
222 using pointer = typename iterator::pointer;
223 using const_pointer = const_iterator::pointer;
224 using reference = typename iterator::reference;
225 using const_reference = const_iterator::reference;
226 using value_type = std::conditional_t<is_const(),
227 const_iterator::value_type,
228 typename iterator::value_type>;
229
250
251 // Interfaces are not copyable or movable; copy and move `Instance`s instead.
252
253 BasicMultiBuf(const BasicMultiBuf&) { InvalidCopyOrMove<>(); }
254
255 template <Property... kOtherProperties>
257 InvalidCopyOrMove<>();
258 }
259
260 BasicMultiBuf& operator=(const BasicMultiBuf&) { InvalidCopyOrMove<>(); }
261
262 template <Property... kOtherProperties>
263 BasicMultiBuf& operator=(const BasicMultiBuf<kOtherProperties...>&) {
264 InvalidCopyOrMove<>();
265 }
266
267 BasicMultiBuf(BasicMultiBuf&&) { InvalidCopyOrMove<>(); }
268
269 BasicMultiBuf& operator=(BasicMultiBuf&&) { InvalidCopyOrMove<>(); }
270
271 // Conversions
272
273 template <typename OtherMultiBuf>
274 constexpr OtherMultiBuf& as() & {
275 internal::AssertIsConvertible<BasicMultiBuf, OtherMultiBuf>();
276 return generic().template as<OtherMultiBuf>();
277 }
278
279 template <typename OtherMultiBuf>
280 constexpr const OtherMultiBuf& as() const& {
281 internal::AssertIsConvertible<BasicMultiBuf, OtherMultiBuf>();
282 return generic().template as<OtherMultiBuf>();
283 }
284
285 template <typename OtherMultiBuf>
286 constexpr OtherMultiBuf&& as() && {
287 internal::AssertIsConvertible<BasicMultiBuf, OtherMultiBuf>();
288 return std::move(generic().template as<OtherMultiBuf>());
289 }
290
291 template <typename OtherMultiBuf>
292 constexpr const OtherMultiBuf&& as() const&& {
293 internal::AssertIsConvertible<BasicMultiBuf, OtherMultiBuf>();
294 return std::move(generic().template as<OtherMultiBuf>());
295 }
296
297 template <
298 typename OtherMultiBuf,
299 typename = internal::EnableIfConvertible<BasicMultiBuf, OtherMultiBuf>>
300 constexpr operator OtherMultiBuf&() & {
301 return as<OtherMultiBuf>();
302 }
303
304 template <
305 typename OtherMultiBuf,
306 typename = internal::EnableIfConvertible<BasicMultiBuf, OtherMultiBuf>>
307 constexpr operator const OtherMultiBuf&() const& {
308 return as<OtherMultiBuf>();
309 }
310
311 template <
312 typename OtherMultiBuf,
313 typename = internal::EnableIfConvertible<BasicMultiBuf, OtherMultiBuf>>
314 constexpr operator OtherMultiBuf&&() && {
315 return std::move(as<OtherMultiBuf>());
316 }
317
318 template <
319 typename OtherMultiBuf,
320 typename = internal::EnableIfConvertible<BasicMultiBuf, OtherMultiBuf>>
321 constexpr operator const OtherMultiBuf&&() const&& {
322 return std::move(as<OtherMultiBuf>());
323 }
324
325 // Accessors
326
328 constexpr Allocator& get_allocator() const {
329 return generic().get_allocator();
330 }
331
334 constexpr bool empty() const { return generic().empty(); }
335
340 constexpr bool at_capacity() const { return generic().at_capacity(); }
341
344 constexpr size_t size() const { return generic().size(); }
345
357 template <bool kMutable = !is_const()>
358 std::enable_if_t<kMutable, reference> at(size_t index) {
359 return *(begin() + static_cast<int>(index));
360 }
361 const_reference at(size_t index) const {
362 return *(begin() + static_cast<int>(index));
363 }
364
365 template <bool kMutable = !is_const()>
366 std::enable_if_t<kMutable, reference> operator[](size_t index) {
367 return at(index);
368 }
369 const_reference operator[](size_t index) const { return at(index); }
371
383 template <bool kMutable = !is_const()>
384 constexpr std::enable_if_t<kMutable, v2::Chunks> Chunks() {
385 return generic().Chunks();
386 }
387 constexpr const v2::ConstChunks Chunks() const {
388 return generic().ConstChunks();
389 }
390 constexpr const v2::ConstChunks ConstChunks() const {
391 return generic().ConstChunks();
392 }
394
395 // Iterators.
396
407 template <bool kMutable = !is_const()>
408 constexpr std::enable_if_t<kMutable, iterator> begin() {
409 return generic().begin();
410 }
411 constexpr const_iterator begin() const { return cbegin(); }
412 constexpr const_iterator cbegin() const { return generic().cbegin(); }
414
425 template <bool kMutable = !is_const()>
426 constexpr std::enable_if_t<kMutable, iterator> end() {
427 return generic().end();
428 }
429 constexpr const_iterator end() const { return cend(); }
430 constexpr const_iterator cend() const { return generic().cend(); }
432
433 // Other methods
434
437 [[nodiscard]] bool TryReserveChunks(size_t num_chunks) {
438 return generic().TryReserveChunks(num_chunks);
439 }
440
441 // Mutators
442
454 template <Property... kOtherProperties>
455 [[nodiscard]] bool TryReserveForInsert(
457
467 [[nodiscard]] bool TryReserveForInsert(const_iterator pos);
468
478 template <Property... kOtherProperties>
480
490 template <
491 int&... kExplicitGuard,
492 typename T,
493 typename =
494 std::enable_if_t<std::is_constructible_v<ConstByteSpan, T>, int>>
495 iterator Insert(const_iterator pos, const T& bytes);
496
508 iterator Insert(const_iterator pos, UniquePtr<std::byte[]>&& bytes);
509 iterator Insert(const_iterator pos, UniquePtr<const std::byte[]>&& bytes);
511
523 iterator Insert(const_iterator pos, const SharedPtr<std::byte[]>& bytes) {
524 return Insert(pos, bytes, 0);
525 }
526 iterator Insert(const_iterator pos,
527 const SharedPtr<const std::byte[]>& bytes) {
528 return Insert(pos, bytes, 0);
529 }
531
545 iterator Insert(const_iterator pos,
546 const SharedPtr<std::byte[]>& bytes,
547 size_t offset,
548 size_t length = dynamic_extent);
549 iterator Insert(const_iterator pos,
550 const SharedPtr<const std::byte[]>& bytes,
551 size_t offset,
552 size_t length = dynamic_extent);
554
562 template <Property... kOtherProperties>
563 [[nodiscard]] bool TryReserveForPushBack(
565
571 [[nodiscard]] bool TryReserveForPushBack();
572
580 template <Property... kOtherProperties>
582
590 template <
591 int&... kExplicitGuard,
592 typename T,
593 typename =
594 std::enable_if_t<std::is_constructible_v<ConstByteSpan, T>, int>>
595 void PushBack(const T& bytes);
596
606 void PushBack(UniquePtr<std::byte[]>&& bytes);
607 void PushBack(UniquePtr<const std::byte[]>&& bytes);
609
619 void PushBack(const SharedPtr<std::byte[]>& bytes) { PushBack(bytes, 0); }
620 void PushBack(const SharedPtr<const std::byte[]>& bytes) {
621 PushBack(bytes, 0);
622 }
624
636 void PushBack(const SharedPtr<std::byte[]>& bytes,
637 size_t offset,
638 size_t length = dynamic_extent);
639 void PushBack(const SharedPtr<const std::byte[]>& bytes,
640 size_t offset,
641 size_t length = dynamic_extent);
643
651 [[nodiscard]] bool IsRemovable(const_iterator pos, size_t size) const {
652 return generic().IsRemovable(pos, size);
653 }
654
675
687
688 // clang-format off
710 // clang-format on
712 return generic().Discard(pos, size);
713 }
714
719 [[nodiscard]] bool IsReleasable(const_iterator pos) const {
720 return generic().IsReleasable(pos);
721 }
722
736
741 [[nodiscard]] bool IsShareable(const_iterator pos) const {
742 return generic().IsShareable(pos);
743 }
744
760
769 size_t CopyTo(ByteSpan dst, size_t offset = 0) const {
770 return generic().CopyTo(dst, offset);
771 }
772
781 size_t CopyFrom(ConstByteSpan src, size_t offset = 0) {
782 static_assert(!is_const(),
783 "`CopyFrom` may only be called on mutable MultiBufs");
784 return generic().CopyFrom(src, offset);
785 }
786
803 ConstByteSpan Get(ByteSpan copy, size_t offset = 0) const {
804 return generic().Get(copy, offset);
805 }
806
822 template <int&... kExplicitGuard, typename Visitor>
823 auto Visit(Visitor visitor, ByteSpan copy, size_t offset) {
824 return visitor(Get(copy, offset));
825 }
826
832 void Clear() { generic().Clear(); }
833
840 void ShrinkToFit() { generic().ShrinkToFit(); }
841
842 // Observable methods.
843
848 constexpr Observer* observer() const {
849 static_assert(is_observable(),
850 "`observer` may only be called on observable MultiBufs");
851 return generic().observer_;
852 }
853
861 static_assert(is_observable(),
862 "`set_observer` may only be called on observable MultiBufs");
863 generic().observer_ = observer;
864 }
865
866 // Layerable methods.
867
874 size_type NumFragments() const {
875 static_assert(is_layerable(),
876 "`NumFragments` may only be called on layerable MultiBufs");
877 return generic().NumFragments();
878 }
879
883 constexpr size_type NumLayers() const {
884 static_assert(is_layerable(),
885 "`NumLayers` may only be called on layerable MultiBufs");
886 return generic().num_layers();
887 }
888
896 [[nodiscard]] bool TryReserveLayers(size_t num_layers,
897 size_t num_chunks = 1) {
898 return generic().TryReserveLayers(num_layers, num_chunks);
899 }
900
920 [[nodiscard]] bool AddLayer(size_t offset, size_t length = dynamic_extent) {
921 static_assert(is_layerable(),
922 "`AddLayer` may only be called on layerable MultiBufs");
923 return generic().AddLayer(offset, length);
924 }
925
929 static_assert(is_layerable(),
930 "`SealTopLayer` may only be called on layerable MultiBufs");
931 return generic().SealTopLayer();
932 }
933
937 static_assert(is_layerable(),
938 "`UnsealTopLayer` may only be called on layerable MultiBufs");
939 return generic().UnsealTopLayer();
940 }
941
943 [[nodiscard]] bool IsTopLayerSealed() {
944 static_assert(
945 is_layerable(),
946 "`IsTopLayerSealed` may only be called on layerable MultiBufs");
947 return generic().IsTopLayerSealed();
948 }
949
958 void TruncateTopLayer(size_t length) {
959 static_assert(
960 is_layerable(),
961 "`TruncateTopLayer` may only be called on layerable MultiBufs");
962 generic().TruncateTopLayer(length);
963 }
964
972 static_assert(!is_const() && is_layerable(),
973 "`SetTopLayer` may only be called on mutable, layerable "
974 "MultiBufs");
975 PW_ASSERT(src.size() <= size());
976 CopyFrom(src);
977 TruncateTopLayer(src.size());
978 }
979
988 void PopLayer() {
989 static_assert(is_layerable(),
990 "`PopLayer` may only be called on layerable MultiBufs");
991 generic().PopLayer();
992 }
993
994 protected:
995 constexpr BasicMultiBuf() { internal::PropertiesAreValid(); }
996
997 private:
998 template <v2::Property...>
999 friend class v2::BasicMultiBuf;
1000
1001 friend class v1_adapter::MultiBuf;
1002 friend class v1_adapter::MultiBufChunks;
1003
1004 template <typename OtherMultiBuf, typename MultiBufType>
1005 friend constexpr OtherMultiBuf& internal::ConstCast(const MultiBufType& mb);
1006
1007 template <bool kValidCopyOrMove = false>
1008 static constexpr void InvalidCopyOrMove() {
1009 static_assert(kValidCopyOrMove,
1010 "Only copies and moves from `BasicMultiBuf<...>::Instance`"
1011 "to `BasicMultiBuf<...>&` or another "
1012 "`BasicMultiBuf<...>::Instance` are valid.");
1013 }
1014
1015 template <Property...>
1016 friend class BasicMultiBuf;
1017
1018 constexpr GenericMultiBuf& generic() {
1019 return static_cast<GenericMultiBuf&>(*this);
1020 }
1021 constexpr const GenericMultiBuf& generic() const {
1022 return static_cast<const GenericMultiBuf&>(*this);
1023 }
1024};
1025
1027
1028namespace internal {
1029
1030template <typename OtherMultiBuf, typename MultiBufType>
1031constexpr OtherMultiBuf& ConstCast(const MultiBufType& mb) {
1032 AssertIsConvertibleIgnoreConst<MultiBufType, OtherMultiBuf>();
1033 return const_cast<OtherMultiBuf&>(mb.generic().template as<OtherMultiBuf>());
1034}
1035
1036// A generic MultiBuf implementation that provides the functionality of any
1037// `BasicMultiBuf<kProperties>` type.
1038//
1039// This class should not be instantiated directly. Instead, use `Instance` as
1040// described below. This is the base class for all `Instance` types, and derives
1041// from every supported `BasicMultiBuf` type. It implements the MultiBuf
1042// behavior in one type, and allows for performing conversions from `Instance`s
1043// and `BasicMultiBuf`s to `BasicMultiBuf`s with different yet compatible
1044// propreties.
1046 : private BasicMultiBuf<>,
1056 private:
1057 using ControlBlock = allocator::internal::ControlBlock;
1058 using ControlBlockHandle = allocator::internal::ControlBlockHandle;
1059 using typename BasicMultiBuf<>::Deque;
1060
1061 public:
1062 using typename BasicMultiBuf<>::const_iterator;
1063 using typename BasicMultiBuf<>::const_pointer;
1064 using typename BasicMultiBuf<>::const_reference;
1065 using typename BasicMultiBuf<>::difference_type;
1066 using typename BasicMultiBuf<>::iterator;
1067 using typename BasicMultiBuf<>::pointer;
1068 using typename BasicMultiBuf<>::reference;
1069 using typename BasicMultiBuf<>::size_type;
1070
1071 ~GenericMultiBuf() { Clear(); }
1072
1073 // Not copyable.
1074 GenericMultiBuf(const GenericMultiBuf&) = delete;
1075 GenericMultiBuf& operator=(const GenericMultiBuf&) = delete;
1076
1077 // Movable.
1079 : GenericMultiBuf(other.deque_.get_allocator()) {
1080 *this = std::move(other);
1081 }
1082 GenericMultiBuf& operator=(GenericMultiBuf&& other);
1083
1084 private:
1085 template <v2::Property...>
1086 friend class v2::BasicMultiBuf;
1087
1088 friend class v1_adapter::MultiBuf;
1089 friend class v1_adapter::MultiBufChunks;
1090
1091 template <typename>
1092 friend class Instance;
1093
1094 template <typename OtherMultiBuf, typename MultiBufType>
1095 friend constexpr OtherMultiBuf& ConstCast(const MultiBufType& mb);
1096
1098 constexpr explicit GenericMultiBuf(Allocator& allocator)
1099 : deque_(allocator) {}
1100
1101 template <typename MultiBufType>
1102 constexpr MultiBufType& as() {
1103 return *this;
1104 }
1105
1106 template <typename MultiBufType>
1107 constexpr const MultiBufType& as() const {
1108 return *this;
1109 }
1110
1111 // Accessors.
1112
1114 constexpr Allocator& get_allocator() const { return deque_.get_allocator(); }
1115
1117 constexpr bool empty() const { return deque_.empty(); }
1118
1120 constexpr bool at_capacity() const {
1121 return deque_.size() == deque_.capacity();
1122 }
1123
1125 constexpr size_t size() const {
1126 return static_cast<size_t>(cend() - cbegin());
1127 }
1128
1129 // Iterators.
1130
1131 constexpr v2::Chunks Chunks() {
1132 return v2::Chunks(deque_, entries_per_chunk_);
1133 }
1134 constexpr const v2::ConstChunks ConstChunks() const {
1135 return v2::ConstChunks(deque_, entries_per_chunk_);
1136 }
1137
1138 constexpr iterator begin() { return MakeIterator(0); }
1139 constexpr const_iterator cbegin() const { return MakeIterator(0); }
1140 constexpr iterator end() { return MakeIterator(num_chunks()); }
1141 constexpr const_iterator cend() const { return MakeIterator(num_chunks()); }
1142
1143 // Mutators.
1144
1146 [[nodiscard]] bool TryReserveChunks(size_t num_chunks) {
1147 return TryReserveLayers(num_layers(), num_chunks);
1148 }
1149
1155 [[nodiscard]] bool TryReserveForInsert(const_iterator pos,
1156 const GenericMultiBuf& mb);
1157
1162 [[nodiscard]] bool TryReserveForInsert(const_iterator pos);
1163
1173 iterator Insert(const_iterator pos, GenericMultiBuf&& mb);
1174
1184 iterator Insert(const_iterator pos, ConstByteSpan bytes);
1185
1195 iterator Insert(const_iterator pos, UniquePtr<const std::byte[]>&& owned);
1196
1206 iterator Insert(const_iterator pos, UniquePtr<std::byte[]>&& owned) {
1207 return Insert(pos, UniquePtr<const std::byte[]>(std::move(owned)));
1208 }
1209
1221 iterator Insert(const_iterator pos,
1222 const SharedPtr<const std::byte[]>& shared,
1223 size_t offset,
1224 size_t length);
1225
1237 iterator Insert(const_iterator pos,
1238 const SharedPtr<std::byte[]>& shared,
1239 size_t offset,
1240 size_t length) {
1241 return Insert(pos, SharedPtr<const std::byte[]>(shared), offset, length);
1242 }
1243
1245 [[nodiscard]] constexpr bool IsRemovable(const_iterator pos,
1246 size_t size) const {
1247 return pos != cend() && size != 0 &&
1248 size <= static_cast<size_t>(cend() - pos);
1249 }
1250
1252 Result<GenericMultiBuf> Remove(const_iterator pos, size_t size);
1253
1255 Result<GenericMultiBuf> PopFrontFragment();
1256
1258 Result<const_iterator> Discard(const_iterator pos, size_t size);
1259
1261 [[nodiscard]] bool IsReleasable(const_iterator pos) const;
1262
1265
1267 [[nodiscard]] bool IsShareable(const_iterator pos) const;
1268
1270 SharedPtr<std::byte[]> Share(const_iterator pos) const;
1271
1273 size_t CopyTo(ByteSpan dst, size_t offset) const {
1274 return CopyToImpl(dst, offset, 0);
1275 }
1276
1278 size_t CopyFrom(ConstByteSpan src, size_t offset);
1279
1281 ConstByteSpan Get(ByteSpan copy, size_t offset) const;
1282
1284 void Clear();
1285
1287 void ShrinkToFit();
1288
1289 // Layerable methods.
1290
1292 size_type NumFragments() const;
1293
1295 [[nodiscard]] bool TryReserveLayers(size_t num_layers, size_t num_chunks = 1);
1296
1298 [[nodiscard]] bool AddLayer(size_t offset, size_t length = dynamic_extent);
1299
1301 void SealTopLayer();
1302
1304 void UnsealTopLayer();
1305
1307 void TruncateTopLayer(size_t length);
1308
1310 void PopLayer();
1311
1312 // Implementation methods.
1313 //
1314 // These methods are used to implement the methods above, and should not be
1315 // called directly by BasicMultiBuf<>.
1316
1321 static size_t CheckRange(size_t offset, size_t length, size_t size);
1322
1324 constexpr size_type num_chunks() const {
1325 return Entry::num_chunks(deque_, entries_per_chunk_);
1326 }
1327
1329 constexpr size_type num_layers() const {
1330 return Entry::num_layers(entries_per_chunk_);
1331 }
1332
1334 constexpr size_type memory_context_index(size_type chunk) const {
1335 return Entry::memory_context_index(chunk, entries_per_chunk_);
1336 }
1337
1339 constexpr size_type data_index(size_type chunk) const {
1340 return Entry::data_index(chunk, entries_per_chunk_);
1341 }
1342
1344 constexpr size_type base_view_index(size_type chunk) const {
1345 return Entry::base_view_index(chunk, entries_per_chunk_);
1346 }
1347
1349 constexpr size_type view_index(size_type chunk, size_type layer) const {
1350 return Entry::view_index(chunk, entries_per_chunk_, layer);
1351 }
1352
1354 constexpr size_type top_view_index(size_type chunk) const {
1355 return Entry::top_view_index(chunk, entries_per_chunk_);
1356 }
1357
1359 constexpr std::byte* GetData(size_type chunk) const {
1360 return Entry::GetData(deque_, chunk, entries_per_chunk_);
1361 }
1362
1364 constexpr bool IsOwned(size_type chunk) const {
1365 return Entry::IsOwned(deque_, chunk, entries_per_chunk_);
1366 }
1367
1369 constexpr bool IsShared(size_type chunk) const {
1370 return Entry::IsShared(deque_, chunk, entries_per_chunk_);
1371 }
1372
1374 constexpr bool IsSealed(size_type chunk) const {
1375 return Entry::IsSealed(deque_, chunk, entries_per_chunk_);
1376 }
1377
1380 constexpr bool IsBoundary(size_type chunk, size_type layer) const {
1381 return Entry::IsBoundary(deque_, chunk, entries_per_chunk_, layer);
1382 }
1383 constexpr bool IsBoundary(size_type chunk) const {
1384 return Entry::IsBoundary(deque_, chunk, entries_per_chunk_);
1385 }
1387
1389 constexpr Deallocator& GetDeallocator(size_type chunk) const {
1390 return Entry::GetDeallocator(deque_, chunk, entries_per_chunk_);
1391 }
1392
1394 constexpr ControlBlock& GetControlBlock(size_type chunk) const {
1395 return Entry::GetControlBlock(deque_, chunk, entries_per_chunk_);
1396 }
1397
1400 constexpr size_type GetOffset(size_type chunk, size_type layer) const {
1401 return Entry::GetOffset(deque_, chunk, entries_per_chunk_, layer);
1402 }
1403 constexpr size_type GetOffset(size_type chunk) const {
1404 return Entry::GetOffset(deque_, chunk, entries_per_chunk_);
1405 }
1407
1410 constexpr size_type GetRelativeOffset(size_type chunk,
1411 size_type layer) const {
1412 return Entry::GetRelativeOffset(deque_, chunk, entries_per_chunk_, layer);
1413 }
1414 constexpr size_type GetRelativeOffset(size_type chunk) const {
1415 return Entry::GetRelativeOffset(deque_, chunk, entries_per_chunk_);
1416 }
1418
1421 constexpr size_type GetLength(size_type chunk, size_type layer) const {
1422 return Entry::GetLength(deque_, chunk, entries_per_chunk_, layer);
1423 }
1424 constexpr size_type GetLength(size_type chunk) const {
1425 return Entry::GetLength(deque_, chunk, entries_per_chunk_);
1426 }
1428
1431 constexpr ByteSpan GetView(size_type chunk, size_type layer) const {
1432 return Entry::GetView(deque_, chunk, entries_per_chunk_, layer);
1433 }
1434 constexpr ByteSpan GetView(size_type chunk) const {
1435 return Entry::GetView(deque_, chunk, entries_per_chunk_);
1436 }
1438
1440 constexpr iterator MakeIterator(size_type chunk, size_type offset = 0) const {
1441 return iterator(deque_, chunk, entries_per_chunk_, 0) + offset;
1442 }
1443
1446 [[nodiscard]] bool TryConvertToShared(size_type chunk);
1447
1452 [[nodiscard]] bool TryReserveEntries(size_type num_entries,
1453 bool split = false);
1454
1457 size_type InsertChunks(const_iterator pos, size_type num_chunks);
1458
1462 std::tuple<iterator, size_type> Insert(const_iterator pos,
1463 ConstByteSpan bytes,
1464 size_t offset,
1465 size_t length);
1466
1476 void SplitBase(size_type chunk, Deque& out_deque, size_type out_chunk);
1477
1483 void SplitBefore(size_type chunk,
1484 size_type split,
1485 Deque& out_deque,
1486 size_type out_chunk);
1487
1490 void SplitBefore(size_type chunk, size_type split);
1491
1497 void SplitAfter(size_type chunk,
1498 size_type split,
1499 Deque& out_deque,
1500 size_type out_chunk);
1501
1504 void SplitAfter(size_type chunk, size_type split);
1505
1516 [[nodiscard]] bool TryReserveForRemove(const_iterator pos,
1517 size_t size,
1518 GenericMultiBuf* out);
1519
1524 void MoveRange(const_iterator pos, size_t size, GenericMultiBuf& out);
1525
1529 void ClearRange(const_iterator pos, size_t size);
1530
1535 void EraseRange(const_iterator pos, size_t size);
1536
1539 size_t CopyToImpl(ByteSpan dst, size_t offset, size_type start) const;
1540
1542 [[nodiscard]] bool IsTopLayerSealed() const;
1543
1544 // Describes the memory and views to that in a one-dimensional sequence.
1545 // Every `entries_per_chunk_`-th entry holds a pointer to memory, each entry
1546 // for a given offset less than `entries_per_chunk_` after that entry is a
1547 // view that is part of the same "layer" in the MultiBuf.
1548 //
1549 // This base type will always have 0 or 2 layers, but derived classes may add
1550 // more.
1551 //
1552 // For example, a MultiBuf that has had 3 `Chunk`s and two additional layers
1553 // added would have a `deque_` resembling the following:
1554 //
1555 // buffer 0: buffer 1: buffer 2:
1556 // layer 3: deque_[0x3].view deque_[0x7].view deque_[0xB].view
1557 // layer 2: deque_[0x2].view deque_[0x6].view deque_[0xA].view
1558 // layer 1: deque_[0x1].view deque_[0x5].view deque_[0x9].view
1559 // layer 0: deque_[0x0].data deque_[0x4].data deque_[0x8].data
1560 Deque deque_;
1561
1562 // Number of entries per chunk in this MultiBuf.
1563 size_type entries_per_chunk_ = Entry::kMinEntriesPerChunk;
1564
1567 Observer* observer_ = nullptr;
1568};
1569
1588template <typename MultiBufType>
1590 public:
1591 constexpr explicit Instance(Allocator& allocator) : base_(allocator) {}
1592
1593 constexpr Instance(Instance&&) = default;
1594 constexpr Instance& operator=(Instance&&) = default;
1595
1596 // Provide a more helpful compile-time error when a user tries to
1597 // copy-construct an interface type.
1598 template <Property... kProperties>
1599 constexpr Instance(const BasicMultiBuf<kProperties...>&)
1600 : base_(allocator::GetNullAllocator()) {
1601 MoveOnly<>();
1602 }
1603
1604 // Provide a more helpful compile-time error when a user tries to copy-assign
1605 // an interface type.
1606 template <Property... kProperties>
1607 constexpr Instance& operator=(const BasicMultiBuf<kProperties...>&) {
1608 MoveOnly<>();
1609 }
1610
1611 constexpr Instance(MultiBufType&& mb)
1612 : base_(std::move(static_cast<GenericMultiBuf&>(mb))) {}
1613
1614 constexpr Instance& operator=(MultiBufType&& mb) {
1615 base_ = std::move(static_cast<GenericMultiBuf&>(mb));
1616 return *this;
1617 }
1618
1619 template <Property... kProperties>
1621 : base_(std::move(static_cast<internal::GenericMultiBuf&>(mb))) {
1622 internal::AssertIsAssignable<BasicMultiBuf<kProperties...>, MultiBufType>();
1623 }
1624
1625 template <Property... kProperties>
1626 constexpr Instance& operator=(BasicMultiBuf<kProperties...>&& mb) {
1627 internal::AssertIsAssignable<BasicMultiBuf<kProperties...>, MultiBufType>();
1628 base_ = std::move(static_cast<internal::GenericMultiBuf&>(mb));
1629 return *this;
1630 }
1631
1632 constexpr MultiBufType* operator->() { return &base_.as<MultiBufType>(); }
1633 constexpr const MultiBufType* operator->() const {
1634 return &base_.as<MultiBufType>();
1635 }
1636
1637 constexpr MultiBufType& operator*() & { return base_.as<MultiBufType>(); }
1638 constexpr const MultiBufType& operator*() const& {
1639 return base_.as<MultiBufType>();
1640 }
1641
1642 constexpr MultiBufType&& operator*() && {
1643 return std::move(base_.as<MultiBufType>());
1644 }
1645 constexpr const MultiBufType&& operator*() const&& {
1646 return std::move(base_.as<MultiBufType>());
1647 }
1648
1649 constexpr operator MultiBufType&() & { return base_.as<MultiBufType>(); }
1650 constexpr operator const MultiBufType&() const& {
1651 return base_.as<MultiBufType>();
1652 }
1653
1654 constexpr operator MultiBufType&&() && {
1655 return std::move(base_.as<MultiBufType>());
1656 }
1657 constexpr operator const MultiBufType&&() const&& {
1658 return std::move(base_.as<MultiBufType>());
1659 }
1660
1661 private:
1662 // Helper functions to provide a more helpful compile-time error when a this
1663 // type is used incorrectly.
1664 template <bool kMoveOnly = false>
1665 static constexpr void MoveOnly() {
1666 static_assert(kMoveOnly,
1667 "Instances can only be created from existing MultiBufs using "
1668 "move-construction or move-assignment.");
1669 }
1670
1671 GenericMultiBuf base_;
1672};
1673
1674} // namespace internal
1675
1676// Template method implementations.
1677
1678template <Property... kProperties>
1680 return generic().TryReserveForInsert(pos);
1681}
1682
1683template <Property... kProperties>
1684template <Property... kOtherProperties>
1687 internal::AssertIsConvertible<BasicMultiBuf<kOtherProperties...>,
1688 BasicMultiBuf>();
1689 return generic().TryReserveForInsert(pos,
1690 static_cast<const GenericMultiBuf&>(mb));
1691}
1692
1693template <Property... kProperties>
1694template <Property... kOtherProperties>
1696 const_iterator pos, BasicMultiBuf<kOtherProperties...>&& mb) -> iterator {
1697 internal::AssertIsConvertible<BasicMultiBuf<kOtherProperties...>,
1698 BasicMultiBuf>();
1699 return generic().Insert(pos, std::move(mb.generic()));
1700}
1701
1702template <Property... kProperties>
1703template <int&... kExplicitGuard, typename T, typename>
1704auto BasicMultiBuf<kProperties...>::Insert(const_iterator pos, const T& bytes)
1705 -> iterator {
1706 using data_ptr_type = decltype(std::data(std::declval<T&>()));
1707 static_assert(std::is_same_v<data_ptr_type, std::byte*> || is_const(),
1708 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1709 return generic().Insert(pos, bytes);
1710}
1711
1712template <Property... kProperties>
1713auto BasicMultiBuf<kProperties...>::Insert(const_iterator pos,
1714 UniquePtr<std::byte[]>&& bytes)
1715 -> iterator {
1716 return generic().Insert(pos, std::move(bytes));
1717}
1718
1719template <Property... kProperties>
1720auto BasicMultiBuf<kProperties...>::Insert(const_iterator pos,
1721 UniquePtr<const std::byte[]>&& bytes)
1722 -> iterator {
1723 static_assert(is_const(),
1724 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1725 return generic().Insert(pos, std::move(bytes));
1726}
1727
1728template <Property... kProperties>
1729auto BasicMultiBuf<kProperties...>::Insert(const_iterator pos,
1730 const SharedPtr<std::byte[]>& bytes,
1731 size_t offset,
1732 size_t length) -> iterator {
1733 return generic().Insert(pos, bytes, offset, length);
1734}
1735
1736template <Property... kProperties>
1738 const_iterator pos,
1739 const SharedPtr<const std::byte[]>& bytes,
1740 size_t offset,
1741 size_t length) -> iterator {
1742 static_assert(is_const(),
1743 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1744 return generic().Insert(pos, bytes, offset, length);
1745}
1746
1747template <Property... kProperties>
1748template <Property... kOtherProperties>
1751 return generic().TryReserveForInsert(end(),
1752 static_cast<const GenericMultiBuf&>(mb));
1753}
1754
1755template <Property... kProperties>
1757 return generic().TryReserveForInsert(end());
1758}
1759
1760template <Property... kProperties>
1761template <Property... kOtherProperties>
1764 Insert(end(), std::move(mb));
1765}
1766
1767template <Property... kProperties>
1768template <int&... kExplicitGuard, typename T, typename>
1770 using data_ptr_type = decltype(std::data(std::declval<T&>()));
1771 static_assert(std::is_same_v<data_ptr_type, std::byte*> || is_const(),
1772 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1773 Insert(end(), bytes);
1774}
1775
1776template <Property... kProperties>
1777void BasicMultiBuf<kProperties...>::PushBack(UniquePtr<std::byte[]>&& bytes) {
1778 Insert(end(), std::move(bytes));
1779}
1780
1781template <Property... kProperties>
1783 UniquePtr<const std::byte[]>&& bytes) {
1784 static_assert(is_const(),
1785 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1786 Insert(end(), std::move(bytes));
1787}
1788
1789template <Property... kProperties>
1791 const SharedPtr<std::byte[]>& bytes, size_t offset, size_t length) {
1792 Insert(end(), bytes, offset, length);
1793}
1794
1795template <Property... kProperties>
1797 const SharedPtr<const std::byte[]>& bytes, size_t offset, size_t length) {
1798 static_assert(is_const(),
1799 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1800 Insert(end(), bytes, offset, length);
1801}
1802
1803template <Property... kProperties>
1804Result<internal::Instance<BasicMultiBuf<kProperties...>>>
1806 auto result = generic().Remove(pos, size);
1807 if (!result.ok()) {
1808 return result.status();
1809 }
1810 return Instance(std::move(*result));
1811}
1812
1813template <Property... kProperties>
1814Result<internal::Instance<BasicMultiBuf<kProperties...>>>
1816 auto result = generic().PopFrontFragment();
1817 if (!result.ok()) {
1818 return result.status();
1819 }
1820 return Instance(std::move(*result));
1821}
1822
1823template <Property... kProperties>
1824UniquePtr<typename BasicMultiBuf<kProperties...>::value_type[]>
1826 UniquePtr<std::byte[]> bytes = generic().Release(pos);
1827 if constexpr (is_const()) {
1828 UniquePtr<const std::byte[]> const_bytes(
1829 bytes.get(), bytes.size(), *(bytes.deallocator()));
1830 bytes.Release();
1831 return const_bytes;
1832 } else {
1833 return bytes;
1834 }
1835}
1836
1837template <Property... kProperties>
1838SharedPtr<typename BasicMultiBuf<kProperties...>::value_type[]>
1840 return SharedPtr<value_type[]>(generic().Share(pos));
1841}
1842
1843} // namespace v2
1844} // namespace pw::multibuf
Definition: allocator.h:42
Abstract interface for releasing memory.
Definition: deallocator.h:30
Definition: dynamic_deque.h:73
Definition: result.h:145
Definition: shared_ptr.h:64
Definition: unique_ptr.h:44
element_type * Release() noexcept
Definition: unique_ptr.h:266
Deallocator * deallocator() const
Returns a pointer to the object that can destroy the value.
Definition: unique_ptr.h:164
size_t size() const
Definition: unique_ptr.h:244
Definition: multibuf.h:213
constexpr size_t size() const
Definition: multibuf.h:344
bool IsReleasable(const_iterator pos) const
Definition: multibuf.h:719
static constexpr bool is_observable()
Returns whether an observer can be registered on the MultiBuf.
Definition: multibuf.h:211
constexpr size_type NumLayers() const
Definition: multibuf.h:883
void PopLayer()
Definition: multibuf.h:988
bool IsTopLayerSealed()
Returns whether the "sealed" flag is set in the top layer.
Definition: multibuf.h:943
void ShrinkToFit()
Definition: multibuf.h:840
iterator Insert(const_iterator pos, BasicMultiBuf< kOtherProperties... > &&mb)
void PushBack(const T &bytes)
Definition: multibuf.h:1769
void PushBack(BasicMultiBuf< kOtherProperties... > &&mb)
Definition: multibuf.h:1762
static constexpr bool is_layerable()
Returns whether additional views can be layered on the MultiBuf.
Definition: multibuf.h:206
iterator Insert(const_iterator pos, const T &bytes)
void SealTopLayer()
Definition: multibuf.h:928
Result< Instance > PopFrontFragment()
Definition: multibuf.h:1815
Result< const_iterator > Discard(const_iterator pos, size_t size)
Definition: multibuf.h:711
constexpr bool empty() const
Definition: multibuf.h:334
void TruncateTopLayer(size_t length)
Definition: multibuf.h:958
ConstByteSpan Get(ByteSpan copy, size_t offset=0) const
Definition: multibuf.h:803
void Clear()
Definition: multibuf.h:832
bool TryReserveForInsert(const_iterator pos, const BasicMultiBuf< kOtherProperties... > &mb)
Definition: multibuf.h:1685
bool IsRemovable(const_iterator pos, size_t size) const
Definition: multibuf.h:651
constexpr Allocator & get_allocator() const
Returns the allocator used to store metadata.
Definition: multibuf.h:328
void SetTopLayer(ConstByteSpan src)
Definition: multibuf.h:971
bool TryReserveChunks(size_t num_chunks)
Definition: multibuf.h:437
constexpr Observer * observer() const
Definition: multibuf.h:848
void set_observer(Observer *observer)
Definition: multibuf.h:860
void UnsealTopLayer()
Definition: multibuf.h:936
SharedPtr< value_type[]> Share(const_iterator pos) const
Definition: multibuf.h:1839
bool IsShareable(const_iterator pos) const
Definition: multibuf.h:741
Result< Instance > Remove(const_iterator pos, size_t size)
Definition: multibuf.h:1805
bool TryReserveLayers(size_t num_layers, size_t num_chunks=1)
Definition: multibuf.h:896
bool TryReserveForInsert(const_iterator pos)
Definition: multibuf.h:1679
size_type NumFragments() const
Definition: multibuf.h:874
size_t CopyFrom(ConstByteSpan src, size_t offset=0)
Definition: multibuf.h:781
bool TryReserveForPushBack()
Definition: multibuf.h:1756
bool AddLayer(size_t offset, size_t length=dynamic_extent)
Definition: multibuf.h:920
static constexpr bool is_const()
Returns whether the MultiBuf data is immutable.
Definition: multibuf.h:201
size_t CopyTo(ByteSpan dst, size_t offset=0) const
Definition: multibuf.h:769
bool TryReserveForPushBack(const BasicMultiBuf< kOtherProperties... > &mb)
Definition: multibuf.h:1749
auto Visit(Visitor visitor, ByteSpan copy, size_t offset)
Definition: multibuf.h:823
constexpr bool at_capacity() const
Definition: multibuf.h:340
UniquePtr< value_type[]> Release(const_iterator pos)
Definition: multibuf.h:1825
Definition: observer.h:29
Byte iterator templated on the const-ness of the bytes it references.
Definition: byte_iterator.h:85
Definition: multibuf.h:1589
NullAllocator & GetNullAllocator()
Returns a reference to a 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:24
BasicMultiBuf< Property::kLayerable > MultiBuf
Definition: multibuf.h:62
Result(T value) -> Result< T >
Deduction guide to allow Result(v) rather than Result<T>(v).