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_containers/dynamic_deque.h"
30#include "pw_multibuf/v2/chunks.h"
31#include "pw_multibuf/v2/internal/byte_iterator.h"
32#include "pw_multibuf/v2/internal/entry.h"
33#include "pw_multibuf/v2/observer.h"
34#include "pw_multibuf/v2/properties.h"
35
36namespace pw::multibuf::v2 {
37
38// Forward declarations.
39template <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
60
64
69
74
80
81namespace internal {
82
83class GenericMultiBuf;
84
85template <typename>
86class Instance;
87
88} // namespace internal
89
183template <Property... kProperties>
185 protected:
188
189 public:
191 [[nodiscard]] static constexpr bool is_const() {
192 return ((kProperties == Property::kConst) || ...);
193 }
194
196 [[nodiscard]] static constexpr bool is_layerable() {
197 return ((kProperties == Property::kLayerable) || ...);
198 }
199
201 [[nodiscard]] static constexpr bool is_observable() {
202 return ((kProperties == Property::kObservable) || ...);
203 }
204
205 using size_type = typename Deque::size_type;
206 using difference_type = typename Deque::difference_type;
207 using iterator =
209 using const_iterator =
211 using pointer = iterator::pointer;
212 using const_pointer = const_iterator::pointer;
213 using reference = iterator::reference;
214 using const_reference = const_iterator::reference;
215 using value_type = std::conditional_t<is_const(),
216 const_iterator::value_type,
217 iterator::value_type>;
218
219 using ChunksType = internal::Chunks<Deque>;
220 using ConstChunksType = internal::ConstChunks<Deque>;
221
242
243 // Interfaces are not copyable or movable; copy and move `Instance`s instead.
244
245 BasicMultiBuf(const BasicMultiBuf&) { InvalidCopyOrMove<>(); }
246
247 template <Property... kOtherProperties>
249 InvalidCopyOrMove<>();
250 }
251
252 BasicMultiBuf& operator=(const BasicMultiBuf&) { InvalidCopyOrMove<>(); }
253
254 template <Property... kOtherProperties>
255 BasicMultiBuf& operator=(const BasicMultiBuf<kOtherProperties...>&) {
256 InvalidCopyOrMove<>();
257 }
258
259 BasicMultiBuf(BasicMultiBuf&&) { InvalidCopyOrMove<>(); }
260
261 BasicMultiBuf& operator=(BasicMultiBuf&&) { InvalidCopyOrMove<>(); }
262
263 // Conversions
264
265 template <typename OtherMultiBuf>
266 constexpr OtherMultiBuf& as() & {
267 internal::AssertIsConvertible<BasicMultiBuf, OtherMultiBuf>();
268 return generic().template as<OtherMultiBuf>();
269 }
270
271 template <typename OtherMultiBuf>
272 constexpr const OtherMultiBuf& as() const& {
273 internal::AssertIsConvertible<BasicMultiBuf, OtherMultiBuf>();
274 return generic().template as<OtherMultiBuf>();
275 }
276
277 template <typename OtherMultiBuf>
278 constexpr OtherMultiBuf&& as() && {
279 internal::AssertIsConvertible<BasicMultiBuf, OtherMultiBuf>();
280 return std::move(generic().template as<OtherMultiBuf>());
281 }
282
283 template <typename OtherMultiBuf>
284 constexpr const OtherMultiBuf&& as() const&& {
285 internal::AssertIsConvertible<BasicMultiBuf, OtherMultiBuf>();
286 return std::move(generic().template as<OtherMultiBuf>());
287 }
288
289 template <
290 typename OtherMultiBuf,
291 typename = internal::EnableIfConvertible<BasicMultiBuf, OtherMultiBuf>>
292 constexpr operator OtherMultiBuf&() & {
293 return as<OtherMultiBuf>();
294 }
295
296 template <
297 typename OtherMultiBuf,
298 typename = internal::EnableIfConvertible<BasicMultiBuf, OtherMultiBuf>>
299 constexpr operator const OtherMultiBuf&() const& {
300 return as<OtherMultiBuf>();
301 }
302
303 template <
304 typename OtherMultiBuf,
305 typename = internal::EnableIfConvertible<BasicMultiBuf, OtherMultiBuf>>
306 constexpr operator OtherMultiBuf&&() && {
307 return std::move(as<OtherMultiBuf>());
308 }
309
310 template <
311 typename OtherMultiBuf,
312 typename = internal::EnableIfConvertible<BasicMultiBuf, OtherMultiBuf>>
313 constexpr operator const OtherMultiBuf&&() const&& {
314 return std::move(as<OtherMultiBuf>());
315 }
316
317 // Accessors
318
321 constexpr bool empty() const { return generic().empty(); }
322
327 constexpr bool at_capacity() const { return generic().at_capacity(); }
328
331 constexpr size_t size() const { return generic().size(); }
332
344 template <bool kMutable = !is_const()>
345 std::enable_if_t<kMutable, reference> at(size_t index) {
346 return *(begin() + static_cast<int>(index));
347 }
348 const_reference at(size_t index) const {
349 return *(begin() + static_cast<int>(index));
350 }
351
352 template <bool kMutable = !is_const()>
353 std::enable_if_t<kMutable, reference> operator[](size_t index) {
354 return at(index);
355 }
356 const_reference operator[](size_t index) const { return at(index); }
358
370 template <bool kMutable = !is_const()>
371 constexpr std::enable_if_t<kMutable, ChunksType> Chunks() {
372 return generic().Chunks();
373 }
374 constexpr ConstChunksType Chunks() const { return generic().ConstChunks(); }
375 constexpr ConstChunksType ConstChunks() const {
376 return generic().ConstChunks();
377 }
379
380 // Iterators.
381
392 template <bool kMutable = !is_const()>
393 constexpr std::enable_if_t<kMutable, iterator> begin() {
394 return generic().begin();
395 }
396 constexpr const_iterator begin() const { return cbegin(); }
397 constexpr const_iterator cbegin() const { return generic().cbegin(); }
399
410 template <bool kMutable = !is_const()>
411 constexpr std::enable_if_t<kMutable, iterator> end() {
412 return generic().end();
413 }
414 constexpr const_iterator end() const { return cend(); }
415 constexpr const_iterator cend() const { return generic().cend(); }
417
418 // Other methods
419
422 [[nodiscard]] bool TryReserveChunks(size_t num_chunks) {
423 return generic().TryReserveChunks(num_chunks);
424 }
425
426 // Mutators
427
439 template <Property... kOtherProperties>
440 [[nodiscard]] bool TryReserveForInsert(
442
452 [[nodiscard]] bool TryReserveForInsert(const_iterator pos);
453
462 template <Property... kOtherProperties>
464
473 template <
474 int&... kExplicitGuard,
475 typename T,
476 typename =
477 std::enable_if_t<std::is_constructible_v<ConstByteSpan, T>, int>>
478 void Insert(const_iterator pos, const T& bytes);
479
490 void Insert(const_iterator pos, UniquePtr<std::byte[]>&& bytes);
491 void Insert(const_iterator pos, UniquePtr<const std::byte[]>&& bytes);
493
504 void Insert(const_iterator pos, const SharedPtr<std::byte[]>& bytes) {
505 Insert(pos, bytes, 0);
506 }
507 void Insert(const_iterator pos, const SharedPtr<const std::byte[]>& bytes) {
508 Insert(pos, bytes, 0);
509 }
511
524 void Insert(const_iterator pos,
525 const SharedPtr<std::byte[]>& bytes,
526 size_t offset,
527 size_t length = dynamic_extent);
528 void Insert(const_iterator pos,
529 const SharedPtr<const std::byte[]>& bytes,
530 size_t offset,
531 size_t length = dynamic_extent);
533
541 template <Property... kOtherProperties>
542 [[nodiscard]] bool TryReserveForPushBack(
544
550 [[nodiscard]] bool TryReserveForPushBack();
551
559 template <Property... kOtherProperties>
561
569 template <
570 int&... kExplicitGuard,
571 typename T,
572 typename =
573 std::enable_if_t<std::is_constructible_v<ConstByteSpan, T>, int>>
574 void PushBack(const T& bytes);
575
585 void PushBack(UniquePtr<std::byte[]>&& bytes);
586 void PushBack(UniquePtr<const std::byte[]>&& bytes);
588
598 void PushBack(const SharedPtr<std::byte[]>& bytes) { PushBack(bytes, 0); }
599 void PushBack(const SharedPtr<const std::byte[]>& bytes) {
600 PushBack(bytes, 0);
601 }
603
615 void PushBack(const SharedPtr<std::byte[]>& bytes,
616 size_t offset,
617 size_t length = dynamic_extent);
618 void PushBack(const SharedPtr<const std::byte[]>& bytes,
619 size_t offset,
620 size_t length = dynamic_extent);
622
630 [[nodiscard]] bool IsRemovable(const_iterator pos, size_t size) const {
631 return generic().IsRemovable(pos, size);
632 }
633
654
666
667 // clang-format off
689 // clang-format on
691 return generic().Discard(pos, size);
692 }
693
698 [[nodiscard]] bool IsReleasable(const_iterator pos) const {
699 return generic().IsReleasable(pos);
700 }
701
715
720 [[nodiscard]] bool IsShareable(const_iterator pos) const {
721 return generic().IsShareable(pos);
722 }
723
739
748 size_t CopyTo(ByteSpan dst, size_t offset = 0) const {
749 return generic().CopyTo(dst, offset);
750 }
751
760 size_t CopyFrom(ConstByteSpan src, size_t offset = 0) {
761 static_assert(!is_const(),
762 "`CopyFrom` may only be called on mutable MultiBufs");
763 return generic().CopyFrom(src, offset);
764 }
765
782 ConstByteSpan Get(ByteSpan copy, size_t offset = 0) const {
783 return generic().Get(copy, offset);
784 }
785
801 template <int&... kExplicitGuard, typename Visitor>
802 auto Visit(Visitor visitor, ByteSpan copy, size_t offset) {
803 return visitor(Get(copy, offset));
804 }
805
811 void Clear() { generic().Clear(); }
812
819 void ShrinkToFit() { generic().ShrinkToFit(); }
820
821 // Observable methods.
822
827 constexpr Observer* observer() const {
828 static_assert(is_observable(),
829 "`observer` may only be called on observable MultiBufs");
830 return generic().observer_;
831 }
832
840 static_assert(is_observable(),
841 "`set_observer` may only be called on observable MultiBufs");
842 generic().observer_ = observer;
843 }
844
845 // Layerable methods.
846
853 size_type NumFragments() const {
854 static_assert(is_layerable(),
855 "`NumFragments` may only be called on layerable MultiBufs");
856 return generic().NumFragments();
857 }
858
862 constexpr size_type NumLayers() const {
863 static_assert(is_layerable(),
864 "`NumLayers` may only be called on layerable MultiBufs");
865 return generic().NumLayers();
866 }
867
875 [[nodiscard]] bool TryReserveLayers(size_t num_layers,
876 size_t num_chunks = 1) {
877 return generic().TryReserveLayers(num_layers, num_chunks);
878 }
879
899 [[nodiscard]] bool AddLayer(size_t offset, size_t length = dynamic_extent) {
900 static_assert(is_layerable(),
901 "`AddLayer` may only be called on layerable MultiBufs");
902 return generic().AddLayer(offset, length);
903 }
904
908 static_assert(is_layerable(),
909 "`SealTopLayer` may only be called on layerable MultiBufs");
910 return generic().SealTopLayer();
911 }
912
916 static_assert(is_layerable(),
917 "`UnsealTopLayer` may only be called on layerable MultiBufs");
918 return generic().UnsealTopLayer();
919 }
920
922 [[nodiscard]] bool IsTopLayerSealed() {
923 static_assert(
924 is_layerable(),
925 "`IsTopLayerSealed` may only be called on layerable MultiBufs");
926 return generic().IsTopLayerSealed();
927 }
928
937 void TruncateTopLayer(size_t length) {
938 static_assert(
939 is_layerable(),
940 "`TruncateTopLayer` may only be called on layerable MultiBufs");
941 generic().TruncateTopLayer(length);
942 }
943
951 static_assert(!is_const() && is_layerable(),
952 "`SetTopLayer` may only be called on mutable, layerable "
953 "MultiBufs");
954 PW_ASSERT(src.size() <= size());
955 CopyFrom(src);
956 TruncateTopLayer(src.size());
957 }
958
967 void PopLayer() {
968 static_assert(is_layerable(),
969 "`PopLayer` may only be called on layerable MultiBufs");
970 generic().PopLayer();
971 }
972
973 protected:
974 constexpr BasicMultiBuf() { internal::PropertiesAreValid(); }
975
976 private:
977 template <bool kValidCopyOrMove = false>
978 static constexpr void InvalidCopyOrMove() {
979 static_assert(kValidCopyOrMove,
980 "Only copies and moves from `BasicMultiBuf<...>::Instance`"
981 "to `BasicMultiBuf<...>&` or another "
982 "`BasicMultiBuf<...>::Instance` are valid.");
983 }
984
985 template <Property...>
986 friend class BasicMultiBuf;
987
988 constexpr GenericMultiBuf& generic() {
989 return static_cast<GenericMultiBuf&>(*this);
990 }
991 constexpr const GenericMultiBuf& generic() const {
992 return static_cast<const GenericMultiBuf&>(*this);
993 }
994};
995
997
999
1000namespace internal {
1001
1002// A generic MultiBuf implementation that provides the functionality of any
1003// `BasicMultiBuf<kProperties>` type.
1004//
1005// This class should not be instantiated directly. Instead, use `Instance` as
1006// described below. This is the base class for all `Instance` types, and derives
1007// from every supported `BasicMultiBuf` type. It implements the MultiBuf
1008// behavior in one type, and allows for performing conversions from `Instance`s
1009// and `BasicMultiBuf`s to `BasicMultiBuf`s with different yet compatible
1010// propreties.
1012 : private BasicMultiBuf<>,
1022 private:
1023 using ControlBlock = allocator::internal::ControlBlock;
1024 using typename BasicMultiBuf<>::ChunksType;
1025 using typename BasicMultiBuf<>::ConstChunksType;
1026 using typename BasicMultiBuf<>::Deque;
1027
1028 public:
1029 using typename BasicMultiBuf<>::const_iterator;
1030 using typename BasicMultiBuf<>::const_pointer;
1031 using typename BasicMultiBuf<>::const_reference;
1032 using typename BasicMultiBuf<>::difference_type;
1033 using typename BasicMultiBuf<>::iterator;
1034 using typename BasicMultiBuf<>::pointer;
1035 using typename BasicMultiBuf<>::reference;
1036 using typename BasicMultiBuf<>::size_type;
1037
1038 ~GenericMultiBuf() { Clear(); }
1039
1040 // Not copyable.
1041 GenericMultiBuf(const GenericMultiBuf&) = delete;
1042 GenericMultiBuf& operator=(const GenericMultiBuf&) = delete;
1043
1044 // Movable.
1046 : GenericMultiBuf(other.deque_.get_allocator()) {
1047 *this = std::move(other);
1048 }
1049 GenericMultiBuf& operator=(GenericMultiBuf&& other);
1050
1051 private:
1052 template <::pw::multibuf::v2::Property...>
1053 friend class ::pw::multibuf::v2::BasicMultiBuf;
1054
1055 template <typename>
1056 friend class Instance;
1057
1059 constexpr explicit GenericMultiBuf(Allocator& allocator)
1060 : deque_(allocator) {}
1061
1062 template <typename MultiBufType>
1063 constexpr MultiBufType& as() {
1064 return *this;
1065 }
1066
1067 template <typename MultiBufType>
1068 constexpr const MultiBufType& as() const {
1069 return *this;
1070 }
1071
1072 // Accessors.
1073
1075 constexpr bool empty() const { return deque_.empty(); }
1076
1078 constexpr bool at_capacity() const {
1079 return deque_.size() == deque_.capacity();
1080 }
1081
1083 constexpr size_t size() const {
1084 return static_cast<size_t>(cend() - cbegin());
1085 }
1086
1087 // Iterators.
1088
1089 constexpr ChunksType Chunks() {
1090 return ChunksType(deque_, entries_per_chunk_);
1091 }
1092 constexpr ConstChunksType ConstChunks() const {
1093 return ConstChunksType(deque_, entries_per_chunk_);
1094 }
1095
1096 constexpr iterator begin() { return iterator(Chunks().begin(), 0); }
1097 constexpr const_iterator cbegin() const {
1098 return const_iterator(ConstChunks().cbegin(), 0);
1099 }
1100
1101 constexpr iterator end() { return iterator(Chunks().end(), 0); }
1102 constexpr const_iterator cend() const {
1103 return const_iterator(ConstChunks().cend(), 0);
1104 }
1105
1106 // Mutators.
1107
1109 [[nodiscard]] bool TryReserveChunks(size_t num_chunks) {
1110 return TryReserveLayers(NumLayers(), num_chunks);
1111 }
1112
1115 [[nodiscard]] bool TryReserveForInsert(const_iterator pos,
1116 const GenericMultiBuf& mb);
1117 [[nodiscard]] bool TryReserveForInsert(const_iterator pos);
1119
1122 void Insert(const_iterator pos, GenericMultiBuf&& mb);
1123 void Insert(const_iterator pos, ConstByteSpan bytes);
1124 void Insert(const_iterator pos,
1125 ConstByteSpan bytes,
1126 size_t offset,
1127 size_t length,
1128 Deallocator* deallocator);
1129 void Insert(const_iterator pos,
1130 ConstByteSpan bytes,
1131 size_t offset,
1132 size_t length,
1133 ControlBlock* control_block);
1135
1137 [[nodiscard]] constexpr bool IsRemovable(const_iterator pos,
1138 size_t size) const {
1139 return pos != cend() && size != 0 &&
1140 size <= static_cast<size_t>(cend() - pos);
1141 }
1142
1144 Result<GenericMultiBuf> Remove(const_iterator pos, size_t size);
1145
1147 Result<GenericMultiBuf> PopFrontFragment();
1148
1150 Result<const_iterator> Discard(const_iterator pos, size_t size);
1151
1153 [[nodiscard]] bool IsReleasable(const_iterator pos) const;
1154
1157
1159 [[nodiscard]] bool IsShareable(const_iterator pos) const;
1160
1163
1165 size_t CopyTo(ByteSpan dst, size_t offset) const {
1166 return CopyToImpl(dst, offset, 0);
1167 }
1168
1170 size_t CopyFrom(ConstByteSpan src, size_t offset);
1171
1173 ConstByteSpan Get(ByteSpan copy, size_t offset) const;
1174
1176 void Clear();
1177
1179 void ShrinkToFit();
1180
1181 // Layerable methods.
1182
1184 size_type NumFragments() const;
1185
1187 constexpr size_type NumLayers() const {
1188 return entries_per_chunk_ - Entry::kMinEntriesPerChunk + 1;
1189 }
1190
1192 [[nodiscard]] bool TryReserveLayers(size_t num_layers, size_t num_chunks = 1);
1193
1195 [[nodiscard]] bool AddLayer(size_t offset, size_t length = dynamic_extent);
1196
1198 void SealTopLayer();
1199
1201 void UnsealTopLayer();
1202
1204 void TruncateTopLayer(size_t length);
1205
1207 void PopLayer();
1208
1209 // Implementation methods.
1210 //
1211 // These methods are used to implement the methods above, and should not be
1212 // called directly by BasicMultiBuf<>.
1213
1218 static size_t CheckRange(size_t offset, size_t length, size_t size);
1219
1221 constexpr size_type num_chunks() const {
1222 return deque_.size() / entries_per_chunk_;
1223 }
1224
1226 constexpr size_type memory_context_index(size_type chunk) const {
1227 return Entry::memory_context_index(chunk, entries_per_chunk_);
1228 }
1229
1231 constexpr size_type data_index(size_type chunk) const {
1232 return Entry::data_index(chunk, entries_per_chunk_);
1233 }
1234
1236 constexpr size_type base_view_index(size_type chunk) const {
1237 return Entry::base_view_index(chunk, entries_per_chunk_);
1238 }
1239
1241 constexpr size_type view_index(size_type chunk, size_type layer) const {
1242 return Entry::view_index(chunk, entries_per_chunk_, layer);
1243 }
1244
1246 constexpr size_type top_view_index(size_type chunk) const {
1247 return Entry::top_view_index(chunk, entries_per_chunk_);
1248 }
1249
1251 constexpr std::byte* GetData(size_type chunk) const {
1252 return deque_[data_index(chunk)].data;
1253 }
1254
1257 constexpr bool IsOwned(size_type chunk) const {
1258 return deque_[base_view_index(chunk)].base_view.owned;
1259 }
1260
1263 constexpr bool IsShared(size_type chunk) const {
1264 return deque_[base_view_index(chunk)].base_view.shared;
1265 }
1266
1268 constexpr bool IsSealed(size_type chunk) const {
1269 return entries_per_chunk_ == Entry::kMinEntriesPerChunk
1270 ? false
1271 : deque_[top_view_index(chunk)].view.sealed;
1272 }
1273
1275 constexpr bool IsBoundary(size_type chunk) const {
1276 return entries_per_chunk_ == Entry::kMinEntriesPerChunk
1277 ? true
1278 : deque_[top_view_index(chunk)].view.boundary;
1279 }
1280
1284 constexpr size_type GetOffset(size_type chunk, uint16_t layer) const {
1285 return layer == 1 ? deque_[base_view_index(chunk)].base_view.offset
1286 : deque_[view_index(chunk, layer)].view.offset;
1287 }
1288
1290 constexpr size_type GetOffset(size_type chunk) const {
1291 return GetOffset(chunk, NumLayers());
1292 }
1293
1295 constexpr size_type GetRelativeOffset(size_type chunk) const {
1296 uint16_t layer = NumLayers();
1297 if (layer == 1) {
1298 return GetOffset(chunk, layer);
1299 }
1300 return GetOffset(chunk, layer) - GetOffset(chunk, layer - 1);
1301 }
1302
1304 constexpr size_type GetLength(size_type chunk) const {
1305 return entries_per_chunk_ == Entry::kMinEntriesPerChunk
1306 ? deque_[base_view_index(chunk)].base_view.length
1307 : deque_[top_view_index(chunk)].view.length;
1308 }
1309
1311 constexpr ByteSpan GetView(size_type chunk) const {
1312 return ByteSpan(GetData(chunk) + GetOffset(chunk), GetLength(chunk));
1313 }
1314
1318 std::pair<size_type, size_type> GetChunkAndOffset(const_iterator pos) const;
1319
1322 [[nodiscard]] bool TryConvertToShared(size_type chunk);
1323
1328 [[nodiscard]] bool TryReserveEntries(size_type num_entries,
1329 bool split = false);
1330
1333 size_type InsertChunks(const_iterator pos, size_type num_chunks);
1334
1337 size_type Insert(const_iterator pos,
1338 ConstByteSpan bytes,
1339 size_t offset,
1340 size_t length);
1341
1351 void SplitBase(size_type chunk, Deque& out_deque, size_type out_chunk);
1352
1358 void SplitBefore(size_type chunk,
1359 size_type split,
1360 Deque& out_deque,
1361 size_type out_chunk);
1362
1365 void SplitBefore(size_type chunk, size_type split);
1366
1372 void SplitAfter(size_type chunk,
1373 size_type split,
1374 Deque& out_deque,
1375 size_type out_chunk);
1376
1379 void SplitAfter(size_type chunk, size_type split);
1380
1391 [[nodiscard]] bool TryReserveForRemove(const_iterator pos,
1392 size_t size,
1393 GenericMultiBuf* out);
1394
1399 void MoveRange(const_iterator pos, size_t size, GenericMultiBuf& out);
1400
1404 void ClearRange(const_iterator pos, size_t size);
1405
1410 void EraseRange(const_iterator pos, size_t size);
1411
1414 size_t CopyToImpl(ByteSpan dst, size_t offset, size_type start) const;
1415
1417 [[nodiscard]] bool IsTopLayerSealed() const;
1418
1421 void SetLayer(size_t offset, size_t length);
1422
1423 // Describes the memory and views to that in a one-dimensional sequence.
1424 // Every `entries_per_chunk_`-th entry holds a pointer to memory, each entry
1425 // for a given offset less than `entries_per_chunk_` after that entry is a
1426 // view that is part of the same "layer" in the MultiBuf.
1427 //
1428 // This base type will always have 0 or 2 layers, but derived classes may add
1429 // more.
1430 //
1431 // For example, a MultiBuf that has had 3 `Chunk`s and two additional layers
1432 // added would have a `deque_` resembling the following:
1433 //
1434 // buffer 0: buffer 1: buffer 2:
1435 // layer 3: deque_[0x3].view deque_[0x7].view deque_[0xB].view
1436 // layer 2: deque_[0x2].view deque_[0x6].view deque_[0xA].view
1437 // layer 1: deque_[0x1].view deque_[0x5].view deque_[0x9].view
1438 // layer 0: deque_[0x0].data deque_[0x4].data deque_[0x8].data
1439 Deque deque_;
1440
1441 // Number of entries per chunk in this MultiBuf.
1442 size_type entries_per_chunk_ = Entry::kMinEntriesPerChunk;
1443
1446 Observer* observer_ = nullptr;
1447};
1448
1467template <typename MultiBufType>
1469 public:
1470 constexpr explicit Instance(Allocator& allocator) : base_(allocator) {}
1471
1472 constexpr Instance(Instance&&) = default;
1473 constexpr Instance& operator=(Instance&&) = default;
1474
1475 // Provide a more helpful compile-time error when a user tries to
1476 // copy-construct an interface type.
1477 template <Property... kProperties>
1478 constexpr Instance(const BasicMultiBuf<kProperties...>&)
1479 : base_(allocator::GetNullAllocator()) {
1480 MoveOnly<>();
1481 }
1482
1483 // Provide a more helpful compile-time error when a user tries to copy-assign
1484 // an interface type.
1485 template <Property... kProperties>
1486 constexpr Instance& operator=(const BasicMultiBuf<kProperties...>&) {
1487 MoveOnly<>();
1488 }
1489
1490 constexpr Instance(MultiBufType&& mb)
1491 : base_(std::move(static_cast<GenericMultiBuf&>(mb))) {}
1492
1493 constexpr Instance& operator=(MultiBufType&& mb) {
1494 base_ = std::move(static_cast<GenericMultiBuf&>(mb));
1495 return *this;
1496 }
1497
1498 template <Property... kProperties>
1500 : base_(std::move(static_cast<internal::GenericMultiBuf&>(mb))) {
1501 internal::AssertIsConvertible<BasicMultiBuf<kProperties...>,
1502 MultiBufType>();
1503 }
1504
1505 template <Property... kProperties>
1506 constexpr Instance& operator=(BasicMultiBuf<kProperties...>&& mb) {
1507 internal::AssertIsConvertible<BasicMultiBuf<kProperties...>,
1508 MultiBufType>();
1509 base_ = std::move(static_cast<internal::GenericMultiBuf&>(mb));
1510 return *this;
1511 }
1512
1513 constexpr MultiBufType* operator->() { return &base_.as<MultiBufType>(); }
1514 constexpr const MultiBufType* operator->() const {
1515 return &base_.as<MultiBufType>();
1516 }
1517
1518 constexpr MultiBufType& operator*() & { return base_.as<MultiBufType>(); }
1519 constexpr const MultiBufType& operator*() const& {
1520 return base_.as<MultiBufType>();
1521 }
1522
1523 constexpr MultiBufType&& operator*() && {
1524 return std::move(base_.as<MultiBufType>());
1525 }
1526 constexpr const MultiBufType&& operator*() const&& {
1527 return std::move(base_.as<MultiBufType>());
1528 }
1529
1530 constexpr operator MultiBufType&() & { return base_.as<MultiBufType>(); }
1531 constexpr operator const MultiBufType&() const& {
1532 return base_.as<MultiBufType>();
1533 }
1534
1535 constexpr operator MultiBufType&&() && {
1536 return std::move(base_.as<MultiBufType>());
1537 }
1538 constexpr operator const MultiBufType&&() const&& {
1539 return std::move(base_.as<MultiBufType>());
1540 }
1541
1542 private:
1543 // Helper functions to provide a more helpful compile-time error when a this
1544 // type is used incorrectly.
1545 template <bool kMoveOnly = false>
1546 static constexpr void MoveOnly() {
1547 static_assert(kMoveOnly,
1548 "Instances can only be created from existing MultiBufs using "
1549 "move-construction or move-assignment.");
1550 }
1551
1552 GenericMultiBuf base_;
1553};
1554
1555} // namespace internal
1556
1557// Template method implementations.
1558
1559template <Property... kProperties>
1561 return generic().TryReserveForInsert(pos);
1562}
1563
1564template <Property... kProperties>
1565template <Property... kOtherProperties>
1568 internal::AssertIsConvertible<BasicMultiBuf<kOtherProperties...>,
1569 BasicMultiBuf>();
1570 return generic().TryReserveForInsert(pos,
1571 static_cast<const GenericMultiBuf&>(mb));
1572}
1573
1574template <Property... kProperties>
1575template <Property... kOtherProperties>
1578 internal::AssertIsConvertible<BasicMultiBuf<kOtherProperties...>,
1579 BasicMultiBuf>();
1580 generic().Insert(pos, std::move(mb.generic()));
1581}
1582
1583template <Property... kProperties>
1584template <int&... kExplicitGuard, typename T, typename>
1586 using data_ptr_type = decltype(std::data(std::declval<T&>()));
1587 static_assert(std::is_same_v<data_ptr_type, std::byte*> || is_const(),
1588 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1589 generic().Insert(pos, bytes);
1590}
1591
1592template <Property... kProperties>
1593void BasicMultiBuf<kProperties...>::Insert(const_iterator pos,
1594 UniquePtr<std::byte[]>&& bytes) {
1595 ConstByteSpan chunk(bytes.get(), bytes.size());
1596 generic().Insert(pos, chunk, 0, bytes.size(), bytes.deallocator());
1597 bytes.Release();
1598}
1599
1600template <Property... kProperties>
1602 const_iterator pos, UniquePtr<const std::byte[]>&& bytes) {
1603 static_assert(is_const(),
1604 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1605 ConstByteSpan chunk(bytes.get(), bytes.size());
1606 generic().Insert(pos, chunk, 0, bytes.size(), bytes.deallocator());
1607 bytes.Release();
1608}
1609
1610template <Property... kProperties>
1611void BasicMultiBuf<kProperties...>::Insert(const_iterator pos,
1612 const SharedPtr<std::byte[]>& bytes,
1613 size_t offset,
1614 size_t length) {
1615 ConstByteSpan chunk(bytes.get(), bytes.size());
1616 generic().Insert(pos, chunk, offset, length, bytes.control_block());
1617}
1618
1619template <Property... kProperties>
1621 const_iterator pos,
1622 const SharedPtr<const std::byte[]>& bytes,
1623 size_t offset,
1624 size_t length) {
1625 static_assert(is_const(),
1626 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1627 ConstByteSpan chunk(bytes.get(), bytes.size());
1628 generic().Insert(pos, chunk, offset, length, bytes.control_block());
1629}
1630
1631template <Property... kProperties>
1632template <Property... kOtherProperties>
1635 return generic().TryReserveForInsert(end(),
1636 static_cast<const GenericMultiBuf&>(mb));
1637}
1638
1639template <Property... kProperties>
1641 return generic().TryReserveForInsert(end());
1642}
1643
1644template <Property... kProperties>
1645template <Property... kOtherProperties>
1648 Insert(end(), std::move(mb));
1649}
1650
1651template <Property... kProperties>
1652template <int&... kExplicitGuard, typename T, typename>
1654 using data_ptr_type = decltype(std::data(std::declval<T&>()));
1655 static_assert(std::is_same_v<data_ptr_type, std::byte*> || is_const(),
1656 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1657 Insert(end(), bytes);
1658}
1659
1660template <Property... kProperties>
1661void BasicMultiBuf<kProperties...>::PushBack(UniquePtr<std::byte[]>&& bytes) {
1662 Insert(end(), std::move(bytes));
1663}
1664
1665template <Property... kProperties>
1667 UniquePtr<const std::byte[]>&& bytes) {
1668 static_assert(is_const(),
1669 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1670 Insert(end(), std::move(bytes));
1671}
1672
1673template <Property... kProperties>
1675 const SharedPtr<std::byte[]>& bytes, size_t offset, size_t length) {
1676 Insert(end(), bytes, offset, length);
1677}
1678
1679template <Property... kProperties>
1681 const SharedPtr<const std::byte[]>& bytes, size_t offset, size_t length) {
1682 static_assert(is_const(),
1683 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1684 Insert(end(), bytes, offset, length);
1685}
1686
1687template <Property... kProperties>
1688Result<internal::Instance<BasicMultiBuf<kProperties...>>>
1690 auto result = generic().Remove(pos, size);
1691 if (!result.ok()) {
1692 return result.status();
1693 }
1694 return Instance(std::move(*result));
1695}
1696
1697template <Property... kProperties>
1698Result<internal::Instance<BasicMultiBuf<kProperties...>>>
1700 auto result = generic().PopFrontFragment();
1701 if (!result.ok()) {
1702 return result.status();
1703 }
1704 return Instance(std::move(*result));
1705}
1706
1707template <Property... kProperties>
1708UniquePtr<typename BasicMultiBuf<kProperties...>::value_type[]>
1710 UniquePtr<std::byte[]> bytes = generic().Release(pos);
1711 if constexpr (is_const()) {
1712 UniquePtr<const std::byte[]> const_bytes(
1713 bytes.get(), bytes.size(), *(bytes.deallocator()));
1714 bytes.Release();
1715 return const_bytes;
1716 } else {
1717 return bytes;
1718 }
1719}
1720
1721template <Property... kProperties>
1722SharedPtr<typename BasicMultiBuf<kProperties...>::value_type[]>
1724 return SharedPtr<value_type[]>(generic().Share(pos));
1725}
1726
1727} // namespace pw::multibuf::v2
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:67
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
constexpr size_t size() const
Definition: multibuf.h:331
bool IsReleasable(const_iterator pos) const
Definition: multibuf.h:698
static constexpr bool is_observable()
Returns whether an observer can be registered on the MultiBuf.
Definition: multibuf.h:201
constexpr size_type NumLayers() const
Definition: multibuf.h:862
void PopLayer()
Definition: multibuf.h:967
bool IsTopLayerSealed()
Returns whether the "sealed" flag is set in the top layer.
Definition: multibuf.h:922
void ShrinkToFit()
Definition: multibuf.h:819
void PushBack(const T &bytes)
Definition: multibuf.h:1653
void PushBack(BasicMultiBuf< kOtherProperties... > &&mb)
Definition: multibuf.h:1646
static constexpr bool is_layerable()
Returns whether additional views can be layered on the MultiBuf.
Definition: multibuf.h:196
void SealTopLayer()
Definition: multibuf.h:907
Result< Instance > PopFrontFragment()
Definition: multibuf.h:1699
Result< const_iterator > Discard(const_iterator pos, size_t size)
Definition: multibuf.h:690
constexpr bool empty() const
Definition: multibuf.h:321
void Insert(const_iterator pos, const T &bytes)
Definition: multibuf.h:1585
void TruncateTopLayer(size_t length)
Definition: multibuf.h:937
ConstByteSpan Get(ByteSpan copy, size_t offset=0) const
Definition: multibuf.h:782
void Clear()
Definition: multibuf.h:811
bool TryReserveForInsert(const_iterator pos, const BasicMultiBuf< kOtherProperties... > &mb)
Definition: multibuf.h:1566
bool IsRemovable(const_iterator pos, size_t size) const
Definition: multibuf.h:630
SharedPtr< value_type[]> Share(const_iterator pos)
Definition: multibuf.h:1723
void SetTopLayer(ConstByteSpan src)
Definition: multibuf.h:950
bool TryReserveChunks(size_t num_chunks)
Definition: multibuf.h:422
void Insert(const_iterator pos, BasicMultiBuf< kOtherProperties... > &&mb)
Definition: multibuf.h:1576
constexpr Observer * observer() const
Definition: multibuf.h:827
void set_observer(Observer *observer)
Definition: multibuf.h:839
void UnsealTopLayer()
Definition: multibuf.h:915
bool IsShareable(const_iterator pos) const
Definition: multibuf.h:720
Result< Instance > Remove(const_iterator pos, size_t size)
Definition: multibuf.h:1689
bool TryReserveLayers(size_t num_layers, size_t num_chunks=1)
Definition: multibuf.h:875
bool TryReserveForInsert(const_iterator pos)
Definition: multibuf.h:1560
size_type NumFragments() const
Definition: multibuf.h:853
size_t CopyFrom(ConstByteSpan src, size_t offset=0)
Definition: multibuf.h:760
bool TryReserveForPushBack()
Definition: multibuf.h:1640
bool AddLayer(size_t offset, size_t length=dynamic_extent)
Definition: multibuf.h:899
static constexpr bool is_const()
Returns whether the MultiBuf data is immutable.
Definition: multibuf.h:191
size_t CopyTo(ByteSpan dst, size_t offset=0) const
Definition: multibuf.h:748
bool TryReserveForPushBack(const BasicMultiBuf< kOtherProperties... > &mb)
Definition: multibuf.h:1633
auto Visit(Visitor visitor, ByteSpan copy, size_t offset)
Definition: multibuf.h:802
constexpr bool at_capacity() const
Definition: multibuf.h:327
UniquePtr< value_type[]> Release(const_iterator pos)
Definition: multibuf.h:1709
Definition: observer.h:29
Definition: byte_iterator.h:39
Base class for ranges of chunks.
Definition: chunks.h:33
Definition: multibuf.h:1468
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
Result(T value) -> Result< T >
Deduction guide to allow Result(v) rather than Result<T>(v).