template <typename Storage>
struct StorageTraits
Defined at line 82 of file ../../src/lib/zbitl/include/lib/zbitl/storage-traits.h
The zbitl::StorageTraits template must be specialized for each type used as
the Storage type parameter to zbitl::View (see
<lib
/zbitl/view.h). The
generic template can only be instantiated with `std::tuple
<
>` as the
Storage type. This is a stub implementation that always fails with an
empty error_type. It also serves to document the API for StorageTraits
specializations.
The underlying storage memory is expected to be
`kStorageAlignment`-aligned.
Public Methods
std::string_view error_string (error_type error)
This method is expected to return a type convertible to std::string_view
(e.g., std::string or const char*) representing the message associated to
a given error value. The returned object is "owning" and so it is
expected that the caller keep the returned object alive for as long as
they use any string_view converted from it.
Defined at line 101 of file ../../src/lib/zbitl/include/lib/zbitl/storage-traits.h
fit::result<error_type, uint32_t> Capacity (Storage & zbi)
This returns the upper bound on available space where the ZBI is stored.
The container must fit within this maximum. Storage past the container's
self-encoded size need not be accessible and will never be accessed.
If the actual upper bound is unknown, this can safely return UINT32_MAX.
Defined at line 107 of file ../../src/lib/zbitl/include/lib/zbitl/storage-traits.h
fit::result<error_type> EnsureCapacity (Storage & zbi, uint32_t capacity)
A specialization must define this if it also defines Write. This method
ensures that the capacity is at least that of the provided value
(possibly larger), for specializations where such an operation is
sensible.
Defined at line 115 of file ../../src/lib/zbitl/include/lib/zbitl/storage-traits.h
fit::result<error_type, payload_type> Payload (Storage & zbi, uint32_t offset, uint32_t length)
This fetches the item payload view object, whatever that means for this
Storage type. This is not expected to read the contents, just transfer a
pointer or offset around so they can be explicitly read later.
Defined at line 122 of file ../../src/lib/zbitl/include/lib/zbitl/storage-traits.h
template <typename Callback>
fit::result<error_type, decltype(callback(ByteView{}))> Read (Storage & zbi, payload_type payload, uint32_t length, Callback && callback)
Referred to as the "buffered read".
This reads the payload indicated by a payload_type as returned by Payload
and feeds it to the callback in chunks sized for the convenience of the
storage backend. The length is guaranteed to match that passed to
Payload to fetch this payload_type value.
The callback returns some type fit::result
<E
>. Read returns
fit::result
<error
_type, fit::result
<E
>>>, yielding a storage error or
the result of the callback. If a callback returns an error, its return
value is used immediately. If a callback returns success, another
callback may be made for another chunk of the payload. If the payload is
empty (`length` == 0), there will always be a single callback made with
an empty data argument.
Defined at line 142 of file ../../src/lib/zbitl/include/lib/zbitl/storage-traits.h
fit::result<error_type> Read (Storage & zbi, payload_type payload, void * buffer, uint32_t length)
Referred to as the "unbuffered read".
A specialization provides this overload if the payload can be read
directly into a provided buffer for zero-copy operation.
Defined at line 151 of file ../../src/lib/zbitl/include/lib/zbitl/storage-traits.h
template <PayloadCompatibleStorage T, bool LowLocality>
fit::result<error_type, std::span<const T>> Read (Storage & zbi, payload_type payload, uint32_t length)
Referred to as the "one-shot read".
A specialization only provides this overload if the payload can be
accessed directly in memory. If this overload is provided, then the other
overloads need not be provided. The returned view is only guaranteed
valid until the next use of the same Storage object. So it could
e.g. point into a cache that's repurposed by this or other calls made
later using the same object.
One may attempt to read the data out in any particular form, parameterized
by `T`, provided that `alignof(T)
<
= kStorageAlignment`. The offset
associated with `payload` is expected to be `alignof(T)`-aligned, though
that is an invariant that the caller must keep track of.
`LowLocality` gives whether there is an expectation that adjacent data
will subsequently be read; if true, the amortized cost of the read might
be determined to be too high and storage backends might decide to perform
the read differently or not implement the method at all in this case.
Defined at line 175 of file ../../src/lib/zbitl/include/lib/zbitl/storage-traits.h
template <PayloadCompatibleStorage T, bool LowLocality>
fit::result<error_type, std::span<const T>> Read (Storage & zbi, payload_type payload, uint32_t length)
Referred to as the "one-shot read".
A specialization only provides this overload if the payload can be
accessed directly in memory. If this overload is provided, then the other
overloads need not be provided. The returned view is only guaranteed
valid until the next use of the same Storage object. So it could
e.g. point into a cache that's repurposed by this or other calls made
later using the same object.
One may attempt to read the data out in any particular form, parameterized
by `T`, provided that `alignof(T)
<
= kStorageAlignment`. The offset
associated with `payload` is expected to be `alignof(T)`-aligned, though
that is an invariant that the caller must keep track of.
`LowLocality` gives whether there is an expectation that adjacent data
will subsequently be read; if true, the amortized cost of the read might
be determined to be too high and storage backends might decide to perform
the read differently or not implement the method at all in this case.
Defined at line 175 of file ../../src/lib/zbitl/include/lib/zbitl/storage-traits.h
template <PayloadCompatibleStorage T, bool LowLocality>
fit::result<error_type, std::span<const T>> Read (Storage & zbi, payload_type payload, uint32_t length)
Referred to as the "one-shot read".
A specialization only provides this overload if the payload can be
accessed directly in memory. If this overload is provided, then the other
overloads need not be provided. The returned view is only guaranteed
valid until the next use of the same Storage object. So it could
e.g. point into a cache that's repurposed by this or other calls made
later using the same object.
One may attempt to read the data out in any particular form, parameterized
by `T`, provided that `alignof(T)
<
= kStorageAlignment`. The offset
associated with `payload` is expected to be `alignof(T)`-aligned, though
that is an invariant that the caller must keep track of.
`LowLocality` gives whether there is an expectation that adjacent data
will subsequently be read; if true, the amortized cost of the read might
be determined to be too high and storage backends might decide to perform
the read differently or not implement the method at all in this case.
Defined at line 175 of file ../../src/lib/zbitl/include/lib/zbitl/storage-traits.h
template <PayloadCompatibleStorage T, bool LowLocality>
fit::result<error_type, std::span<const T>> Read (Storage & zbi, payload_type payload, uint32_t length)
Referred to as the "one-shot read".
A specialization only provides this overload if the payload can be
accessed directly in memory. If this overload is provided, then the other
overloads need not be provided. The returned view is only guaranteed
valid until the next use of the same Storage object. So it could
e.g. point into a cache that's repurposed by this or other calls made
later using the same object.
One may attempt to read the data out in any particular form, parameterized
by `T`, provided that `alignof(T)
<
= kStorageAlignment`. The offset
associated with `payload` is expected to be `alignof(T)`-aligned, though
that is an invariant that the caller must keep track of.
`LowLocality` gives whether there is an expectation that adjacent data
will subsequently be read; if true, the amortized cost of the read might
be determined to be too high and storage backends might decide to perform
the read differently or not implement the method at all in this case.
Defined at line 175 of file ../../src/lib/zbitl/include/lib/zbitl/storage-traits.h
fit::result<error_type> Write (Storage & zbi, uint32_t offset, ByteView data)
Referred to as the "buffered write".
A specialization defines this only if it supports mutation. It might be
called to write whole or partial headers and/or payloads, but it will
never be called with an offset and size that would exceed the capacity
previously reported by Capacity (above). It returns success only if it
wrote the whole chunk specified. If it returns an error, any subset of
the chunk that failed to write might be corrupted in the image and the
container will always revalidate everything.
Defined at line 189 of file ../../src/lib/zbitl/include/lib/zbitl/storage-traits.h
fit::result<error_type> Write (Storage & zbi, uint32_t offset, ByteView data)
Referred to as the "buffered write".
A specialization defines this only if it supports mutation. It might be
called to write whole or partial headers and/or payloads, but it will
never be called with an offset and size that would exceed the capacity
previously reported by Capacity (above). It returns success only if it
wrote the whole chunk specified. If it returns an error, any subset of
the chunk that failed to write might be corrupted in the image and the
container will always revalidate everything.
Defined at line 189 of file ../../src/lib/zbitl/include/lib/zbitl/storage-traits.h
fit::result<error_type> Write (Storage & zbi, uint32_t offset, ByteView data)
Referred to as the "buffered write".
A specialization defines this only if it supports mutation. It might be
called to write whole or partial headers and/or payloads, but it will
never be called with an offset and size that would exceed the capacity
previously reported by Capacity (above). It returns success only if it
wrote the whole chunk specified. If it returns an error, any subset of
the chunk that failed to write might be corrupted in the image and the
container will always revalidate everything.
Defined at line 189 of file ../../src/lib/zbitl/include/lib/zbitl/storage-traits.h
fit::result<error_type> Write (Storage & zbi, uint32_t offset, ByteView data)
Referred to as the "buffered write".
A specialization defines this only if it supports mutation. It might be
called to write whole or partial headers and/or payloads, but it will
never be called with an offset and size that would exceed the capacity
previously reported by Capacity (above). It returns success only if it
wrote the whole chunk specified. If it returns an error, any subset of
the chunk that failed to write might be corrupted in the image and the
container will always revalidate everything.
Defined at line 189 of file ../../src/lib/zbitl/include/lib/zbitl/storage-traits.h
fit::result<error_type, void *> Write (Storage & zbi, uint32_t offset, uint32_t length)
Referred to as the "unbuffered write".
A specialization may define this if it also defines Write. It returns a
pointer where the data can be mutated directly in memory. That pointer is
only guaranteed valid until the next use of the same Storage object. So
it could e.g. point into a cache that's repurposed by this or other calls
made later using the same object.
Defined at line 200 of file ../../src/lib/zbitl/include/lib/zbitl/storage-traits.h
fit::result<error_type, Storage> Create (Storage & zbi, uint32_t capacity, uint32_t initial_zero_size)
A specialization defines this only if it supports mutation and if creating
new storage from whole cloth makes sense for the storage type somehow.
Its successful return value is whatever makes sense for returning a new,
owning object of a type akin to Storage (possibly Storage itself, possibly
another type). The new object refers to new storage of at least the given
capacity (in bytes) with a provided zero-fill header size. The old
storage object might be used as a prototype in some sense, but the new
object is distinct storage.
Defined at line 212 of file ../../src/lib/zbitl/include/lib/zbitl/storage-traits.h
template <typename SlopCheck>
fit::result<error_type, std::optional<std::pair<Storage, uint32_t>>> Clone (Storage & zbi, uint32_t offset, uint32_t length, uint32_t to_offset, SlopCheck && slopcheck)
A specialization defines this only if it defines Create, and if Clone adds
any value. The new object is new storage that doesn't mutate the original
storage, whose capacity is at least `to_offset + length`, and whose
contents are the subrange of the original storage starting at `offset`,
with zero-fill from the beginning of the storage up to `to_offset` bytes.
The successful return value is `std::optional
<std
::pair
<T
, uint32_t>>`
where T is what a successful Create call returns and the uint32_t is the
actual offset into the new storage, aka the "slop" (see below). If this
doesn't have something more efficient to do than just allocating storage
space for and copying all `length` bytes of data (using Create and Write),
then it can just return std::nullopt. If the method would *always* return
std::nullopt then it can just be omitted entirely. The "slop" refers to
some number of bytes at the beginning of the storage that will read as
zero before the requested range of the original storage begins. The
storage backend will endeavor to make this match `to_offset`, but might
deliver a different result due to factors like page-rounding. The
`slopcheck` parameter is a `(uint32_t) -> bool` predicate function object
that says whether a given byte count is acceptable as slop for this clone.
If `slopcheck(slop)` returns false, Clone *must* return std::nullopt
rather than yielding storage with a rejected slop byte count.
Defined at line 238 of file ../../src/lib/zbitl/include/lib/zbitl/storage-traits.h
template <typename SlopCheck>
fit::result<error_type, std::optional<std::pair<Storage, uint32_t>>> Clone (Storage & zbi, uint32_t offset, uint32_t length, uint32_t to_offset, SlopCheck && slopcheck)
A specialization defines this only if it defines Create, and if Clone adds
any value. The new object is new storage that doesn't mutate the original
storage, whose capacity is at least `to_offset + length`, and whose
contents are the subrange of the original storage starting at `offset`,
with zero-fill from the beginning of the storage up to `to_offset` bytes.
The successful return value is `std::optional
<std
::pair
<T
, uint32_t>>`
where T is what a successful Create call returns and the uint32_t is the
actual offset into the new storage, aka the "slop" (see below). If this
doesn't have something more efficient to do than just allocating storage
space for and copying all `length` bytes of data (using Create and Write),
then it can just return std::nullopt. If the method would *always* return
std::nullopt then it can just be omitted entirely. The "slop" refers to
some number of bytes at the beginning of the storage that will read as
zero before the requested range of the original storage begins. The
storage backend will endeavor to make this match `to_offset`, but might
deliver a different result due to factors like page-rounding. The
`slopcheck` parameter is a `(uint32_t) -> bool` predicate function object
that says whether a given byte count is acceptable as slop for this clone.
If `slopcheck(slop)` returns false, Clone *must* return std::nullopt
rather than yielding storage with a rejected slop byte count.
Defined at line 238 of file ../../src/lib/zbitl/include/lib/zbitl/storage-traits.h