4#include <crunch/core/crunch_endian.hpp>
5#include <crunch/core/crunch_types.hpp>
6#include <crunch/fields/crunch_scalar.hpp>
7#include <crunch/fields/crunch_string.hpp>
8#include <crunch/messages/crunch_field.hpp>
9#include <crunch/messages/crunch_messages.hpp>
10#include <crunch/serdes/crunch_varint.hpp>
22namespace Crunch::serdes {
42 using type =
typename T::FieldType;
46 requires Crunch::fields::is_scalar_v<T>
81 template <
typename Message>
82 [[nodiscard]]
static consteval std::size_t
Size() noexcept {
83 return Crunch::StandardHeaderSize +
sizeof(uint32_t) +
84 calculate_max_message_size<Message>();
94 template <
typename Message>
95 [[nodiscard]]
static constexpr std::size_t
Serialize(
96 const Message& msg, std::span<std::byte> output)
noexcept {
98 std::size_t offset = Crunch::StandardHeaderSize;
100 const std::size_t length_field_offset = offset;
101 offset +=
sizeof(uint32_t);
103 const std::size_t payload_start = offset;
104 offset = serialize_fields_helper(msg.get_fields(), output, offset);
106 const std::size_t payload_size = offset - payload_start;
107 const uint32_t le_len =
109 std::memcpy(output.data() + length_field_offset, &le_len,
122 template <
typename Message>
124 std::span<const std::byte> input, Message& msg)
noexcept
125 -> std::optional<Error> {
127 std::size_t offset = Crunch::StandardHeaderSize;
129 if (offset +
sizeof(uint32_t) > input.size()) {
134 std::memcpy(&le_len, input.data() + offset,
sizeof(uint32_t));
136 offset +=
sizeof(uint32_t);
138 if (offset + payload_len > input.size()) {
142 return deserialize_message_payload(
143 input.subspan(0, offset + payload_len), msg, offset);
155 [[nodiscard]]
static constexpr std::size_t write_tag(
157 std::size_t offset)
noexcept {
158 const uint32_t tag = (
static_cast<uint32_t
>(id) <<
WireTypeBits) |
159 static_cast<uint8_t
>(wt);
166 template <
typename Tuple, std::size_t... Is>
167 [[nodiscard]]
static consteval std::size_t sum_fields_impl(
168 std::index_sequence<Is...>)
noexcept {
169 return (calculate_max_field_size_type<
170 std::remove_cvref_t<std::tuple_element_t<Is, Tuple>>>() +
174 template <
typename Tuple>
175 [[nodiscard]]
static consteval std::size_t sum_fields_helper() noexcept {
176 return sum_fields_impl<Tuple>(
177 std::make_index_sequence<
178 std::tuple_size_v<std::remove_cvref_t<Tuple>>>{});
186 template <
typename Message>
187 [[nodiscard]]
static consteval std::size_t
188 calculate_max_message_size() noexcept {
189 using FieldsTuple =
decltype(std::declval<Message>().get_fields());
190 return sum_fields_helper<FieldsTuple>();
203 template <
typename ElemT>
204 [[nodiscard]]
static consteval std::size_t calculate_max_array_field_size(
205 std::size_t max_elements)
noexcept {
209 constexpr std::size_t elem_size = calculate_max_value_size<ElemT>();
211 return tag_size + length_size + count_size + (max_elements * elem_size);
219 template <
typename ScalarT>
220 [[nodiscard]]
static consteval std::size_t
221 calculate_max_scalar_field_size() noexcept {
231 template <
typename StringT>
232 [[nodiscard]]
static consteval std::size_t
233 calculate_max_string_field_size() noexcept {
243 template <
typename MsgT>
244 [[nodiscard]]
static consteval std::size_t
245 calculate_max_nested_message_field_size() noexcept {
255 template <
typename T>
256 [[nodiscard]]
static consteval std::size_t
257 calculate_max_value_size() noexcept {
258 if constexpr (Crunch::fields::is_scalar_v<T>) {
261 }
else if constexpr (Crunch::fields::is_string_v<T>) {
267 }
else if constexpr (Crunch::messages::is_array_field_v<T>) {
268 return calculate_max_array_field_size<typename T::ValueType>(
270 }
else if constexpr (Crunch::messages::is_map_field_v<T>) {
271 using KeyType =
typename T::PairType::first_type;
272 using ValueType =
typename T::PairType::second_type;
273 return calculate_max_map_field_size<KeyType, ValueType>(
289 template <
typename KeyT,
typename ValueT>
290 [[nodiscard]]
static consteval std::size_t calculate_max_map_field_size(
291 std::size_t max_elements)
noexcept {
295 constexpr std::size_t key_size = calculate_max_value_size<KeyT>();
296 constexpr std::size_t value_size = calculate_max_value_size<ValueT>();
298 return tag_size + length_size + count_size +
299 max_elements * (key_size + value_size);
307 template <
typename FieldT>
308 [[nodiscard]]
static consteval std::size_t
309 calculate_max_field_size_type() noexcept {
310 if constexpr (Crunch::messages::is_array_field_v<FieldT>) {
311 return calculate_max_array_field_size<typename FieldT::ValueType>(
313 }
else if constexpr (Crunch::messages::is_map_field_v<FieldT>) {
314 using KeyType =
typename FieldT::PairType::first_type;
315 using ValueType =
typename FieldT::PairType::second_type;
316 return calculate_max_map_field_size<KeyType, ValueType>(
319 using ValueType =
typename detail::ext<FieldT>::type;
320 if constexpr (Crunch::fields::is_scalar_v<ValueType>) {
321 return calculate_max_scalar_field_size<ValueType>();
322 }
else if constexpr (Crunch::fields::is_string_v<ValueType>) {
323 return calculate_max_string_field_size<ValueType>();
326 return calculate_max_nested_message_field_size<ValueType>();
340 template <
typename T>
341 [[nodiscard]]
static constexpr std::size_t serialize_scalar_value(
342 const T& value, std::span<std::byte> output,
343 std::size_t offset)
noexcept {
344 uint64_t encoded_val = 0;
345 if constexpr (std::is_same_v<T, bool>) {
346 encoded_val = value ? 1 : 0;
347 }
else if constexpr (std::is_floating_point_v<T>) {
348 if constexpr (
sizeof(T) == 4) {
350 static_cast<uint64_t
>(std::bit_cast<uint32_t>(value));
352 encoded_val = std::bit_cast<uint64_t>(value);
355 encoded_val =
static_cast<uint64_t
>(
356 std::bit_cast<std::make_unsigned_t<T>>(value));
369 template <
typename T>
370 [[nodiscard]]
static constexpr std::size_t serialize_string_value(
371 const T& value, std::span<std::byte> output,
372 std::size_t offset)
noexcept {
373 auto sv = value.get();
375 std::memcpy(output.data() + offset, sv.data(), sv.size());
376 return offset + sv.size();
393 [[nodiscard]]
static constexpr std::size_t fixup_length_prefix(
394 std::span<std::byte> output, std::size_t len_offset,
395 std::size_t content_start, std::size_t offset)
noexcept {
396 const std::size_t content_size = offset - content_start;
397 const std::size_t actual_len_varint_size =
Varint::size(content_size);
400 std::memmove(output.data() + len_offset + actual_len_varint_size,
401 output.data() + content_start, content_size);
416 template <
typename T>
417 [[nodiscard]]
static constexpr std::size_t serialize_nested_message(
418 const T& value, std::span<std::byte> output,
419 std::size_t offset)
noexcept {
420 const std::size_t len_offset = offset;
423 const std::size_t content_start = offset;
424 const std::size_t msg_size = serialize_fields_helper(
425 value.get_fields(), output.subspan(content_start), 0);
428 return fixup_length_prefix(output, len_offset, content_start, offset);
439 template <
typename FieldT>
440 [[nodiscard]]
static constexpr std::size_t serialize_array_content(
441 const FieldT& field, std::span<std::byte> output,
442 std::size_t offset)
noexcept {
443 using ElemT =
typename FieldT::ValueType;
449 for (
const auto& item : field) {
450 if constexpr (Crunch::fields::is_scalar_v<ElemT>) {
451 offset = serialize_scalar_value(item.get(), output, offset);
452 }
else if constexpr (Crunch::fields::is_string_v<ElemT>) {
453 offset = serialize_string_value(item, output, offset);
456 offset = serialize_nested_message(item, output, offset);
457 }
else if constexpr (Crunch::messages::is_array_field_v<ElemT>) {
458 offset = serialize_array_value(item, output, offset);
459 }
else if constexpr (Crunch::messages::is_map_field_v<ElemT>) {
460 offset = serialize_map_value(item, output, offset);
474 template <
typename FieldT>
475 [[nodiscard]]
static constexpr std::size_t serialize_array_value(
476 const FieldT& field, std::span<std::byte> output,
477 std::size_t offset)
noexcept {
478 const std::size_t len_offset = offset;
480 const std::size_t content_start = offset;
482 offset = serialize_array_content(field, output, offset);
484 return fixup_length_prefix(output, len_offset, content_start, offset);
502 template <
typename FieldT>
503 [[nodiscard]]
static constexpr std::size_t serialize_array_field(
504 const FieldT& field, std::span<std::byte> output,
505 std::size_t offset)
noexcept {
506 const FieldId id = field.field_id;
507 offset = write_tag(
id, WireType::LengthDelimited, output, offset);
508 return serialize_array_value(field, output, offset);
519 template <
typename FieldT>
520 [[nodiscard]]
static constexpr std::size_t serialize_value_without_tag(
521 const FieldT& field, std::span<std::byte> output,
522 std::size_t offset)
noexcept {
523 if constexpr (Crunch::fields::is_scalar_v<FieldT>) {
524 return serialize_scalar_value(field.get(), output, offset);
525 }
else if constexpr (Crunch::fields::is_string_v<FieldT>) {
526 return serialize_string_value(field, output, offset);
529 return serialize_nested_message(field, output, offset);
530 }
else if constexpr (Crunch::messages::is_array_field_v<FieldT>) {
531 return serialize_array_value(field, output, offset);
532 }
else if constexpr (Crunch::messages::is_map_field_v<FieldT>) {
533 return serialize_map_value(field, output, offset);
546 template <
typename FieldT>
547 [[nodiscard]]
static constexpr std::size_t serialize_map_content(
548 const FieldT& field, std::span<std::byte> output,
549 std::size_t offset)
noexcept {
550 using KeyFieldT =
typename FieldT::PairType::first_type;
551 using ValueFieldT =
typename FieldT::PairType::second_type;
557 return std::accumulate(
558 field.begin(), field.end(), offset,
559 [&](std::size_t off,
const auto& item) {
560 off = serialize_value_without_tag<KeyFieldT>(item.first, output,
562 return serialize_value_without_tag<ValueFieldT>(item.second,
575 template <
typename FieldT>
576 [[nodiscard]]
static constexpr std::size_t serialize_map_value(
577 const FieldT& field, std::span<std::byte> output,
578 std::size_t offset)
noexcept {
579 const std::size_t len_offset = offset;
581 const std::size_t content_start = offset;
583 offset = serialize_map_content(field, output, offset);
585 return fixup_length_prefix(output, len_offset, content_start, offset);
603 template <
typename FieldT>
604 [[nodiscard]]
static constexpr std::size_t serialize_map_field(
605 const FieldT& field, std::span<std::byte> output,
606 std::size_t offset)
noexcept {
607 const FieldId id = field.field_id;
608 offset = write_tag(
id, WireType::LengthDelimited, output, offset);
609 return serialize_map_value(field, output, offset);
620 template <
typename FieldT>
621 [[nodiscard]]
static constexpr std::size_t serialize_field(
622 const FieldT& field, std::span<std::byte> output,
623 std::size_t offset)
noexcept {
625 if constexpr (Crunch::messages::is_array_field_v<FieldT> ||
626 Crunch::messages::is_map_field_v<FieldT>) {
627 is_set = !field.empty();
636 const FieldId id = field.field_id;
638 if constexpr (Crunch::messages::is_array_field_v<FieldT>) {
639 return serialize_array_field(field, output, offset);
640 }
else if constexpr (Crunch::messages::is_map_field_v<FieldT>) {
641 return serialize_map_field(field, output, offset);
643 using ValueType =
typename detail::ext<FieldT>::type;
644 if constexpr (Crunch::fields::is_scalar_v<ValueType>) {
645 offset = write_tag(
id, WireType::Varint, output, offset);
646 return serialize_scalar_value(field.value_.get(), output,
648 }
else if constexpr (Crunch::fields::is_string_v<ValueType>) {
650 write_tag(
id, WireType::LengthDelimited, output, offset);
651 return serialize_string_value(field.value_, output, offset);
655 write_tag(
id, WireType::LengthDelimited, output, offset);
656 return serialize_nested_message(field.value_, output, offset);
671 template <
typename Tuple, std::
size_t I = 0>
672 [[nodiscard]]
static constexpr std::size_t serialize_fields_helper(
673 const Tuple& t, std::span<std::byte> output,
674 std::size_t offset)
noexcept {
675 if constexpr (I < std::tuple_size_v<std::remove_cvref_t<Tuple>>) {
676 offset = serialize_field(std::get<I>(t), output, offset);
677 return serialize_fields_helper<Tuple, I + 1>(t, output, offset);
690 template <
typename ElemT>
691 [[nodiscard]]
static constexpr std::optional<Error>
692 deserialize_scalar_array(
typename ElemT::ValueType& val_out,
693 std::span<const std::byte> input,
694 std::size_t& offset)
noexcept {
695 using T =
typename ElemT::ValueType;
700 offset += res->second;
702 if constexpr (std::is_same_v<T, bool>) {
703 val_out = (res->first != 0);
704 }
else if constexpr (std::is_floating_point_v<T>) {
705 if constexpr (
sizeof(T) == 4) {
706 val_out = std::bit_cast<T>(
static_cast<uint32_t
>(res->first));
708 val_out = std::bit_cast<T>(res->first);
711 val_out = std::bit_cast<T>(
712 static_cast<std::make_unsigned_t<T>
>(res->first));
724 [[nodiscard]]
static constexpr std::expected<std::size_t, Error>
725 read_length_prefix(std::span<const std::byte> input, std::size_t& offset,
726 const char* error_msg)
noexcept {
731 offset += len_res->second;
732 std::size_t len =
static_cast<std::size_t
>(len_res->first);
733 if (offset + len > input.size()) {
747 template <
typename ElemT>
748 [[nodiscard]]
static constexpr std::optional<Error>
749 deserialize_scalar_value(ElemT& val, std::span<const std::byte> input,
750 std::size_t& offset)
noexcept {
751 typename ElemT::ValueType scalar_val;
753 deserialize_scalar_array<ElemT>(scalar_val, input, offset)) {
756 val.set_without_validation(scalar_val);
768 template <
typename ElemT>
769 [[nodiscard]]
static constexpr std::optional<Error>
770 deserialize_string_value(ElemT& val, std::span<const std::byte> input,
771 std::size_t& offset)
noexcept {
773 read_length_prefix(input, offset,
"invalid string length");
775 return len_result.error();
777 std::size_t len = *len_result;
779 reinterpret_cast<const char*
>(input.data() + offset), len);
792 template <
typename ElemT>
793 [[nodiscard]]
static constexpr std::optional<Error>
794 deserialize_message_value(ElemT& val, std::span<const std::byte> input,
795 std::size_t& offset)
noexcept {
797 read_length_prefix(input, offset,
"invalid message length");
799 return len_result.error();
801 std::size_t len = *len_result;
802 if (
const auto err = deserialize_message_payload(
803 input.subspan(offset, len), val, 0)) {
818 template <
typename ElemT>
819 [[nodiscard]]
static constexpr std::optional<Error> deserialize_array_value(
820 ElemT& val, std::span<const std::byte> input,
821 std::size_t& offset)
noexcept {
823 read_length_prefix(input, offset,
"invalid array length");
825 return len_result.error();
827 std::size_t len = *len_result;
828 auto subspan = input.subspan(offset, len);
829 std::size_t sub_offset = 0;
831 deserialize_array_elements(val, subspan, sub_offset, len)) {
846 template <
typename ElemT>
847 [[nodiscard]]
static constexpr std::optional<Error> deserialize_map_value(
848 ElemT& val, std::span<const std::byte> input,
849 std::size_t& offset)
noexcept {
851 read_length_prefix(input, offset,
"invalid map length");
853 return len_result.error();
855 std::size_t len = *len_result;
856 auto subspan = input.subspan(offset, len);
857 std::size_t sub_offset = 0;
859 deserialize_map_elements(val, subspan, sub_offset, len)) {
874 template <
typename ElemT>
875 [[nodiscard]]
static constexpr std::optional<Error>
876 deserialize_value_without_tag(ElemT& val, std::span<const std::byte> input,
877 std::size_t& offset)
noexcept {
878 if constexpr (Crunch::fields::is_scalar_v<ElemT>) {
879 return deserialize_scalar_value<ElemT>(val, input, offset);
880 }
else if constexpr (Crunch::fields::is_string_v<ElemT>) {
881 return deserialize_string_value<ElemT>(val, input, offset);
884 return deserialize_message_value<ElemT>(val, input, offset);
885 }
else if constexpr (Crunch::messages::is_array_field_v<ElemT>) {
886 return deserialize_array_value<ElemT>(val, input, offset);
887 }
else if constexpr (Crunch::messages::is_map_field_v<ElemT>) {
888 return deserialize_map_value<ElemT>(val, input, offset);
904 template <
typename FieldT>
905 [[nodiscard]]
static constexpr std::optional<Error>
906 deserialize_array_elements(FieldT& field, std::span<const std::byte> input,
908 std::size_t end_offset)
noexcept {
909 using ElemT =
typename FieldT::ValueType;
916 offset += count_res->second;
917 const std::size_t count =
static_cast<std::size_t
>(count_res->first);
920 for (std::size_t i = 0; i < count; ++i) {
923 deserialize_value_without_tag<ElemT>(elem, input, offset)) {
926 if constexpr (Crunch::fields::is_scalar_v<ElemT>) {
927 if (
const auto err = field.add(elem.get())) {
931 if (
const auto err = field.add(elem)) {
947 template <
typename Message>
948 [[nodiscard]]
static constexpr std::optional<Error>
949 deserialize_message_payload(std::span<const std::byte> input, Message& msg,
950 std::size_t offset)
noexcept {
951 while (offset < input.size()) {
956 const auto [tag, tag_bytes] = *tag_res;
960 const uint32_t field_id =
965 std::optional<Error> err = visit_fields_tlv(
966 msg.get_fields(),
static_cast<FieldId>(field_id), wire_type,
967 input, offset, found);
992 template <
typename FieldT>
993 [[nodiscard]]
static constexpr std::optional<Error> deserialize_array_field(
994 FieldT& field,
WireType wire_type, std::span<const std::byte> input,
995 std::size_t& offset)
noexcept {
996 if (wire_type != WireType::LengthDelimited) {
1005 offset += len_res->second;
1006 const std::size_t len =
static_cast<std::size_t
>(len_res->first);
1007 if (offset + len > input.size()) {
1011 auto subspan = input.subspan(offset, len);
1012 std::size_t sub_offset = 0;
1013 if (
const auto err =
1014 deserialize_array_elements(field, subspan, sub_offset, len)) {
1019 return std::nullopt;
1031 template <
typename FieldT>
1032 [[nodiscard]]
static constexpr std::optional<Error>
1033 deserialize_scalar_field(FieldT& field,
WireType wire_type,
1034 std::span<const std::byte> input,
1035 std::size_t& offset)
noexcept {
1036 using ValueType =
typename detail::ext<FieldT>::type;
1037 using T =
typename ValueType::ValueType;
1039 if (wire_type != WireType::Varint) {
1047 offset += res->second;
1050 if constexpr (std::is_same_v<T, bool>) {
1051 val = (res->first != 0);
1052 }
else if constexpr (std::is_floating_point_v<T>) {
1053 if constexpr (
sizeof(T) == 4) {
1054 val = std::bit_cast<T>(
static_cast<uint32_t
>(res->first));
1056 val = std::bit_cast<T>(res->first);
1059 val = std::bit_cast<T>(
1060 static_cast<std::make_unsigned_t<T>
>(res->first));
1064 field.set_without_validation(val);
1065 return std::nullopt;
1077 template <
typename FieldT>
1078 [[nodiscard]]
static constexpr std::optional<Error>
1079 deserialize_string_field(FieldT& field,
WireType wire_type,
1080 std::span<const std::byte> input,
1081 std::size_t& offset)
noexcept {
1082 if (wire_type != WireType::LengthDelimited) {
1090 offset += len_res->second;
1091 const std::size_t len =
static_cast<std::size_t
>(len_res->first);
1092 if (offset + len > input.size()) {
1096 const std::string_view sv(
1097 reinterpret_cast<const char*
>(input.data() + offset), len);
1099 if (
const auto err = field.set(sv)) {
1102 return std::nullopt;
1114 template <
typename FieldT>
1115 [[nodiscard]]
static constexpr std::optional<Error>
1116 deserialize_nested_message_field(FieldT& field,
WireType wire_type,
1117 std::span<const std::byte> input,
1118 std::size_t& offset)
noexcept {
1119 if (wire_type != WireType::LengthDelimited) {
1121 "nested msg requires length delimited");
1128 offset += len_res->second;
1129 const std::size_t len =
static_cast<std::size_t
>(len_res->first);
1130 if (offset + len > input.size()) {
1135 if (
const auto err = deserialize_message_payload(
1136 input.subspan(offset, len), field, 0)) {
1140 typename FieldT::FieldType temp;
1141 if (
const auto err = deserialize_message_payload(
1142 input.subspan(offset, len), temp, 0)) {
1149 return std::nullopt;
1158 template <
typename T>
1159 [[nodiscard]]
static constexpr auto extract_map_value(
const T& f)
noexcept
1161 if constexpr (Crunch::messages::is_array_field_v<T> ||
1162 Crunch::messages::is_map_field_v<T> ||
1179 template <
typename FieldT>
1180 [[nodiscard]]
static constexpr std::optional<Error>
1181 deserialize_map_elements(FieldT& field, std::span<const std::byte> input,
1182 std::size_t& offset,
1183 std::size_t end_offset)
noexcept {
1184 using KeyFieldT =
typename FieldT::PairType::first_type;
1185 using ValueFieldT =
typename FieldT::PairType::second_type;
1192 offset += count_res->second;
1193 const std::size_t count =
static_cast<std::size_t
>(count_res->first);
1196 for (std::size_t i = 0; i < count; ++i) {
1197 KeyFieldT key_field{};
1198 ValueFieldT val_field{};
1200 if (
const auto err = deserialize_value_without_tag<KeyFieldT>(
1201 key_field, input, offset)) {
1204 if (
const auto err = deserialize_value_without_tag<ValueFieldT>(
1205 val_field, input, offset)) {
1209 if (
const auto err = field.insert(extract_map_value(key_field),
1210 extract_map_value(val_field))) {
1214 return std::nullopt;
1229 template <
typename FieldT>
1230 [[nodiscard]]
static constexpr std::optional<Error> deserialize_map_field(
1231 FieldT& field,
WireType wire_type, std::span<const std::byte> input,
1232 std::size_t& offset)
noexcept {
1233 if (wire_type != WireType::LengthDelimited) {
1242 offset += len_res->second;
1243 const std::size_t len =
static_cast<std::size_t
>(len_res->first);
1244 if (offset + len > input.size()) {
1248 auto subspan = input.subspan(offset, len);
1249 std::size_t sub_offset = 0;
1250 if (
const auto err =
1251 deserialize_map_elements(field, subspan, sub_offset, len)) {
1256 return std::nullopt;
1268 template <
typename FieldT>
1269 [[nodiscard]]
static constexpr std::optional<Error> deserialize_field_value(
1270 FieldT& field,
WireType wire_type, std::span<const std::byte> input,
1271 std::size_t& offset)
noexcept {
1272 if constexpr (Crunch::messages::is_array_field_v<FieldT>) {
1273 return deserialize_array_field(field, wire_type, input, offset);
1274 }
else if constexpr (Crunch::messages::is_map_field_v<FieldT>) {
1275 return deserialize_map_field(field, wire_type, input, offset);
1277 using ValueType =
typename detail::ext<FieldT>::type;
1278 if constexpr (Crunch::fields::is_scalar_v<ValueType>) {
1279 return deserialize_scalar_field(field, wire_type, input,
1281 }
else if constexpr (Crunch::fields::is_string_v<ValueType>) {
1282 return deserialize_string_field(field, wire_type, input,
1286 return deserialize_nested_message_field(field, wire_type, input,
1290 return std::nullopt;
1306 template <
typename Tuple, std::
size_t I = 0>
1307 [[nodiscard]]
static constexpr std::optional<Error> visit_fields_tlv(
1309 std::span<const std::byte> input, std::size_t& offset,
1310 bool& found)
noexcept {
1311 using TupleT = std::remove_cvref_t<Tuple>;
1312 if constexpr (I < std::tuple_size_v<TupleT>) {
1313 auto& f = std::get<I>(fields_tuple);
1314 if (f.field_id == target_id) {
1316 return deserialize_field_value(f, wt, input, offset);
1318 return visit_fields_tlv<Tuple, I + 1>(
1319 std::forward<Tuple>(fields_tuple), target_id, wt, input, offset,
1322 return std::nullopt;
Concept to detect if a type quacks like a Crunch Message.
Definition: crunch_field.hpp:226
int32_t FieldId
Unique identifier for a field within a Crunch message.
Definition: crunch_types.hpp:11
constexpr T LittleEndian(T value) noexcept
Converts a value to/from Little Endian byte order.
Definition: crunch_endian.hpp:21
Format
Serialization format identifier stored in the message header.
Definition: crunch_types.hpp:30
@ TLV
Tag-Length-Value encoding.
static constexpr Error deserialization(std::string_view msg="deserialization error") noexcept
Creates an error representing a deserialization failure.
Definition: crunch_types.hpp:104
Definition: crunch_tlv_layout.hpp:53
static constexpr std::size_t Serialize(const Message &msg, std::span< std::byte > output) noexcept
Serializes a message into the output buffer.
Definition: crunch_tlv_layout.hpp:95
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_tlv_layout.hpp:123
WireType
Wire types for TLV encoding.
Definition: crunch_tlv_layout.hpp:57
static consteval std::size_t Size() noexcept
Calculates the maximum possible serialized size of a message.
Definition: crunch_tlv_layout.hpp:82
static constexpr std::size_t WireTypeBits
Number of bits used for the wire type in a tag.
Definition: crunch_tlv_layout.hpp:67
static constexpr std::size_t MaxTagBits
The maximum number of bits required for a Tag (FieldID + WireType). Used for calculating the maximum ...
Definition: crunch_tlv_layout.hpp:73
Utility for Varint encoding/decoding.
Definition: crunch_varint.hpp:16
static constexpr std::optional< std::pair< uint64_t, std::size_t > > decode(std::span< const std::byte > input, std::size_t offset) noexcept
Decodes a Varint from a buffer.
Definition: crunch_varint.hpp:45
static constexpr std::size_t size(uint64_t value) noexcept
Calculates the size requirement for a value encoded as Varint.
Definition: crunch_varint.hpp:77
static consteval std::size_t max_varint_size(std::size_t value_bits)
Calculates the maximum varint size for a given number of bits.
Definition: crunch_varint.hpp:101
static constexpr std::size_t max_size
The maximum size required for a 64-bit integer encoded as Varint. ceil(64 / 7) = 10.
Definition: crunch_varint.hpp:94
static constexpr std::size_t encode(uint64_t value, std::span< std::byte > output, std::size_t offset) noexcept
Encodes a value as a Varint.
Definition: crunch_varint.hpp:25
Helper to extract the underlying scalar type from a field wrapper.
Definition: crunch_tlv_layout.hpp:41