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
1029
1030namespace internal {
1031
1032template <typename OtherMultiBuf, typename MultiBufType>
1033constexpr OtherMultiBuf& ConstCast(const MultiBufType& mb) {
1034 AssertIsConvertibleIgnoreConst<MultiBufType, OtherMultiBuf>();
1035 return const_cast<OtherMultiBuf&>(mb.generic().template as<OtherMultiBuf>());
1036}
1037
1038// A generic MultiBuf implementation that provides the functionality of any
1039// `BasicMultiBuf<kProperties>` type.
1040//
1041// This class should not be instantiated directly. Instead, use `Instance` as
1042// described below. This is the base class for all `Instance` types, and derives
1043// from every supported `BasicMultiBuf` type. It implements the MultiBuf
1044// behavior in one type, and allows for performing conversions from `Instance`s
1045// and `BasicMultiBuf`s to `BasicMultiBuf`s with different yet compatible
1046// propreties.
1048 : private BasicMultiBuf<>,
1058 private:
1059 using ControlBlock = allocator::internal::ControlBlock;
1060 using ControlBlockHandle = allocator::internal::ControlBlockHandle;
1061 using typename BasicMultiBuf<>::Deque;
1062
1063 public:
1064 using typename BasicMultiBuf<>::const_iterator;
1065 using typename BasicMultiBuf<>::const_pointer;
1066 using typename BasicMultiBuf<>::const_reference;
1067 using typename BasicMultiBuf<>::difference_type;
1068 using typename BasicMultiBuf<>::iterator;
1069 using typename BasicMultiBuf<>::pointer;
1070 using typename BasicMultiBuf<>::reference;
1071 using typename BasicMultiBuf<>::size_type;
1072
1073 ~GenericMultiBuf() { Clear(); }
1074
1075 // Not copyable.
1076 GenericMultiBuf(const GenericMultiBuf&) = delete;
1077 GenericMultiBuf& operator=(const GenericMultiBuf&) = delete;
1078
1079 // Movable.
1081 : GenericMultiBuf(other.deque_.get_allocator()) {
1082 *this = std::move(other);
1083 }
1084 GenericMultiBuf& operator=(GenericMultiBuf&& other);
1085
1086 private:
1087 template <v2::Property...>
1088 friend class v2::BasicMultiBuf;
1089
1090 friend class v1_adapter::MultiBuf;
1091 friend class v1_adapter::MultiBufChunks;
1092
1093 template <typename>
1094 friend class Instance;
1095
1096 template <typename OtherMultiBuf, typename MultiBufType>
1097 friend constexpr OtherMultiBuf& ConstCast(const MultiBufType& mb);
1098
1100 constexpr explicit GenericMultiBuf(Allocator& allocator)
1101 : deque_(allocator) {}
1102
1103 template <typename MultiBufType>
1104 constexpr MultiBufType& as() {
1105 return *this;
1106 }
1107
1108 template <typename MultiBufType>
1109 constexpr const MultiBufType& as() const {
1110 return *this;
1111 }
1112
1113 // Accessors.
1114
1116 constexpr Allocator& get_allocator() const { return deque_.get_allocator(); }
1117
1119 constexpr bool empty() const { return deque_.empty(); }
1120
1122 constexpr bool at_capacity() const {
1123 return deque_.size() == deque_.capacity();
1124 }
1125
1127 constexpr size_t size() const {
1128 return static_cast<size_t>(cend() - cbegin());
1129 }
1130
1131 // Iterators.
1132
1133 constexpr v2::Chunks Chunks() {
1134 return v2::Chunks(deque_, entries_per_chunk_);
1135 }
1136 constexpr const v2::ConstChunks ConstChunks() const {
1137 return v2::ConstChunks(deque_, entries_per_chunk_);
1138 }
1139
1140 constexpr iterator begin() { return MakeIterator(0); }
1141 constexpr const_iterator cbegin() const { return MakeIterator(0); }
1142 constexpr iterator end() { return MakeIterator(num_chunks()); }
1143 constexpr const_iterator cend() const { return MakeIterator(num_chunks()); }
1144
1145 // Mutators.
1146
1148 [[nodiscard]] bool TryReserveChunks(size_t num_chunks) {
1149 return TryReserveLayers(num_layers(), num_chunks);
1150 }
1151
1154 [[nodiscard]] bool TryReserveForInsert(const_iterator pos,
1155 const GenericMultiBuf& mb);
1156 [[nodiscard]] bool TryReserveForInsert(const_iterator pos);
1158
1161 iterator Insert(const_iterator pos, GenericMultiBuf&& mb);
1162 iterator Insert(const_iterator pos, ConstByteSpan bytes);
1163 iterator Insert(const_iterator pos, UniquePtr<const std::byte[]>&& owned);
1164 iterator Insert(const_iterator pos, UniquePtr<std::byte[]>&& owned) {
1165 return Insert(pos, UniquePtr<const std::byte[]>(std::move(owned)));
1166 }
1167 iterator Insert(const_iterator pos,
1168 const SharedPtr<const std::byte[]>& shared,
1169 size_t offset,
1170 size_t length);
1171 iterator Insert(const_iterator pos,
1172 const SharedPtr<std::byte[]>& shared,
1173 size_t offset,
1174 size_t length) {
1175 return Insert(pos, SharedPtr<const std::byte[]>(shared), offset, length);
1176 }
1177
1179 [[nodiscard]] constexpr bool IsRemovable(const_iterator pos,
1180 size_t size) const {
1181 return pos != cend() && size != 0 &&
1182 size <= static_cast<size_t>(cend() - pos);
1183 }
1184
1186 Result<GenericMultiBuf> Remove(const_iterator pos, size_t size);
1187
1189 Result<GenericMultiBuf> PopFrontFragment();
1190
1192 Result<const_iterator> Discard(const_iterator pos, size_t size);
1193
1195 [[nodiscard]] bool IsReleasable(const_iterator pos) const;
1196
1199
1201 [[nodiscard]] bool IsShareable(const_iterator pos) const;
1202
1204 SharedPtr<std::byte[]> Share(const_iterator pos) const;
1205
1207 size_t CopyTo(ByteSpan dst, size_t offset) const {
1208 return CopyToImpl(dst, offset, 0);
1209 }
1210
1212 size_t CopyFrom(ConstByteSpan src, size_t offset);
1213
1215 ConstByteSpan Get(ByteSpan copy, size_t offset) const;
1216
1218 void Clear();
1219
1221 void ShrinkToFit();
1222
1223 // Layerable methods.
1224
1226 size_type NumFragments() const;
1227
1229 [[nodiscard]] bool TryReserveLayers(size_t num_layers, size_t num_chunks = 1);
1230
1232 [[nodiscard]] bool AddLayer(size_t offset, size_t length = dynamic_extent);
1233
1235 void SealTopLayer();
1236
1238 void UnsealTopLayer();
1239
1241 void TruncateTopLayer(size_t length);
1242
1244 void PopLayer();
1245
1246 // Implementation methods.
1247 //
1248 // These methods are used to implement the methods above, and should not be
1249 // called directly by BasicMultiBuf<>.
1250
1255 static size_t CheckRange(size_t offset, size_t length, size_t size);
1256
1258 constexpr size_type num_chunks() const {
1259 return Entry::num_chunks(deque_, entries_per_chunk_);
1260 }
1261
1263 constexpr size_type num_layers() const {
1264 return Entry::num_layers(entries_per_chunk_);
1265 }
1266
1268 constexpr size_type memory_context_index(size_type chunk) const {
1269 return Entry::memory_context_index(chunk, entries_per_chunk_);
1270 }
1271
1273 constexpr size_type data_index(size_type chunk) const {
1274 return Entry::data_index(chunk, entries_per_chunk_);
1275 }
1276
1278 constexpr size_type base_view_index(size_type chunk) const {
1279 return Entry::base_view_index(chunk, entries_per_chunk_);
1280 }
1281
1283 constexpr size_type view_index(size_type chunk, size_type layer) const {
1284 return Entry::view_index(chunk, entries_per_chunk_, layer);
1285 }
1286
1288 constexpr size_type top_view_index(size_type chunk) const {
1289 return Entry::top_view_index(chunk, entries_per_chunk_);
1290 }
1291
1293 constexpr std::byte* GetData(size_type chunk) const {
1294 return Entry::GetData(deque_, chunk, entries_per_chunk_);
1295 }
1296
1298 constexpr bool IsOwned(size_type chunk) const {
1299 return Entry::IsOwned(deque_, chunk, entries_per_chunk_);
1300 }
1301
1303 constexpr bool IsShared(size_type chunk) const {
1304 return Entry::IsShared(deque_, chunk, entries_per_chunk_);
1305 }
1306
1308 constexpr bool IsSealed(size_type chunk) const {
1309 return Entry::IsSealed(deque_, chunk, entries_per_chunk_);
1310 }
1311
1314 constexpr bool IsBoundary(size_type chunk, size_type layer) const {
1315 return Entry::IsBoundary(deque_, chunk, entries_per_chunk_, layer);
1316 }
1317 constexpr bool IsBoundary(size_type chunk) const {
1318 return Entry::IsBoundary(deque_, chunk, entries_per_chunk_);
1319 }
1321
1323 constexpr Deallocator& GetDeallocator(size_type chunk) const {
1324 return Entry::GetDeallocator(deque_, chunk, entries_per_chunk_);
1325 }
1326
1328 constexpr ControlBlock& GetControlBlock(size_type chunk) const {
1329 return Entry::GetControlBlock(deque_, chunk, entries_per_chunk_);
1330 }
1331
1334 constexpr size_type GetOffset(size_type chunk, size_type layer) const {
1335 return Entry::GetOffset(deque_, chunk, entries_per_chunk_, layer);
1336 }
1337 constexpr size_type GetOffset(size_type chunk) const {
1338 return Entry::GetOffset(deque_, chunk, entries_per_chunk_);
1339 }
1341
1344 constexpr size_type GetRelativeOffset(size_type chunk,
1345 size_type layer) const {
1346 return Entry::GetRelativeOffset(deque_, chunk, entries_per_chunk_, layer);
1347 }
1348 constexpr size_type GetRelativeOffset(size_type chunk) const {
1349 return Entry::GetRelativeOffset(deque_, chunk, entries_per_chunk_);
1350 }
1352
1355 constexpr size_type GetLength(size_type chunk, size_type layer) const {
1356 return Entry::GetLength(deque_, chunk, entries_per_chunk_, layer);
1357 }
1358 constexpr size_type GetLength(size_type chunk) const {
1359 return Entry::GetLength(deque_, chunk, entries_per_chunk_);
1360 }
1362
1365 constexpr ByteSpan GetView(size_type chunk, size_type layer) const {
1366 return Entry::GetView(deque_, chunk, entries_per_chunk_, layer);
1367 }
1368 constexpr ByteSpan GetView(size_type chunk) const {
1369 return Entry::GetView(deque_, chunk, entries_per_chunk_);
1370 }
1372
1374 constexpr iterator MakeIterator(size_type chunk, size_type offset = 0) const {
1375 return iterator(deque_, chunk, entries_per_chunk_, 0) + offset;
1376 }
1377
1380 [[nodiscard]] bool TryConvertToShared(size_type chunk);
1381
1386 [[nodiscard]] bool TryReserveEntries(size_type num_entries,
1387 bool split = false);
1388
1391 size_type InsertChunks(const_iterator pos, size_type num_chunks);
1392
1396 std::tuple<iterator, size_type> Insert(const_iterator pos,
1397 ConstByteSpan bytes,
1398 size_t offset,
1399 size_t length);
1400
1410 void SplitBase(size_type chunk, Deque& out_deque, size_type out_chunk);
1411
1417 void SplitBefore(size_type chunk,
1418 size_type split,
1419 Deque& out_deque,
1420 size_type out_chunk);
1421
1424 void SplitBefore(size_type chunk, size_type split);
1425
1431 void SplitAfter(size_type chunk,
1432 size_type split,
1433 Deque& out_deque,
1434 size_type out_chunk);
1435
1438 void SplitAfter(size_type chunk, size_type split);
1439
1450 [[nodiscard]] bool TryReserveForRemove(const_iterator pos,
1451 size_t size,
1452 GenericMultiBuf* out);
1453
1458 void MoveRange(const_iterator pos, size_t size, GenericMultiBuf& out);
1459
1463 void ClearRange(const_iterator pos, size_t size);
1464
1469 void EraseRange(const_iterator pos, size_t size);
1470
1473 size_t CopyToImpl(ByteSpan dst, size_t offset, size_type start) const;
1474
1476 [[nodiscard]] bool IsTopLayerSealed() const;
1477
1478 // Describes the memory and views to that in a one-dimensional sequence.
1479 // Every `entries_per_chunk_`-th entry holds a pointer to memory, each entry
1480 // for a given offset less than `entries_per_chunk_` after that entry is a
1481 // view that is part of the same "layer" in the MultiBuf.
1482 //
1483 // This base type will always have 0 or 2 layers, but derived classes may add
1484 // more.
1485 //
1486 // For example, a MultiBuf that has had 3 `Chunk`s and two additional layers
1487 // added would have a `deque_` resembling the following:
1488 //
1489 // buffer 0: buffer 1: buffer 2:
1490 // layer 3: deque_[0x3].view deque_[0x7].view deque_[0xB].view
1491 // layer 2: deque_[0x2].view deque_[0x6].view deque_[0xA].view
1492 // layer 1: deque_[0x1].view deque_[0x5].view deque_[0x9].view
1493 // layer 0: deque_[0x0].data deque_[0x4].data deque_[0x8].data
1494 Deque deque_;
1495
1496 // Number of entries per chunk in this MultiBuf.
1497 size_type entries_per_chunk_ = Entry::kMinEntriesPerChunk;
1498
1501 Observer* observer_ = nullptr;
1502};
1503
1522template <typename MultiBufType>
1524 public:
1525 constexpr explicit Instance(Allocator& allocator) : base_(allocator) {}
1526
1527 constexpr Instance(Instance&&) = default;
1528 constexpr Instance& operator=(Instance&&) = default;
1529
1530 // Provide a more helpful compile-time error when a user tries to
1531 // copy-construct an interface type.
1532 template <Property... kProperties>
1533 constexpr Instance(const BasicMultiBuf<kProperties...>&)
1534 : base_(allocator::GetNullAllocator()) {
1535 MoveOnly<>();
1536 }
1537
1538 // Provide a more helpful compile-time error when a user tries to copy-assign
1539 // an interface type.
1540 template <Property... kProperties>
1541 constexpr Instance& operator=(const BasicMultiBuf<kProperties...>&) {
1542 MoveOnly<>();
1543 }
1544
1545 constexpr Instance(MultiBufType&& mb)
1546 : base_(std::move(static_cast<GenericMultiBuf&>(mb))) {}
1547
1548 constexpr Instance& operator=(MultiBufType&& mb) {
1549 base_ = std::move(static_cast<GenericMultiBuf&>(mb));
1550 return *this;
1551 }
1552
1553 template <Property... kProperties>
1555 : base_(std::move(static_cast<internal::GenericMultiBuf&>(mb))) {
1556 internal::AssertIsAssignable<BasicMultiBuf<kProperties...>, MultiBufType>();
1557 }
1558
1559 template <Property... kProperties>
1560 constexpr Instance& operator=(BasicMultiBuf<kProperties...>&& mb) {
1561 internal::AssertIsAssignable<BasicMultiBuf<kProperties...>, MultiBufType>();
1562 base_ = std::move(static_cast<internal::GenericMultiBuf&>(mb));
1563 return *this;
1564 }
1565
1566 constexpr MultiBufType* operator->() { return &base_.as<MultiBufType>(); }
1567 constexpr const MultiBufType* operator->() const {
1568 return &base_.as<MultiBufType>();
1569 }
1570
1571 constexpr MultiBufType& operator*() & { return base_.as<MultiBufType>(); }
1572 constexpr const MultiBufType& operator*() const& {
1573 return base_.as<MultiBufType>();
1574 }
1575
1576 constexpr MultiBufType&& operator*() && {
1577 return std::move(base_.as<MultiBufType>());
1578 }
1579 constexpr const MultiBufType&& operator*() const&& {
1580 return std::move(base_.as<MultiBufType>());
1581 }
1582
1583 constexpr operator MultiBufType&() & { return base_.as<MultiBufType>(); }
1584 constexpr operator const MultiBufType&() const& {
1585 return base_.as<MultiBufType>();
1586 }
1587
1588 constexpr operator MultiBufType&&() && {
1589 return std::move(base_.as<MultiBufType>());
1590 }
1591 constexpr operator const MultiBufType&&() const&& {
1592 return std::move(base_.as<MultiBufType>());
1593 }
1594
1595 private:
1596 // Helper functions to provide a more helpful compile-time error when a this
1597 // type is used incorrectly.
1598 template <bool kMoveOnly = false>
1599 static constexpr void MoveOnly() {
1600 static_assert(kMoveOnly,
1601 "Instances can only be created from existing MultiBufs using "
1602 "move-construction or move-assignment.");
1603 }
1604
1605 GenericMultiBuf base_;
1606};
1607
1608} // namespace internal
1609
1610// Template method implementations.
1611
1612template <Property... kProperties>
1614 return generic().TryReserveForInsert(pos);
1615}
1616
1617template <Property... kProperties>
1618template <Property... kOtherProperties>
1621 internal::AssertIsConvertible<BasicMultiBuf<kOtherProperties...>,
1622 BasicMultiBuf>();
1623 return generic().TryReserveForInsert(pos,
1624 static_cast<const GenericMultiBuf&>(mb));
1625}
1626
1627template <Property... kProperties>
1628template <Property... kOtherProperties>
1630 const_iterator pos, BasicMultiBuf<kOtherProperties...>&& mb) -> iterator {
1631 internal::AssertIsConvertible<BasicMultiBuf<kOtherProperties...>,
1632 BasicMultiBuf>();
1633 return generic().Insert(pos, std::move(mb.generic()));
1634}
1635
1636template <Property... kProperties>
1637template <int&... kExplicitGuard, typename T, typename>
1638auto BasicMultiBuf<kProperties...>::Insert(const_iterator pos, const T& bytes)
1639 -> iterator {
1640 using data_ptr_type = decltype(std::data(std::declval<T&>()));
1641 static_assert(std::is_same_v<data_ptr_type, std::byte*> || is_const(),
1642 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1643 return generic().Insert(pos, bytes);
1644}
1645
1646template <Property... kProperties>
1647auto BasicMultiBuf<kProperties...>::Insert(const_iterator pos,
1648 UniquePtr<std::byte[]>&& bytes)
1649 -> iterator {
1650 return generic().Insert(pos, std::move(bytes));
1651}
1652
1653template <Property... kProperties>
1654auto BasicMultiBuf<kProperties...>::Insert(const_iterator pos,
1655 UniquePtr<const std::byte[]>&& bytes)
1656 -> iterator {
1657 static_assert(is_const(),
1658 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1659 return generic().Insert(pos, std::move(bytes));
1660}
1661
1662template <Property... kProperties>
1663auto BasicMultiBuf<kProperties...>::Insert(const_iterator pos,
1664 const SharedPtr<std::byte[]>& bytes,
1665 size_t offset,
1666 size_t length) -> iterator {
1667 return generic().Insert(pos, bytes, offset, length);
1668}
1669
1670template <Property... kProperties>
1672 const_iterator pos,
1673 const SharedPtr<const std::byte[]>& bytes,
1674 size_t offset,
1675 size_t length) -> iterator {
1676 static_assert(is_const(),
1677 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1678 return generic().Insert(pos, bytes, offset, length);
1679}
1680
1681template <Property... kProperties>
1682template <Property... kOtherProperties>
1685 return generic().TryReserveForInsert(end(),
1686 static_cast<const GenericMultiBuf&>(mb));
1687}
1688
1689template <Property... kProperties>
1691 return generic().TryReserveForInsert(end());
1692}
1693
1694template <Property... kProperties>
1695template <Property... kOtherProperties>
1698 Insert(end(), std::move(mb));
1699}
1700
1701template <Property... kProperties>
1702template <int&... kExplicitGuard, typename T, typename>
1704 using data_ptr_type = decltype(std::data(std::declval<T&>()));
1705 static_assert(std::is_same_v<data_ptr_type, std::byte*> || is_const(),
1706 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1707 Insert(end(), bytes);
1708}
1709
1710template <Property... kProperties>
1711void BasicMultiBuf<kProperties...>::PushBack(UniquePtr<std::byte[]>&& bytes) {
1712 Insert(end(), std::move(bytes));
1713}
1714
1715template <Property... kProperties>
1717 UniquePtr<const std::byte[]>&& bytes) {
1718 static_assert(is_const(),
1719 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1720 Insert(end(), std::move(bytes));
1721}
1722
1723template <Property... kProperties>
1725 const SharedPtr<std::byte[]>& bytes, size_t offset, size_t length) {
1726 Insert(end(), bytes, offset, length);
1727}
1728
1729template <Property... kProperties>
1731 const SharedPtr<const std::byte[]>& bytes, size_t offset, size_t length) {
1732 static_assert(is_const(),
1733 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1734 Insert(end(), bytes, offset, length);
1735}
1736
1737template <Property... kProperties>
1738Result<internal::Instance<BasicMultiBuf<kProperties...>>>
1740 auto result = generic().Remove(pos, size);
1741 if (!result.ok()) {
1742 return result.status();
1743 }
1744 return Instance(std::move(*result));
1745}
1746
1747template <Property... kProperties>
1748Result<internal::Instance<BasicMultiBuf<kProperties...>>>
1750 auto result = generic().PopFrontFragment();
1751 if (!result.ok()) {
1752 return result.status();
1753 }
1754 return Instance(std::move(*result));
1755}
1756
1757template <Property... kProperties>
1758UniquePtr<typename BasicMultiBuf<kProperties...>::value_type[]>
1760 UniquePtr<std::byte[]> bytes = generic().Release(pos);
1761 if constexpr (is_const()) {
1762 UniquePtr<const std::byte[]> const_bytes(
1763 bytes.get(), bytes.size(), *(bytes.deallocator()));
1764 bytes.Release();
1765 return const_bytes;
1766 } else {
1767 return bytes;
1768 }
1769}
1770
1771template <Property... kProperties>
1772SharedPtr<typename BasicMultiBuf<kProperties...>::value_type[]>
1774 return SharedPtr<value_type[]>(generic().Share(pos));
1775}
1776
1777} // namespace v2
1778} // namespace pw::multibuf
Definition: allocator.h:45
Abstract interface for releasing memory.
Definition: deallocator.h:29
Definition: dynamic_deque.h:60
Definition: result.h:143
Definition: shared_ptr.h:68
Definition: unique_ptr.h:43
element_type * Release() noexcept
Definition: unique_ptr.h:256
Deallocator * deallocator() const
Returns a pointer to the object that can destroy the value.
Definition: unique_ptr.h:187
size_t size() const
Definition: unique_ptr.h:180
Definition: multibuf.h:184
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:1703
void PushBack(BasicMultiBuf< kOtherProperties... > &&mb)
Definition: multibuf.h:1696
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:1749
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:1619
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:1773
bool IsShareable(const_iterator pos) const
Definition: multibuf.h:741
Result< Instance > Remove(const_iterator pos, size_t size)
Definition: multibuf.h:1739
bool TryReserveLayers(size_t num_layers, size_t num_chunks=1)
Definition: multibuf.h:896
bool TryReserveForInsert(const_iterator pos)
Definition: multibuf.h:1613
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:1690
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:1683
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:1759
Definition: observer.h:29
Byte iterator templated on the const-ness of the bytes it references.
Definition: byte_iterator.h:85
Definition: multibuf.h:1523
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: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).