23 { Layout<T>::SIZE } -> std::convertible_to<std::size_t>;
34template <
typename Class,
typename Value>
44 if constexpr (std::is_array_v<T>) {
45 return std::extent_v<T> * wireSize<std::remove_extent_t<T>>();
55template <auto Member, std::
size_t Offset>
60 static constexpr auto MEMBER = Member;
61 static constexpr std::size_t
OFFSET = Offset;
62 static constexpr std::size_t
SIZE = detail::wireSize<Value>();
69 const std::array<std::size_t,
sizeof...(field)> begin{
decltype(field)::OFFSET...};
70 const std::array<std::size_t,
sizeof...(field)> end{(
decltype(field)::OFFSET +
decltype(field)
::SIZE)...};
71 for (std::size_t i = 0; i < begin.size(); ++i) {
72 if (end[i] > SIZE<T>) {
75 for (std::size_t j = 0; j < i; ++j) {
76 if (begin[i] < end[j] && begin[j] < end[i]) {
81 return (std::same_as<
typename decltype(field)::Class, T> && ...);
86template <
typename Value>
87constexpr void read(Value& value, std::span<const uint8_t> bytes, std::size_t offset)
89 if constexpr (std::is_array_v<Value>) {
90 for (std::size_t i = 0; i < std::extent_v<Value>; ++i) {
94 static_assert(VALID_LAYOUT<Value>,
"Layout<T> fields must belong to T, fit in SIZE and not overlap");
95 const auto record = offset <= bytes.size() ? bytes.subspan(offset) : std::span<const uint8_t>{};
98 (
detail::read(value.*
decltype(field)::MEMBER, record,
decltype(field)::OFFSET), ...);
102 value = LittleEndian::read<Value>(bytes, offset).value_or(Value{});
106template <
typename Value>
107constexpr void write(std::span<uint8_t> bytes, std::size_t offset,
const Value& value)
109 if constexpr (std::is_array_v<Value>) {
110 for (std::size_t i = 0; i < std::extent_v<Value>; ++i) {
114 static_assert(VALID_LAYOUT<Value>,
"Layout<T> fields must belong to T, fit in SIZE and not overlap");
117 (
detail::write(bytes, offset +
decltype(field)::OFFSET, value.*
decltype(field)::MEMBER), ...);
128constexpr T
decode(std::span<const uint8_t> bytes, std::size_t offset = 0)
136constexpr std::array<uint8_t, SIZE<T>>
encode(
const T& value)
138 std::array<uint8_t, SIZE<T>> bytes{};
constexpr bool write(std::span< uint8_t > bytes, std::size_t offset, T value)
Encode an unaligned little-endian scalar. Insufficient space leaves the destination unchanged.
constexpr std::size_t wireSize()
constexpr void read(Value &value, std::span< const uint8_t > bytes, std::size_t offset)
constexpr void write(std::span< uint8_t > bytes, std::size_t offset, const Value &value)
constexpr T decode(std::span< const uint8_t > bytes, std::size_t offset=0)
Decodes the record at offset. Fields whose bytes are unavailable decode as zero.
constexpr bool VALID_LAYOUT
Every field belongs to T, fits within SIZE<T> and overlaps no other field.
constexpr std::array< uint8_t, SIZE< T > > encode(const T &value)
constexpr std::size_t SIZE
typename detail::MemberPointer< decltype(Member)>::ClassType Class
static constexpr auto MEMBER
static constexpr std::size_t SIZE
typename detail::MemberPointer< decltype(Member)>::ValueType Value
static constexpr std::size_t OFFSET