3#include <crunch/core/crunch_endian.hpp>
4#include <crunch/core/crunch_header.hpp>
5#include <crunch/integrity/crunch_integrity.hpp>
6#include <crunch/messages/crunch_messages.hpp>
7#include <crunch/serdes/crunch_serdes.hpp>
8#include <crunch/serdes/crunch_static_layout.hpp>
30template <
typename Message,
typename Integrity,
typename Serdes, std::
size_t N>
32 using MessageType = Message;
33 using IntegrityType = Integrity;
34 using SerdesType = Serdes;
37 static constexpr std::size_t Size = N;
39 std::array<std::byte, N> data;
40 std::size_t used_bytes{0};
42 [[nodiscard]]
constexpr auto span()
noexcept {
43 return std::span<std::byte, N>{data};
45 [[nodiscard]]
constexpr auto span()
const noexcept {
46 return std::span<const std::byte, N>{data};
49 [[nodiscard]]
constexpr auto serialized_message_span()
const noexcept {
50 return std::span<const std::byte>{data.data(), used_bytes};
58template <
typename Message,
typename Integrity,
typename Serdes, std::
size_t N>
74template <messages::CrunchMessage Message,
typename Integrity,
typename Serdes>
77 return Serdes::template Size<Message>() + Integrity::size();
91template <
typename Message>
93[[nodiscard]]
constexpr auto Validate(
const Message& message)
noexcept
94 -> std::optional<Error>;
106 if constexpr (
requires { field.validate_presence(); }) {
107 if (
auto err = field.validate_presence(); err) {
113 if (
const auto* ptr = field.get()) {
115 if (
auto err =
Validate(*ptr); err) {
121 if (
auto err = field.Validate(); err) {
136template <
typename Message>
138[[nodiscard]]
constexpr auto Validate(
const Message& message)
noexcept
139 -> std::optional<Error> {
140 std::optional<Error> err;
142 [&](
const auto&... fields) {
144 if (err.has_value()) {
148 return !err.has_value();
152 message.get_fields());
154 if (err.has_value()) {
158 return message.Validate();
176 std::array<std::byte, N>& buffer,
const Message& message)
noexcept {
177 constexpr std::size_t ChecksumSize = Integrity::size();
178 constexpr std::size_t PayloadSize = N - ChecksumSize;
180 std::span<std::byte, PayloadSize> payload_span(buffer.data(), PayloadSize);
183 WriteHeader<Message, Serdes>(payload_span);
186 const std::size_t bytes_written = Serdes::Serialize(message, payload_span);
189 if constexpr (ChecksumSize > 0) {
192 std::span<std::byte> used_payload_span =
193 payload_span.subspan(0, bytes_written);
194 auto checksum = Integrity::calculate(used_payload_span);
196 std::span<std::byte, ChecksumSize> checksum_span(
197 buffer.data() + bytes_written, ChecksumSize);
198 std::copy(checksum.begin(), checksum.end(), checksum_span.begin());
200 return bytes_written + ChecksumSize;
222[[nodiscard]]
auto Serialize(std::array<std::byte, N>& buffer,
223 const Message& message)
noexcept
224 -> std::expected<std::size_t, Error> {
226 if (
auto err =
Validate(message); err.has_value()) {
227 return std::unexpected(*err);
229 return SerializeWithoutValidation<Integrity, Serdes>(buffer, message);
247template <
typename Integrity,
typename Serdes,
typename Message>
251 Message& message)
noexcept
252 -> std::optional<Error> {
253 constexpr std::size_t ChecksumSize = Integrity::size();
255 if (buffer.size() < ChecksumSize) {
258 const std::size_t PayloadSize = buffer.size() - ChecksumSize;
260 std::span<const std::byte> payload_span = buffer.subspan(0, PayloadSize);
263 if constexpr (ChecksumSize > 0) {
264 const auto expected_checksum = Integrity::calculate(payload_span);
265 std::span<const std::byte, ChecksumSize> actual_checksum_span(
266 buffer.data() + PayloadSize, ChecksumSize);
270 std::equal(expected_checksum.begin(), expected_checksum.end(),
271 actual_checksum_span.begin());
278 auto header_result = ValidateHeader<Message, Serdes>(payload_span);
279 if (!header_result) {
280 return header_result.error();
284 if (
const auto err = Serdes::Deserialize(payload_span, message);
290 if (
auto err =
Validate(message); err.has_value()) {
300template <
MessageId Id,
typename... Messages>
302 return ((Messages::message_id == Id ? 1 : 0) + ...);
308template <
typename... Messages>
310 return ((CountMessageId<Messages::message_id, Messages...>() > 1) || ...);
313template <
typename... Messages>
325template <
typename Serdes,
typename Integrity,
330 using VariantType = std::variant<Messages...>;
332 [[nodiscard]]
constexpr std::optional<Error> Decode(
333 std::span<const std::byte> buffer, VariantType& out_message) {
337 return header.error();
343 if (Messages::message_id == header->message_id) {
345 auto err = Deserialize<Integrity, Serdes>(buffer, msg);
349 out_message = std::move(msg);
350 result = std::nullopt;
364 Buffer<Messages, Integrity, Serdes,
365 GetBufferSize<Messages, Integrity, Serdes>()>...>
Decoder class for deserializing one of N possible message types from a buffer.
Definition: crunch_detail.hpp:328
Concept defining the interface for message integrity policies.
Definition: crunch_integrity.hpp:24
Concept defining a Serialization/Deserialization policy.
Definition: crunch_serdes.hpp:36
Definition: crunch_detail.hpp:63
Definition: crunch_detail.hpp:314
Concept ensuring a type is a fully valid CrunchMessage.
Definition: crunch_messages.hpp:119
Concept to detect if a type quacks like a Crunch Message.
Definition: crunch_field.hpp:226
Internal implementation details for Crunch's public API.
Definition: crunch_detail.hpp:16
auto Serialize(std::array< std::byte, N > &buffer, const Message &message) noexcept -> std::expected< std::size_t, Error >
implementation of Serialize.
Definition: crunch_detail.hpp:222
consteval bool HasDuplicateMessageIds()
Returns true if any message ID appears more than once.
Definition: crunch_detail.hpp:309
consteval std::size_t CountMessageId()
Counts how many messages have the given message ID.
Definition: crunch_detail.hpp:301
constexpr auto Validate(const Message &message) noexcept -> std::optional< Error >
Forward declaration of Validate to enable recursion in ValidateField.
Definition: crunch_detail.hpp:138
auto Deserialize(std::span< const std::byte > buffer, Message &message) noexcept -> std::optional< Error >
implementation of Deserialize.
Definition: crunch_detail.hpp:250
std::size_t SerializeWithoutValidation(std::array< std::byte, N > &buffer, const Message &message) noexcept
Serializes the message without any validation checks.
Definition: crunch_detail.hpp:175
constexpr std::optional< Error > ValidateField(const T &field)
Helper to validate a single field.
Definition: crunch_detail.hpp:105
consteval auto GetBufferSize() noexcept -> std::size_t
Compile-time calculation of the size of a buffer for a given message, integrity, and serdes combinati...
Definition: crunch_detail.hpp:76
int32_t MessageId
Unique identifier for a message type.
Definition: crunch_types.hpp:25
constexpr std::expected< CrunchHeader, Error > GetHeader(std::span< const std::byte > input) noexcept
Parses and returns a copy of the header from the input buffer.
Definition: crunch_header.hpp:31
static constexpr Error deserialization(std::string_view msg="deserialization error") noexcept
Creates an error representing a deserialization failure.
Definition: crunch_types.hpp:104
static constexpr Error invalid_message_id() noexcept
Creates an error representing an invalid message ID.
Definition: crunch_types.hpp:112
static constexpr Error integrity() noexcept
Creates an error representing an integrity check failure.
Definition: crunch_types.hpp:84
A lightweight wrapper around a std::array for serializing/deserializing messages.
Definition: crunch_detail.hpp:31
Definition: crunch_detail.hpp:56