26#include "pw_allocator/allocator.h"
27#include "pw_bytes/span.h"
28#include "pw_containers/dynamic_deque.h"
29#include "pw_multibuf/internal/byte_iterator.h"
30#include "pw_multibuf/internal/chunk_iterator.h"
31#include "pw_multibuf/internal/entry.h"
32#include "pw_multibuf/observer.h"
33#include "pw_multibuf/properties.h"
34#include "pw_span/span.h"
42namespace multibuf_impl {
196 return ((kProperties == Property::kConst) || ...);
201 return ((kProperties == Property::kLayerable) || ...);
206 return ((kProperties == Property::kObservable) || ...);
209 using size_type =
typename Deque::size_type;
210 using difference_type =
typename Deque::difference_type;
212 using const_iterator =
214 using pointer = iterator::pointer;
215 using const_pointer = const_iterator::pointer;
216 using reference = iterator::reference;
217 using const_reference = const_iterator::reference;
218 using value_type = std::conditional_t<
is_const(),
219 const_iterator::value_type,
220 iterator::value_type>;
247 template <
Property... kOtherProperties>
252 template <
Property... kOtherProperties>
261 template <
typename OtherMultiBuf>
262 constexpr OtherMultiBuf& as() & {
263 multibuf_impl::AssertIsConvertible<BasicMultiBuf, OtherMultiBuf>();
264 return generic().
template as<OtherMultiBuf>();
267 template <
typename OtherMultiBuf>
268 constexpr const OtherMultiBuf& as() const& {
269 multibuf_impl::AssertIsConvertible<BasicMultiBuf, OtherMultiBuf>();
270 return generic().
template as<OtherMultiBuf>();
273 template <
typename OtherMultiBuf>
274 constexpr OtherMultiBuf&& as() && {
275 multibuf_impl::AssertIsConvertible<BasicMultiBuf, OtherMultiBuf>();
276 return std::move(generic().
template as<OtherMultiBuf>());
279 template <
typename OtherMultiBuf>
280 constexpr const OtherMultiBuf&& as() const&& {
281 multibuf_impl::AssertIsConvertible<BasicMultiBuf, OtherMultiBuf>();
282 return std::move(generic().
template as<OtherMultiBuf>());
285 template <
typename OtherMultiBuf,
286 typename = multibuf_impl::EnableIfConvertible<BasicMultiBuf,
288 constexpr operator OtherMultiBuf&() & {
289 return as<OtherMultiBuf>();
292 template <
typename OtherMultiBuf,
293 typename = multibuf_impl::EnableIfConvertible<BasicMultiBuf,
295 constexpr operator const OtherMultiBuf&()
const& {
296 return as<OtherMultiBuf>();
299 template <
typename OtherMultiBuf,
300 typename = multibuf_impl::EnableIfConvertible<BasicMultiBuf,
302 constexpr operator OtherMultiBuf&&() && {
303 return std::move(as<OtherMultiBuf>());
306 template <
typename OtherMultiBuf,
307 typename = multibuf_impl::EnableIfConvertible<BasicMultiBuf,
309 constexpr operator const OtherMultiBuf&&()
const&& {
310 return std::move(as<OtherMultiBuf>());
321 constexpr size_t size()
const {
return generic().
size(); }
334 template <
bool kMutable = !is_const()>
335 std::enable_if_t<kMutable, reference> at(
size_t index) {
336 return *(begin() +
static_cast<int>(index));
338 const_reference at(
size_t index)
const {
339 return *(begin() +
static_cast<int>(index));
342 template <
bool kMutable = !is_const()>
343 std::enable_if_t<kMutable, reference> operator[](
size_t index) {
346 const_reference operator[](
size_t index)
const {
return at(index); }
360 template <
bool kMutable = !is_const()>
361 constexpr std::enable_if_t<kMutable, ChunksType> Chunks() {
362 return generic().Chunks();
364 constexpr ConstChunksType Chunks()
const {
return generic().ConstChunks(); }
365 constexpr ConstChunksType ConstChunks()
const {
366 return generic().ConstChunks();
382 template <
bool kMutable = !is_const()>
383 constexpr std::enable_if_t<kMutable, iterator> begin() {
384 return generic().begin();
386 constexpr const_iterator begin()
const {
return cbegin(); }
387 constexpr const_iterator cbegin()
const {
return generic().cbegin(); }
400 template <
bool kMutable = !is_const()>
401 constexpr std::enable_if_t<kMutable, iterator> end() {
402 return generic().end();
404 constexpr const_iterator end()
const {
return cend(); }
405 constexpr const_iterator cend()
const {
return generic().cend(); }
435 bool IsCompatible(
const UniquePtr<
const std::byte[]>& bytes)
const {
448 bool IsCompatible(
const SharedPtr<std::byte[]>& bytes)
const {
451 bool IsCompatible(
const SharedPtr<
const std::byte[]>& bytes)
const {
475 template <Property... kOtherProperties>
491 int&... kExplicitGuard,
494 std::enable_if_t<std::is_constructible_v<ConstByteSpan, T>,
int>>
543 template <
Property... kOtherProperties>
555 int&... kExplicitGuard,
558 std::enable_if_t<std::is_constructible_v<ConstByteSpan, T>,
int>>
572 Insert(pos, std::move(bytes), 0);
574 void Insert(const_iterator pos,
UniquePtr<
const std::byte[]>&& bytes) {
575 Insert(pos, std::move(bytes), 0);
591 void Insert(const_iterator pos,
592 UniquePtr<std::byte[]>&& bytes,
594 size_t length = dynamic_extent);
595 void Insert(const_iterator pos,
596 UniquePtr<
const std::byte[]>&& bytes,
598 size_t length = dynamic_extent);
611 void Insert(const_iterator pos,
const SharedPtr<std::byte[]>& bytes) {
614 void Insert(const_iterator pos,
const SharedPtr<
const std::byte[]>& bytes) {
631 void Insert(const_iterator pos,
632 const SharedPtr<std::byte[]>& bytes,
634 size_t length = dynamic_extent);
635 void Insert(const_iterator pos,
636 const SharedPtr<
const std::byte[]>& bytes,
638 size_t length = dynamic_extent);
648 template <Property... kOtherProperties>
660 int&... kExplicitGuard,
663 std::enable_if_t<std::is_constructible_v<ConstByteSpan, T>,
int>>
677 const UniquePtr<
const std::byte[]>& bytes);
691 const SharedPtr<
const std::byte[]>& bytes);
701 template <
Property... kOtherProperties>
712 int&... kExplicitGuard,
715 std::enable_if_t<std::is_constructible_v<ConstByteSpan, T>,
int>>
746 void PushBack(UniquePtr<std::byte[]>&& bytes,
748 size_t length = dynamic_extent);
750 void PushBack(UniquePtr<
const std::byte[]>&& bytes,
752 size_t length = dynamic_extent);
765 void PushBack(
const SharedPtr<
const std::byte[]>& bytes) {
781 void PushBack(
const SharedPtr<std::byte[]>& bytes,
783 size_t length = dynamic_extent);
784 void PushBack(
const SharedPtr<
const std::byte[]>& bytes,
786 size_t length = dynamic_extent);
917 return generic().
CopyTo(dst, offset);
930 "`CopyFrom` may only be called on mutable MultiBufs");
931 return generic().
CopyFrom(src, offset);
951 return generic().
Get(
copy, offset);
969 template <
int&... kExplicitGuard,
typename Visitor>
971 return visitor(
Get(
copy, offset));
989 "`observer` may only be called on observable MultiBufs");
990 return generic().observer_;
1001 "`set_observer` may only be called on observable MultiBufs");
1015 "`NumFragments` may only be called on layerable MultiBufs");
1024 "`NumLayers` may only be called on layerable MultiBufs");
1036 size_t num_chunks = 1) {
1059 [[nodiscard]]
bool AddLayer(
size_t offset,
size_t length = dynamic_extent) {
1061 "`AddLayer` may only be called on layerable MultiBufs");
1062 return generic().
AddLayer(offset, length);
1069 "`SealTopLayer` may only be called on layerable MultiBufs");
1077 "`UnsealTopLayer` may only be called on layerable MultiBufs");
1085 "`IsTopLayerSealed` may only be called on layerable MultiBufs");
1100 "`TruncateTopLayer` may only be called on layerable MultiBufs");
1112 "`SetTopLayer` may only be called on mutable, layerable "
1114 PW_ASSERT(src.size() <=
size());
1129 "`PopLayer` may only be called on layerable MultiBufs");
1134 constexpr BasicMultiBuf() { multibuf_impl::PropertiesAreValid(); }
1137 template <Property...>
1138 friend class BasicMultiBuf;
1140 constexpr GenericMultiBuf&
generic() {
1141 return static_cast<GenericMultiBuf&
>(*this);
1143 constexpr const GenericMultiBuf&
generic()
const {
1144 return static_cast<const GenericMultiBuf&
>(*this);
1148namespace multibuf_impl {
1176 using ControlBlock = allocator::internal::ControlBlock;
1198 *
this = std::move(other);
1204 friend class ::pw::BasicMultiBuf;
1207 friend class ::pw::multibuf_impl::Instance;
1211 : deque_(allocator) {}
1213 template <
typename MultiBufType>
1214 constexpr MultiBufType& as() {
1218 template <
typename MultiBufType>
1219 constexpr const MultiBufType& as()
const {
1226 constexpr bool empty()
const {
return deque_.empty(); }
1229 constexpr size_t size()
const {
1230 return static_cast<size_t>(cend() - cbegin());
1234 constexpr bool has_deallocator()
const {
1235 return memory_tag_ == MemoryTag::kDeallocator || has_control_block();
1239 constexpr bool has_control_block()
const {
1240 return memory_tag_ == MemoryTag::kControlBlock;
1264 bool IsCompatible(
const Deallocator* other)
const;
1265 bool IsCompatible(
const ControlBlock* other)
const;
1268 [[nodiscard]]
bool TryReserveChunks(
size_t num_chunks);
1274 [[nodiscard]]
bool TryReserveForInsert(
const_iterator pos,
size_t size);
1280 const ControlBlock* control_block);
1296 ControlBlock* control_block);
1300 [[nodiscard]]
bool IsRemovable(
const_iterator pos,
size_t size)
const;
1324 size_t CopyTo(
ByteSpan dst,
size_t offset)
const;
1338 size_type NumFragments()
const;
1341 constexpr size_type NumLayers()
const {
return depth_ - 1; }
1344 [[nodiscard]]
bool TryReserveLayers(
size_t num_layers,
size_t num_chunks = 1);
1347 [[nodiscard]]
bool AddLayer(
size_t offset,
size_t length = dynamic_extent);
1350 void SealTopLayer();
1353 void UnsealTopLayer();
1356 void TruncateTopLayer(
size_t length);
1370 static size_t CheckRange(
size_t offset,
size_t length,
size_t size);
1373 constexpr std::byte* GetData(size_type index)
const {
1374 return deque_[index].data;
1379 constexpr bool IsOwned(size_type index)
const {
1380 return deque_[index + 1].base_view.owned;
1385 constexpr bool IsShared(size_type index)
const {
1386 return deque_[index + 1].base_view.shared;
1390 constexpr bool IsSealed(size_type index)
const {
1391 return depth_ == 2 ? false : deque_[index + depth_ - 1].view.sealed;
1396 constexpr bool IsBoundary(size_type index)
const {
1397 return depth_ == 2 ? true : deque_[index + depth_ - 1].view.boundary;
1403 constexpr size_type GetOffset(size_type index, uint16_t layer)
const {
1404 return layer == 1 ? deque_[index + 1].base_view.offset
1405 : deque_[index + layer].view.offset;
1409 constexpr size_type GetOffset(size_type index)
const {
1410 return GetOffset(index, NumLayers());
1415 constexpr size_type GetRelativeOffset(size_type index)
const {
1416 uint16_t layer = NumLayers();
1418 return GetOffset(index, layer);
1420 return GetOffset(index, layer) - GetOffset(index, layer - 1);
1424 constexpr size_type GetLength(size_type index)
const {
1425 return depth_ == 2 ? deque_[index + 1].base_view.length
1426 : deque_[index + depth_ - 1].view.length;
1430 constexpr ByteSpan GetView(size_type index)
const {
1431 return ByteSpan(GetData(index) + GetOffset(index), GetLength(index));
1441 ControlBlock* GetControlBlock()
const;
1444 void SetControlBlock(ControlBlock* control_block);
1450 void ClearMemoryContext();
1455 std::pair<size_type, size_type> GetIndexAndOffset(
const_iterator pos)
const;
1462 bool TryReserveEntries(
const_iterator pos, size_type num_entries);
1463 bool TryReserveEntries(size_type num_entries,
bool split =
false);
1468 size_type InsertEntries(
const_iterator pos, size_type num_entries);
1486 void SplitBase(size_type index,
Deque& out_deque, size_type out_index);
1493 void SplitBefore(size_type index,
1496 size_type out_index);
1500 void SplitBefore(size_type index, size_type split);
1507 void SplitAfter(size_type index,
1510 size_type out_index);
1514 void SplitAfter(size_type index, size_type split);
1549 size_type FindShared(size_type index, size_type start);
1553 size_t CopyToImpl(
ByteSpan dst,
size_t offset, size_type start)
const;
1556 [[nodiscard]]
bool IsTopLayerSealed()
const;
1560 void SetLayer(
size_t offset,
size_t length);
1581 size_type depth_ = 2;
1599 enum class MemoryTag : uint8_t {
1603 } memory_tag_ = MemoryTag::kEmpty;
1605 union MemoryContext {
1607 ControlBlock* control_block;
1608 } memory_context_ = {.deallocator =
nullptr};
1634template <
typename MultiBufType>
1642 constexpr Instance(MultiBufType&& mb)
1645 constexpr Instance& operator=(MultiBufType&& mb) {
1650 constexpr MultiBufType* operator->() {
return &base_.as<MultiBufType>(); }
1651 constexpr const MultiBufType* operator->()
const {
1652 return &base_.as<MultiBufType>();
1655 constexpr MultiBufType& operator*() & {
return base_.as<MultiBufType>(); }
1656 constexpr const MultiBufType& operator*()
const& {
1657 return base_.as<MultiBufType>();
1660 constexpr MultiBufType&& operator*() && {
1661 return std::move(base_.as<MultiBufType>());
1663 constexpr const MultiBufType&& operator*()
const&& {
1664 return std::move(base_.as<MultiBufType>());
1667 constexpr operator MultiBufType&() & {
return base_.as<MultiBufType>(); }
1668 constexpr operator const MultiBufType&()
const& {
1669 return base_.as<MultiBufType>();
1672 constexpr operator MultiBufType&&() && {
1673 return std::move(base_.as<MultiBufType>());
1675 constexpr operator const MultiBufType&&()
const&& {
1676 return std::move(base_.as<MultiBufType>());
1695 multibuf_impl::AssertIsConvertible<
BasicMultiBuf<kOtherProperties...>,
1697 return generic().TryReserveForInsert(pos,
1702template <
int&... kExplicitGuard,
typename T,
typename>
1705 using data_ptr_type =
decltype(std::data(std::declval<T&>()));
1706 static_assert(std::is_same_v<data_ptr_type, std::byte*> || is_const(),
1707 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1708 return generic().TryReserveForInsert(pos, bytes.size());
1713 const_iterator pos,
const UniquePtr<std::byte[]>& bytes) {
1714 return generic().TryReserveForInsert(pos, bytes.size(), bytes.deallocator());
1719 const_iterator pos,
const UniquePtr<
const std::byte[]>& bytes) {
1720 static_assert(is_const(),
1721 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1722 return generic().TryReserveForInsert(pos, bytes.size(), bytes.deallocator());
1727 const_iterator pos,
const SharedPtr<std::byte[]>& bytes) {
1728 return generic().TryReserveForInsert(
1729 pos, bytes.size(), bytes.control_block());
1734 const_iterator pos,
const SharedPtr<
const std::byte[]>& bytes) {
1735 static_assert(is_const(),
1736 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1737 return generic().TryReserveForInsert(
1738 pos, bytes.size(), bytes.control_block());
1745 multibuf_impl::AssertIsConvertible<
BasicMultiBuf<kOtherProperties...>,
1747 generic().Insert(pos, std::move(mb.generic()));
1751template <
int&... kExplicitGuard,
typename T,
typename>
1753 using data_ptr_type =
decltype(std::data(std::declval<T&>()));
1754 static_assert(std::is_same_v<data_ptr_type, std::byte*> || is_const(),
1755 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1756 generic().Insert(pos, bytes);
1765 generic().Insert(pos, chunk, offset, length, bytes.deallocator());
1771 UniquePtr<
const std::byte[]>&& bytes,
1774 static_assert(is_const(),
1775 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1776 ConstByteSpan chunk(bytes.get(), bytes.size());
1777 generic().Insert(pos, chunk, offset, length, bytes.deallocator());
1783 const SharedPtr<std::byte[]>& bytes,
1786 ConstByteSpan chunk(bytes.get(), bytes.size());
1787 generic().Insert(pos, chunk, offset, length, bytes.control_block());
1793 const SharedPtr<
const std::byte[]>& bytes,
1796 static_assert(is_const(),
1797 "Cannot `Insert` read-only bytes into mutable MultiBuf");
1798 ConstByteSpan chunk(bytes.get(), bytes.size());
1799 generic().Insert(pos, chunk, offset, length, bytes.control_block());
1806 return TryReserveForInsert(end(), mb);
1810template <
int&... kExplicitGuard,
typename T,
typename>
1812 using data_ptr_type =
decltype(std::data(std::declval<T&>()));
1813 static_assert(std::is_same_v<data_ptr_type, std::byte*> || is_const(),
1814 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1815 return TryReserveForInsert(end(), bytes);
1821 return TryReserveForInsert(end(), std::move(bytes));
1826 const UniquePtr<
const std::byte[]>& bytes) {
1827 static_assert(is_const(),
1828 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1829 return TryReserveForInsert(end(), std::move(bytes));
1834 const SharedPtr<std::byte[]>& bytes) {
1835 return TryReserveForInsert(end(), bytes);
1840 const SharedPtr<
const std::byte[]>& bytes) {
1841 static_assert(is_const(),
1842 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1843 return TryReserveForInsert(end(), bytes);
1850 Insert(end(), std::move(mb));
1854template <
int&... kExplicitGuard,
typename T,
typename>
1856 using data_ptr_type =
decltype(std::data(std::declval<T&>()));
1857 static_assert(std::is_same_v<data_ptr_type, std::byte*> || is_const(),
1858 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1859 Insert(end(), bytes);
1866 Insert(end(), std::move(bytes), offset, length);
1871 UniquePtr<
const std::byte[]>&& bytes,
size_t offset,
size_t length) {
1872 static_assert(is_const(),
1873 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1874 Insert(end(), std::move(bytes), offset, length);
1879 const SharedPtr<std::byte[]>& bytes,
size_t offset,
size_t length) {
1880 Insert(end(), bytes, offset, length);
1885 const SharedPtr<
const std::byte[]>& bytes,
size_t offset,
size_t length) {
1886 static_assert(is_const(),
1887 "Cannot `PushBack` read-only bytes into mutable MultiBuf");
1888 Insert(end(), bytes, offset, length);
1892Result<multibuf_impl::Instance<BasicMultiBuf<kProperties...>>>
1894 auto result =
generic().Remove(pos, size);
1896 return result.status();
1898 return Instance(std::move(*result));
1908 return Instance(std::move(*result));
1915 if constexpr (is_const()) {
1929 generic().GetControlBlock());
Definition: allocator.h:36
bool IsTopLayerSealed()
Returns whether the "sealed" flag is set in the top layer.
Definition: multibuf_v2.h:1082
bool AddLayer(size_t offset, size_t length=dynamic_extent)
Definition: multibuf_v2.h:1059
Result< Instance > Remove(const_iterator pos, size_t size)
Definition: multibuf_v2.h:1893
void Insert(const_iterator pos, BasicMultiBuf< kOtherProperties... > &&mb)
Definition: multibuf_v2.h:1743
bool TryReserveForPushBack(const T &bytes)
Definition: multibuf_v2.h:1811
void SealTopLayer()
Definition: multibuf_v2.h:1067
static constexpr bool is_layerable()
Returns whether additional views can be layered on the MultiBuf.
Definition: multibuf_v2.h:200
void UnsealTopLayer()
Definition: multibuf_v2.h:1075
SharedPtr< value_type[]> Share(const_iterator pos)
Definition: multibuf_v2.h:1927
void Insert(const_iterator pos, const T &bytes)
Definition: multibuf_v2.h:1752
void TruncateTopLayer(size_t length)
Definition: multibuf_v2.h:1097
void set_observer(MultiBufObserver *observer)
Definition: multibuf_v2.h:999
void SetTopLayer(ConstByteSpan src)
Definition: multibuf_v2.h:1110
void PushBack(const T &bytes)
Definition: multibuf_v2.h:1855
static constexpr bool is_const()
Returns whether the MultiBuf data is immutable.
Definition: multibuf_v2.h:195
void PopLayer()
Definition: multibuf_v2.h:1127
void Clear()
Definition: multibuf_v2.h:979
ConstByteSpan Get(ByteSpan copy, size_t offset=0) const
Definition: multibuf_v2.h:950
Result< Instance > PopFrontFragment()
Definition: multibuf_v2.h:1903
UniquePtr< value_type[]> Release(const_iterator pos)
Definition: multibuf_v2.h:1913
bool TryReserveChunks(size_t num_chunks)
Definition: multibuf_v2.h:458
size_t CopyTo(ByteSpan dst, size_t offset=0) const
Definition: multibuf_v2.h:916
size_t CopyFrom(ConstByteSpan src, size_t offset=0)
Definition: multibuf_v2.h:928
bool TryReserveForPushBack(const BasicMultiBuf< kOtherProperties... > &mb)
Definition: multibuf_v2.h:1804
static constexpr bool is_observable()
Returns whether an observer can be registered on the MultiBuf.
Definition: multibuf_v2.h:205
constexpr size_t size() const
Definition: multibuf_v2.h:321
bool TryReserveLayers(size_t num_layers, size_t num_chunks=1)
Definition: multibuf_v2.h:1035
bool IsShareable(const_iterator pos) const
Definition: multibuf_v2.h:888
constexpr size_type NumLayers() const
Definition: multibuf_v2.h:1022
constexpr bool empty() const
Definition: multibuf_v2.h:317
Result< const_iterator > Discard(const_iterator pos, size_t size)
Definition: multibuf_v2.h:858
bool IsReleasable(const_iterator pos) const
Definition: multibuf_v2.h:866
bool IsRemovable(const_iterator pos, size_t size) const
Definition: multibuf_v2.h:798
bool TryReserveForInsert(const_iterator pos, const BasicMultiBuf< kOtherProperties... > &mb)
Definition: multibuf_v2.h:1693
size_type NumFragments() const
Definition: multibuf_v2.h:1013
void PushBack(BasicMultiBuf< kOtherProperties... > &&mb)
Definition: multibuf_v2.h:1848
constexpr MultiBufObserver * observer() const
Definition: multibuf_v2.h:987
auto Visit(Visitor visitor, ByteSpan copy, size_t offset)
Definition: multibuf_v2.h:970
bool TryReserveForInsert(const_iterator pos, const T &bytes)
Definition: multibuf_v2.h:1703
bool IsCompatible(const BasicMultiBuf &mb) const
Definition: multibuf_v2.h:419
Abstract interface for releasing memory.
Definition: deallocator.h:29
Definition: dynamic_deque.h:60
Definition: observer.h:29
constexpr bool ok() const
Definition: result.h:447
constexpr Status status() const
Definition: result.h:803
Definition: shared_ptr.h:60
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: byte_iterator.h:38
Definition: chunk_iterator.h:203
Definition: chunk_iterator.h:248
Definition: multibuf_v2.h:1172
Definition: multibuf_v2.h:1635
constexpr OutputIt copy(InputIt first, InputIt last, OutputIt d_first)
constexpr backport of <algorithm>'s std::copy for C++17.
Definition: algorithm.h:348
MultiBufProperty
Basic properties of a MultiBuf.
Definition: properties.h:24
Result(T value) -> Result< T >
Deduction guide to allow Result(v) rather than Result<T>(v).
The Pigweed namespace.
Definition: alignment.h:27