5#include <crunch/core/crunch_endian.hpp>
6#include <crunch/core/crunch_types.hpp>
7#include <crunch/fields/crunch_string.hpp>
8#include <crunch/messages/crunch_field.hpp>
9#include <crunch/messages/crunch_messages.hpp>
18namespace Crunch::serdes {
23template <std::
size_t Alignment = 1>
25 static_assert(Alignment == 1 || Alignment == 4 || Alignment == 8,
26 "StaticLayout only supports 1, 4, or 8 byte alignment.");
33 if constexpr (Alignment == 1) {
35 }
else if constexpr (Alignment == 4) {
47 template <
typename Message>
48 [[nodiscard]]
static constexpr std::size_t
Size() noexcept {
49 return PayloadStartOffset +
sizeof(
MessageId) +
50 calculate_payload_size(Message{});
60 template <
typename Message>
62 const Message& msg, std::span<std::byte> output)
noexcept {
63 std::size_t offset = StandardHeaderSize;
64 if (PayloadStartOffset > StandardHeaderSize) {
65 std::memset(output.data() + offset, 0,
66 PayloadStartOffset - StandardHeaderSize);
68 offset = PayloadStartOffset;
70 const MessageId msgId = Message::message_id;
72 std::memcpy(output.data() + offset, &le_msgId,
sizeof(msgId));
73 offset +=
sizeof(msgId);
76 [&](
const auto&... fields) {
77 ((offset = serialize_field(fields, output, offset)), ...);
90 template <
typename Message>
92 std::span<const std::byte> input, Message& msg)
noexcept
93 -> std::optional<Error> {
94 std::size_t offset = PayloadStartOffset;
97 std::memcpy(&msg_id, input.data() + offset,
sizeof(
MessageId));
99 if (msg_id != Message::message_id) {
104 std::optional<Error> err = std::nullopt;
106 [&](
auto&... fields) {
111 deserialize_field(fields, input, offset);
112 if (result.has_value()) {
113 offset = result.value();
115 err = result.error();
131 [[nodiscard]]
static constexpr std::size_t align_up(
132 std::size_t value, std::size_t alignment)
noexcept {
133 return (value + alignment - 1) & ~(alignment - 1);
143 template <
typename T>
144 [[nodiscard]]
static constexpr std::size_t calculate_padding(
145 std::size_t offset)
noexcept {
146 const std::size_t align = std::min(
sizeof(T), Alignment);
147 return (align - (offset % align)) % align;
150 static constexpr std::size_t PayloadStartOffset =
151 align_up(StandardHeaderSize, Alignment);
159 template <
typename Message>
160 [[nodiscard]]
static constexpr std::size_t calculate_payload_size(
161 const Message& msg)
noexcept {
162 std::size_t offset = 0;
164 [&](
const auto&... fields) {
165 ((offset = calculate_field_end_offset(fields, offset)), ...);
177 template <
typename T>
178 [[nodiscard]]
static constexpr std::size_t calculate_value_end_offset(
179 std::size_t offset)
noexcept {
180 if constexpr (fields::is_string_v<T>) {
181 return calculate_string_end_offset<T>(offset);
182 }
else if constexpr (messages::CrunchMessage<T>) {
183 return calculate_message_end_offset<T>(offset);
184 }
else if constexpr (messages::is_array_field_v<T>) {
185 return calculate_array_end_offset<T>(offset);
186 }
else if constexpr (messages::is_map_field_v<T>) {
187 return calculate_map_end_offset<T>(offset);
189 return calculate_scalar_end_offset<T>(offset);
199 template <
typename Field>
200 [[nodiscard]]
static constexpr std::size_t calculate_field_end_offset(
201 const Field&, std::size_t offset)
noexcept {
202 using ValueType =
typename Field::FieldType;
204 if constexpr (!messages::is_array_field_v<Field> &&
205 !messages::is_map_field_v<Field>) {
208 return calculate_value_end_offset<ValueType>(offset);
217 template <
typename T>
218 [[nodiscard]]
static constexpr std::size_t calculate_message_end_offset(
219 std::size_t offset)
noexcept {
220 const std::size_t padding =
221 calculate_padding<std::byte[Alignment]>(offset);
223 offset +=
sizeof(
MessageId) + calculate_payload_size(T{});
233 template <
typename T>
234 [[nodiscard]]
static constexpr std::size_t calculate_scalar_end_offset(
235 std::size_t offset)
noexcept {
236 using ValT =
typename T::ValueType;
237 offset += calculate_padding<ValT>(offset) +
sizeof(ValT);
247 template <
typename T>
248 [[nodiscard]]
static constexpr std::size_t calculate_string_end_offset(
249 std::size_t offset)
noexcept {
250 offset += calculate_padding<uint32_t>(offset) +
sizeof(uint32_t) +
261 template <
typename T>
262 [[nodiscard]]
static constexpr std::size_t calculate_array_end_offset(
263 std::size_t offset)
noexcept {
264 using ValT =
typename T::ValueType;
266 offset += calculate_padding<uint32_t>(offset) +
sizeof(uint32_t);
271 for (std::size_t i = 0; i < T::max_size; ++i) {
272 offset = calculate_value_end_offset<ValT>(offset);
283 template <
typename T>
284 [[nodiscard]]
static constexpr std::size_t calculate_map_end_offset(
285 std::size_t offset)
noexcept {
286 using KeyField =
typename T::PairType::first_type;
287 using ValueField =
typename T::PairType::second_type;
290 offset += calculate_padding<uint32_t>(offset) +
sizeof(uint32_t);
293 for (std::size_t i = 0; i < T::max_size; ++i) {
294 offset = calculate_value_end_offset<KeyField>(offset);
295 offset = calculate_value_end_offset<ValueField>(offset);
308 template <
typename T>
309 [[nodiscard]]
static constexpr std::size_t serialize_value(
310 const T& value, std::span<std::byte> output,
311 std::size_t offset)
noexcept {
312 if constexpr (fields::is_string_v<T>) {
313 return serialize_string(value, output, offset);
314 }
else if constexpr (messages::CrunchMessage<T>) {
315 return serialize_message(value, output, offset);
316 }
else if constexpr (messages::is_array_field_v<T>) {
317 return serialize_array(value, output, offset);
318 }
else if constexpr (messages::is_map_field_v<T>) {
319 return serialize_map(value, output, offset);
321 return serialize_scalar(value, output, offset);
333 template <
typename Field>
334 [[nodiscard]]
static constexpr std::size_t serialize_field(
335 const Field& field, std::span<std::byte> output,
336 std::size_t offset)
noexcept {
337 using ValueType =
typename Field::FieldType;
340 if constexpr (messages::is_array_field_v<Field>) {
341 return serialize_array(field, output, offset);
342 }
else if constexpr (messages::is_map_field_v<Field>) {
343 return serialize_map(field, output, offset);
345 const bool set = field.set_;
346 output[offset++] =
static_cast<std::byte
>(set ? 1 : 0);
349 return serialize_value(field.value_, output, offset);
352 const std::size_t end_offset =
353 calculate_value_end_offset<ValueType>(offset);
354 const std::size_t size = end_offset - offset;
355 std::memset(output.data() + offset, 0, size);
369 template <
typename T>
370 [[nodiscard]]
static constexpr std::size_t serialize_string(
371 const T& value, std::span<std::byte> output,
372 std::size_t offset)
noexcept {
373 const std::size_t padding = calculate_padding<uint32_t>(offset);
375 std::memset(output.data() + offset, 0, padding);
379 const uint32_t len =
static_cast<uint32_t
>(value.current_len_);
381 std::memcpy(output.data() + offset, &le_len,
sizeof(len));
382 offset +=
sizeof(len);
384 std::memcpy(output.data() + offset, value.buffer_.data(), T::max_size);
385 offset += T::max_size;
397 template <
typename T>
398 [[nodiscard]]
static constexpr std::size_t serialize_message(
399 const T& value, std::span<std::byte> output,
400 std::size_t offset)
noexcept {
401 const std::size_t padding =
402 calculate_padding<std::byte[Alignment]>(offset);
404 std::memset(output.data() + offset, 0, padding);
410 std::memcpy(output.data() + offset, &le_msgId,
sizeof(msgId));
411 offset +=
sizeof(msgId);
414 [&](
const auto&... fields) {
415 ((offset = serialize_field(fields, output, offset)), ...);
429 template <
typename T>
430 [[nodiscard]]
static constexpr std::size_t serialize_scalar(
431 const T& value, std::span<std::byte> output,
432 std::size_t offset)
noexcept {
433 using ValT =
typename T::ValueType;
434 const std::size_t padding = calculate_padding<ValT>(offset);
436 std::memset(output.data() + offset, 0, padding);
441 std::memcpy(output.data() + offset, &le_value,
sizeof(le_value));
442 offset +=
sizeof(le_value);
454 template <
typename T>
455 [[nodiscard]]
static constexpr std::size_t serialize_array(
456 const T& value, std::span<std::byte> output,
457 std::size_t offset)
noexcept {
458 using ValT =
typename T::ValueType;
459 const std::size_t padding = calculate_padding<uint32_t>(offset);
461 std::memset(output.data() + offset, 0, padding);
465 const uint32_t len =
static_cast<uint32_t
>(value.current_len_);
467 std::memcpy(output.data() + offset, &le_len,
sizeof(len));
468 offset +=
sizeof(len);
471 for (std::size_t i = 0; i < value.current_len_; ++i) {
472 offset = serialize_value(value.items_[i], output, offset);
476 for (std::size_t i = value.current_len_; i < T::max_size; ++i) {
477 const std::size_t end = calculate_value_end_offset<ValT>(offset);
478 std::memset(output.data() + offset, 0, end - offset);
492 template <
typename T>
493 [[nodiscard]]
static constexpr std::size_t serialize_map(
494 const T& value, std::span<std::byte> output,
495 std::size_t offset)
noexcept {
496 using KeyField =
typename T::PairType::first_type;
497 using ValueField =
typename T::PairType::second_type;
499 const std::size_t padding = calculate_padding<uint32_t>(offset);
501 std::memset(output.data() + offset, 0, padding);
505 const uint32_t len =
static_cast<uint32_t
>(value.current_len_);
507 std::memcpy(output.data() + offset, &le_len,
sizeof(len));
508 offset +=
sizeof(len);
511 for (std::size_t i = 0; i < value.current_len_; ++i) {
512 const auto& pair = value.items_[i];
513 offset = serialize_value(pair.first, output, offset);
514 offset = serialize_value(pair.second, output, offset);
518 for (std::size_t i = value.current_len_; i < T::max_size; ++i) {
519 const std::size_t key_end =
520 calculate_value_end_offset<KeyField>(offset);
521 std::memset(output.data() + offset, 0, key_end - offset);
524 const std::size_t val_end =
525 calculate_value_end_offset<ValueField>(offset);
526 std::memset(output.data() + offset, 0, val_end - offset);
541 template <
typename T>
542 [[nodiscard]]
static constexpr auto deserialize_value(
543 T& value,
bool set, std::span<const std::byte> input,
544 std::size_t offset)
noexcept -> std::expected<std::size_t, Error> {
545 if constexpr (fields::is_string_v<T>) {
546 return deserialize_string(value, set, input, offset);
547 }
else if constexpr (messages::CrunchMessage<T>) {
548 return deserialize_message(value, set, input, offset);
549 }
else if constexpr (messages::is_array_field_v<T>) {
550 return deserialize_array(value, set, input, offset);
551 }
else if constexpr (messages::is_map_field_v<T>) {
552 return deserialize_map(value, set, input, offset);
554 return deserialize_scalar(value, set, input, offset);
566 template <
typename Field>
567 [[nodiscard]]
static constexpr auto deserialize_field(
568 Field& field, std::span<const std::byte> input,
569 std::size_t offset)
noexcept -> std::expected<std::size_t, Error> {
571 if constexpr (messages::is_array_field_v<Field>) {
572 return deserialize_array(field,
true, input, offset);
573 }
else if constexpr (messages::is_map_field_v<Field>) {
574 return deserialize_map(field,
true, input, offset);
576 const bool set =
static_cast<bool>(input[offset++]);
578 return deserialize_value(field.value_, set, input, offset);
591 template <
typename T>
592 [[nodiscard]]
static constexpr auto deserialize_message(
593 T& value,
bool set, std::span<const std::byte> input,
594 std::size_t offset)
noexcept -> std::expected<std::size_t, Error> {
595 const std::size_t padding =
596 calculate_padding<std::byte[Alignment]>(offset);
600 std::memcpy(&msg_id, input.data() + offset,
sizeof(
MessageId));
603 if (set && msg_id != T::message_id) {
609 std::optional<Error> err = std::nullopt;
611 [&](
auto&... sub_fields) {
616 deserialize_field(sub_fields, input, offset);
617 if (result.has_value()) {
618 offset = result.value();
620 err = result.error();
627 return std::unexpected(err.value());
631 offset += calculate_payload_size(T{});
645 template <
typename T>
646 [[nodiscard]]
static constexpr auto deserialize_scalar(
647 T& value,
bool set, std::span<const std::byte> input,
648 std::size_t offset)
noexcept -> std::expected<std::size_t, Error> {
649 using ValT =
typename T::ValueType;
650 offset += calculate_padding<ValT>(offset);
654 std::memcpy(&le_value, input.data() + offset,
sizeof(ValT));
659 offset +=
sizeof(ValT);
672 template <
typename T>
673 [[nodiscard]]
static constexpr auto deserialize_string(
674 T& value,
bool set, std::span<const std::byte> input,
675 std::size_t offset)
noexcept -> std::expected<std::size_t, Error> {
676 offset += calculate_padding<uint32_t>(offset);
679 std::memcpy(&le_len, input.data() + offset,
sizeof(le_len));
680 offset +=
sizeof(le_len);
684 if (len > T::max_size) {
687 "deserialized string too long"));
689 std::memcpy(value.buffer_.data(), input.data() + offset,
691 value.current_len_ = len;
695 offset += T::max_size;
708 template <
typename T>
709 [[nodiscard]]
static constexpr auto deserialize_array(
710 T& value,
bool set, std::span<const std::byte> input,
711 std::size_t offset)
noexcept -> std::expected<std::size_t, Error> {
712 const auto array_end = calculate_array_end_offset<T>(offset);
719 offset += calculate_padding<uint32_t>(offset);
722 std::memcpy(&le_len, input.data() + offset,
sizeof(le_len));
723 offset +=
sizeof(le_len);
726 if (len > T::max_size) {
727 return std::unexpected(
730 value.current_len_ = len;
733 for (
size_t i = 0; i < len; ++i) {
734 auto res = deserialize_value(value.items_[i],
true, input, offset);
736 return std::unexpected(res.error());
738 offset = res.value();
753 template <
typename T>
754 [[nodiscard]]
static constexpr auto deserialize_map(
755 T& value,
bool set, std::span<const std::byte> input,
756 std::size_t offset)
noexcept -> std::expected<std::size_t, Error> {
757 const auto map_end = calculate_map_end_offset<T>(offset);
764 offset += calculate_padding<uint32_t>(offset);
768 std::memcpy(&le_len, input.data() + offset,
sizeof(le_len));
769 offset +=
sizeof(le_len);
772 if (len > T::max_size) {
773 return std::unexpected(
776 value.current_len_ = len;
779 for (
size_t i = 0; i < len; ++i) {
780 auto& pair = value.items_[i];
783 deserialize_value(pair.first,
true, input, offset);
785 return std::unexpected(res_key.error());
787 offset = res_key.value();
790 deserialize_value(pair.second,
true, input, offset);
792 return std::unexpected(res_val.error());
794 offset = res_val.value();
801using PackedLayout = StaticLayout<1>;
802using Aligned32Layout = StaticLayout<4>;
803using Aligned64Layout = StaticLayout<8>;
constexpr T LittleEndian(T value) noexcept
Converts a value to/from Little Endian byte order.
Definition: crunch_endian.hpp:21
int32_t MessageId
Unique identifier for a message type.
Definition: crunch_types.hpp:25
Format
Serialization format identifier stored in the message header.
Definition: crunch_types.hpp:30
@ Aligned4
4-byte alignment padding.
@ Packed
No alignment padding (Alignment = 1).
@ Aligned8
8-byte alignment padding.
static constexpr Error invalid_message_id() noexcept
Creates an error representing an invalid message ID.
Definition: crunch_types.hpp:114
static constexpr Error capacity_exceeded(FieldId id, const char(&msg)[N]) noexcept
Creates an error representing capacity exceeded. Used for strings and aggregated fields.
Definition: crunch_types.hpp:133
A deterministic, fixed-size binary serialization policy.
Definition: crunch_static_layout.hpp:24
static constexpr std::size_t Size() noexcept
Calculates the serialized size of a message.
Definition: crunch_static_layout.hpp:48
static constexpr Crunch::Format GetFormat() noexcept
Gets the Crunch format corresponding to the alignment.
Definition: crunch_static_layout.hpp:32
static constexpr auto Deserialize(std::span< const std::byte > input, Message &msg) noexcept -> std::optional< Error >
Deserializes a message from the input buffer.
Definition: crunch_static_layout.hpp:91
static constexpr std::size_t Serialize(const Message &msg, std::span< std::byte > output) noexcept
Serializes a message into the output buffer.
Definition: crunch_static_layout.hpp:61