Pigweed
C/C++ API Reference
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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// __ ___ ___ _ _ ___ _ _ ___
17// \ \ / /_\ | _ \ \| |_ _| \| |/ __|
18// \ \/\/ / _ \| / .` || || .` | (_ |
19// \_/\_/_/ \_\_|_\_|\_|___|_|\_|\___|
20// _____ _____ ___ ___ ___ __ __ ___ _ _ _____ _ _
21// | __\ \/ / _ \ __| _ \_ _| \/ | __| \| |_ _/_\ | |
22// | _| > <| _/ _|| /| || |\/| | _|| .` | | |/ _ \| |__
23// |___/_/\_\_| |___|_|_\___|_| |_|___|_|\_| |_/_/ \_\____|
24//
25// MultiBuf version 2 is in an early, experimental state. The APIs are in flux
26// and may change without notice. Please do not rely on it in production code,
27// but feel free to explore and share feedback with the Pigweed team!
28
29#include <cstddef>
30#include <cstdint>
31#include <numeric>
32#include <type_traits>
33
34#include "pw_allocator/allocator.h"
35#include "pw_bytes/span.h"
36#include "pw_containers/dynamic_deque.h"
37#include "pw_multibuf/internal/byte_iterator.h"
38#include "pw_multibuf/internal/chunk_iterator.h"
39#include "pw_multibuf/internal/entry.h"
40#include "pw_multibuf/observer.h"
41#include "pw_multibuf/properties.h"
42#include "pw_span/span.h"
43
44namespace pw {
45
46// Forward declarations.
47template <MultiBufProperty...>
48class BasicMultiBuf;
49
50namespace multibuf_impl {
51
52template <typename>
53class Instance;
54
55class GenericMultiBuf;
56
57} // namespace multibuf_impl
58
61
62// Type aliases for convenience, listed here for easier discoverability.
63
66
69
73
78
82
87
92
98
192template <MultiBufProperty... kProperties>
194 protected:
196 using ChunksType = multibuf_impl::Chunks<typename Deque::size_type>;
197 using ConstChunksType = multibuf_impl::ConstChunks<typename Deque::size_type>;
199
201
202 public:
204 [[nodiscard]] static constexpr bool is_const() {
205 return ((kProperties == Property::kConst) || ...);
206 }
207
209 [[nodiscard]] static constexpr bool is_layerable() {
210 return ((kProperties == Property::kLayerable) || ...);
211 }
212
214 [[nodiscard]] static constexpr bool is_observable() {
215 return ((kProperties == Property::kObservable) || ...);
216 }
217
218 using size_type = typename Deque::size_type;
219 using difference_type = typename Deque::difference_type;
220 using iterator = multibuf_impl::ByteIterator<size_type, /*kIsConst=*/false>;
221 using const_iterator =
222 multibuf_impl::ByteIterator<size_type, /*kIsConst=*/true>;
223 using pointer = iterator::pointer;
224 using const_pointer = const_iterator::pointer;
225 using reference = iterator::reference;
226 using const_reference = const_iterator::reference;
227 using value_type = std::conditional_t<is_const(),
228 const_iterator::value_type,
229 iterator::value_type>;
230
251
252 // Interfaces are not copyable or movable; copy and move `Instance`s instead.
253
254 BasicMultiBuf(const BasicMultiBuf&) = delete;
255
256 template <Property... kOtherProperties>
258
259 BasicMultiBuf& operator=(const BasicMultiBuf&) = delete;
260
261 template <Property... kOtherProperties>
262 BasicMultiBuf& operator=(const BasicMultiBuf<kOtherProperties...>&) = delete;
263
264 BasicMultiBuf(BasicMultiBuf&&) = delete;
265
266 template <Property... kOtherProperties>
268
269 BasicMultiBuf& operator=(BasicMultiBuf&&) = delete;
270
271 template <Property... kOtherProperties>
273
274 // Conversions
275
276 template <typename OtherMultiBuf>
277 OtherMultiBuf& as() {
278 multibuf_impl::AssertIsConvertible<BasicMultiBuf, OtherMultiBuf>();
279 return generic().template as<OtherMultiBuf>();
280 }
281
282 template <typename OtherMultiBuf>
283 const OtherMultiBuf& as() const {
284 multibuf_impl::AssertIsConvertible<BasicMultiBuf, OtherMultiBuf>();
285 return generic().template as<OtherMultiBuf>();
286 }
287
288 template <typename OtherMultiBuf,
289 typename = multibuf_impl::EnableIfConvertible<BasicMultiBuf,
290 OtherMultiBuf>>
291 operator OtherMultiBuf&() {
292 return as<OtherMultiBuf>();
293 }
294
295 template <typename OtherMultiBuf,
296 typename = multibuf_impl::EnableIfConvertible<BasicMultiBuf,
297 OtherMultiBuf>>
298 operator const OtherMultiBuf&() const {
299 return as<OtherMultiBuf>();
300 }
301
302 // Accessors
303
306 constexpr bool empty() const { return generic().empty(); }
307
310 constexpr size_t size() const { return generic().size(); }
311
323 template <bool kMutable = !is_const()>
324 std::enable_if_t<kMutable, reference> at(size_t index) {
325 return *(begin() + static_cast<int>(index));
326 }
327 const_reference at(size_t index) const {
328 return *(begin() + static_cast<int>(index));
329 }
330
331 template <bool kMutable = !is_const()>
332 std::enable_if_t<kMutable, reference> operator[](size_t index) {
333 return at(index);
334 }
335 const_reference operator[](size_t index) const { return at(index); }
337
349 template <bool kMutable = !is_const()>
350 constexpr std::enable_if_t<kMutable, ChunksType> Chunks() {
351 return generic().Chunks();
352 }
353 ConstChunksType Chunks() const { return generic().ConstChunks(); }
354 ConstChunksType ConstChunks() const { return generic().ConstChunks(); }
356
357 // Iterators.
358
369 template <bool kMutable = !is_const()>
370 constexpr std::enable_if_t<kMutable, iterator> begin() {
371 return generic().begin();
372 }
373 constexpr const_iterator begin() const { return cbegin(); }
374 constexpr const_iterator cbegin() const { return generic().cbegin(); }
376
387 template <bool kMutable = !is_const()>
388 constexpr std::enable_if_t<kMutable, iterator> end() {
389 return generic().end();
390 }
391 constexpr const_iterator end() const { return cend(); }
392 constexpr const_iterator cend() const { return generic().cend(); }
394
395 // Other methods
396
406 bool IsCompatible(const BasicMultiBuf& mb) const {
407 return generic().IsCompatible(mb.generic());
408 }
409
419 bool IsCompatible(const UniquePtr<std::byte[]>& bytes) const {
420 return generic().IsCompatible(bytes.deallocator());
421 }
422 bool IsCompatible(const UniquePtr<const std::byte[]>& bytes) const {
423 return generic().IsCompatible(bytes.deallocator());
424 }
426
435 bool IsCompatible(const SharedPtr<std::byte[]>& bytes) const {
436 return generic().IsCompatible(bytes.control_block());
437 }
438 bool IsCompatible(const SharedPtr<const std::byte[]>& bytes) const {
439 return generic().IsCompatible(bytes.control_block());
440 }
442
455 [[nodiscard]] bool TryReserveChunks(size_type num_chunks) {
456 return generic().TryReserveChunks(num_chunks);
457 }
458
459 // Mutators
460
472 template <Property... kOtherProperties>
473 [[nodiscard]] bool TryReserveForInsert(
474 const_iterator pos, const BasicMultiBuf<kOtherProperties...>& mb);
475
487 template <
488 int&... kExplicitGuard,
489 typename T,
490 typename =
491 std::enable_if_t<std::is_constructible_v<ConstByteSpan, T>, int>>
492 [[nodiscard]] bool TryReserveForInsert(const_iterator pos, const T& bytes);
493
507 [[nodiscard]] bool TryReserveForInsert(const_iterator pos,
508 const UniquePtr<std::byte[]>& bytes);
509 [[nodiscard]] bool TryReserveForInsert(
510 const_iterator pos, const UniquePtr<const std::byte[]>& bytes);
512
526 [[nodiscard]] bool TryReserveForInsert(const_iterator pos,
527 const SharedPtr<std::byte[]>& bytes);
528 [[nodiscard]] bool TryReserveForInsert(
529 const_iterator pos, const SharedPtr<const std::byte[]>& bytes);
531
540 template <Property... kOtherProperties>
541 void Insert(const_iterator pos, BasicMultiBuf<kOtherProperties...>&& mb);
542
551 template <
552 int&... kExplicitGuard,
553 typename T,
554 typename =
555 std::enable_if_t<std::is_constructible_v<ConstByteSpan, T>, int>>
556 void Insert(const_iterator pos, const T& bytes);
557
568 void Insert(const_iterator pos, UniquePtr<std::byte[]>&& bytes) {
569 Insert(pos, std::move(bytes), 0);
570 }
571 void Insert(const_iterator pos, UniquePtr<const std::byte[]>&& bytes) {
572 Insert(pos, std::move(bytes), 0);
573 }
575
588 void Insert(const_iterator pos,
589 UniquePtr<std::byte[]>&& bytes,
590 size_t offset,
591 size_t length = dynamic_extent);
592 void Insert(const_iterator pos,
593 UniquePtr<const std::byte[]>&& bytes,
594 size_t offset,
595 size_t length = dynamic_extent);
597
608 void Insert(const_iterator pos, const SharedPtr<std::byte[]>& bytes) {
609 Insert(pos, bytes, 0);
610 }
611 void Insert(const_iterator pos, const SharedPtr<const std::byte[]>& bytes) {
612 Insert(pos, bytes, 0);
613 }
615
628 void Insert(const_iterator pos,
629 const SharedPtr<std::byte[]>& bytes,
630 size_t offset,
631 size_t length = dynamic_extent);
632 void Insert(const_iterator pos,
633 const SharedPtr<const std::byte[]>& bytes,
634 size_t offset,
635 size_t length = dynamic_extent);
637
645 template <Property... kOtherProperties>
646 [[nodiscard]] bool TryReserveForPushBack(
648
656 template <
657 int&... kExplicitGuard,
658 typename T,
659 typename =
660 std::enable_if_t<std::is_constructible_v<ConstByteSpan, T>, int>>
661 [[nodiscard]] bool TryReserveForPushBack(const T& bytes);
662
672 [[nodiscard]] bool TryReserveForPushBack(const UniquePtr<std::byte[]>& bytes);
673 [[nodiscard]] bool TryReserveForPushBack(
674 const UniquePtr<const std::byte[]>& bytes);
676
686 [[nodiscard]] bool TryReserveForPushBack(const SharedPtr<std::byte[]>& bytes);
687 [[nodiscard]] bool TryReserveForPushBack(
688 const SharedPtr<const std::byte[]>& bytes);
690
698 template <Property... kOtherProperties>
700
708 template <
709 int&... kExplicitGuard,
710 typename T,
711 typename =
712 std::enable_if_t<std::is_constructible_v<ConstByteSpan, T>, int>>
713 void PushBack(const T& bytes);
714
724 void PushBack(UniquePtr<std::byte[]>&& bytes) {
725 PushBack(std::move(bytes), 0);
726 }
727 void PushBack(UniquePtr<const std::byte[]>&& bytes) {
728 PushBack(std::move(bytes), 0);
729 }
731
743 void PushBack(UniquePtr<std::byte[]>&& bytes,
744 size_t offset,
745 size_t length = dynamic_extent);
746
747 void PushBack(UniquePtr<const std::byte[]>&& bytes,
748 size_t offset,
749 size_t length = dynamic_extent);
751
761 void PushBack(const SharedPtr<std::byte[]>& bytes) { PushBack(bytes, 0); }
762 void PushBack(const SharedPtr<const std::byte[]>& bytes) {
763 PushBack(bytes, 0);
764 }
766
778 void PushBack(const SharedPtr<std::byte[]>& bytes,
779 size_t offset,
780 size_t length = dynamic_extent);
781 void PushBack(const SharedPtr<const std::byte[]>& bytes,
782 size_t offset,
783 size_t length = dynamic_extent);
785
795 [[nodiscard]] bool IsRemovable(const_iterator pos, size_t size) const {
796 return generic().IsRemovable(pos, size);
797 }
798
824 Result<Instance> Remove(const_iterator pos, size_t size);
825
843 Result<Instance> PopFrontFragment();
844
872 Result<const_iterator> Discard(const_iterator pos, size_t size) {
873 return generic().Discard(pos, size);
874 }
875
880 [[nodiscard]] bool IsReleasable(const_iterator pos) const {
881 return generic().IsReleasable(pos);
882 }
883
896 UniquePtr<value_type[]> Release(const_iterator pos);
897
902 [[nodiscard]] bool IsShareable(const_iterator pos) const {
903 return generic().IsShareable(pos);
904 }
905
920 SharedPtr<value_type[]> Share(const_iterator pos);
921
930 size_t CopyTo(ByteSpan dst, size_t offset = 0) const {
931 return generic().CopyTo(dst, offset);
932 }
933
942 size_t CopyFrom(ConstByteSpan src, size_t offset = 0) {
943 static_assert(!is_const(),
944 "`CopyFrom` may only be called on mutable MultiBufs");
945 return generic().CopyFrom(src, offset);
946 }
947
964 ConstByteSpan Get(ByteSpan copy, size_t offset = 0) const {
965 return generic().Get(copy, offset);
966 }
967
983 template <int&... kExplicitGuard, typename Visitor>
984 auto Visit(Visitor visitor, ByteSpan copy, size_t offset) {
985 return visitor(Get(copy, offset));
986 }
987
993 void Clear() { generic().Clear(); }
994
995 // Observable methods.
996
1001 constexpr MultiBufObserver* observer() const {
1002 static_assert(is_observable(),
1003 "`observer` may only be called on observable MultiBufs");
1004 return generic().observer_;
1005 }
1006
1014 static_assert(is_observable(),
1015 "`set_observer` may only be called on observable MultiBufs");
1016 generic().observer_ = observer;
1017 }
1018
1019 // Layerable methods.
1020
1027 size_type NumFragments() const {
1028 static_assert(is_layerable(),
1029 "`NumFragments` may only be called on layerable MultiBufs");
1030 return generic().NumFragments();
1031 }
1032
1036 constexpr size_type NumLayers() const {
1037 static_assert(is_layerable(),
1038 "`NumLayers` may only be called on layerable MultiBufs");
1039 return generic().NumLayers();
1040 }
1041
1061 [[nodiscard]] bool AddLayer(size_t offset, size_t length = dynamic_extent) {
1062 static_assert(is_layerable(),
1063 "`AddLayer` may only be called on layerable MultiBufs");
1064 return generic().AddLayer(offset, length);
1065 }
1066
1070 static_assert(is_layerable(),
1071 "`SealTopLayer` may only be called on layerable MultiBufs");
1072 return generic().SealTopLayer();
1073 }
1074
1078 static_assert(is_layerable(),
1079 "`UnsealTopLayer` may only be called on layerable MultiBufs");
1080 return generic().UnsealTopLayer();
1081 }
1082
1094 [[nodiscard]] bool ResizeTopLayer(size_t offset,
1095 size_t length = dynamic_extent) {
1096 static_assert(is_layerable(),
1097 "`ResizeTopLayer` may only be called on layerable MultiBufs");
1098 return generic().ResizeTopLayer(offset, length);
1099 }
1100
1110 [[nodiscard]] bool PopLayer() {
1111 static_assert(is_layerable(),
1112 "`PopLayer` may only be called on layerable MultiBufs");
1113 return generic().PopLayer();
1114 }
1115
1116 protected:
1117 constexpr BasicMultiBuf() { multibuf_impl::PropertiesAreValid(); }
1118
1119 private:
1120 template <Property...>
1121 friend class BasicMultiBuf;
1122
1123 constexpr GenericMultiBuf& generic() {
1124 return static_cast<GenericMultiBuf&>(*this);
1125 }
1126 constexpr const GenericMultiBuf& generic() const {
1127 return static_cast<const GenericMultiBuf&>(*this);
1128 }
1129};
1130
1131namespace multibuf_impl {
1132
1133// A generic MultiBuf implementation that provides the functionality of any
1134// `BasicMultiBuf<kProperties>` type.
1135//
1136// This class should not be instantiated directly. Instead, use `Instance` as
1137// described below. This is the base class for all `Instance` types, and derives
1138// from every supported `BasicMultiBuf` type. It implements the MultiBuf
1139// behavior in one type, and allows for performing conversions from `Instance`s
1140// and `BasicMultiBuf`s to `BasicMultiBuf`s with different yet compatible
1141// propreties.
1143 : private BasicMultiBuf<>,
1156 private:
1157 using typename BasicMultiBuf<>::ChunksType;
1158 using typename BasicMultiBuf<>::ConstChunksType;
1159 using ControlBlock = allocator::internal::ControlBlock;
1160 using typename BasicMultiBuf<>::Deque;
1161
1162 public:
1163 using typename BasicMultiBuf<>::const_iterator;
1164 using typename BasicMultiBuf<>::const_pointer;
1165 using typename BasicMultiBuf<>::const_reference;
1166 using typename BasicMultiBuf<>::difference_type;
1167 using typename BasicMultiBuf<>::iterator;
1168 using typename BasicMultiBuf<>::pointer;
1169 using typename BasicMultiBuf<>::reference;
1170 using typename BasicMultiBuf<>::size_type;
1171
1172 ~GenericMultiBuf() { Clear(); }
1173
1174 // Not copyable.
1175 GenericMultiBuf(const GenericMultiBuf&) = delete;
1176 GenericMultiBuf& operator=(const GenericMultiBuf&) = delete;
1177
1178 // Movable.
1180 : GenericMultiBuf(other.deque_.get_allocator()) {
1181 *this = std::move(other);
1182 }
1183 GenericMultiBuf& operator=(GenericMultiBuf&& other);
1184
1185 private:
1186 template <MultiBufProperty...>
1187 friend class ::pw::BasicMultiBuf;
1188
1189 template <typename>
1190 friend class ::pw::multibuf_impl::Instance;
1191
1193 constexpr explicit GenericMultiBuf(Allocator& allocator)
1194 : deque_(allocator) {}
1195
1196 template <typename MultiBufType>
1197 MultiBufType& as() {
1198 return *this;
1199 }
1200
1201 template <typename MultiBufType>
1202 const MultiBufType& as() const {
1203 return *this;
1204 }
1205
1206 // Accessors.
1207
1209 constexpr bool empty() const { return deque_.empty(); }
1210
1212 constexpr size_t size() const {
1213 return static_cast<size_t>(cend() - cbegin());
1214 }
1215
1217 constexpr bool has_deallocator() const {
1218 return memory_tag_ == MemoryTag::kDeallocator || has_control_block();
1219 }
1220
1222 constexpr bool has_control_block() const {
1223 return memory_tag_ == MemoryTag::kControlBlock;
1224 }
1225
1226 // Iterators.
1227
1228 constexpr ChunksType Chunks() { return ChunksType(deque_, depth_); }
1229 constexpr ConstChunksType ConstChunks() const {
1230 return ConstChunksType(deque_, depth_);
1231 }
1232
1233 constexpr iterator begin() { return iterator(Chunks().begin(), 0); }
1234 constexpr const_iterator cbegin() const {
1235 return const_iterator(ConstChunks().cbegin(), 0);
1236 }
1237
1238 constexpr iterator end() { return iterator(Chunks().end(), 0); }
1239 constexpr const_iterator cend() const {
1240 return const_iterator(ConstChunks().cend(), 0);
1241 }
1242
1243 // Mutators.
1244
1246 bool IsCompatible(const GenericMultiBuf& other) const;
1247 bool IsCompatible(const Deallocator* other) const;
1248 bool IsCompatible(const ControlBlock* other) const;
1249
1251 [[nodiscard]] bool TryReserveChunks(size_type num_chunks);
1252
1255 [[nodiscard]] bool TryReserveForInsert(const_iterator pos,
1256 const GenericMultiBuf& mb);
1257 [[nodiscard]] bool TryReserveForInsert(const_iterator pos, size_t size);
1258 [[nodiscard]] bool TryReserveForInsert(const_iterator pos,
1259 size_t size,
1260 const Deallocator* deallocator);
1261 [[nodiscard]] bool TryReserveForInsert(const_iterator pos,
1262 size_t size,
1263 const ControlBlock* control_block);
1265
1268 void Insert(const_iterator pos, GenericMultiBuf&& mb);
1269 void Insert(const_iterator pos, ConstByteSpan bytes);
1270 void Insert(const_iterator pos,
1271 ConstByteSpan bytes,
1272 size_t offset,
1273 size_t length,
1274 Deallocator* deallocator);
1275 void Insert(const_iterator pos,
1276 ConstByteSpan bytes,
1277 size_t offset,
1278 size_t length,
1279 ControlBlock* control_block);
1281
1283 [[nodiscard]] bool IsRemovable(const_iterator pos, size_t size) const;
1284
1286 Result<GenericMultiBuf> Remove(const_iterator pos, size_t size);
1287
1289 Result<GenericMultiBuf> PopFrontFragment();
1290
1292 Result<const_iterator> Discard(const_iterator pos, size_t size);
1293
1295 [[nodiscard]] bool IsReleasable(const_iterator pos) const;
1296
1298 UniquePtr<std::byte[]> Release(const_iterator pos);
1299
1301 [[nodiscard]] bool IsShareable(const_iterator pos) const;
1302
1304 std::byte* Share(const_iterator pos);
1305
1307 size_t CopyTo(ByteSpan dst, size_t offset) const;
1308
1310 size_t CopyFrom(ConstByteSpan src, size_t offset);
1311
1313 ConstByteSpan Get(ByteSpan copy, size_t offset) const;
1314
1316 void Clear();
1317
1318 // Layerable methods.
1319
1321 size_type NumFragments() const;
1322
1324 constexpr size_type NumLayers() const { return depth_ - 1; }
1325
1327 [[nodiscard]] bool AddLayer(size_t offset, size_t length = dynamic_extent);
1328
1330 void SealTopLayer();
1331
1333 void UnsealTopLayer();
1334
1336 [[nodiscard]] bool ResizeTopLayer(size_t offset,
1337 size_t length = dynamic_extent);
1338
1340 [[nodiscard]] bool PopLayer();
1341
1342 // Implementation methods.
1343 //
1344 // These methods are used to implement the methods above, and should not be
1345 // called directly by BasicMultiBuf<>.
1346
1351 static size_t CheckRange(size_t offset, size_t length, size_t size);
1352
1354 constexpr std::byte* GetData(size_type index) const {
1355 return deque_[index].data;
1356 }
1357
1360 constexpr bool IsOwned(size_type index) const {
1361 return deque_[index + 1].base_view.owned;
1362 }
1363
1366 constexpr bool IsShared(size_type index) const {
1367 return deque_[index + 1].base_view.shared;
1368 }
1369
1371 constexpr bool IsSealed(size_type index) const {
1372 return depth_ == 2 ? false : deque_[index + depth_ - 1].view.sealed;
1373 }
1374
1377 constexpr bool IsBoundary(size_type index) const {
1378 return depth_ == 2 ? true : deque_[index + depth_ - 1].view.boundary;
1379 }
1380
1382 constexpr size_type GetOffset(size_type index) const {
1383 return depth_ == 2 ? deque_[index + 1].base_view.offset
1384 : deque_[index + depth_ - 1].view.offset;
1385 }
1386
1388 constexpr size_type GetLength(size_type index) const {
1389 return depth_ == 2 ? deque_[index + 1].base_view.length
1390 : deque_[index + depth_ - 1].view.length;
1391 }
1392
1394 constexpr ByteSpan GetView(size_type index) const {
1395 return ByteSpan(GetData(index) + GetOffset(index), GetLength(index));
1396 }
1397
1399 Deallocator* GetDeallocator() const;
1400
1402 void SetDeallocator(Deallocator* deallocator);
1403
1405 ControlBlock* GetControlBlock() const;
1406
1408 void SetControlBlock(ControlBlock* control_block);
1409
1411 void CopyMemoryContext(const GenericMultiBuf& other);
1412
1414 void ClearMemoryContext();
1415
1419 std::pair<size_type, size_type> GetIndexAndOffset(const_iterator pos) const;
1420
1426 bool TryReserveEntries(const_iterator pos, size_type num_entries);
1427 bool TryReserveEntries(size_type num_entries, bool split = false);
1429
1432 size_type InsertEntries(const_iterator pos, size_type num_entries);
1433
1436 size_type Insert(const_iterator pos,
1437 ConstByteSpan bytes,
1438 size_t offset,
1439 size_t length);
1440
1450 void SplitBase(size_type index, Deque& out_deque, size_type out_index);
1451
1457 void SplitBefore(size_type index,
1458 size_type split,
1459 Deque& out_deque,
1460 size_type out_index);
1461
1464 void SplitBefore(size_type index, size_type split);
1465
1471 void SplitAfter(size_type index,
1472 size_type split,
1473 Deque& out_deque,
1474 size_type out_index);
1475
1478 void SplitAfter(size_type index, size_type split);
1479
1490 [[nodiscard]] bool TryReserveForRemove(const_iterator pos,
1491 size_t size,
1492 GenericMultiBuf* out);
1493
1498 void CopyRange(const_iterator pos, size_t size, GenericMultiBuf& out);
1499
1503 void ClearRange(const_iterator pos, size_t size);
1504
1509 void EraseRange(const_iterator pos, size_t size);
1510
1513 size_type FindShared(size_type index, size_type start);
1514
1517 size_t CopyToImpl(ByteSpan dst, size_t offset, size_type start) const;
1518
1520 [[nodiscard]] bool IsTopLayerSealed() const;
1521
1524 void SetLayer(size_t offset, size_t length);
1525
1526 // Describes the memory and views to that in a one-dimensional sequence.
1527 // Every `depth_`-th entry holds a pointer to memory, each entry for a given
1528 // offset less than `depth_` after that entry is a view that is part of the
1529 // same "layer" in the MultiBuf.
1530 //
1531 // This base type will always have 0 or 2 layers, but derived classes may add
1532 // more.
1533 //
1534 // For example, a MultiBuf that has had 3 `Chunk`s and two additional layers
1535 // added would have a `deque_` resembling the following:
1536 //
1537 // buffer 0: buffer 1: buffer 2:
1538 // layer 3: deque_[0x3].view deque_[0x7].view deque_[0xB].view
1539 // layer 2: deque_[0x2].view deque_[0x6].view deque_[0xA].view
1540 // layer 1: deque_[0x1].view deque_[0x5].view deque_[0x9].view
1541 // layer 0: deque_[0x0].data deque_[0x4].data deque_[0x8].data
1542 Deque deque_;
1543
1544 // Number of entries per chunk in this MultiBuf.
1545 size_type depth_ = 2;
1546
1563 enum class MemoryTag : uint8_t {
1564 kEmpty,
1565 kDeallocator,
1566 kControlBlock,
1567 } memory_tag_ = MemoryTag::kEmpty;
1568
1569 union MemoryContext {
1570 Deallocator* deallocator;
1571 ControlBlock* control_block;
1572 } memory_context_ = {.deallocator = nullptr};
1574
1577 MultiBufObserver* observer_ = nullptr;
1578};
1579
1598template <typename MultiBufType>
1600 public:
1601 constexpr explicit Instance(Allocator& allocator) : base_(allocator) {}
1602
1603 constexpr Instance(Instance&&) = default;
1604 constexpr Instance& operator=(Instance&&) = default;
1605
1606 constexpr Instance(MultiBufType&& mb)
1607 : base_(std::move(static_cast<GenericMultiBuf&>(mb))) {}
1608
1609 constexpr Instance& operator=(MultiBufType&& mb) { base_ = std::move(mb); }
1610
1611 MultiBufType& operator*() { return base_.as<MultiBufType>(); }
1612 const MultiBufType& operator*() const { return base_.as<MultiBufType>(); }
1613
1614 MultiBufType* operator->() { return &base_.as<MultiBufType>(); }
1615 const MultiBufType* operator->() const { return &base_.as<MultiBufType>(); }
1616
1617 operator MultiBufType&() { return base_.as<MultiBufType>(); }
1618 operator const MultiBufType&() const { return base_.as<MultiBufType>(); }
1619
1620 private:
1621 GenericMultiBuf base_;
1622};
1623
1624} // namespace multibuf_impl
1625
1627
1628// Template method implementations.
1629
1630template <MultiBufProperty... kProperties>
1631template <MultiBufProperty... kOtherProperties>
1633 const_iterator pos, const BasicMultiBuf<kOtherProperties...>& mb) {
1634 multibuf_impl::AssertIsConvertible<BasicMultiBuf<kOtherProperties...>,
1635 BasicMultiBuf>();
1636 return generic().TryReserveForInsert(pos,
1637 static_cast<const GenericMultiBuf&>(mb));
1638}
1639
1640template <MultiBufProperty... kProperties>
1641template <int&... kExplicitGuard, typename T, typename>
1643 const T& bytes) {
1644 using data_ptr_type = decltype(std::data(std::declval<T&>()));
1645 static_assert(std::is_same_v<data_ptr_type, std::byte*> || is_const(),
1646 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1647 return generic().TryReserveForInsert(pos, bytes.size());
1648}
1649
1650template <MultiBufProperty... kProperties>
1652 const_iterator pos, const UniquePtr<std::byte[]>& bytes) {
1653 return generic().TryReserveForInsert(pos, bytes.size(), bytes.deallocator());
1654}
1655
1656template <MultiBufProperty... kProperties>
1658 const_iterator pos, const UniquePtr<const std::byte[]>& bytes) {
1659 static_assert(is_const(),
1660 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1661 return generic().TryReserveForInsert(pos, bytes.size(), bytes.deallocator());
1662}
1663
1664template <MultiBufProperty... kProperties>
1666 const_iterator pos, const SharedPtr<std::byte[]>& bytes) {
1667 return generic().TryReserveForInsert(
1668 pos, bytes.size(), bytes.control_block());
1669}
1670
1671template <MultiBufProperty... kProperties>
1673 const_iterator pos, const SharedPtr<const std::byte[]>& bytes) {
1674 static_assert(is_const(),
1675 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1676 return generic().TryReserveForInsert(
1677 pos, bytes.size(), bytes.control_block());
1678}
1679
1680template <MultiBufProperty... kProperties>
1681template <MultiBufProperty... kOtherProperties>
1683 const_iterator pos, BasicMultiBuf<kOtherProperties...>&& mb) {
1684 multibuf_impl::AssertIsConvertible<BasicMultiBuf<kOtherProperties...>,
1685 BasicMultiBuf>();
1686 generic().Insert(pos, std::move(mb.generic()));
1687}
1688
1689template <MultiBufProperty... kProperties>
1690template <int&... kExplicitGuard, typename T, typename>
1691void BasicMultiBuf<kProperties...>::Insert(const_iterator pos, const T& bytes) {
1692 using data_ptr_type = decltype(std::data(std::declval<T&>()));
1693 static_assert(std::is_same_v<data_ptr_type, std::byte*> || is_const(),
1694 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1695 generic().Insert(pos, bytes);
1696}
1697
1698template <MultiBufProperty... kProperties>
1699void BasicMultiBuf<kProperties...>::Insert(const_iterator pos,
1700 UniquePtr<std::byte[]>&& bytes,
1701 size_t offset,
1702 size_t length) {
1703 ConstByteSpan chunk(bytes.get(), bytes.size());
1704 generic().Insert(pos, chunk, offset, length, bytes.deallocator());
1705 bytes.Release();
1706}
1707
1708template <MultiBufProperty... kProperties>
1709void BasicMultiBuf<kProperties...>::Insert(const_iterator pos,
1710 UniquePtr<const std::byte[]>&& bytes,
1711 size_t offset,
1712 size_t length) {
1713 static_assert(is_const(),
1714 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1715 ConstByteSpan chunk(bytes.get(), bytes.size());
1716 generic().Insert(pos, chunk, offset, length, bytes.deallocator());
1717 bytes.Release();
1718}
1719
1720template <MultiBufProperty... kProperties>
1721void BasicMultiBuf<kProperties...>::Insert(const_iterator pos,
1722 const SharedPtr<std::byte[]>& bytes,
1723 size_t offset,
1724 size_t length) {
1725 ConstByteSpan chunk(bytes.get(), bytes.size());
1726 generic().Insert(pos, chunk, offset, length, bytes.control_block());
1727}
1728
1729template <MultiBufProperty... kProperties>
1731 const_iterator pos,
1732 const SharedPtr<const std::byte[]>& bytes,
1733 size_t offset,
1734 size_t length) {
1735 static_assert(is_const(),
1736 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1737 ConstByteSpan chunk(bytes.get(), bytes.size());
1738 generic().Insert(pos, chunk, offset, length, bytes.control_block());
1739}
1740
1741template <MultiBufProperty... kProperties>
1742template <MultiBufProperty... kOtherProperties>
1745 return TryReserveForInsert(end(), mb);
1746}
1747
1748template <MultiBufProperty... kProperties>
1749template <int&... kExplicitGuard, typename T, typename>
1751 using data_ptr_type = decltype(std::data(std::declval<T&>()));
1752 static_assert(std::is_same_v<data_ptr_type, std::byte*> || is_const(),
1753 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1754 return TryReserveForInsert(end(), bytes);
1755}
1756
1757template <MultiBufProperty... kProperties>
1759 const UniquePtr<std::byte[]>& bytes) {
1760 return TryReserveForInsert(end(), std::move(bytes));
1761}
1762
1763template <MultiBufProperty... kProperties>
1765 const UniquePtr<const std::byte[]>& bytes) {
1766 static_assert(is_const(),
1767 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1768 return TryReserveForInsert(end(), std::move(bytes));
1769}
1770
1771template <MultiBufProperty... kProperties>
1773 const SharedPtr<std::byte[]>& bytes) {
1774 return TryReserveForInsert(end(), bytes);
1775}
1776
1777template <MultiBufProperty... kProperties>
1779 const SharedPtr<const std::byte[]>& bytes) {
1780 static_assert(is_const(),
1781 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1782 return TryReserveForInsert(end(), bytes);
1783}
1784
1785template <MultiBufProperty... kProperties>
1786template <MultiBufProperty... kOtherProperties>
1789 Insert(end(), std::move(mb));
1790}
1791
1792template <MultiBufProperty... kProperties>
1793template <int&... kExplicitGuard, typename T, typename>
1795 using data_ptr_type = decltype(std::data(std::declval<T&>()));
1796 static_assert(std::is_same_v<data_ptr_type, std::byte*> || is_const(),
1797 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1798 Insert(end(), bytes);
1799}
1800
1801template <MultiBufProperty... kProperties>
1802void BasicMultiBuf<kProperties...>::PushBack(UniquePtr<std::byte[]>&& bytes,
1803 size_t offset,
1804 size_t length) {
1805 Insert(end(), std::move(bytes), offset, length);
1806}
1807
1808template <MultiBufProperty... kProperties>
1810 UniquePtr<const std::byte[]>&& bytes, size_t offset, size_t length) {
1811 static_assert(is_const(),
1812 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1813 Insert(end(), std::move(bytes), offset, length);
1814}
1815
1816template <MultiBufProperty... kProperties>
1818 const SharedPtr<std::byte[]>& bytes, size_t offset, size_t length) {
1819 Insert(end(), bytes, offset, length);
1820}
1821
1822template <MultiBufProperty... kProperties>
1824 const SharedPtr<const std::byte[]>& bytes, size_t offset, size_t length) {
1825 static_assert(is_const(),
1826 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1827 Insert(end(), bytes, offset, length);
1828}
1829
1830template <MultiBufProperty... kProperties>
1831Result<multibuf_impl::Instance<BasicMultiBuf<kProperties...>>>
1832BasicMultiBuf<kProperties...>::Remove(const_iterator pos, size_t size) {
1833 auto result = generic().Remove(pos, size);
1834 if (!result.ok()) {
1835 return result.status();
1836 }
1837 return Instance(std::move(*result));
1838}
1839
1840template <MultiBufProperty... kProperties>
1841Result<multibuf_impl::Instance<BasicMultiBuf<kProperties...>>>
1843 Result<GenericMultiBuf> result = generic().PopFrontFragment();
1844 if (!result.ok()) {
1845 return result.status();
1846 }
1847 return Instance(std::move(*result));
1848}
1849
1850template <MultiBufProperty... kProperties>
1851UniquePtr<typename BasicMultiBuf<kProperties...>::value_type[]>
1853 UniquePtr<std::byte[]> bytes = generic().Release(pos);
1854 if constexpr (is_const()) {
1855 UniquePtr<const std::byte[]> const_bytes(
1856 bytes.get(), bytes.size(), *(bytes.deallocator()));
1857 bytes.Release();
1858 return const_bytes;
1859 } else {
1860 return bytes;
1861 }
1862}
1863
1864template <MultiBufProperty... kProperties>
1865SharedPtr<typename BasicMultiBuf<kProperties...>::value_type[]>
1867 return SharedPtr<value_type[]>(generic().Share(pos),
1868 generic().GetControlBlock());
1869}
1870
1871} // namespace pw
Definition: allocator.h:34
bool AddLayer(size_t offset, size_t length=dynamic_extent)
Definition: multibuf_v2.h:1061
Result< Instance > Remove(const_iterator pos, size_t size)
Definition: multibuf_v2.h:1832
void Insert(const_iterator pos, BasicMultiBuf< kOtherProperties... > &&mb)
Definition: multibuf_v2.h:1682
bool TryReserveForPushBack(const T &bytes)
Definition: multibuf_v2.h:1750
void SealTopLayer()
Definition: multibuf_v2.h:1069
static constexpr bool is_layerable()
Returns whether additional views can be layered on the MultiBuf.
Definition: multibuf_v2.h:209
void UnsealTopLayer()
Definition: multibuf_v2.h:1077
SharedPtr< value_type[]> Share(const_iterator pos)
Definition: multibuf_v2.h:1866
void Insert(const_iterator pos, const T &bytes)
Definition: multibuf_v2.h:1691
bool TryReserveChunks(size_type num_chunks)
Definition: multibuf_v2.h:455
void set_observer(MultiBufObserver *observer)
Definition: multibuf_v2.h:1013
void PushBack(const T &bytes)
Definition: multibuf_v2.h:1794
static constexpr bool is_const()
Returns whether the MultiBuf data is immutable.
Definition: multibuf_v2.h:204
void Clear()
Definition: multibuf_v2.h:993
ConstByteSpan Get(ByteSpan copy, size_t offset=0) const
Definition: multibuf_v2.h:964
Result< Instance > PopFrontFragment()
Definition: multibuf_v2.h:1842
UniquePtr< value_type[]> Release(const_iterator pos)
Definition: multibuf_v2.h:1852
size_t CopyTo(ByteSpan dst, size_t offset=0) const
Definition: multibuf_v2.h:930
size_t CopyFrom(ConstByteSpan src, size_t offset=0)
Definition: multibuf_v2.h:942
bool TryReserveForPushBack(const BasicMultiBuf< kOtherProperties... > &mb)
Definition: multibuf_v2.h:1743
static constexpr bool is_observable()
Returns whether an observer can be registered on the MultiBuf.
Definition: multibuf_v2.h:214
constexpr size_t size() const
Definition: multibuf_v2.h:310
bool PopLayer()
Definition: multibuf_v2.h:1110
bool IsShareable(const_iterator pos) const
Definition: multibuf_v2.h:902
constexpr size_type NumLayers() const
Definition: multibuf_v2.h:1036
constexpr bool empty() const
Definition: multibuf_v2.h:306
Result< const_iterator > Discard(const_iterator pos, size_t size)
Definition: multibuf_v2.h:872
bool IsReleasable(const_iterator pos) const
Definition: multibuf_v2.h:880
bool IsRemovable(const_iterator pos, size_t size) const
Definition: multibuf_v2.h:795
bool TryReserveForInsert(const_iterator pos, const BasicMultiBuf< kOtherProperties... > &mb)
Definition: multibuf_v2.h:1632
size_type NumFragments() const
Definition: multibuf_v2.h:1027
void PushBack(BasicMultiBuf< kOtherProperties... > &&mb)
Definition: multibuf_v2.h:1787
bool ResizeTopLayer(size_t offset, size_t length=dynamic_extent)
Definition: multibuf_v2.h:1094
constexpr MultiBufObserver * observer() const
Definition: multibuf_v2.h:1001
auto Visit(Visitor visitor, ByteSpan copy, size_t offset)
Definition: multibuf_v2.h:984
bool TryReserveForInsert(const_iterator pos, const T &bytes)
Definition: multibuf_v2.h:1642
bool IsCompatible(const BasicMultiBuf &mb) const
Definition: multibuf_v2.h:406
Abstract interface for releasing memory.
Definition: deallocator.h:27
Definition: dynamic_deque.h:55
Definition: observer.h:27
Definition: shared_ptr.h:57
Definition: unique_ptr.h:41
element_type * Release() noexcept
Definition: unique_ptr.h:201
Deallocator * deallocator() const
Returns a pointer to the object that can destroy the value.
Definition: unique_ptr.h:135
size_t size() const
Definition: unique_ptr.h:128
Definition: multibuf_v2.h:1155
Definition: multibuf_v2.h:1599
Provides basic helpers for reading and writing UTF-8 encoded strings.
Definition: alignment.h:27
MultiBufProperty
Basic properties of a MultiBuf.
Definition: properties.h:22