Skip to main content

const_serialize/
lib.rs

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