6#include <crunch/core/crunch_types.hpp>
7#include <crunch/fields/crunch_scalar.hpp>
8#include <crunch/fields/crunch_string.hpp>
9#include <crunch/validators/crunch_validators.hpp>
15namespace Crunch::serdes {
16template <std::
size_t Alignment>
21namespace Crunch::messages {
36 { T::check_presence(set,
id) } -> std::same_as<std::optional<Error>>;
48template <FieldId Id,
typename PresenceVal
idator,
typename Type>
52 static_assert(Id <= MaxFieldId,
"FieldId must be <= MaxFieldId (2^29 - 1)");
53 static constexpr FieldId field_id = Id;
54 using FieldType = Type;
56 constexpr Field()
noexcept =
default;
64 -> std::optional<
Error> {
65 return PresenceValidator::check_presence(set_, Id);
76 [[nodiscard]]
constexpr auto set(
const T& val)
noexcept {
77 if constexpr (
requires {
80 } -> std::same_as<std::optional<Error>>;
82 auto err = value_.set(val);
87 }
else if constexpr (
requires { value_.set(val); }) {
100 template <
typename T>
104 value_.set_without_validation(val);
117 [[nodiscard]]
constexpr auto get() const noexcept {
119 return get_message_impl();
120 }
else if constexpr (fields::is_scalar_v<Type>) {
121 return get_scalar_impl();
122 }
else if constexpr (fields::is_string_v<Type>) {
123 return get_string_impl();
142 [[nodiscard]]
constexpr auto Validate() const noexcept
143 -> std::optional<
Error> {
147 return value_.Validate();
150 [[nodiscard]]
constexpr bool operator==(
const Field& other)
const noexcept {
151 if (set_ != other.set_) {
157 return value_ == other.value_;
161 [[nodiscard]]
constexpr auto get_message_impl() const noexcept {
162 return set_ ? &value_ :
nullptr;
165 [[nodiscard]]
constexpr auto get_scalar_impl() const noexcept {
166 using V =
typename Type::ValueType;
167 auto ret = std::optional<V>{};
169 ret.emplace(value_.get());
174 [[nodiscard]]
constexpr auto get_string_impl() const noexcept {
175 auto ret = std::optional<std::string_view>{};
177 ret.emplace(value_.get());
192 template <std::
size_t Alignment>
200template <FieldId Id,
typename P,
typename T>
211 { t.Validate(
id) } -> std::same_as<std::optional<Error>>;
219 { t.Validate() } -> std::same_as<std::optional<Error>>;
227 { std::integral_constant<MessageId, T::message_id>{} };
228 { T{}.get_fields() };
234template <
FieldId Id,
typename ElementType, std::size_t MaxSize,
235 typename... Validators>
238template <
FieldId Id,
typename KeyField,
typename ValueField,
239 std::size_t MaxSize,
typename... Validators>
246template <
FieldId Id,
typename E, std::size_t M,
typename... V>
255template <
FieldId Id,
typename K,
typename V, std::size_t M,
typename... Vs>
267 Crunch::fields::is_scalar_v<std::remove_cvref_t<T>> ||
268 Crunch::fields::is_string_v<std::remove_cvref_t<T>> ||
270 is_array_field_v<std::remove_cvref_t<T>> ||
271 is_map_field_v<std::remove_cvref_t<T>>;
285template <
FieldId Id,
typename ElementType, std::size_t MaxSize,
286 typename... Validators>
289 "Invalid ElementType for ArrayField");
290 static_assert(
sizeof...(Validators) >= 1,
291 "ArrayField requires at least one validator");
292 static_assert(Id <= MaxFieldId,
"FieldId must be <= MaxFieldId (2^29 - 1)");
295 static constexpr FieldId field_id = Id;
297 static constexpr std::size_t max_size = MaxSize;
298 using ValueType = ElementType;
308 constexpr std::optional<Error>
add(
const ElementType& val)
noexcept {
309 if (current_len_ >= MaxSize) {
312 items_[current_len_++] = val;
321 template <std::
size_t N>
322 requires(N <= MaxSize)
323 constexpr std::optional<Error>
set(
324 const std::array<ElementType, N>& other)
noexcept {
326 std::copy(other.begin(), other.end(), items_.begin());
342 constexpr void clear() noexcept { current_len_ = 0; }
348 [[nodiscard]]
constexpr std::size_t
size() const noexcept {
356 [[nodiscard]]
constexpr bool empty() const noexcept {
357 return current_len_ == 0;
364 [[nodiscard]]
constexpr auto get() const noexcept {
365 using SpanT = std::span<const ElementType>;
366 return SpanT{items_.data(), current_len_};
373 constexpr const ElementType&
operator[](std::size_t index)
const noexcept {
374 return items_[index];
381 constexpr const ElementType&
at(std::size_t index)
const {
382 return items_[index];
389 [[nodiscard]]
constexpr auto Validate() const noexcept
390 -> std::optional<
Error> {
392 for (std::size_t i = 0; i < current_len_; ++i) {
393 std::optional<Error> err;
395 err = items_[i].Validate(Id);
397 err = items_[i].Validate();
399 if (err.has_value()) {
405 if constexpr (
sizeof...(Validators) > 0) {
406 for (
const auto& result : {Validators::Check(*
this, Id)...}) {
407 if (result.has_value()) {
416 [[nodiscard]]
constexpr bool operator==(
418 if (current_len_ != other.current_len_) {
421 return std::equal(items_.begin(), items_.begin() + current_len_,
422 other.items_.begin());
426 auto begin() const noexcept {
return items_.begin(); }
427 auto end() const noexcept {
return items_.begin() + current_len_; }
428 auto begin() noexcept {
return items_.begin(); }
429 auto end() noexcept {
return items_.begin() + current_len_; }
432 std::array<ElementType, MaxSize> items_{};
433 std::size_t current_len_{0};
435 template <std::
size_t Alignment>
447 requires Crunch::fields::is_scalar_v<T> || Crunch::fields::is_string_v<T>
449 using type =
typename T::ValueType;
453using field_value_type_t =
typename field_value_type<T>::type;
467template <
FieldId Id,
typename KeyField,
typename ValueField,
468 std::size_t MaxSize,
typename... Validators>
472 static_assert(Id <= MaxFieldId,
"FieldId must be <= MaxFieldId (2^29 - 1)");
475 static constexpr FieldId field_id = Id;
477 static constexpr std::size_t max_size = MaxSize;
479 using KeyType = field_value_type_t<KeyField>;
480 using ValueType = field_value_type_t<ValueField>;
481 using PairType = std::pair<KeyField, ValueField>;
484 constexpr MapField()
noexcept =
default;
493 constexpr std::optional<Error>
insert(
const KeyType& key,
494 const ValueType& value)
noexcept {
495 std::optional<Error> err;
498 if constexpr (Crunch::fields::is_scalar_v<KeyField> ||
499 Crunch::fields::is_string_v<KeyField>) {
502 err = KeyField::Validate(key, 0);
506 err = key.Validate();
513 if constexpr (Crunch::fields::is_scalar_v<ValueField> ||
514 Crunch::fields::is_string_v<ValueField>) {
515 err = ValueField::Validate(value, 0);
517 err = value.Validate();
523 if (current_len_ >= MaxSize) {
528 if (
at(key).has_value()) {
532 auto& pair = items_[current_len_];
535 if constexpr (Crunch::fields::is_scalar_v<KeyField>) {
536 pair.first.set_without_validation(key);
537 }
else if constexpr (Crunch::fields::is_string_v<KeyField>) {
538 static_cast<void>(pair.first.set(key));
544 if constexpr (Crunch::fields::is_scalar_v<ValueField>) {
545 pair.second.set_without_validation(value);
546 }
else if constexpr (Crunch::fields::is_string_v<ValueField>) {
547 static_cast<void>(pair.second.set(value));
557 constexpr std::optional<Error>
insert(
558 const std::pair<KeyType, ValueType>& p)
noexcept {
559 return insert(p.first, p.second);
568 constexpr bool remove(
const KeyType& key)
noexcept {
569 for (std::size_t i = 0; i < current_len_; ++i) {
570 const auto& stored_key_field = items_[i].first;
571 if (key_equals(stored_key_field, key)) {
574 items_[i].first.clear();
575 items_[i].second.clear();
578 for (std::size_t j = i; j < current_len_ - 1; ++j) {
579 items_[j] = items_[j + 1];
584 items_[current_len_ - 1].first.clear();
585 items_[current_len_ - 1].second.clear();
599 constexpr std::optional<ValueField*>
at(
const KeyType& key)
noexcept {
600 for (std::size_t i = 0; i < current_len_; ++i) {
603 const auto& stored_key_field = items_[i].first;
606 if (key_equals(stored_key_field, key)) {
607 return &items_[i].second;
616 [[nodiscard]]
constexpr std::size_t
size() const noexcept {
623 [[nodiscard]]
constexpr bool empty() const noexcept {
624 return current_len_ == 0;
630 constexpr void clear() noexcept { current_len_ = 0; }
635 [[nodiscard]]
constexpr auto Validate() const noexcept
636 -> std::optional<
Error> {
638 for (std::size_t i = 0; i < current_len_; ++i) {
639 if (
const auto err = items_[i].first.Validate(); err) {
642 if (
const auto err = items_[i].second.Validate(); err) {
648 for (
const auto& result : {Validators::Check(*
this, Id)...}) {
649 if (result.has_value()) {
663 const MapField& other)
const noexcept {
664 if (current_len_ != other.current_len_) {
672 for (std::size_t i = 0; i < current_len_; ++i) {
673 const auto& my_key = items_[i].first;
674 const auto& my_val = items_[i].second;
676 if (!other.has_entry(my_key, my_val)) {
683 auto begin() const noexcept {
return items_.begin(); }
684 auto end() const noexcept {
return items_.begin() + current_len_; }
685 auto begin() noexcept {
return items_.begin(); }
686 auto end() noexcept {
return items_.begin() + current_len_; }
689 std::array<PairType, MaxSize> items_{};
690 std::size_t current_len_{0};
692 constexpr bool has_entry(
const KeyField& key,
const ValueField& val)
const {
693 auto it = std::find_if(
694 begin(), end(), [&](
const PairType& p) {
return p.first == key; });
695 return it != end() && it->second == val;
698 static constexpr bool key_equals(
const KeyField& stored,
699 const KeyType& key) {
700 if constexpr (Crunch::fields::is_scalar_v<KeyField> ||
701 Crunch::fields::is_string_v<KeyField>) {
702 return stored.get() == key;
704 return stored == key;
708 template <std::
size_t Alignment>
Concept for valid element types within Field or ArrayField.
Definition: crunch_field.hpp:287
constexpr std::optional< Error > set(const ArrayField &other) noexcept
Set array contents from another ArrayField.
Definition: crunch_field.hpp:334
constexpr std::optional< Error > add(const ElementType &val) noexcept
Adds an element to the array.
Definition: crunch_field.hpp:308
constexpr auto Validate() const noexcept -> std::optional< Error >
Validates the array and its elements.
Definition: crunch_field.hpp:389
constexpr bool empty() const noexcept
Check if the array is empty.
Definition: crunch_field.hpp:356
constexpr const ElementType & at(std::size_t index) const
Access element at index.
Definition: crunch_field.hpp:381
constexpr std::optional< Error > set(const std::array< ElementType, N > &other) noexcept
Set array contents from a std::array.
Definition: crunch_field.hpp:323
constexpr auto get() const noexcept
Get read-only span of active elements.
Definition: crunch_field.hpp:364
constexpr std::size_t size() const noexcept
Get the current number of elements.
Definition: crunch_field.hpp:348
constexpr const ElementType & operator[](std::size_t index) const noexcept
Access element at index (unchecked).
Definition: crunch_field.hpp:373
constexpr void clear() noexcept
Clear the array (sets size to 0).
Definition: crunch_field.hpp:342
Wrapper for a message field within a CrunchMessage.
Definition: crunch_field.hpp:50
constexpr auto set(const T &val) noexcept
Set the value.
Definition: crunch_field.hpp:76
constexpr void clear() noexcept
Clear the field value.
Definition: crunch_field.hpp:132
constexpr auto validate_presence() const noexcept -> std::optional< Error >
Check presence requirement.
Definition: crunch_field.hpp:63
constexpr void set_without_validation(const T &val) noexcept
Set the value, bypassing validation.
Definition: crunch_field.hpp:101
constexpr auto Validate() const noexcept -> std::optional< Error >
Validate the field value.
Definition: crunch_field.hpp:142
constexpr auto get() const noexcept
Get the user-facing value.
Definition: crunch_field.hpp:117
Map field mapping keys to values.
Definition: crunch_field.hpp:469
constexpr bool remove(const KeyType &key) noexcept
Removes a key and its value from the map.
Definition: crunch_field.hpp:568
constexpr std::optional< Error > insert(const KeyType &key, const ValueType &value) noexcept
Inserts a key-value pair into the map.
Definition: crunch_field.hpp:493
constexpr auto Validate() const noexcept -> std::optional< Error >
Validate the map and its elements.
Definition: crunch_field.hpp:635
constexpr bool empty() const noexcept
Check if the map is empty.
Definition: crunch_field.hpp:623
constexpr void clear() noexcept
Clear the map.
Definition: crunch_field.hpp:630
constexpr std::size_t size() const noexcept
Get the current number of elements.
Definition: crunch_field.hpp:616
constexpr bool operator==(const MapField &other) const noexcept
Checks if two maps are equal (set equality).
Definition: crunch_field.hpp:662
constexpr std::optional< ValueField * > at(const KeyType &key) noexcept
Get reference to value by key.
Definition: crunch_field.hpp:599
Concept to detect if a type quacks like a Crunch Message.
Definition: crunch_field.hpp:226
Concept to detect if a type has a parameterless Validate() method.
Definition: crunch_field.hpp:218
Concept to detect if a type has a Validate(FieldId) method.
Definition: crunch_field.hpp:210
Concept to identify a message type.
Definition: crunch_field.hpp:27
Concept for a Presence Validator.
Definition: crunch_field.hpp:35
Concept for valid element type in Array/Map. Must be a scalar, string, message, array,...
Definition: crunch_field.hpp:266
int32_t FieldId
Unique identifier for a field within a Crunch message.
Definition: crunch_types.hpp:11
Represents an error occurred during Crunch operations.
Definition: crunch_types.hpp:75
static constexpr Error validation(FieldId id, const char(&msg)[N]) noexcept
Creates an error representing a validation failure.
Definition: crunch_types.hpp:97
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
Definition: crunch_field.hpp:442
Definition: crunch_field.hpp:244
Definition: crunch_field.hpp:198
Definition: crunch_field.hpp:253
A deterministic, fixed-size binary serialization policy.
Definition: crunch_static_layout.hpp:24
Definition: crunch_tlv_layout.hpp:52