C/C++ API Reference
Loading...
Searching...
No Matches
multibuf_v2.h
1// Copyright 2025 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14#pragma once
15
16// NOTE: MultiBuf version 2 is in a nearly stable state, but may still change.
17// The Pigweed team will announce breaking changes in the API. It is strongly
18// recommended to coordinate with the Pigweed team if you want to start using
19// this code.
20
21#include <cstddef>
22#include <cstdint>
23#include <numeric>
24#include <type_traits>
25
26#include "pw_allocator/allocator.h"
27#include "pw_allocator/null_allocator.h"
28#include "pw_bytes/span.h"
29#include "pw_containers/dynamic_deque.h"
30#include "pw_multibuf/chunks.h"
31#include "pw_multibuf/internal/byte_iterator.h"
32#include "pw_multibuf/internal/entry.h"
33#include "pw_multibuf/observer.h"
34#include "pw_multibuf/properties.h"
35
36namespace pw {
37
38// Forward declarations.
39template <multibuf::Property...>
40class BasicMultiBuf;
41
43
44// Type aliases of the BasicMultiBuf interface types for each different
45// combination of propeties, listed here for easier discoverability.
46
49
52
56
61
65
70
75
81
82namespace multibuf::internal {
83
84class GenericMultiBuf;
85
86template <typename>
87class Instance;
88
89} // namespace multibuf::internal
90
184template <multibuf::Property... kProperties>
186 protected:
189
190 public:
192 [[nodiscard]] static constexpr bool is_const() {
193 return ((kProperties == multibuf::Property::kConst) || ...);
194 }
195
197 [[nodiscard]] static constexpr bool is_layerable() {
198 return ((kProperties == multibuf::Property::kLayerable) || ...);
199 }
200
202 [[nodiscard]] static constexpr bool is_observable() {
203 return ((kProperties == multibuf::Property::kObservable) || ...);
204 }
205
206 using size_type = typename Deque::size_type;
207 using difference_type = typename Deque::difference_type;
208 using iterator = multibuf::internal::
209 ByteIterator<size_type, multibuf::internal::ChunkMutability::kMutable>;
210 using const_iterator = multibuf::internal::
211 ByteIterator<size_type, multibuf::internal::ChunkMutability::kConst>;
212 using pointer = iterator::pointer;
213 using const_pointer = const_iterator::pointer;
214 using reference = iterator::reference;
215 using const_reference = const_iterator::reference;
216 using value_type = std::conditional_t<is_const(),
217 const_iterator::value_type,
218 iterator::value_type>;
219
220 using ChunksType = multibuf::internal::Chunks<Deque>;
221 using ConstChunksType = multibuf::internal::ConstChunks<Deque>;
222
243
244 // Interfaces are not copyable or movable; copy and move `Instance`s instead.
245
246 BasicMultiBuf(const BasicMultiBuf&) { InvalidCopyOrMove<>(); }
247
248 template <multibuf::Property... kOtherProperties>
250 InvalidCopyOrMove<>();
251 }
252
253 BasicMultiBuf& operator=(const BasicMultiBuf&) { InvalidCopyOrMove<>(); }
254
255 template <multibuf::Property... kOtherProperties>
256 BasicMultiBuf& operator=(const BasicMultiBuf<kOtherProperties...>&) {
257 InvalidCopyOrMove<>();
258 }
259
260 BasicMultiBuf(BasicMultiBuf&&) { InvalidCopyOrMove<>(); }
261
262 BasicMultiBuf& operator=(BasicMultiBuf&&) { InvalidCopyOrMove<>(); }
263
264 // Conversions
265
266 template <typename OtherMultiBuf>
267 constexpr OtherMultiBuf& as() & {
268 multibuf::internal::AssertIsConvertible<BasicMultiBuf, OtherMultiBuf>();
269 return generic().template as<OtherMultiBuf>();
270 }
271
272 template <typename OtherMultiBuf>
273 constexpr const OtherMultiBuf& as() const& {
274 multibuf::internal::AssertIsConvertible<BasicMultiBuf, OtherMultiBuf>();
275 return generic().template as<OtherMultiBuf>();
276 }
277
278 template <typename OtherMultiBuf>
279 constexpr OtherMultiBuf&& as() && {
280 multibuf::internal::AssertIsConvertible<BasicMultiBuf, OtherMultiBuf>();
281 return std::move(generic().template as<OtherMultiBuf>());
282 }
283
284 template <typename OtherMultiBuf>
285 constexpr const OtherMultiBuf&& as() const&& {
286 multibuf::internal::AssertIsConvertible<BasicMultiBuf, OtherMultiBuf>();
287 return std::move(generic().template as<OtherMultiBuf>());
288 }
289
290 template <typename OtherMultiBuf,
291 typename = multibuf::internal::EnableIfConvertible<BasicMultiBuf,
292 OtherMultiBuf>>
293 constexpr operator OtherMultiBuf&() & {
294 return as<OtherMultiBuf>();
295 }
296
297 template <typename OtherMultiBuf,
298 typename = multibuf::internal::EnableIfConvertible<BasicMultiBuf,
299 OtherMultiBuf>>
300 constexpr operator const OtherMultiBuf&() const& {
301 return as<OtherMultiBuf>();
302 }
303
304 template <typename OtherMultiBuf,
305 typename = multibuf::internal::EnableIfConvertible<BasicMultiBuf,
306 OtherMultiBuf>>
307 constexpr operator OtherMultiBuf&&() && {
308 return std::move(as<OtherMultiBuf>());
309 }
310
311 template <typename OtherMultiBuf,
312 typename = multibuf::internal::EnableIfConvertible<BasicMultiBuf,
313 OtherMultiBuf>>
314 constexpr operator const OtherMultiBuf&&() const&& {
315 return std::move(as<OtherMultiBuf>());
316 }
317
318 // Accessors
319
322 constexpr bool empty() const { return generic().empty(); }
323
328 constexpr bool at_capacity() const { return generic().at_capacity(); }
329
332 constexpr size_t size() const { return generic().size(); }
333
345 template <bool kMutable = !is_const()>
346 std::enable_if_t<kMutable, reference> at(size_t index) {
347 return *(begin() + static_cast<int>(index));
348 }
349 const_reference at(size_t index) const {
350 return *(begin() + static_cast<int>(index));
351 }
352
353 template <bool kMutable = !is_const()>
354 std::enable_if_t<kMutable, reference> operator[](size_t index) {
355 return at(index);
356 }
357 const_reference operator[](size_t index) const { return at(index); }
359
371 template <bool kMutable = !is_const()>
372 constexpr std::enable_if_t<kMutable, ChunksType> Chunks() {
373 return generic().Chunks();
374 }
375 constexpr ConstChunksType Chunks() const { return generic().ConstChunks(); }
376 constexpr ConstChunksType ConstChunks() const {
377 return generic().ConstChunks();
378 }
380
381 // Iterators.
382
393 template <bool kMutable = !is_const()>
394 constexpr std::enable_if_t<kMutable, iterator> begin() {
395 return generic().begin();
396 }
397 constexpr const_iterator begin() const { return cbegin(); }
398 constexpr const_iterator cbegin() const { return generic().cbegin(); }
400
411 template <bool kMutable = !is_const()>
412 constexpr std::enable_if_t<kMutable, iterator> end() {
413 return generic().end();
414 }
415 constexpr const_iterator end() const { return cend(); }
416 constexpr const_iterator cend() const { return generic().cend(); }
418
419 // Other methods
420
423 [[nodiscard]] bool TryReserveChunks(size_t num_chunks) {
424 return generic().TryReserveChunks(num_chunks);
425 }
426
427 // Mutators
428
440 template <multibuf::Property... kOtherProperties>
441 [[nodiscard]] bool TryReserveForInsert(
443
453 [[nodiscard]] bool TryReserveForInsert(const_iterator pos);
454
463 template <multibuf::Property... kOtherProperties>
465
474 template <
475 int&... kExplicitGuard,
476 typename T,
477 typename =
478 std::enable_if_t<std::is_constructible_v<ConstByteSpan, T>, int>>
479 void Insert(const_iterator pos, const T& bytes);
480
491 void Insert(const_iterator pos, UniquePtr<std::byte[]>&& bytes) {
492 Insert(pos, std::move(bytes), 0);
493 }
494 void Insert(const_iterator pos, UniquePtr<const std::byte[]>&& bytes) {
495 Insert(pos, std::move(bytes), 0);
496 }
498
511 void Insert(const_iterator pos,
512 UniquePtr<std::byte[]>&& bytes,
513 size_t offset,
514 size_t length = dynamic_extent);
515 void Insert(const_iterator pos,
516 UniquePtr<const std::byte[]>&& bytes,
517 size_t offset,
518 size_t length = dynamic_extent);
520
531 void Insert(const_iterator pos, const SharedPtr<std::byte[]>& bytes) {
532 Insert(pos, bytes, 0);
533 }
534 void Insert(const_iterator pos, const SharedPtr<const std::byte[]>& bytes) {
535 Insert(pos, bytes, 0);
536 }
538
551 void Insert(const_iterator pos,
552 const SharedPtr<std::byte[]>& bytes,
553 size_t offset,
554 size_t length = dynamic_extent);
555 void Insert(const_iterator pos,
556 const SharedPtr<const std::byte[]>& bytes,
557 size_t offset,
558 size_t length = dynamic_extent);
560
568 template <multibuf::Property... kOtherProperties>
569 [[nodiscard]] bool TryReserveForPushBack(
571
577 [[nodiscard]] bool TryReserveForPushBack();
578
586 template <multibuf::Property... kOtherProperties>
588
596 template <
597 int&... kExplicitGuard,
598 typename T,
599 typename =
600 std::enable_if_t<std::is_constructible_v<ConstByteSpan, T>, int>>
601 void PushBack(const T& bytes);
602
612 void PushBack(UniquePtr<std::byte[]>&& bytes) {
613 PushBack(std::move(bytes), 0);
614 }
615 void PushBack(UniquePtr<const std::byte[]>&& bytes) {
616 PushBack(std::move(bytes), 0);
617 }
619
631 void PushBack(UniquePtr<std::byte[]>&& bytes,
632 size_t offset,
633 size_t length = dynamic_extent);
634
635 void PushBack(UniquePtr<const std::byte[]>&& bytes,
636 size_t offset,
637 size_t length = dynamic_extent);
639
649 void PushBack(const SharedPtr<std::byte[]>& bytes) { PushBack(bytes, 0); }
650 void PushBack(const SharedPtr<const std::byte[]>& bytes) {
651 PushBack(bytes, 0);
652 }
654
666 void PushBack(const SharedPtr<std::byte[]>& bytes,
667 size_t offset,
668 size_t length = dynamic_extent);
669 void PushBack(const SharedPtr<const std::byte[]>& bytes,
670 size_t offset,
671 size_t length = dynamic_extent);
673
681 [[nodiscard]] bool IsRemovable(const_iterator pos, size_t size) const {
682 return generic().IsRemovable(pos, size);
683 }
684
705
717
718 // clang-format off
740 // clang-format on
742 return generic().Discard(pos, size);
743 }
744
749 [[nodiscard]] bool IsReleasable(const_iterator pos) const {
750 return generic().IsReleasable(pos);
751 }
752
766
771 [[nodiscard]] bool IsShareable(const_iterator pos) const {
772 return generic().IsShareable(pos);
773 }
774
790
799 size_t CopyTo(ByteSpan dst, size_t offset = 0) const {
800 return generic().CopyTo(dst, offset);
801 }
802
811 size_t CopyFrom(ConstByteSpan src, size_t offset = 0) {
812 static_assert(!is_const(),
813 "`CopyFrom` may only be called on mutable MultiBufs");
814 return generic().CopyFrom(src, offset);
815 }
816
833 ConstByteSpan Get(ByteSpan copy, size_t offset = 0) const {
834 return generic().Get(copy, offset);
835 }
836
852 template <int&... kExplicitGuard, typename Visitor>
853 auto Visit(Visitor visitor, ByteSpan copy, size_t offset) {
854 return visitor(Get(copy, offset));
855 }
856
862 void Clear() { generic().Clear(); }
863
870 void ShrinkToFit() { generic().ShrinkToFit(); }
871
872 // Observable methods.
873
878 constexpr multibuf::Observer* observer() const {
879 static_assert(is_observable(),
880 "`observer` may only be called on observable MultiBufs");
881 return generic().observer_;
882 }
883
891 static_assert(is_observable(),
892 "`set_observer` may only be called on observable MultiBufs");
893 generic().observer_ = observer;
894 }
895
896 // Layerable methods.
897
904 size_type NumFragments() const {
905 static_assert(is_layerable(),
906 "`NumFragments` may only be called on layerable MultiBufs");
907 return generic().NumFragments();
908 }
909
913 constexpr size_type NumLayers() const {
914 static_assert(is_layerable(),
915 "`NumLayers` may only be called on layerable MultiBufs");
916 return generic().NumLayers();
917 }
918
926 [[nodiscard]] bool TryReserveLayers(size_t num_layers,
927 size_t num_chunks = 1) {
928 return generic().TryReserveLayers(num_layers, num_chunks);
929 }
930
950 [[nodiscard]] bool AddLayer(size_t offset, size_t length = dynamic_extent) {
951 static_assert(is_layerable(),
952 "`AddLayer` may only be called on layerable MultiBufs");
953 return generic().AddLayer(offset, length);
954 }
955
959 static_assert(is_layerable(),
960 "`SealTopLayer` may only be called on layerable MultiBufs");
961 return generic().SealTopLayer();
962 }
963
967 static_assert(is_layerable(),
968 "`UnsealTopLayer` may only be called on layerable MultiBufs");
969 return generic().UnsealTopLayer();
970 }
971
973 [[nodiscard]] bool IsTopLayerSealed() {
974 static_assert(
975 is_layerable(),
976 "`IsTopLayerSealed` may only be called on layerable MultiBufs");
977 return generic().IsTopLayerSealed();
978 }
979
988 void TruncateTopLayer(size_t length) {
989 static_assert(
990 is_layerable(),
991 "`TruncateTopLayer` may only be called on layerable MultiBufs");
992 generic().TruncateTopLayer(length);
993 }
994
1002 static_assert(!is_const() && is_layerable(),
1003 "`SetTopLayer` may only be called on mutable, layerable "
1004 "MultiBufs");
1005 PW_ASSERT(src.size() <= size());
1006 CopyFrom(src);
1007 TruncateTopLayer(src.size());
1008 }
1009
1018 void PopLayer() {
1019 static_assert(is_layerable(),
1020 "`PopLayer` may only be called on layerable MultiBufs");
1021 generic().PopLayer();
1022 }
1023
1024 protected:
1025 constexpr BasicMultiBuf() { multibuf::internal::PropertiesAreValid(); }
1026
1027 private:
1028 template <bool kValidCopyOrMove = false>
1029 static constexpr void InvalidCopyOrMove() {
1030 static_assert(kValidCopyOrMove,
1031 "Only copies and moves from `BasicMultiBuf<...>::Instance`"
1032 "to `BasicMultiBuf<...>&` or another "
1033 "`BasicMultiBuf<...>::Instance` are valid.");
1034 }
1035
1036 template <multibuf::Property...>
1037 friend class BasicMultiBuf;
1038
1039 constexpr GenericMultiBuf& generic() {
1040 return static_cast<GenericMultiBuf&>(*this);
1041 }
1042 constexpr const GenericMultiBuf& generic() const {
1043 return static_cast<const GenericMultiBuf&>(*this);
1044 }
1045};
1046
1048
1050
1051namespace multibuf::internal {
1052
1053// A generic MultiBuf implementation that provides the functionality of any
1054// `BasicMultiBuf<kProperties>` type.
1055//
1056// This class should not be instantiated directly. Instead, use `Instance` as
1057// described below. This is the base class for all `Instance` types, and derives
1058// from every supported `BasicMultiBuf` type. It implements the MultiBuf
1059// behavior in one type, and allows for performing conversions from `Instance`s
1060// and `BasicMultiBuf`s to `BasicMultiBuf`s with different yet compatible
1061// propreties.
1063 : private BasicMultiBuf<>,
1070 private BasicMultiBuf<Property::kConst,
1071 Property::kLayerable,
1072 Property::kObservable> {
1073 private:
1074 using ControlBlock = allocator::internal::ControlBlock;
1075 using typename BasicMultiBuf<>::ChunksType;
1076 using typename BasicMultiBuf<>::ConstChunksType;
1077 using typename BasicMultiBuf<>::Deque;
1078
1079 public:
1080 using typename BasicMultiBuf<>::const_iterator;
1081 using typename BasicMultiBuf<>::const_pointer;
1082 using typename BasicMultiBuf<>::const_reference;
1083 using typename BasicMultiBuf<>::difference_type;
1084 using typename BasicMultiBuf<>::iterator;
1085 using typename BasicMultiBuf<>::pointer;
1086 using typename BasicMultiBuf<>::reference;
1087 using typename BasicMultiBuf<>::size_type;
1088
1089 ~GenericMultiBuf() { Clear(); }
1090
1091 // Not copyable.
1092 GenericMultiBuf(const GenericMultiBuf&) = delete;
1093 GenericMultiBuf& operator=(const GenericMultiBuf&) = delete;
1094
1095 // Movable.
1097 : GenericMultiBuf(other.deque_.get_allocator()) {
1098 *this = std::move(other);
1099 }
1100 GenericMultiBuf& operator=(GenericMultiBuf&& other);
1101
1102 private:
1103 template <::pw::multibuf::Property...>
1104 friend class ::pw::BasicMultiBuf;
1105
1106 template <typename>
1107 friend class Instance;
1108
1110 constexpr explicit GenericMultiBuf(Allocator& allocator)
1111 : deque_(allocator) {}
1112
1113 template <typename MultiBufType>
1114 constexpr MultiBufType& as() {
1115 return *this;
1116 }
1117
1118 template <typename MultiBufType>
1119 constexpr const MultiBufType& as() const {
1120 return *this;
1121 }
1122
1123 // Accessors.
1124
1126 constexpr bool empty() const { return deque_.empty(); }
1127
1129 constexpr bool at_capacity() const {
1130 return deque_.size() == deque_.capacity();
1131 }
1132
1134 constexpr size_t size() const {
1135 return static_cast<size_t>(cend() - cbegin());
1136 }
1137
1138 // Iterators.
1139
1140 constexpr ChunksType Chunks() {
1141 return ChunksType(deque_, entries_per_chunk_);
1142 }
1143 constexpr ConstChunksType ConstChunks() const {
1144 return ConstChunksType(deque_, entries_per_chunk_);
1145 }
1146
1147 constexpr iterator begin() { return iterator(Chunks().begin(), 0); }
1148 constexpr const_iterator cbegin() const {
1149 return const_iterator(ConstChunks().cbegin(), 0);
1150 }
1151
1152 constexpr iterator end() { return iterator(Chunks().end(), 0); }
1153 constexpr const_iterator cend() const {
1154 return const_iterator(ConstChunks().cend(), 0);
1155 }
1156
1157 // Mutators.
1158
1160 [[nodiscard]] bool TryReserveChunks(size_t num_chunks) {
1161 return TryReserveLayers(NumLayers(), num_chunks);
1162 }
1163
1166 [[nodiscard]] bool TryReserveForInsert(const_iterator pos,
1167 const GenericMultiBuf& mb);
1168 [[nodiscard]] bool TryReserveForInsert(const_iterator pos);
1170
1173 void Insert(const_iterator pos, GenericMultiBuf&& mb);
1174 void Insert(const_iterator pos, ConstByteSpan bytes);
1175 void Insert(const_iterator pos,
1176 ConstByteSpan bytes,
1177 size_t offset,
1178 size_t length,
1179 Deallocator* deallocator);
1180 void Insert(const_iterator pos,
1181 ConstByteSpan bytes,
1182 size_t offset,
1183 size_t length,
1184 ControlBlock* control_block);
1186
1188 [[nodiscard]] constexpr bool IsRemovable(const_iterator pos,
1189 size_t size) const {
1190 return pos != cend() && size != 0 &&
1191 size <= static_cast<size_t>(cend() - pos);
1192 }
1193
1195 Result<GenericMultiBuf> Remove(const_iterator pos, size_t size);
1196
1198 Result<GenericMultiBuf> PopFrontFragment();
1199
1201 Result<const_iterator> Discard(const_iterator pos, size_t size);
1202
1204 [[nodiscard]] bool IsReleasable(const_iterator pos) const;
1205
1208
1210 [[nodiscard]] bool IsShareable(const_iterator pos) const;
1211
1214
1216 size_t CopyTo(ByteSpan dst, size_t offset) const {
1217 return CopyToImpl(dst, offset, 0);
1218 }
1219
1221 size_t CopyFrom(ConstByteSpan src, size_t offset);
1222
1224 ConstByteSpan Get(ByteSpan copy, size_t offset) const;
1225
1227 void Clear();
1228
1230 void ShrinkToFit();
1231
1232 // Layerable methods.
1233
1235 size_type NumFragments() const;
1236
1238 constexpr size_type NumLayers() const {
1239 return entries_per_chunk_ - Entry::kMinEntriesPerChunk + 1;
1240 }
1241
1243 [[nodiscard]] bool TryReserveLayers(size_t num_layers, size_t num_chunks = 1);
1244
1246 [[nodiscard]] bool AddLayer(size_t offset, size_t length = dynamic_extent);
1247
1249 void SealTopLayer();
1250
1252 void UnsealTopLayer();
1253
1255 void TruncateTopLayer(size_t length);
1256
1258 void PopLayer();
1259
1260 // Implementation methods.
1261 //
1262 // These methods are used to implement the methods above, and should not be
1263 // called directly by BasicMultiBuf<>.
1264
1269 static size_t CheckRange(size_t offset, size_t length, size_t size);
1270
1272 constexpr size_type num_chunks() const {
1273 return deque_.size() / entries_per_chunk_;
1274 }
1275
1277 constexpr size_type memory_context_index(size_type chunk) const {
1278 return Entry::memory_context_index(chunk, entries_per_chunk_);
1279 }
1280
1282 constexpr size_type data_index(size_type chunk) const {
1283 return Entry::data_index(chunk, entries_per_chunk_);
1284 }
1285
1287 constexpr size_type base_view_index(size_type chunk) const {
1288 return Entry::base_view_index(chunk, entries_per_chunk_);
1289 }
1290
1292 constexpr size_type view_index(size_type chunk, size_type layer) const {
1293 return Entry::view_index(chunk, entries_per_chunk_, layer);
1294 }
1295
1297 constexpr size_type top_view_index(size_type chunk) const {
1298 return Entry::top_view_index(chunk, entries_per_chunk_);
1299 }
1300
1302 constexpr std::byte* GetData(size_type chunk) const {
1303 return deque_[data_index(chunk)].data;
1304 }
1305
1308 constexpr bool IsOwned(size_type chunk) const {
1309 return deque_[base_view_index(chunk)].base_view.owned;
1310 }
1311
1314 constexpr bool IsShared(size_type chunk) const {
1315 return deque_[base_view_index(chunk)].base_view.shared;
1316 }
1317
1319 constexpr bool IsSealed(size_type chunk) const {
1320 return entries_per_chunk_ == Entry::kMinEntriesPerChunk
1321 ? false
1322 : deque_[top_view_index(chunk)].view.sealed;
1323 }
1324
1326 constexpr bool IsBoundary(size_type chunk) const {
1327 return entries_per_chunk_ == Entry::kMinEntriesPerChunk
1328 ? true
1329 : deque_[top_view_index(chunk)].view.boundary;
1330 }
1331
1335 constexpr size_type GetOffset(size_type chunk, uint16_t layer) const {
1336 return layer == 1 ? deque_[base_view_index(chunk)].base_view.offset
1337 : deque_[view_index(chunk, layer)].view.offset;
1338 }
1339
1341 constexpr size_type GetOffset(size_type chunk) const {
1342 return GetOffset(chunk, NumLayers());
1343 }
1344
1346 constexpr size_type GetRelativeOffset(size_type chunk) const {
1347 uint16_t layer = NumLayers();
1348 if (layer == 1) {
1349 return GetOffset(chunk, layer);
1350 }
1351 return GetOffset(chunk, layer) - GetOffset(chunk, layer - 1);
1352 }
1353
1355 constexpr size_type GetLength(size_type chunk) const {
1356 return entries_per_chunk_ == Entry::kMinEntriesPerChunk
1357 ? deque_[base_view_index(chunk)].base_view.length
1358 : deque_[top_view_index(chunk)].view.length;
1359 }
1360
1362 constexpr ByteSpan GetView(size_type chunk) const {
1363 return ByteSpan(GetData(chunk) + GetOffset(chunk), GetLength(chunk));
1364 }
1365
1369 std::pair<size_type, size_type> GetChunkAndOffset(const_iterator pos) const;
1370
1373 [[nodiscard]] bool TryConvertToShared(size_type chunk);
1374
1379 [[nodiscard]] bool TryReserveEntries(size_type num_entries,
1380 bool split = false);
1381
1384 size_type InsertChunks(const_iterator pos, size_type num_chunks);
1385
1388 size_type Insert(const_iterator pos,
1389 ConstByteSpan bytes,
1390 size_t offset,
1391 size_t length);
1392
1402 void SplitBase(size_type chunk, Deque& out_deque, size_type out_chunk);
1403
1409 void SplitBefore(size_type chunk,
1410 size_type split,
1411 Deque& out_deque,
1412 size_type out_chunk);
1413
1416 void SplitBefore(size_type chunk, size_type split);
1417
1423 void SplitAfter(size_type chunk,
1424 size_type split,
1425 Deque& out_deque,
1426 size_type out_chunk);
1427
1430 void SplitAfter(size_type chunk, size_type split);
1431
1442 [[nodiscard]] bool TryReserveForRemove(const_iterator pos,
1443 size_t size,
1444 GenericMultiBuf* out);
1445
1450 void MoveRange(const_iterator pos, size_t size, GenericMultiBuf& out);
1451
1455 void ClearRange(const_iterator pos, size_t size);
1456
1461 void EraseRange(const_iterator pos, size_t size);
1462
1465 size_t CopyToImpl(ByteSpan dst, size_t offset, size_type start) const;
1466
1468 [[nodiscard]] bool IsTopLayerSealed() const;
1469
1472 void SetLayer(size_t offset, size_t length);
1473
1474 // Describes the memory and views to that in a one-dimensional sequence.
1475 // Every `entries_per_chunk_`-th entry holds a pointer to memory, each entry
1476 // for a given offset less than `entries_per_chunk_` after that entry is a
1477 // view that is part of the same "layer" in the MultiBuf.
1478 //
1479 // This base type will always have 0 or 2 layers, but derived classes may add
1480 // more.
1481 //
1482 // For example, a MultiBuf that has had 3 `Chunk`s and two additional layers
1483 // added would have a `deque_` resembling the following:
1484 //
1485 // buffer 0: buffer 1: buffer 2:
1486 // layer 3: deque_[0x3].view deque_[0x7].view deque_[0xB].view
1487 // layer 2: deque_[0x2].view deque_[0x6].view deque_[0xA].view
1488 // layer 1: deque_[0x1].view deque_[0x5].view deque_[0x9].view
1489 // layer 0: deque_[0x0].data deque_[0x4].data deque_[0x8].data
1490 Deque deque_;
1491
1492 // Number of entries per chunk in this MultiBuf.
1493 size_type entries_per_chunk_ = Entry::kMinEntriesPerChunk;
1494
1497 Observer* observer_ = nullptr;
1498};
1499
1518template <typename MultiBufType>
1520 public:
1521 constexpr explicit Instance(Allocator& allocator) : base_(allocator) {}
1522
1523 constexpr Instance(Instance&&) = default;
1524 constexpr Instance& operator=(Instance&&) = default;
1525
1526 // Provide a more helpful compile-time error when a user tries to
1527 // copy-construct an interface type.
1528 template <Property... kProperties>
1529 constexpr Instance(const BasicMultiBuf<kProperties...>&)
1530 : base_(allocator::GetNullAllocator()) {
1531 MoveOnly<>();
1532 }
1533
1534 // Provide a more helpful compile-time error when a user tries to copy-assign
1535 // an interface type.
1536 template <Property... kProperties>
1537 constexpr Instance& operator=(const BasicMultiBuf<kProperties...>&) {
1538 MoveOnly<>();
1539 }
1540
1541 constexpr Instance(MultiBufType&& mb)
1542 : base_(std::move(static_cast<GenericMultiBuf&>(mb))) {}
1543
1544 constexpr Instance& operator=(MultiBufType&& mb) {
1545 base_ = std::move(static_cast<GenericMultiBuf&>(mb));
1546 return *this;
1547 }
1548
1549 template <Property... kProperties>
1551 : base_(std::move(static_cast<internal::GenericMultiBuf&>(mb))) {
1552 internal::AssertIsConvertible<BasicMultiBuf<kProperties...>,
1553 MultiBufType>();
1554 }
1555
1556 template <Property... kProperties>
1557 constexpr Instance& operator=(BasicMultiBuf<kProperties...>&& mb) {
1558 internal::AssertIsConvertible<BasicMultiBuf<kProperties...>,
1559 MultiBufType>();
1560 base_ = std::move(static_cast<internal::GenericMultiBuf&>(mb));
1561 return *this;
1562 }
1563
1564 constexpr MultiBufType* operator->() { return &base_.as<MultiBufType>(); }
1565 constexpr const MultiBufType* operator->() const {
1566 return &base_.as<MultiBufType>();
1567 }
1568
1569 constexpr MultiBufType& operator*() & { return base_.as<MultiBufType>(); }
1570 constexpr const MultiBufType& operator*() const& {
1571 return base_.as<MultiBufType>();
1572 }
1573
1574 constexpr MultiBufType&& operator*() && {
1575 return std::move(base_.as<MultiBufType>());
1576 }
1577 constexpr const MultiBufType&& operator*() const&& {
1578 return std::move(base_.as<MultiBufType>());
1579 }
1580
1581 constexpr operator MultiBufType&() & { return base_.as<MultiBufType>(); }
1582 constexpr operator const MultiBufType&() const& {
1583 return base_.as<MultiBufType>();
1584 }
1585
1586 constexpr operator MultiBufType&&() && {
1587 return std::move(base_.as<MultiBufType>());
1588 }
1589 constexpr operator const MultiBufType&&() const&& {
1590 return std::move(base_.as<MultiBufType>());
1591 }
1592
1593 private:
1594 // Helper functions to provide a more helpful compile-time error when a this
1595 // type is used incorrectly.
1596 template <bool kMoveOnly = false>
1597 static constexpr void MoveOnly() {
1598 static_assert(kMoveOnly,
1599 "Instances can only be created from existing MultiBufs using "
1600 "move-construction or move-assignment.");
1601 }
1602
1603 GenericMultiBuf base_;
1604};
1605
1606} // namespace multibuf::internal
1607
1608// Template method implementations.
1609
1610template <multibuf::Property... kProperties>
1612 return generic().TryReserveForInsert(pos);
1613}
1614
1615template <multibuf::Property... kProperties>
1616template <multibuf::Property... kOtherProperties>
1619 multibuf::internal::AssertIsConvertible<BasicMultiBuf<kOtherProperties...>,
1620 BasicMultiBuf>();
1621 return generic().TryReserveForInsert(pos,
1622 static_cast<const GenericMultiBuf&>(mb));
1623}
1624
1625template <multibuf::Property... kProperties>
1626template <multibuf::Property... kOtherProperties>
1629 multibuf::internal::AssertIsConvertible<BasicMultiBuf<kOtherProperties...>,
1630 BasicMultiBuf>();
1631 generic().Insert(pos, std::move(mb.generic()));
1632}
1633
1634template <multibuf::Property... kProperties>
1635template <int&... kExplicitGuard, typename T, typename>
1637 using data_ptr_type = decltype(std::data(std::declval<T&>()));
1638 static_assert(std::is_same_v<data_ptr_type, std::byte*> || is_const(),
1639 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1640 generic().Insert(pos, bytes);
1641}
1642
1643template <multibuf::Property... kProperties>
1644void BasicMultiBuf<kProperties...>::Insert(const_iterator pos,
1645 UniquePtr<std::byte[]>&& bytes,
1646 size_t offset,
1647 size_t length) {
1648 ConstByteSpan chunk(bytes.get(), bytes.size());
1649 generic().Insert(pos, chunk, offset, length, bytes.deallocator());
1650 bytes.Release();
1651}
1652
1653template <multibuf::Property... kProperties>
1654void BasicMultiBuf<kProperties...>::Insert(const_iterator pos,
1655 UniquePtr<const std::byte[]>&& bytes,
1656 size_t offset,
1657 size_t length) {
1658 static_assert(is_const(),
1659 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1660 ConstByteSpan chunk(bytes.get(), bytes.size());
1661 generic().Insert(pos, chunk, offset, length, bytes.deallocator());
1662 bytes.Release();
1663}
1664
1665template <multibuf::Property... kProperties>
1666void BasicMultiBuf<kProperties...>::Insert(const_iterator pos,
1667 const SharedPtr<std::byte[]>& bytes,
1668 size_t offset,
1669 size_t length) {
1670 ConstByteSpan chunk(bytes.get(), bytes.size());
1671 generic().Insert(pos, chunk, offset, length, bytes.control_block());
1672}
1673
1674template <multibuf::Property... kProperties>
1676 const_iterator pos,
1677 const SharedPtr<const std::byte[]>& bytes,
1678 size_t offset,
1679 size_t length) {
1680 static_assert(is_const(),
1681 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1682 ConstByteSpan chunk(bytes.get(), bytes.size());
1683 generic().Insert(pos, chunk, offset, length, bytes.control_block());
1684}
1685
1686template <multibuf::Property... kProperties>
1687template <multibuf::Property... kOtherProperties>
1690 return generic().TryReserveForInsert(end(),
1691 static_cast<const GenericMultiBuf&>(mb));
1692}
1693
1694template <multibuf::Property... kProperties>
1696 return generic().TryReserveForInsert(end());
1697}
1698
1699template <multibuf::Property... kProperties>
1700template <multibuf::Property... kOtherProperties>
1703 Insert(end(), std::move(mb));
1704}
1705
1706template <multibuf::Property... kProperties>
1707template <int&... kExplicitGuard, typename T, typename>
1709 using data_ptr_type = decltype(std::data(std::declval<T&>()));
1710 static_assert(std::is_same_v<data_ptr_type, std::byte*> || is_const(),
1711 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1712 Insert(end(), bytes);
1713}
1714
1715template <multibuf::Property... kProperties>
1716void BasicMultiBuf<kProperties...>::PushBack(UniquePtr<std::byte[]>&& bytes,
1717 size_t offset,
1718 size_t length) {
1719 Insert(end(), std::move(bytes), offset, length);
1720}
1721
1722template <multibuf::Property... kProperties>
1724 UniquePtr<const std::byte[]>&& bytes, size_t offset, size_t length) {
1725 static_assert(is_const(),
1726 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1727 Insert(end(), std::move(bytes), offset, length);
1728}
1729
1730template <multibuf::Property... kProperties>
1732 const SharedPtr<std::byte[]>& bytes, size_t offset, size_t length) {
1733 Insert(end(), bytes, offset, length);
1734}
1735
1736template <multibuf::Property... kProperties>
1738 const SharedPtr<const std::byte[]>& bytes, size_t offset, size_t length) {
1739 static_assert(is_const(),
1740 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1741 Insert(end(), bytes, offset, length);
1742}
1743
1744template <multibuf::Property... kProperties>
1745Result<multibuf::internal::Instance<BasicMultiBuf<kProperties...>>>
1747 auto result = generic().Remove(pos, size);
1748 if (!result.ok()) {
1749 return result.status();
1750 }
1751 return Instance(std::move(*result));
1752}
1753
1754template <multibuf::Property... kProperties>
1757 auto result = generic().PopFrontFragment();
1758 if (!result.ok()) {
1759 return result.status();
1760 }
1761 return Instance(std::move(*result));
1762}
1763
1764template <multibuf::Property... kProperties>
1765UniquePtr<typename BasicMultiBuf<kProperties...>::value_type[]>
1767 UniquePtr<std::byte[]> bytes = generic().Release(pos);
1768 if constexpr (is_const()) {
1769 UniquePtr<const std::byte[]> const_bytes(
1770 bytes.get(), bytes.size(), *(bytes.deallocator()));
1771 bytes.Release();
1772 return const_bytes;
1773 } else {
1774 return bytes;
1775 }
1776}
1777
1778template <multibuf::Property... kProperties>
1779SharedPtr<typename BasicMultiBuf<kProperties...>::value_type[]>
1781 return SharedPtr<value_type[]>(generic().Share(pos));
1782}
1783
1784} // namespace pw
Definition: allocator.h:43
bool IsTopLayerSealed()
Returns whether the "sealed" flag is set in the top layer.
Definition: multibuf_v2.h:973
bool AddLayer(size_t offset, size_t length=dynamic_extent)
Definition: multibuf_v2.h:950
void Insert(const_iterator pos, BasicMultiBuf< kOtherProperties... > &&mb)
Definition: multibuf_v2.h:1627
void SealTopLayer()
Definition: multibuf_v2.h:958
static constexpr bool is_layerable()
Returns whether additional views can be layered on the MultiBuf.
Definition: multibuf_v2.h:197
void UnsealTopLayer()
Definition: multibuf_v2.h:966
SharedPtr< value_type[]> Share(const_iterator pos)
Definition: multibuf_v2.h:1780
void Insert(const_iterator pos, const T &bytes)
Definition: multibuf_v2.h:1636
void TruncateTopLayer(size_t length)
Definition: multibuf_v2.h:988
void SetTopLayer(ConstByteSpan src)
Definition: multibuf_v2.h:1001
void PushBack(const T &bytes)
Definition: multibuf_v2.h:1708
static constexpr bool is_const()
Returns whether the MultiBuf data is immutable.
Definition: multibuf_v2.h:192
void PopLayer()
Definition: multibuf_v2.h:1018
void Clear()
Definition: multibuf_v2.h:862
ConstByteSpan Get(ByteSpan copy, size_t offset=0) const
Definition: multibuf_v2.h:833
void set_observer(multibuf::Observer *observer)
Definition: multibuf_v2.h:890
UniquePtr< value_type[]> Release(const_iterator pos)
Definition: multibuf_v2.h:1766
bool TryReserveChunks(size_t num_chunks)
Definition: multibuf_v2.h:423
size_t CopyTo(ByteSpan dst, size_t offset=0) const
Definition: multibuf_v2.h:799
size_t CopyFrom(ConstByteSpan src, size_t offset=0)
Definition: multibuf_v2.h:811
bool TryReserveForInsert(const_iterator pos)
Definition: multibuf_v2.h:1611
void ShrinkToFit()
Definition: multibuf_v2.h:870
constexpr multibuf::Observer * observer() const
Definition: multibuf_v2.h:878
bool TryReserveForPushBack(const BasicMultiBuf< kOtherProperties... > &mb)
Definition: multibuf_v2.h:1688
Result< Instance > PopFrontFragment()
Definition: multibuf_v2.h:1756
Result< Instance > Remove(const_iterator pos, size_t size)
Definition: multibuf_v2.h:1746
static constexpr bool is_observable()
Returns whether an observer can be registered on the MultiBuf.
Definition: multibuf_v2.h:202
constexpr size_t size() const
Definition: multibuf_v2.h:332
bool TryReserveLayers(size_t num_layers, size_t num_chunks=1)
Definition: multibuf_v2.h:926
bool IsShareable(const_iterator pos) const
Definition: multibuf_v2.h:771
constexpr size_type NumLayers() const
Definition: multibuf_v2.h:913
constexpr bool empty() const
Definition: multibuf_v2.h:322
Result< const_iterator > Discard(const_iterator pos, size_t size)
Definition: multibuf_v2.h:741
constexpr bool at_capacity() const
Definition: multibuf_v2.h:328
bool IsReleasable(const_iterator pos) const
Definition: multibuf_v2.h:749
bool IsRemovable(const_iterator pos, size_t size) const
Definition: multibuf_v2.h:681
bool TryReserveForInsert(const_iterator pos, const BasicMultiBuf< kOtherProperties... > &mb)
Definition: multibuf_v2.h:1617
size_type NumFragments() const
Definition: multibuf_v2.h:904
void PushBack(BasicMultiBuf< kOtherProperties... > &&mb)
Definition: multibuf_v2.h:1701
bool TryReserveForPushBack()
Definition: multibuf_v2.h:1695
auto Visit(Visitor visitor, ByteSpan copy, size_t offset)
Definition: multibuf_v2.h:853
Abstract interface for releasing memory.
Definition: deallocator.h:29
Definition: dynamic_deque.h:60
Definition: result.h:143
Definition: shared_ptr.h:63
Definition: unique_ptr.h:43
element_type * Release() noexcept
Definition: unique_ptr.h:252
Deallocator * deallocator() const
Returns a pointer to the object that can destroy the value.
Definition: unique_ptr.h:183
size_t size() const
Definition: unique_ptr.h:176
Definition: observer.h:29
Definition: byte_iterator.h:43
Base class for ranges of chunks.
Definition: chunks.h:33
Definition: multibuf_v2.h:1072
Definition: multibuf_v2.h:1519
NullAllocator & GetNullAllocator()
Returns a reference to the NullAllocator singleton.
constexpr OutputIt copy(InputIt first, InputIt last, OutputIt d_first)
constexpr backport of <algorithm>'s std::copy for C++17.
Definition: algorithm.h:355
Property
Basic properties of a MultiBuf.
Definition: properties.h:25
Result(T value) -> Result< T >
Deduction guide to allow Result(v) rather than Result<T>(v).
The Pigweed namespace.
Definition: alignment.h:27