1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
//!
//! # Cyphal data type serialization and deserialization
//!

#![cfg_attr(not(test), no_std)]
#![deny(missing_docs)]

extern crate half;
extern crate zerocopy;

pub mod bits;
mod cursor;

pub use crate::cursor::deserialize::ReadCursor;
pub use crate::cursor::serialize::WriteCursor;
use core::cmp;
use zerocopy::{AsBytes, FromBytes};

/// Trait for types that can be encoded into Cyphal transfers, or decoded from transfers
pub trait DataType {
    /// The sealed or delimited property of this type
    const EXTENT_BYTES: Option<u32>;
}

/// Trait for types that can be serialized into Cyphal transfers
pub trait Serialize: DataType {
    /// Returns the size of the encoded form of this value, in bits
    ///
    /// The returned value may not be a multiple of 8.
    fn size_bits(&self) -> usize;

    /// Serializes this value into a buffer
    ///
    /// The provided cursor will allow writing at least the number of bits returned by the
    /// [`size_bits()`](#tymethod.size_bits) function.
    fn serialize(&self, cursor: &mut WriteCursor<'_>);

    /// A convenience function that creates a cursor around the provided bytes and calls
    /// [`serialize`](#tymethod.serialize)
    fn serialize_to_bytes(&self, bytes: &mut [u8]) {
        let mut cursor = WriteCursor::new(bytes);
        self.serialize(&mut cursor);
    }
}

/// Trait for types that can be deserialized from Cyphal transfers
pub trait Deserialize: DataType {
    /// Deserializes a value and returns it
    fn deserialize(cursor: &mut ReadCursor<'_>) -> Result<Self, DeserializeError>
    where
        Self: Sized;

    /// Deserializes a value from a slice of bytes and returns it
    ///
    /// This is available only for types that implement [`Sized`], [`AsBytes`], and [`FromBytes`].
    ///
    /// # Panics
    ///
    /// This function panics if the provided cursor is not aligned to a byte boundary.
    fn deserialize_zero_copy(cursor: &mut ReadCursor<'_>) -> Self
    where
        Self: Sized + AsBytes + FromBytes,
    {
        // This isn't quite zero-copy. It's one-copy, but it eliminates handling each field
        // individually.
        let cursor_bytes = cursor.as_bytes().expect("Cursor not aligned");
        let mut value = Self::new_zeroed();

        let value_bytes = value.as_bytes_mut();
        // To apply implicit truncation and zero extension, copy whatever bytes we can
        let bytes_to_copy = cmp::min(value_bytes.len(), cursor_bytes.len());
        value_bytes[..bytes_to_copy].copy_from_slice(&cursor_bytes[..bytes_to_copy]);

        cursor.advance_bytes(bytes_to_copy);

        value
    }

    /// A convenience function that creates a cursor around the provided bytes and calls
    /// [`deserialize`](#tymethod.deserialize)
    fn deserialize_from_bytes(bytes: &[u8]) -> Result<Self, DeserializeError>
    where
        Self: Sized,
    {
        let mut cursor = ReadCursor::new(bytes);
        Self::deserialize(&mut cursor)
    }
}

/// Marker for message data types
pub trait Message {}
/// Marker for service request data types
pub trait Request {}
/// Marker for service response data types
pub trait Response {}

/// Errors that can occur when deserializing
#[non_exhaustive]
#[derive(Debug)]
pub enum DeserializeError {
    /// A variable-length array length field was greater than the maximum allowed length
    ArrayLength,
    /// A union tag field did not correspond to a known variant
    UnionTag,
    /// A delimiter header had a length that was not valid for the expected type
    DelimitedLength,
}