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"
82namespace multibuf::internal {
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>;
250 InvalidCopyOrMove<>();
253 BasicMultiBuf& operator=(
const BasicMultiBuf&) { InvalidCopyOrMove<>(); }
256 BasicMultiBuf& operator=(
const BasicMultiBuf<kOtherProperties...>&) {
257 InvalidCopyOrMove<>();
260 BasicMultiBuf(BasicMultiBuf&&) { InvalidCopyOrMove<>(); }
262 BasicMultiBuf& operator=(BasicMultiBuf&&) { InvalidCopyOrMove<>(); }
266 template <
typename OtherMultiBuf>
267 constexpr OtherMultiBuf& as() & {
268 multibuf::internal::AssertIsConvertible<BasicMultiBuf, OtherMultiBuf>();
269 return generic().
template as<OtherMultiBuf>();
272 template <
typename OtherMultiBuf>
273 constexpr const OtherMultiBuf& as() const& {
274 multibuf::internal::AssertIsConvertible<BasicMultiBuf, OtherMultiBuf>();
275 return generic().
template as<OtherMultiBuf>();
278 template <
typename OtherMultiBuf>
279 constexpr OtherMultiBuf&& as() && {
280 multibuf::internal::AssertIsConvertible<BasicMultiBuf, OtherMultiBuf>();
281 return std::move(generic().
template as<OtherMultiBuf>());
284 template <
typename OtherMultiBuf>
285 constexpr const OtherMultiBuf&& as() const&& {
286 multibuf::internal::AssertIsConvertible<BasicMultiBuf, OtherMultiBuf>();
287 return std::move(generic().
template as<OtherMultiBuf>());
290 template <
typename OtherMultiBuf,
291 typename = multibuf::internal::EnableIfConvertible<BasicMultiBuf,
293 constexpr operator OtherMultiBuf&() & {
294 return as<OtherMultiBuf>();
297 template <
typename OtherMultiBuf,
298 typename = multibuf::internal::EnableIfConvertible<BasicMultiBuf,
300 constexpr operator const OtherMultiBuf&()
const& {
301 return as<OtherMultiBuf>();
304 template <
typename OtherMultiBuf,
305 typename = multibuf::internal::EnableIfConvertible<BasicMultiBuf,
307 constexpr operator OtherMultiBuf&&() && {
308 return std::move(as<OtherMultiBuf>());
311 template <
typename OtherMultiBuf,
312 typename = multibuf::internal::EnableIfConvertible<BasicMultiBuf,
314 constexpr operator const OtherMultiBuf&&()
const&& {
315 return std::move(as<OtherMultiBuf>());
332 constexpr size_t size()
const {
return generic().
size(); }
345 template <
bool kMutable = !is_const()>
346 std::enable_if_t<kMutable, reference> at(
size_t index) {
347 return *(begin() +
static_cast<int>(index));
349 const_reference at(
size_t index)
const {
350 return *(begin() +
static_cast<int>(index));
353 template <
bool kMutable = !is_const()>
354 std::enable_if_t<kMutable, reference> operator[](
size_t index) {
357 const_reference operator[](
size_t index)
const {
return at(index); }
371 template <
bool kMutable = !is_const()>
372 constexpr std::enable_if_t<kMutable, ChunksType> Chunks() {
373 return generic().Chunks();
375 constexpr ConstChunksType Chunks()
const {
return generic().ConstChunks(); }
376 constexpr ConstChunksType ConstChunks()
const {
377 return generic().ConstChunks();
393 template <
bool kMutable = !is_const()>
394 constexpr std::enable_if_t<kMutable, iterator> begin() {
395 return generic().begin();
397 constexpr const_iterator begin()
const {
return cbegin(); }
398 constexpr const_iterator cbegin()
const {
return generic().cbegin(); }
411 template <
bool kMutable = !is_const()>
412 constexpr std::enable_if_t<kMutable, iterator> end() {
413 return generic().end();
415 constexpr const_iterator end()
const {
return cend(); }
416 constexpr const_iterator cend()
const {
return generic().cend(); }
475 int&... kExplicitGuard,
478 std::enable_if_t<std::is_constructible_v<ConstByteSpan, T>,
int>>
492 Insert(pos, std::move(bytes), 0);
494 void Insert(const_iterator pos,
UniquePtr<
const std::byte[]>&& bytes) {
495 Insert(pos, std::move(bytes), 0);
511 void Insert(const_iterator pos,
512 UniquePtr<std::byte[]>&& bytes,
514 size_t length = dynamic_extent);
515 void Insert(const_iterator pos,
516 UniquePtr<
const std::byte[]>&& bytes,
518 size_t length = dynamic_extent);
531 void Insert(const_iterator pos,
const SharedPtr<std::byte[]>& bytes) {
534 void Insert(const_iterator pos,
const SharedPtr<
const std::byte[]>& bytes) {
551 void Insert(const_iterator pos,
552 const SharedPtr<std::byte[]>& bytes,
554 size_t length = dynamic_extent);
555 void Insert(const_iterator pos,
556 const SharedPtr<
const std::byte[]>& bytes,
558 size_t length = dynamic_extent);
597 int&... kExplicitGuard,
600 std::enable_if_t<std::is_constructible_v<ConstByteSpan, T>,
int>>
631 void PushBack(UniquePtr<std::byte[]>&& bytes,
633 size_t length = dynamic_extent);
635 void PushBack(UniquePtr<
const std::byte[]>&& bytes,
637 size_t length = dynamic_extent);
650 void PushBack(
const SharedPtr<
const std::byte[]>& bytes) {
666 void PushBack(
const SharedPtr<std::byte[]>& bytes,
668 size_t length = dynamic_extent);
669 void PushBack(
const SharedPtr<
const std::byte[]>& bytes,
671 size_t length = dynamic_extent);
800 return generic().
CopyTo(dst, offset);
813 "`CopyFrom` may only be called on mutable MultiBufs");
814 return generic().
CopyFrom(src, offset);
834 return generic().
Get(
copy, offset);
852 template <
int&... kExplicitGuard,
typename Visitor>
854 return visitor(
Get(
copy, offset));
880 "`observer` may only be called on observable MultiBufs");
881 return generic().observer_;
892 "`set_observer` may only be called on observable MultiBufs");
906 "`NumFragments` may only be called on layerable MultiBufs");
915 "`NumLayers` may only be called on layerable MultiBufs");
927 size_t num_chunks = 1) {
950 [[nodiscard]]
bool AddLayer(
size_t offset,
size_t length = dynamic_extent) {
952 "`AddLayer` may only be called on layerable MultiBufs");
953 return generic().
AddLayer(offset, length);
960 "`SealTopLayer` may only be called on layerable MultiBufs");
968 "`UnsealTopLayer` may only be called on layerable MultiBufs");
976 "`IsTopLayerSealed` may only be called on layerable MultiBufs");
991 "`TruncateTopLayer` may only be called on layerable MultiBufs");
1003 "`SetTopLayer` may only be called on mutable, layerable "
1005 PW_ASSERT(src.size() <=
size());
1020 "`PopLayer` may only be called on layerable MultiBufs");
1025 constexpr BasicMultiBuf() { multibuf::internal::PropertiesAreValid(); }
1028 template <
bool kVal
idCopyOrMove = 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.");
1037 friend class BasicMultiBuf;
1039 constexpr GenericMultiBuf&
generic() {
1040 return static_cast<GenericMultiBuf&
>(*this);
1042 constexpr const GenericMultiBuf&
generic()
const {
1043 return static_cast<const GenericMultiBuf&
>(*this);
1051namespace multibuf::internal {
1071 Property::kLayerable,
1072 Property::kObservable> {
1074 using ControlBlock = allocator::internal::ControlBlock;
1098 *
this = std::move(other);
1104 friend class ::pw::BasicMultiBuf;
1111 : deque_(allocator) {}
1113 template <
typename MultiBufType>
1114 constexpr MultiBufType& as() {
1118 template <
typename MultiBufType>
1119 constexpr const MultiBufType& as()
const {
1126 constexpr bool empty()
const {
return deque_.empty(); }
1129 constexpr bool at_capacity()
const {
1130 return deque_.size() == deque_.capacity();
1134 constexpr size_t size()
const {
1135 return static_cast<size_t>(cend() - cbegin());
1141 return ChunksType(deque_, entries_per_chunk_);
1160 [[nodiscard]]
bool TryReserveChunks(
size_t num_chunks) {
1161 return TryReserveLayers(NumLayers(), num_chunks);
1184 ControlBlock* control_block);
1189 size_t size)
const {
1190 return pos != cend() && size != 0 &&
1191 size <= static_cast<size_t>(cend() - pos);
1216 size_t CopyTo(
ByteSpan dst,
size_t offset)
const {
1217 return CopyToImpl(dst, offset, 0);
1235 size_type NumFragments()
const;
1238 constexpr size_type NumLayers()
const {
1239 return entries_per_chunk_ - Entry::kMinEntriesPerChunk + 1;
1243 [[nodiscard]]
bool TryReserveLayers(
size_t num_layers,
size_t num_chunks = 1);
1246 [[nodiscard]]
bool AddLayer(
size_t offset,
size_t length = dynamic_extent);
1249 void SealTopLayer();
1252 void UnsealTopLayer();
1255 void TruncateTopLayer(
size_t length);
1269 static size_t CheckRange(
size_t offset,
size_t length,
size_t size);
1272 constexpr size_type num_chunks()
const {
1273 return deque_.size() / entries_per_chunk_;
1277 constexpr size_type memory_context_index(size_type chunk)
const {
1278 return Entry::memory_context_index(chunk, entries_per_chunk_);
1282 constexpr size_type data_index(size_type chunk)
const {
1283 return Entry::data_index(chunk, entries_per_chunk_);
1287 constexpr size_type base_view_index(size_type chunk)
const {
1288 return Entry::base_view_index(chunk, entries_per_chunk_);
1292 constexpr size_type view_index(size_type chunk, size_type layer)
const {
1293 return Entry::view_index(chunk, entries_per_chunk_, layer);
1297 constexpr size_type top_view_index(size_type chunk)
const {
1298 return Entry::top_view_index(chunk, entries_per_chunk_);
1302 constexpr std::byte* GetData(size_type chunk)
const {
1303 return deque_[data_index(chunk)].data;
1308 constexpr bool IsOwned(size_type chunk)
const {
1309 return deque_[base_view_index(chunk)].base_view.owned;
1314 constexpr bool IsShared(size_type chunk)
const {
1315 return deque_[base_view_index(chunk)].base_view.shared;
1319 constexpr bool IsSealed(size_type chunk)
const {
1320 return entries_per_chunk_ == Entry::kMinEntriesPerChunk
1322 : deque_[top_view_index(chunk)].view.sealed;
1326 constexpr bool IsBoundary(size_type chunk)
const {
1327 return entries_per_chunk_ == Entry::kMinEntriesPerChunk
1329 : deque_[top_view_index(chunk)].view.boundary;
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;
1341 constexpr size_type GetOffset(size_type chunk)
const {
1342 return GetOffset(chunk, NumLayers());
1346 constexpr size_type GetRelativeOffset(size_type chunk)
const {
1347 uint16_t layer = NumLayers();
1349 return GetOffset(chunk, layer);
1351 return GetOffset(chunk, layer) - GetOffset(chunk, layer - 1);
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;
1362 constexpr ByteSpan GetView(size_type chunk)
const {
1363 return ByteSpan(GetData(chunk) + GetOffset(chunk), GetLength(chunk));
1369 std::pair<size_type, size_type> GetChunkAndOffset(
const_iterator pos)
const;
1373 [[nodiscard]]
bool TryConvertToShared(size_type chunk);
1379 [[nodiscard]]
bool TryReserveEntries(size_type num_entries,
1380 bool split =
false);
1384 size_type InsertChunks(
const_iterator pos, size_type num_chunks);
1402 void SplitBase(size_type chunk,
Deque& out_deque, size_type out_chunk);
1409 void SplitBefore(size_type chunk,
1412 size_type out_chunk);
1416 void SplitBefore(size_type chunk, size_type split);
1423 void SplitAfter(size_type chunk,
1426 size_type out_chunk);
1430 void SplitAfter(size_type chunk, size_type split);
1465 size_t CopyToImpl(
ByteSpan dst,
size_t offset, size_type start)
const;
1468 [[nodiscard]]
bool IsTopLayerSealed()
const;
1472 void SetLayer(
size_t offset,
size_t length);
1493 size_type entries_per_chunk_ = Entry::kMinEntriesPerChunk;
1518template <
typename MultiBufType>
1541 constexpr Instance(MultiBufType&& mb)
1544 constexpr Instance& operator=(MultiBufType&& mb) {
1552 internal::AssertIsConvertible<
BasicMultiBuf<kProperties...>,
1558 internal::AssertIsConvertible<
BasicMultiBuf<kProperties...>,
1564 constexpr MultiBufType* operator->() {
return &base_.as<MultiBufType>(); }
1565 constexpr const MultiBufType* operator->()
const {
1566 return &base_.as<MultiBufType>();
1569 constexpr MultiBufType& operator*() & {
return base_.as<MultiBufType>(); }
1570 constexpr const MultiBufType& operator*()
const& {
1571 return base_.as<MultiBufType>();
1574 constexpr MultiBufType&& operator*() && {
1575 return std::move(base_.as<MultiBufType>());
1577 constexpr const MultiBufType&& operator*()
const&& {
1578 return std::move(base_.as<MultiBufType>());
1581 constexpr operator MultiBufType&() & {
return base_.as<MultiBufType>(); }
1582 constexpr operator const MultiBufType&()
const& {
1583 return base_.as<MultiBufType>();
1586 constexpr operator MultiBufType&&() && {
1587 return std::move(base_.as<MultiBufType>());
1589 constexpr operator const MultiBufType&&()
const&& {
1590 return std::move(base_.as<MultiBufType>());
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.");
1612 return generic().TryReserveForInsert(pos);
1619 multibuf::internal::AssertIsConvertible<
BasicMultiBuf<kOtherProperties...>,
1621 return generic().TryReserveForInsert(pos,
1629 multibuf::internal::AssertIsConvertible<
BasicMultiBuf<kOtherProperties...>,
1631 generic().Insert(pos, std::move(mb.generic()));
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);
1649 generic().Insert(pos, chunk, offset, length, bytes.deallocator());
1655 UniquePtr<
const std::byte[]>&& bytes,
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());
1667 const SharedPtr<std::byte[]>& bytes,
1670 ConstByteSpan chunk(bytes.get(), bytes.size());
1671 generic().Insert(pos, chunk, offset, length, bytes.control_block());
1677 const SharedPtr<
const std::byte[]>& bytes,
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());
1690 return generic().TryReserveForInsert(end(),
1696 return generic().TryReserveForInsert(end());
1703 Insert(end(), std::move(mb));
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);
1719 Insert(end(), std::move(bytes), offset, length);
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);
1732 const SharedPtr<std::byte[]>& bytes,
size_t offset,
size_t length) {
1733 Insert(end(), bytes, offset, length);
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);
1745Result<multibuf::internal::Instance<BasicMultiBuf<kProperties...>>>
1747 auto result =
generic().Remove(pos, size);
1749 return result.status();
1751 return Instance(std::move(*result));
1757 auto result =
generic().PopFrontFragment();
1759 return result.status();
1761 return Instance(std::move(*result));
1768 if constexpr (is_const()) {
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: 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