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 + calculate_payload_size(Message{});
59 template <
typename Message>
61 const Message& msg, std::span<std::byte> output)
noexcept {
64 if (PayloadStartOffset > StandardHeaderSize) {
65 std::memset(output.data() + StandardHeaderSize, 0,
66 PayloadStartOffset - StandardHeaderSize);
68 std::size_t offset = PayloadStartOffset;
71 [&](
const auto&... fields) {
72 ((offset = serialize_field(fields, output, offset)), ...);
85 template <
typename Message>
87 std::span<const std::byte> input, Message& msg)
noexcept
88 -> std::optional<Error> {
90 std::size_t offset = PayloadStartOffset;
92 std::optional<Error> err = std::nullopt;
94 [&](
auto&... fields) {
99 deserialize_field(fields, input, offset);
100 if (result.has_value()) {
101 offset = result.value();
103 err = result.error();
119 [[nodiscard]]
static constexpr std::size_t align_up(
120 std::size_t value, std::size_t alignment)
noexcept {
121 return (value + alignment - 1) & ~(alignment - 1);
131 template <
typename T>
132 [[nodiscard]]
static constexpr std::size_t calculate_padding(
133 std::size_t offset)
noexcept {
134 const std::size_t align = std::min(
sizeof(T), Alignment);
135 return (align - (offset % align)) % align;
138 static constexpr std::size_t PayloadStartOffset =
139 align_up(StandardHeaderSize, Alignment);
147 template <
typename Message>
148 [[nodiscard]]
static constexpr std::size_t calculate_payload_size(
149 const Message& msg)
noexcept {
150 std::size_t offset = 0;
152 [&](
const auto&... fields) {
153 ((offset = calculate_field_end_offset(fields, offset)), ...);
165 template <
typename T>
166 [[nodiscard]]
static constexpr std::size_t calculate_value_end_offset(
167 std::size_t offset)
noexcept {
168 if constexpr (fields::is_string_v<T>) {
169 return calculate_string_end_offset<T>(offset);
170 }
else if constexpr (messages::CrunchMessage<T>) {
171 return calculate_message_end_offset<T>(offset);
172 }
else if constexpr (messages::is_array_field_v<T>) {
173 return calculate_array_end_offset<T>(offset);
174 }
else if constexpr (messages::is_map_field_v<T>) {
175 return calculate_map_end_offset<T>(offset);
177 return calculate_scalar_end_offset<T>(offset);
187 template <
typename Field>
188 [[nodiscard]]
static constexpr std::size_t calculate_field_end_offset(
189 const Field&, std::size_t offset)
noexcept {
190 using ValueType =
typename Field::FieldType;
192 if constexpr (!messages::is_array_field_v<Field> &&
193 !messages::is_map_field_v<Field>) {
196 return calculate_value_end_offset<ValueType>(offset);
205 template <
typename T>
206 [[nodiscard]]
static constexpr std::size_t calculate_message_end_offset(
207 std::size_t offset)
noexcept {
208 const std::size_t padding =
209 calculate_padding<std::byte[Alignment]>(offset);
211 offset +=
sizeof(
MessageId) + calculate_payload_size(T{});
221 template <
typename T>
222 [[nodiscard]]
static constexpr std::size_t calculate_scalar_end_offset(
223 std::size_t offset)
noexcept {
224 using ValT =
typename T::ValueType;
225 offset += calculate_padding<ValT>(offset) +
sizeof(ValT);
235 template <
typename T>
236 [[nodiscard]]
static constexpr std::size_t calculate_string_end_offset(
237 std::size_t offset)
noexcept {
238 offset += calculate_padding<uint32_t>(offset) +
sizeof(uint32_t) +
249 template <
typename T>
250 [[nodiscard]]
static constexpr std::size_t calculate_array_end_offset(
251 std::size_t offset)
noexcept {
252 using ValT =
typename T::ValueType;
254 offset += calculate_padding<uint32_t>(offset) +
sizeof(uint32_t);
259 for (std::size_t i = 0; i < T::max_size; ++i) {
260 offset = calculate_value_end_offset<ValT>(offset);
271 template <
typename T>
272 [[nodiscard]]
static constexpr std::size_t calculate_map_end_offset(
273 std::size_t offset)
noexcept {
274 using KeyField =
typename T::PairType::first_type;
275 using ValueField =
typename T::PairType::second_type;
278 offset += calculate_padding<uint32_t>(offset) +
sizeof(uint32_t);
281 for (std::size_t i = 0; i < T::max_size; ++i) {
282 offset = calculate_value_end_offset<KeyField>(offset);
283 offset = calculate_value_end_offset<ValueField>(offset);
296 template <
typename T>
297 [[nodiscard]]
static constexpr std::size_t serialize_value(
298 const T& value, std::span<std::byte> output,
299 std::size_t offset)
noexcept {
300 if constexpr (fields::is_string_v<T>) {
301 return serialize_string(value, output, offset);
302 }
else if constexpr (messages::CrunchMessage<T>) {
303 return serialize_message(value, output, offset);
304 }
else if constexpr (messages::is_array_field_v<T>) {
305 return serialize_array(value, output, offset);
306 }
else if constexpr (messages::is_map_field_v<T>) {
307 return serialize_map(value, output, offset);
309 return serialize_scalar(value, output, offset);
321 template <
typename Field>
322 [[nodiscard]]
static constexpr std::size_t serialize_field(
323 const Field& field, std::span<std::byte> output,
324 std::size_t offset)
noexcept {
325 using ValueType =
typename Field::FieldType;
328 if constexpr (messages::is_array_field_v<Field>) {
329 return serialize_array(field, output, offset);
330 }
else if constexpr (messages::is_map_field_v<Field>) {
331 return serialize_map(field, output, offset);
333 const bool set = field.set_;
334 output[offset++] =
static_cast<std::byte
>(set ? 1 : 0);
337 return serialize_value(field.value_, output, offset);
340 const std::size_t end_offset =
341 calculate_value_end_offset<ValueType>(offset);
342 const std::size_t size = end_offset - offset;
343 std::memset(output.data() + offset, 0, size);
357 template <
typename T>
358 [[nodiscard]]
static constexpr std::size_t serialize_string(
359 const T& value, std::span<std::byte> output,
360 std::size_t offset)
noexcept {
361 const std::size_t padding = calculate_padding<uint32_t>(offset);
363 std::memset(output.data() + offset, 0, padding);
367 const uint32_t len =
static_cast<uint32_t
>(value.current_len_);
369 std::memcpy(output.data() + offset, &le_len,
sizeof(len));
370 offset +=
sizeof(len);
372 std::memcpy(output.data() + offset, value.buffer_.data(), T::max_size);
373 offset += T::max_size;
385 template <
typename T>
386 [[nodiscard]]
static constexpr std::size_t serialize_message(
387 const T& value, std::span<std::byte> output,
388 std::size_t offset)
noexcept {
389 const std::size_t padding =
390 calculate_padding<std::byte[Alignment]>(offset);
392 std::memset(output.data() + offset, 0, padding);
398 std::memcpy(output.data() + offset, &le_msgId,
sizeof(msgId));
399 offset +=
sizeof(msgId);
402 [&](
const auto&... fields) {
403 ((offset = serialize_field(fields, output, offset)), ...);
417 template <
typename T>
418 [[nodiscard]]
static constexpr std::size_t serialize_scalar(
419 const T& value, std::span<std::byte> output,
420 std::size_t offset)
noexcept {
421 using ValT =
typename T::ValueType;
422 const std::size_t padding = calculate_padding<ValT>(offset);
424 std::memset(output.data() + offset, 0, padding);
429 std::memcpy(output.data() + offset, &le_value,
sizeof(le_value));
430 offset +=
sizeof(le_value);
442 template <
typename T>
443 [[nodiscard]]
static constexpr std::size_t serialize_array(
444 const T& value, std::span<std::byte> output,
445 std::size_t offset)
noexcept {
446 using ValT =
typename T::ValueType;
447 const std::size_t padding = calculate_padding<uint32_t>(offset);
449 std::memset(output.data() + offset, 0, padding);
453 const uint32_t len =
static_cast<uint32_t
>(value.current_len_);
455 std::memcpy(output.data() + offset, &le_len,
sizeof(len));
456 offset +=
sizeof(len);
459 for (std::size_t i = 0; i < value.current_len_; ++i) {
460 offset = serialize_value(value.items_[i], output, offset);
464 for (std::size_t i = value.current_len_; i < T::max_size; ++i) {
465 const std::size_t end = calculate_value_end_offset<ValT>(offset);
466 std::memset(output.data() + offset, 0, end - offset);
480 template <
typename T>
481 [[nodiscard]]
static constexpr std::size_t serialize_map(
482 const T& value, std::span<std::byte> output,
483 std::size_t offset)
noexcept {
484 using KeyField =
typename T::PairType::first_type;
485 using ValueField =
typename T::PairType::second_type;
487 const std::size_t padding = calculate_padding<uint32_t>(offset);
489 std::memset(output.data() + offset, 0, padding);
493 const uint32_t len =
static_cast<uint32_t
>(value.current_len_);
495 std::memcpy(output.data() + offset, &le_len,
sizeof(len));
496 offset +=
sizeof(len);
499 for (std::size_t i = 0; i < value.current_len_; ++i) {
500 const auto& pair = value.items_[i];
501 offset = serialize_value(pair.first, output, offset);
502 offset = serialize_value(pair.second, output, offset);
506 for (std::size_t i = value.current_len_; i < T::max_size; ++i) {
507 const std::size_t key_end =
508 calculate_value_end_offset<KeyField>(offset);
509 std::memset(output.data() + offset, 0, key_end - offset);
512 const std::size_t val_end =
513 calculate_value_end_offset<ValueField>(offset);
514 std::memset(output.data() + offset, 0, val_end - offset);
529 template <
typename T>
530 [[nodiscard]]
static constexpr auto deserialize_value(
531 T& value,
bool set, std::span<const std::byte> input,
532 std::size_t offset)
noexcept -> std::expected<std::size_t, Error> {
533 if constexpr (fields::is_string_v<T>) {
534 return deserialize_string(value, set, input, offset);
535 }
else if constexpr (messages::CrunchMessage<T>) {
536 return deserialize_message(value, set, input, offset);
537 }
else if constexpr (messages::is_array_field_v<T>) {
538 return deserialize_array(value, set, input, offset);
539 }
else if constexpr (messages::is_map_field_v<T>) {
540 return deserialize_map(value, set, input, offset);
542 return deserialize_scalar(value, set, input, offset);
554 template <
typename Field>
555 [[nodiscard]]
static constexpr auto deserialize_field(
556 Field& field, std::span<const std::byte> input,
557 std::size_t offset)
noexcept -> std::expected<std::size_t, Error> {
559 if constexpr (messages::is_array_field_v<Field>) {
560 return deserialize_array(field,
true, input, offset);
561 }
else if constexpr (messages::is_map_field_v<Field>) {
562 return deserialize_map(field,
true, input, offset);
564 const bool set =
static_cast<bool>(input[offset++]);
566 return deserialize_value(field.value_, set, input, offset);
579 template <
typename T>
580 [[nodiscard]]
static constexpr auto deserialize_message(
581 T& value,
bool set, std::span<const std::byte> input,
582 std::size_t offset)
noexcept -> std::expected<std::size_t, Error> {
583 const std::size_t padding =
584 calculate_padding<std::byte[Alignment]>(offset);
588 std::memcpy(&msg_id, input.data() + offset,
sizeof(
MessageId));
591 if (set && msg_id != T::message_id) {
597 std::optional<Error> err = std::nullopt;
599 [&](
auto&... sub_fields) {
604 deserialize_field(sub_fields, input, offset);
605 if (result.has_value()) {
606 offset = result.value();
608 err = result.error();
615 return std::unexpected(err.value());
619 offset += calculate_payload_size(T{});
633 template <
typename T>
634 [[nodiscard]]
static constexpr auto deserialize_scalar(
635 T& value,
bool set, std::span<const std::byte> input,
636 std::size_t offset)
noexcept -> std::expected<std::size_t, Error> {
637 using ValT =
typename T::ValueType;
638 offset += calculate_padding<ValT>(offset);
642 std::memcpy(&le_value, input.data() + offset,
sizeof(ValT));
647 offset +=
sizeof(ValT);
660 template <
typename T>
661 [[nodiscard]]
static constexpr auto deserialize_string(
662 T& value,
bool set, std::span<const std::byte> input,
663 std::size_t offset)
noexcept -> std::expected<std::size_t, Error> {
664 offset += calculate_padding<uint32_t>(offset);
667 std::memcpy(&le_len, input.data() + offset,
sizeof(le_len));
668 offset +=
sizeof(le_len);
672 if (len > T::max_size) {
675 "deserialized string too long"));
677 std::memcpy(value.buffer_.data(), input.data() + offset,
679 value.current_len_ = len;
683 offset += T::max_size;
696 template <
typename T>
697 [[nodiscard]]
static constexpr auto deserialize_array(
698 T& value,
bool set, std::span<const std::byte> input,
699 std::size_t offset)
noexcept -> std::expected<std::size_t, Error> {
700 const auto array_end = calculate_array_end_offset<T>(offset);
707 offset += calculate_padding<uint32_t>(offset);
710 std::memcpy(&le_len, input.data() + offset,
sizeof(le_len));
711 offset +=
sizeof(le_len);
714 if (len > T::max_size) {
715 return std::unexpected(
718 value.current_len_ = len;
721 for (
size_t i = 0; i < len; ++i) {
722 auto res = deserialize_value(value.items_[i],
true, input, offset);
724 return std::unexpected(res.error());
726 offset = res.value();
741 template <
typename T>
742 [[nodiscard]]
static constexpr auto deserialize_map(
743 T& value,
bool set, std::span<const std::byte> input,
744 std::size_t offset)
noexcept -> std::expected<std::size_t, Error> {
745 const auto map_end = calculate_map_end_offset<T>(offset);
752 offset += calculate_padding<uint32_t>(offset);
756 std::memcpy(&le_len, input.data() + offset,
sizeof(le_len));
757 offset +=
sizeof(le_len);
760 if (len > T::max_size) {
761 return std::unexpected(
764 value.current_len_ = len;
767 for (
size_t i = 0; i < len; ++i) {
768 auto& pair = value.items_[i];
771 deserialize_value(pair.first,
true, input, offset);
773 return std::unexpected(res_key.error());
775 offset = res_key.value();
778 deserialize_value(pair.second,
true, input, offset);
780 return std::unexpected(res_val.error());
782 offset = res_val.value();
789using PackedLayout = StaticLayout<1>;
790using Aligned32Layout = StaticLayout<4>;
791using 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:112
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:131
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:86
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:60