const_serialize/
lib.rs

1#![doc = include_str!("../README.md")]
2#![warn(missing_docs)]
3
4use std::{char, mem::MaybeUninit};
5
6mod const_buffers;
7mod const_vec;
8
9pub use const_buffers::ConstReadBuffer;
10pub use const_serialize_macro::SerializeConst;
11pub use const_vec::ConstVec;
12
13/// Plain old data for a field. Stores the offset of the field in the struct and the layout of the field.
14#[derive(Debug, Copy, Clone)]
15pub struct StructFieldLayout {
16    offset: usize,
17    layout: Layout,
18}
19
20impl StructFieldLayout {
21    /// Create a new struct field layout
22    pub const fn new(offset: usize, layout: Layout) -> Self {
23        Self { offset, layout }
24    }
25}
26
27/// Layout for a struct. The struct layout is just a list of fields with offsets
28#[derive(Debug, Copy, Clone)]
29pub struct StructLayout {
30    size: usize,
31    data: &'static [StructFieldLayout],
32}
33
34impl StructLayout {
35    /// Create a new struct layout
36    pub const fn new(size: usize, data: &'static [StructFieldLayout]) -> Self {
37        Self { size, data }
38    }
39}
40
41/// The layout for an enum. The enum layout is just a discriminate size and a tag layout.
42#[derive(Debug, Copy, Clone)]
43pub struct EnumLayout {
44    size: usize,
45    discriminant: PrimitiveLayout,
46    variants_offset: usize,
47    variants: &'static [EnumVariant],
48}
49
50impl EnumLayout {
51    /// Create a new enum layout
52    pub const fn new(
53        size: usize,
54        discriminant: PrimitiveLayout,
55        variants: &'static [EnumVariant],
56    ) -> Self {
57        let mut max_align = 1;
58        let mut i = 0;
59        while i < variants.len() {
60            let EnumVariant { align, .. } = &variants[i];
61            if *align > max_align {
62                max_align = *align;
63            }
64            i += 1;
65        }
66
67        let variants_offset_raw = discriminant.size;
68        let padding = (max_align - (variants_offset_raw % max_align)) % max_align;
69        let variants_offset = variants_offset_raw + padding;
70
71        assert!(variants_offset % max_align == 0);
72
73        Self {
74            size,
75            discriminant,
76            variants_offset,
77            variants,
78        }
79    }
80}
81
82/// The layout for an enum variant. The enum variant layout is just a struct layout with a tag and alignment.
83#[derive(Debug, Copy, Clone)]
84pub struct EnumVariant {
85    // Note: tags may not be sequential
86    tag: u32,
87    data: StructLayout,
88    align: usize,
89}
90
91impl EnumVariant {
92    /// Create a new enum variant layout
93    pub const fn new(tag: u32, data: StructLayout, align: usize) -> Self {
94        Self { tag, data, align }
95    }
96}
97
98/// The layout for a constant sized array. The array layout is just a length and an item layout.
99#[derive(Debug, Copy, Clone)]
100pub struct ListLayout {
101    len: usize,
102    item_layout: &'static Layout,
103}
104
105impl ListLayout {
106    /// Create a new list layout
107    pub const fn new(len: usize, item_layout: &'static Layout) -> Self {
108        Self { len, item_layout }
109    }
110}
111
112/// The layout for a primitive type. The bytes will be reversed if the target is big endian.
113#[derive(Debug, Copy, Clone)]
114pub struct PrimitiveLayout {
115    size: usize,
116}
117
118impl PrimitiveLayout {
119    /// Create a new primitive layout
120    pub const fn new(size: usize) -> Self {
121        Self { size }
122    }
123}
124
125/// 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.
126#[derive(Debug, Copy, Clone)]
127pub enum Layout {
128    /// An enum layout
129    Enum(EnumLayout),
130    /// A struct layout
131    Struct(StructLayout),
132    /// A list layout
133    List(ListLayout),
134    /// A primitive layout
135    Primitive(PrimitiveLayout),
136}
137
138impl Layout {
139    /// The size of the type in bytes.
140    const fn size(&self) -> usize {
141        match self {
142            Layout::Enum(layout) => layout.size,
143            Layout::Struct(layout) => layout.size,
144            Layout::List(layout) => layout.len * layout.item_layout.size(),
145            Layout::Primitive(layout) => layout.size,
146        }
147    }
148}
149
150/// A trait for types that can be serialized and deserialized in const.
151///
152/// # Safety
153/// The layout must accurately describe the memory layout of the type
154pub unsafe trait SerializeConst: Sized {
155    /// The memory layout of the type. This type must have plain old data; no pointers or references.
156    const MEMORY_LAYOUT: Layout;
157    /// Assert that the memory layout of the type is the same as the size of the type
158    const _ASSERT: () = assert!(Self::MEMORY_LAYOUT.size() == std::mem::size_of::<Self>());
159}
160
161macro_rules! impl_serialize_const {
162    ($type:ty) => {
163        unsafe impl SerializeConst for $type {
164            const MEMORY_LAYOUT: Layout = Layout::Primitive(PrimitiveLayout {
165                size: std::mem::size_of::<$type>(),
166            });
167        }
168    };
169}
170
171impl_serialize_const!(u8);
172impl_serialize_const!(u16);
173impl_serialize_const!(u32);
174impl_serialize_const!(u64);
175impl_serialize_const!(i8);
176impl_serialize_const!(i16);
177impl_serialize_const!(i32);
178impl_serialize_const!(i64);
179impl_serialize_const!(bool);
180impl_serialize_const!(f32);
181impl_serialize_const!(f64);
182
183unsafe impl<const N: usize, T: SerializeConst> SerializeConst for [T; N] {
184    const MEMORY_LAYOUT: Layout = Layout::List(ListLayout {
185        len: N,
186        item_layout: &T::MEMORY_LAYOUT,
187    });
188}
189
190macro_rules! impl_serialize_const_tuple {
191    ($($generic:ident: $generic_number:expr),*) => {
192        impl_serialize_const_tuple!(@impl ($($generic,)*) = $($generic: $generic_number),*);
193    };
194    (@impl $inner:ty = $($generic:ident: $generic_number:expr),*) => {
195        unsafe impl<$($generic: SerializeConst),*> SerializeConst for ($($generic,)*) {
196            const MEMORY_LAYOUT: Layout = {
197                Layout::Struct(StructLayout {
198                    size: std::mem::size_of::<($($generic,)*)>(),
199                    data: &[
200                        $(
201                            StructFieldLayout::new(std::mem::offset_of!($inner, $generic_number), $generic::MEMORY_LAYOUT),
202                        )*
203                    ],
204                })
205            };
206        }
207    };
208}
209
210impl_serialize_const_tuple!(T1: 0);
211impl_serialize_const_tuple!(T1: 0, T2: 1);
212impl_serialize_const_tuple!(T1: 0, T2: 1, T3: 2);
213impl_serialize_const_tuple!(T1: 0, T2: 1, T3: 2, T4: 3);
214impl_serialize_const_tuple!(T1: 0, T2: 1, T3: 2, T4: 3, T5: 4);
215impl_serialize_const_tuple!(T1: 0, T2: 1, T3: 2, T4: 3, T5: 4, T6: 5);
216impl_serialize_const_tuple!(T1: 0, T2: 1, T3: 2, T4: 3, T5: 4, T6: 5, T7: 6);
217impl_serialize_const_tuple!(T1: 0, T2: 1, T3: 2, T4: 3, T5: 4, T6: 5, T7: 6, T8: 7);
218impl_serialize_const_tuple!(T1: 0, T2: 1, T3: 2, T4: 3, T5: 4, T6: 5, T7: 6, T8: 7, T9: 8);
219impl_serialize_const_tuple!(T1: 0, T2: 1, T3: 2, T4: 3, T5: 4, T6: 5, T7: 6, T8: 7, T9: 8, T10: 9);
220
221const MAX_STR_SIZE: usize = 256;
222
223/// A string that is stored in a constant sized buffer that can be serialized and deserialized at compile time
224#[derive(PartialEq, PartialOrd, Clone, Copy, Hash)]
225#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
226pub struct ConstStr {
227    #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
228    bytes: [u8; MAX_STR_SIZE],
229    len: u32,
230}
231
232#[cfg(feature = "serde")]
233mod serde_bytes {
234    use serde::{Deserialize, Serializer};
235
236    pub fn serialize<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
237    where
238        S: Serializer,
239    {
240        serializer.serialize_bytes(bytes)
241    }
242
243    pub fn deserialize<'de, D>(deserializer: D) -> Result<[u8; super::MAX_STR_SIZE], D::Error>
244    where
245        D: serde::Deserializer<'de>,
246    {
247        let bytes = Vec::<u8>::deserialize(deserializer)?;
248        bytes
249            .try_into()
250            .map_err(|_| serde::de::Error::custom("Failed to convert bytes to a fixed size array"))
251    }
252}
253
254unsafe impl SerializeConst for ConstStr {
255    const MEMORY_LAYOUT: Layout = Layout::Struct(StructLayout {
256        size: std::mem::size_of::<Self>(),
257        data: &[
258            StructFieldLayout::new(
259                std::mem::offset_of!(Self, bytes),
260                Layout::List(ListLayout {
261                    len: MAX_STR_SIZE,
262                    item_layout: &Layout::Primitive(PrimitiveLayout {
263                        size: std::mem::size_of::<u8>(),
264                    }),
265                }),
266            ),
267            StructFieldLayout::new(
268                std::mem::offset_of!(Self, len),
269                Layout::Primitive(PrimitiveLayout {
270                    size: std::mem::size_of::<u32>(),
271                }),
272            ),
273        ],
274    });
275}
276
277impl ConstStr {
278    /// Create a new constant string
279    pub const fn new(s: &str) -> Self {
280        let str_bytes = s.as_bytes();
281        let mut bytes = [0; MAX_STR_SIZE];
282        let mut i = 0;
283        while i < str_bytes.len() {
284            bytes[i] = str_bytes[i];
285            i += 1;
286        }
287        Self {
288            bytes,
289            len: str_bytes.len() as u32,
290        }
291    }
292
293    /// Get a reference to the string
294    pub const fn as_str(&self) -> &str {
295        let str_bytes = self.bytes.split_at(self.len as usize).0;
296        match std::str::from_utf8(str_bytes) {
297            Ok(s) => s,
298            Err(_) => panic!(
299                "Invalid utf8; ConstStr should only ever be constructed from valid utf8 strings"
300            ),
301        }
302    }
303
304    /// Get the length of the string
305    pub const fn len(&self) -> usize {
306        self.len as usize
307    }
308
309    /// Check if the string is empty
310    pub const fn is_empty(&self) -> bool {
311        self.len == 0
312    }
313
314    /// Push a character onto the string
315    pub const fn push(self, byte: char) -> Self {
316        assert!(byte.is_ascii(), "Only ASCII bytes are supported");
317        let (bytes, len) = char_to_bytes(byte);
318        let (str, _) = bytes.split_at(len);
319        let Ok(str) = std::str::from_utf8(str) else {
320            panic!("Invalid utf8; char_to_bytes should always return valid utf8 bytes")
321        };
322        self.push_str(str)
323    }
324
325    /// Push a str onto the string
326    pub const fn push_str(self, str: &str) -> Self {
327        let Self { mut bytes, len } = self;
328        assert!(
329            str.len() + len as usize <= MAX_STR_SIZE,
330            "String is too long"
331        );
332        let str_bytes = str.as_bytes();
333        let new_len = len as usize + str_bytes.len();
334        let mut i = 0;
335        while i < str_bytes.len() {
336            bytes[len as usize + i] = str_bytes[i];
337            i += 1;
338        }
339        Self {
340            bytes,
341            len: new_len as u32,
342        }
343    }
344
345    /// Split the string at a byte index. The byte index must be a char boundary
346    pub const fn split_at(self, index: usize) -> (Self, Self) {
347        let (left, right) = self.bytes.split_at(index);
348        let left = match std::str::from_utf8(left) {
349            Ok(s) => s,
350            Err(_) => {
351                panic!("Invalid utf8; you cannot split at a byte that is not a char boundary")
352            }
353        };
354        let right = match std::str::from_utf8(right) {
355            Ok(s) => s,
356            Err(_) => {
357                panic!("Invalid utf8; you cannot split at a byte that is not a char boundary")
358            }
359        };
360        (Self::new(left), Self::new(right))
361    }
362
363    /// Split the string at the last occurrence of a character
364    pub const fn rsplit_once(&self, char: char) -> Option<(Self, Self)> {
365        let str = self.as_str();
366        let mut index = str.len() - 1;
367        // First find the bytes we are searching for
368        let (char_bytes, len) = char_to_bytes(char);
369        let (char_bytes, _) = char_bytes.split_at(len);
370        let bytes = str.as_bytes();
371
372        // Then walk backwards from the end of the string
373        loop {
374            let byte = bytes[index];
375            // Look for char boundaries in the string and check if the bytes match
376            if let Some(char_boundary_len) = utf8_char_boundary_to_char_len(byte) {
377                // Split up the string into three sections: [before_char, in_char, after_char]
378                let (before_char, after_index) = bytes.split_at(index);
379                let (in_char, after_char) = after_index.split_at(char_boundary_len as usize);
380                if in_char.len() != char_boundary_len as usize {
381                    panic!("in_char.len() should always be equal to char_boundary_len as usize")
382                }
383                // Check if the bytes for the current char and the target char match
384                let mut in_char_eq = true;
385                let mut i = 0;
386                let min_len = if in_char.len() < char_bytes.len() {
387                    in_char.len()
388                } else {
389                    char_bytes.len()
390                };
391                while i < min_len {
392                    in_char_eq &= in_char[i] == char_bytes[i];
393                    i += 1;
394                }
395                // If they do, convert the bytes to strings and return the split strings
396                if in_char_eq {
397                    let Ok(before_char_str) = std::str::from_utf8(before_char) else {
398                        panic!("Invalid utf8; utf8_char_boundary_to_char_len should only return Some when the byte is a character boundary")
399                    };
400                    let Ok(after_char_str) = std::str::from_utf8(after_char) else {
401                        panic!("Invalid utf8; utf8_char_boundary_to_char_len should only return Some when the byte is a character boundary")
402                    };
403                    return Some((Self::new(before_char_str), Self::new(after_char_str)));
404                }
405            }
406            match index.checked_sub(1) {
407                Some(new_index) => index = new_index,
408                None => return None,
409            }
410        }
411    }
412
413    /// Split the string at the first occurrence of a character
414    pub const fn split_once(&self, char: char) -> Option<(Self, Self)> {
415        let str = self.as_str();
416        let mut index = 0;
417        // First find the bytes we are searching for
418        let (char_bytes, len) = char_to_bytes(char);
419        let (char_bytes, _) = char_bytes.split_at(len);
420        let bytes = str.as_bytes();
421
422        // Then walk forwards from the start of the string
423        while index < bytes.len() {
424            let byte = bytes[index];
425            // Look for char boundaries in the string and check if the bytes match
426            if let Some(char_boundary_len) = utf8_char_boundary_to_char_len(byte) {
427                // Split up the string into three sections: [before_char, in_char, after_char]
428                let (before_char, after_index) = bytes.split_at(index);
429                let (in_char, after_char) = after_index.split_at(char_boundary_len as usize);
430                if in_char.len() != char_boundary_len as usize {
431                    panic!("in_char.len() should always be equal to char_boundary_len as usize")
432                }
433                // Check if the bytes for the current char and the target char match
434                let mut in_char_eq = true;
435                let mut i = 0;
436                let min_len = if in_char.len() < char_bytes.len() {
437                    in_char.len()
438                } else {
439                    char_bytes.len()
440                };
441                while i < min_len {
442                    in_char_eq &= in_char[i] == char_bytes[i];
443                    i += 1;
444                }
445                // If they do, convert the bytes to strings and return the split strings
446                if in_char_eq {
447                    let Ok(before_char_str) = std::str::from_utf8(before_char) else {
448                        panic!("Invalid utf8; utf8_char_boundary_to_char_len should only return Some when the byte is a character boundary")
449                    };
450                    let Ok(after_char_str) = std::str::from_utf8(after_char) else {
451                        panic!("Invalid utf8; utf8_char_boundary_to_char_len should only return Some when the byte is a character boundary")
452                    };
453                    return Some((Self::new(before_char_str), Self::new(after_char_str)));
454                }
455            }
456            index += 1
457        }
458        None
459    }
460}
461
462impl std::fmt::Debug for ConstStr {
463    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
464        write!(f, "{:?}", self.as_str())
465    }
466}
467
468#[test]
469fn test_rsplit_once() {
470    let str = ConstStr::new("hello world");
471    assert_eq!(
472        str.rsplit_once(' '),
473        Some((ConstStr::new("hello"), ConstStr::new("world")))
474    );
475
476    let unicode_str = ConstStr::new("hi😀hello😀world😀world");
477    assert_eq!(
478        unicode_str.rsplit_once('😀'),
479        Some((ConstStr::new("hi😀hello😀world"), ConstStr::new("world")))
480    );
481    assert_eq!(unicode_str.rsplit_once('❌'), None);
482
483    for _ in 0..100 {
484        let random_str: String = (0..rand::random::<u8>() % 50)
485            .map(|_| rand::random::<char>())
486            .collect();
487        let konst = ConstStr::new(&random_str);
488        let mut seen_chars = std::collections::HashSet::new();
489        for char in random_str.chars().rev() {
490            let (char_bytes, len) = char_to_bytes(char);
491            let char_bytes = &char_bytes[..len];
492            assert_eq!(char_bytes, char.to_string().as_bytes());
493            if seen_chars.contains(&char) {
494                continue;
495            }
496            seen_chars.insert(char);
497            let (correct_left, correct_right) = random_str.rsplit_once(char).unwrap();
498            let (left, right) = konst.rsplit_once(char).unwrap();
499            println!("splitting {random_str:?} at {char:?}");
500            assert_eq!(left.as_str(), correct_left);
501            assert_eq!(right.as_str(), correct_right);
502        }
503    }
504}
505
506const CONTINUED_CHAR_MASK: u8 = 0b10000000;
507const BYTE_CHAR_BOUNDARIES: [u8; 4] = [0b00000000, 0b11000000, 0b11100000, 0b11110000];
508
509// Const version of https://doc.rust-lang.org/src/core/char/methods.rs.html#1765-1797
510const fn char_to_bytes(char: char) -> ([u8; 4], usize) {
511    let code = char as u32;
512    let len = char.len_utf8();
513    let mut bytes = [0; 4];
514    match len {
515        1 => {
516            bytes[0] = code as u8;
517        }
518        2 => {
519            bytes[0] = (code >> 6 & 0x1F) as u8 | BYTE_CHAR_BOUNDARIES[1];
520            bytes[1] = (code & 0x3F) as u8 | CONTINUED_CHAR_MASK;
521        }
522        3 => {
523            bytes[0] = (code >> 12 & 0x0F) as u8 | BYTE_CHAR_BOUNDARIES[2];
524            bytes[1] = (code >> 6 & 0x3F) as u8 | CONTINUED_CHAR_MASK;
525            bytes[2] = (code & 0x3F) as u8 | CONTINUED_CHAR_MASK;
526        }
527        4 => {
528            bytes[0] = (code >> 18 & 0x07) as u8 | BYTE_CHAR_BOUNDARIES[3];
529            bytes[1] = (code >> 12 & 0x3F) as u8 | CONTINUED_CHAR_MASK;
530            bytes[2] = (code >> 6 & 0x3F) as u8 | CONTINUED_CHAR_MASK;
531            bytes[3] = (code & 0x3F) as u8 | CONTINUED_CHAR_MASK;
532        }
533        _ => panic!(
534            "encode_utf8: need more than 4 bytes to encode the unicode character, but the buffer has 4 bytes"
535        ),
536    };
537    (bytes, len)
538}
539
540#[test]
541fn fuzz_char_to_bytes() {
542    use std::char;
543    for _ in 0..100 {
544        let char = rand::random::<char>();
545        let (bytes, len) = char_to_bytes(char);
546        let str = std::str::from_utf8(&bytes[..len]).unwrap();
547        assert_eq!(char.to_string(), str);
548    }
549}
550
551const fn utf8_char_boundary_to_char_len(byte: u8) -> Option<u8> {
552    match byte {
553        0b00000000..=0b01111111 => Some(1),
554        0b11000000..=0b11011111 => Some(2),
555        0b11100000..=0b11101111 => Some(3),
556        0b11110000..=0b11111111 => Some(4),
557        _ => None,
558    }
559}
560
561#[test]
562fn fuzz_utf8_byte_to_char_len() {
563    for _ in 0..100 {
564        let random_string: String = (0..rand::random::<u8>())
565            .map(|_| rand::random::<char>())
566            .collect();
567        let bytes = random_string.as_bytes();
568        let chars: std::collections::HashMap<_, _> = random_string.char_indices().collect();
569        for (i, byte) in bytes.iter().enumerate() {
570            match utf8_char_boundary_to_char_len(*byte) {
571                Some(char_len) => {
572                    let char = chars
573                        .get(&i)
574                        .unwrap_or_else(|| panic!("{byte:b} is not a character boundary"));
575                    assert_eq!(char.len_utf8(), char_len as usize);
576                }
577                None => {
578                    assert!(!chars.contains_key(&i), "{byte:b} is a character boundary");
579                }
580            }
581        }
582    }
583}
584
585/// Serialize a struct that is stored at the pointer passed in
586const fn serialize_const_struct(
587    ptr: *const (),
588    mut to: ConstVec<u8>,
589    layout: &StructLayout,
590) -> ConstVec<u8> {
591    let mut i = 0;
592    while i < layout.data.len() {
593        // Serialize the field at the offset pointer in the struct
594        let StructFieldLayout { offset, layout } = &layout.data[i];
595        let field = ptr.wrapping_byte_add(*offset as _);
596        to = serialize_const_ptr(field, to, layout);
597        i += 1;
598    }
599    to
600}
601
602/// Serialize an enum that is stored at the pointer passed in
603const fn serialize_const_enum(
604    ptr: *const (),
605    mut to: ConstVec<u8>,
606    layout: &EnumLayout,
607) -> ConstVec<u8> {
608    let mut discriminant = 0;
609
610    let byte_ptr = ptr as *const u8;
611    let mut offset = 0;
612    while offset < layout.discriminant.size {
613        // If the bytes are reversed, walk backwards from the end of the number when pushing bytes
614        let byte = if cfg!(target_endian = "big") {
615            unsafe {
616                byte_ptr
617                    .wrapping_byte_add((layout.discriminant.size - offset - 1) as _)
618                    .read()
619            }
620        } else {
621            unsafe { byte_ptr.wrapping_byte_add(offset as _).read() }
622        };
623        to = to.push(byte);
624        discriminant |= (byte as u32) << (offset * 8);
625        offset += 1;
626    }
627
628    let mut i = 0;
629    while i < layout.variants.len() {
630        // If the variant is the discriminated one, serialize it
631        let EnumVariant { tag, data, .. } = &layout.variants[i];
632        if discriminant == *tag {
633            let data_ptr = ptr.wrapping_byte_offset(layout.variants_offset as _);
634            to = serialize_const_struct(data_ptr, to, data);
635            break;
636        }
637        i += 1;
638    }
639    to
640}
641
642/// Serialize a primitive type that is stored at the pointer passed in
643const fn serialize_const_primitive(
644    ptr: *const (),
645    mut to: ConstVec<u8>,
646    layout: &PrimitiveLayout,
647) -> ConstVec<u8> {
648    let ptr = ptr as *const u8;
649    let mut offset = 0;
650    while offset < layout.size {
651        // If the bytes are reversed, walk backwards from the end of the number when pushing bytes
652        if cfg!(any(target_endian = "big", feature = "test-big-endian")) {
653            to = to.push(unsafe {
654                ptr.wrapping_byte_offset((layout.size - offset - 1) as _)
655                    .read()
656            });
657        } else {
658            to = to.push(unsafe { ptr.wrapping_byte_offset(offset as _).read() });
659        }
660        offset += 1;
661    }
662    to
663}
664
665/// Serialize a constant sized array that is stored at the pointer passed in
666const fn serialize_const_list(
667    ptr: *const (),
668    mut to: ConstVec<u8>,
669    layout: &ListLayout,
670) -> ConstVec<u8> {
671    let len = layout.len;
672    let mut i = 0;
673    while i < len {
674        let field = ptr.wrapping_byte_offset((i * layout.item_layout.size()) as _);
675        to = serialize_const_ptr(field, to, layout.item_layout);
676        i += 1;
677    }
678    to
679}
680
681/// Serialize a pointer to a type that is stored at the pointer passed in
682const fn serialize_const_ptr(ptr: *const (), to: ConstVec<u8>, layout: &Layout) -> ConstVec<u8> {
683    match layout {
684        Layout::Enum(layout) => serialize_const_enum(ptr, to, layout),
685        Layout::Struct(layout) => serialize_const_struct(ptr, to, layout),
686        Layout::List(layout) => serialize_const_list(ptr, to, layout),
687        Layout::Primitive(layout) => serialize_const_primitive(ptr, to, layout),
688    }
689}
690
691/// Serialize a type into a buffer
692///
693/// # Example
694///
695/// ```rust
696/// use const_serialize::{ConstVec, SerializeConst, serialize_const};
697///
698/// #[derive(Clone, Copy, Debug, PartialEq, SerializeConst)]
699/// struct Struct {
700///     a: u32,
701///     b: u8,
702///     c: u32,
703/// }
704///
705/// let mut buffer = ConstVec::new();
706/// buffer = serialize_const(&Struct {
707///     a: 0x11111111,
708///     b: 0x22,
709///     c: 0x33333333,
710/// }, buffer);
711/// let buf = buffer.read();
712/// assert_eq!(buf.as_ref(), &[0x11, 0x11, 0x11, 0x11, 0x22, 0x33, 0x33, 0x33, 0x33]);
713/// ```
714#[must_use = "The data is serialized into the returned buffer"]
715pub const fn serialize_const<T: SerializeConst>(data: &T, to: ConstVec<u8>) -> ConstVec<u8> {
716    let ptr = data as *const T as *const ();
717    serialize_const_ptr(ptr, to, &T::MEMORY_LAYOUT)
718}
719
720/// Deserialize a primitive type into the out buffer at the offset passed in. Returns a new version of the buffer with the data added.
721const fn deserialize_const_primitive<'a, const N: usize>(
722    mut from: ConstReadBuffer<'a>,
723    layout: &PrimitiveLayout,
724    out: (usize, [MaybeUninit<u8>; N]),
725) -> Option<(ConstReadBuffer<'a>, [MaybeUninit<u8>; N])> {
726    let (start, mut out) = out;
727    let mut offset = 0;
728    while offset < layout.size {
729        // If the bytes are reversed, walk backwards from the end of the number when filling in bytes
730        let (from_new, value) = match from.get() {
731            Some(data) => data,
732            None => return None,
733        };
734        from = from_new;
735        if cfg!(any(target_endian = "big", feature = "test-big-endian")) {
736            out[start + layout.size - offset - 1] = MaybeUninit::new(value);
737        } else {
738            out[start + offset] = MaybeUninit::new(value);
739        }
740        offset += 1;
741    }
742    Some((from, out))
743}
744
745/// Deserialize a struct type into the out buffer at the offset passed in. Returns a new version of the buffer with the data added.
746const fn deserialize_const_struct<'a, const N: usize>(
747    mut from: ConstReadBuffer<'a>,
748    layout: &StructLayout,
749    out: (usize, [MaybeUninit<u8>; N]),
750) -> Option<(ConstReadBuffer<'a>, [MaybeUninit<u8>; N])> {
751    let (start, mut out) = out;
752    let mut i = 0;
753    while i < layout.data.len() {
754        // Deserialize the field at the offset pointer in the struct
755        let StructFieldLayout { offset, layout } = &layout.data[i];
756        let (new_from, new_out) = match deserialize_const_ptr(from, layout, (start + *offset, out))
757        {
758            Some(data) => data,
759            None => return None,
760        };
761        from = new_from;
762        out = new_out;
763        i += 1;
764    }
765    Some((from, out))
766}
767
768/// Deserialize an enum type into the out buffer at the offset passed in. Returns a new version of the buffer with the data added.
769const fn deserialize_const_enum<'a, const N: usize>(
770    mut from: ConstReadBuffer<'a>,
771    layout: &EnumLayout,
772    out: (usize, [MaybeUninit<u8>; N]),
773) -> Option<(ConstReadBuffer<'a>, [MaybeUninit<u8>; N])> {
774    let (start, mut out) = out;
775    let mut discriminant = 0;
776
777    // First, deserialize the discriminant
778    let mut offset = 0;
779    while offset < layout.discriminant.size {
780        // If the bytes are reversed, walk backwards from the end of the number when filling in bytes
781        let (from_new, value) = match from.get() {
782            Some(data) => data,
783            None => return None,
784        };
785        from = from_new;
786        if cfg!(target_endian = "big") {
787            out[start + layout.size - offset - 1] = MaybeUninit::new(value);
788            discriminant |= (value as u32) << ((layout.discriminant.size - offset - 1) * 8);
789        } else {
790            out[start + offset] = MaybeUninit::new(value);
791            discriminant |= (value as u32) << (offset * 8);
792        }
793        offset += 1;
794    }
795
796    // Then, deserialize the variant
797    let mut i = 0;
798    let mut matched_variant = false;
799    while i < layout.variants.len() {
800        // If the variant is the discriminated one, deserialize it
801        let EnumVariant { tag, data, .. } = &layout.variants[i];
802        if discriminant == *tag {
803            let offset = layout.variants_offset;
804            let (new_from, new_out) =
805                match deserialize_const_struct(from, data, (start + offset, out)) {
806                    Some(data) => data,
807                    None => return None,
808                };
809            from = new_from;
810            out = new_out;
811            matched_variant = true;
812            break;
813        }
814        i += 1;
815    }
816    if !matched_variant {
817        return None;
818    }
819
820    Some((from, out))
821}
822
823/// Deserialize a list type into the out buffer at the offset passed in. Returns a new version of the buffer with the data added.
824const fn deserialize_const_list<'a, const N: usize>(
825    mut from: ConstReadBuffer<'a>,
826    layout: &ListLayout,
827    out: (usize, [MaybeUninit<u8>; N]),
828) -> Option<(ConstReadBuffer<'a>, [MaybeUninit<u8>; N])> {
829    let (start, mut out) = out;
830    let len = layout.len;
831    let item_layout = layout.item_layout;
832    let mut i = 0;
833    while i < len {
834        let (new_from, new_out) =
835            match deserialize_const_ptr(from, item_layout, (start + i * item_layout.size(), out)) {
836                Some(data) => data,
837                None => return None,
838            };
839        from = new_from;
840        out = new_out;
841        i += 1;
842    }
843    Some((from, out))
844}
845
846/// Deserialize a type into the out buffer at the offset passed in. Returns a new version of the buffer with the data added.
847const fn deserialize_const_ptr<'a, const N: usize>(
848    from: ConstReadBuffer<'a>,
849    layout: &Layout,
850    out: (usize, [MaybeUninit<u8>; N]),
851) -> Option<(ConstReadBuffer<'a>, [MaybeUninit<u8>; N])> {
852    match layout {
853        Layout::Enum(layout) => deserialize_const_enum(from, layout, out),
854        Layout::Struct(layout) => deserialize_const_struct(from, layout, out),
855        Layout::List(layout) => deserialize_const_list(from, layout, out),
856        Layout::Primitive(layout) => deserialize_const_primitive(from, layout, out),
857    }
858}
859
860/// Deserialize a type into the output buffer. Accepts (Type, ConstVec<u8>) as input and returns Option<(ConstReadBuffer, Instance of type)>
861///
862/// # Example
863/// ```rust
864/// # use const_serialize::{deserialize_const, serialize_const, ConstVec, SerializeConst};
865/// #[derive(Clone, Copy, Debug, PartialEq, SerializeConst)]
866/// struct Struct {
867///     a: u32,
868///     b: u8,
869///     c: u32,
870///     d: u32,
871/// }
872///
873/// let mut buffer = ConstVec::new();
874/// buffer = serialize_const(&Struct {
875///     a: 0x11111111,
876///     b: 0x22,
877///     c: 0x33333333,
878///     d: 0x44444444,
879/// }, buffer);
880/// let buf = buffer.read();
881/// assert_eq!(deserialize_const!(Struct, buf).unwrap().1, Struct {
882///     a: 0x11111111,
883///     b: 0x22,
884///     c: 0x33333333,
885///     d: 0x44444444,
886/// });
887/// ```
888#[macro_export]
889macro_rules! deserialize_const {
890    ($type:ty, $buffer:expr) => {
891        unsafe {
892            const __SIZE: usize = std::mem::size_of::<$type>();
893            $crate::deserialize_const_raw::<__SIZE, $type>($buffer)
894        }
895    };
896}
897
898/// Deserialize a buffer into a type. This will return None if the buffer doesn't have enough data to fill the type.
899/// # Safety
900/// N must be `std::mem::size_of::<T>()`
901#[must_use = "The data is deserialized from the input buffer"]
902pub const unsafe fn deserialize_const_raw<const N: usize, T: SerializeConst>(
903    from: ConstReadBuffer,
904) -> Option<(ConstReadBuffer, T)> {
905    // Create uninitized memory with the size of the type
906    let out = [MaybeUninit::uninit(); N];
907    // Fill in the bytes into the buffer for the type
908    let (from, out) = match deserialize_const_ptr(from, &T::MEMORY_LAYOUT, (0, out)) {
909        Some(data) => data,
910        None => return None,
911    };
912    // Now that the memory is filled in, transmute it into the type
913    Some((from, unsafe {
914        std::mem::transmute_copy::<[MaybeUninit<u8>; N], T>(&out)
915    }))
916}
917
918/// Check if the serialized representation of two items are the same
919pub const fn serialize_eq<T: SerializeConst>(first: &T, second: &T) -> bool {
920    let first_serialized = ConstVec::<u8>::new();
921    let first_serialized = serialize_const(first, first_serialized);
922    let second_serialized = ConstVec::<u8>::new();
923    let second_serialized = serialize_const(second, second_serialized);
924    let first_buf = first_serialized.as_ref();
925    let second_buf = second_serialized.as_ref();
926    if first_buf.len() != second_buf.len() {
927        return false;
928    }
929    let mut i = 0;
930    while i < first_buf.len() {
931        if first_buf[i] != second_buf[i] {
932            return false;
933        }
934        i += 1;
935    }
936    true
937}