const_serialize/
lib.rs

1#![doc = include_str!("../README.md")]
2#![warn(missing_docs)]
3
4use std::mem::MaybeUninit;
5
6mod const_buffers;
7pub use const_buffers::ConstReadBuffer;
8mod cbor;
9mod const_vec;
10mod r#enum;
11pub use r#enum::*;
12mod r#struct;
13pub use r#struct::*;
14mod primitive;
15pub use primitive::*;
16mod list;
17pub use list::*;
18mod array;
19pub use array::*;
20mod str;
21pub use str::*;
22
23pub use const_serialize_macro::SerializeConst;
24pub use const_vec::ConstVec;
25
26use crate::cbor::{
27    str_eq, take_array, take_bytes, take_map, take_number, take_str, write_array, write_bytes,
28    write_map, write_map_key, write_number,
29};
30
31/// The layout for a type. This layout defines a sequence of locations and reversed or not bytes. These bytes will be copied from during serialization and copied into during deserialization.
32#[derive(Debug, Copy, Clone)]
33pub enum Layout {
34    /// An enum layout
35    Enum(EnumLayout),
36    /// A struct layout
37    Struct(StructLayout),
38    /// An array layout
39    Array(ArrayLayout),
40    /// A primitive layout
41    Primitive(PrimitiveLayout),
42    /// A dynamically sized list layout
43    List(ListLayout),
44}
45
46impl Layout {
47    /// The size of the type in bytes.
48    pub const fn size(&self) -> usize {
49        match self {
50            Layout::Enum(layout) => layout.size,
51            Layout::Struct(layout) => layout.size,
52            Layout::Array(layout) => layout.len * layout.item_layout.size(),
53            Layout::List(layout) => layout.size,
54            Layout::Primitive(layout) => layout.size,
55        }
56    }
57}
58
59/// A trait for types that can be serialized and deserialized in const.
60///
61/// # Safety
62/// The layout must accurately describe the memory layout of the type
63pub unsafe trait SerializeConst: Sized {
64    /// The memory layout of the type. This type must have plain old data; no pointers or references.
65    const MEMORY_LAYOUT: Layout;
66    /// Assert that the memory layout of the type is the same as the size of the type
67    const _ASSERT: () = assert!(Self::MEMORY_LAYOUT.size() == std::mem::size_of::<Self>());
68}
69
70/// Serialize a pointer to a type that is stored at the pointer passed in
71const unsafe fn serialize_const_ptr(
72    ptr: *const (),
73    to: ConstVec<u8>,
74    layout: &Layout,
75) -> ConstVec<u8> {
76    match layout {
77        Layout::Enum(layout) => serialize_const_enum(ptr, to, layout),
78        Layout::Struct(layout) => serialize_const_struct(ptr, to, layout),
79        Layout::Array(layout) => serialize_const_array(ptr, to, layout),
80        Layout::List(layout) => serialize_const_list(ptr, to, layout),
81        Layout::Primitive(layout) => serialize_const_primitive(ptr, to, layout),
82    }
83}
84
85/// Serialize a type into a buffer
86///
87/// # Example
88///
89/// ```rust
90/// use const_serialize::{ConstVec, SerializeConst, serialize_const};
91///
92/// #[derive(Clone, Copy, Debug, PartialEq, SerializeConst)]
93/// struct Struct {
94///     a: u32,
95///     b: u8,
96///     c: u32,
97/// }
98///
99/// let mut buffer = ConstVec::new();
100/// buffer = serialize_const(&Struct {
101///     a: 0x11111111,
102///     b: 0x22,
103///     c: 0x33333333,
104/// }, buffer);
105/// assert_eq!(buffer.as_ref(), &[0xa3, 0x61, 0x61, 0x1a, 0x11, 0x11, 0x11, 0x11, 0x61, 0x62, 0x18, 0x22, 0x61, 0x63, 0x1a, 0x33, 0x33, 0x33, 0x33]);
106/// ```
107#[must_use = "The data is serialized into the returned buffer"]
108pub const fn serialize_const<T: SerializeConst>(data: &T, to: ConstVec<u8>) -> ConstVec<u8> {
109    let ptr = data as *const T as *const ();
110    // SAFETY: The pointer is valid and the layout is correct
111    unsafe { serialize_const_ptr(ptr, to, &T::MEMORY_LAYOUT) }
112}
113
114/// Deserialize a type into the out buffer at the offset passed in. Returns a new version of the buffer with the data added.
115const fn deserialize_const_ptr<'a>(
116    from: &'a [u8],
117    layout: &Layout,
118    out: &mut [MaybeUninit<u8>],
119) -> Option<&'a [u8]> {
120    match layout {
121        Layout::Enum(layout) => deserialize_const_enum(from, layout, out),
122        Layout::Struct(layout) => deserialize_const_struct(from, layout, out),
123        Layout::Array(layout) => deserialize_const_array(from, layout, out),
124        Layout::List(layout) => deserialize_const_list(from, layout, out),
125        Layout::Primitive(layout) => deserialize_const_primitive(from, layout, out),
126    }
127}
128
129/// Deserialize a type into the output buffer. Accepts `(type, ConstVec<u8>)` as input and returns `Option<(&'a [u8], Instance of type)>`
130///
131/// # Example
132/// ```rust
133/// # use const_serialize::{deserialize_const, serialize_const, ConstVec, SerializeConst};
134/// #[derive(Clone, Copy, Debug, PartialEq, SerializeConst)]
135/// struct Struct {
136///     a: u32,
137///     b: u8,
138///     c: u32,
139///     d: u32,
140/// }
141///
142/// let mut buffer = ConstVec::new();
143/// buffer = serialize_const(&Struct {
144///     a: 0x11111111,
145///     b: 0x22,
146///     c: 0x33333333,
147///     d: 0x44444444,
148/// }, buffer);
149/// let buf = buffer.as_ref();
150/// assert_eq!(deserialize_const!(Struct, buf).unwrap().1, Struct {
151///     a: 0x11111111,
152///     b: 0x22,
153///     c: 0x33333333,
154///     d: 0x44444444,
155/// });
156/// ```
157#[macro_export]
158macro_rules! deserialize_const {
159    ($type:ty, $buffer:expr) => {
160        unsafe {
161            const __SIZE: usize = std::mem::size_of::<$type>();
162            $crate::deserialize_const_raw::<__SIZE, $type>($buffer)
163        }
164    };
165}
166
167/// Deserialize a buffer into a type. This will return None if the buffer doesn't have enough data to fill the type.
168/// # Safety
169/// N must be `std::mem::size_of::<T>()`
170#[must_use = "The data is deserialized from the input buffer"]
171pub const unsafe fn deserialize_const_raw<const N: usize, T: SerializeConst>(
172    from: &[u8],
173) -> Option<(&[u8], T)> {
174    // Create uninitized memory with the size of the type
175    let mut out = [MaybeUninit::uninit(); N];
176    // Fill in the bytes into the buffer for the type
177    let Some(from) = deserialize_const_ptr(from, &T::MEMORY_LAYOUT, &mut out) else {
178        return None;
179    };
180    // Now that the memory is filled in, transmute it into the type
181    Some((from, unsafe {
182        std::mem::transmute_copy::<[MaybeUninit<u8>; N], T>(&out)
183    }))
184}
185
186/// Check if the serialized representation of two items are the same
187pub const fn serialize_eq<T: SerializeConst>(first: &T, second: &T) -> bool {
188    let first_serialized = ConstVec::<u8>::new();
189    let first_serialized = serialize_const(first, first_serialized);
190    let second_serialized = ConstVec::<u8>::new();
191    let second_serialized = serialize_const(second, second_serialized);
192    let first_buf = first_serialized.as_ref();
193    let second_buf = second_serialized.as_ref();
194    if first_buf.len() != second_buf.len() {
195        return false;
196    }
197    let mut i = 0;
198    while i < first_buf.len() {
199        if first_buf[i] != second_buf[i] {
200            return false;
201        }
202        i += 1;
203    }
204    true
205}