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    pub 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(Eq, PartialEq, PartialOrd, Clone, Copy, Hash)]
225pub struct ConstStr {
226    bytes: [u8; MAX_STR_SIZE],
227    len: u32,
228}
229
230#[cfg(feature = "serde")]
231mod serde_bytes {
232    use serde::{Deserialize, Serialize, Serializer};
233
234    use crate::ConstStr;
235
236    impl Serialize for ConstStr {
237        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
238        where
239            S: Serializer,
240        {
241            serializer.serialize_str(self.as_str())
242        }
243    }
244
245    impl<'de> Deserialize<'de> for ConstStr {
246        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
247        where
248            D: serde::Deserializer<'de>,
249        {
250            let s = String::deserialize(deserializer)?;
251            Ok(ConstStr::new(&s))
252        }
253    }
254}
255
256unsafe impl SerializeConst for ConstStr {
257    const MEMORY_LAYOUT: Layout = Layout::Struct(StructLayout {
258        size: std::mem::size_of::<Self>(),
259        data: &[
260            StructFieldLayout::new(
261                std::mem::offset_of!(Self, bytes),
262                Layout::List(ListLayout {
263                    len: MAX_STR_SIZE,
264                    item_layout: &Layout::Primitive(PrimitiveLayout {
265                        size: std::mem::size_of::<u8>(),
266                    }),
267                }),
268            ),
269            StructFieldLayout::new(
270                std::mem::offset_of!(Self, len),
271                Layout::Primitive(PrimitiveLayout {
272                    size: std::mem::size_of::<u32>(),
273                }),
274            ),
275        ],
276    });
277}
278
279impl ConstStr {
280    /// Create a new constant string
281    pub const fn new(s: &str) -> Self {
282        let str_bytes = s.as_bytes();
283        let mut bytes = [0; MAX_STR_SIZE];
284        let mut i = 0;
285        while i < str_bytes.len() {
286            bytes[i] = str_bytes[i];
287            i += 1;
288        }
289        Self {
290            bytes,
291            len: str_bytes.len() as u32,
292        }
293    }
294
295    /// Get a reference to the string
296    pub const fn as_str(&self) -> &str {
297        let str_bytes = self.bytes.split_at(self.len as usize).0;
298        match std::str::from_utf8(str_bytes) {
299            Ok(s) => s,
300            Err(_) => panic!(
301                "Invalid utf8; ConstStr should only ever be constructed from valid utf8 strings"
302            ),
303        }
304    }
305
306    /// Get the length of the string
307    pub const fn len(&self) -> usize {
308        self.len as usize
309    }
310
311    /// Check if the string is empty
312    pub const fn is_empty(&self) -> bool {
313        self.len == 0
314    }
315
316    /// Push a character onto the string
317    pub const fn push(self, byte: char) -> Self {
318        assert!(byte.is_ascii(), "Only ASCII bytes are supported");
319        let (bytes, len) = char_to_bytes(byte);
320        let (str, _) = bytes.split_at(len);
321        let Ok(str) = std::str::from_utf8(str) else {
322            panic!("Invalid utf8; char_to_bytes should always return valid utf8 bytes")
323        };
324        self.push_str(str)
325    }
326
327    /// Push a str onto the string
328    pub const fn push_str(self, str: &str) -> Self {
329        let Self { mut bytes, len } = self;
330        assert!(
331            str.len() + len as usize <= MAX_STR_SIZE,
332            "String is too long"
333        );
334        let str_bytes = str.as_bytes();
335        let new_len = len as usize + str_bytes.len();
336        let mut i = 0;
337        while i < str_bytes.len() {
338            bytes[len as usize + i] = str_bytes[i];
339            i += 1;
340        }
341        Self {
342            bytes,
343            len: new_len as u32,
344        }
345    }
346
347    /// Split the string at a byte index. The byte index must be a char boundary
348    pub const fn split_at(self, index: usize) -> (Self, Self) {
349        let (left, right) = self.bytes.split_at(index);
350        let left = match std::str::from_utf8(left) {
351            Ok(s) => s,
352            Err(_) => {
353                panic!("Invalid utf8; you cannot split at a byte that is not a char boundary")
354            }
355        };
356        let right = match std::str::from_utf8(right) {
357            Ok(s) => s,
358            Err(_) => {
359                panic!("Invalid utf8; you cannot split at a byte that is not a char boundary")
360            }
361        };
362        (Self::new(left), Self::new(right))
363    }
364
365    /// Split the string at the last occurrence of a character
366    pub const fn rsplit_once(&self, char: char) -> Option<(Self, Self)> {
367        let str = self.as_str();
368        let mut index = str.len() - 1;
369        // First find the bytes we are searching for
370        let (char_bytes, len) = char_to_bytes(char);
371        let (char_bytes, _) = char_bytes.split_at(len);
372        let bytes = str.as_bytes();
373
374        // Then walk backwards from the end of the string
375        loop {
376            let byte = bytes[index];
377            // Look for char boundaries in the string and check if the bytes match
378            if let Some(char_boundary_len) = utf8_char_boundary_to_char_len(byte) {
379                // Split up the string into three sections: [before_char, in_char, after_char]
380                let (before_char, after_index) = bytes.split_at(index);
381                let (in_char, after_char) = after_index.split_at(char_boundary_len as usize);
382                if in_char.len() != char_boundary_len as usize {
383                    panic!("in_char.len() should always be equal to char_boundary_len as usize")
384                }
385                // Check if the bytes for the current char and the target char match
386                let mut in_char_eq = true;
387                let mut i = 0;
388                let min_len = if in_char.len() < char_bytes.len() {
389                    in_char.len()
390                } else {
391                    char_bytes.len()
392                };
393                while i < min_len {
394                    in_char_eq &= in_char[i] == char_bytes[i];
395                    i += 1;
396                }
397                // If they do, convert the bytes to strings and return the split strings
398                if in_char_eq {
399                    let Ok(before_char_str) = std::str::from_utf8(before_char) else {
400                        panic!("Invalid utf8; utf8_char_boundary_to_char_len should only return Some when the byte is a character boundary")
401                    };
402                    let Ok(after_char_str) = std::str::from_utf8(after_char) else {
403                        panic!("Invalid utf8; utf8_char_boundary_to_char_len should only return Some when the byte is a character boundary")
404                    };
405                    return Some((Self::new(before_char_str), Self::new(after_char_str)));
406                }
407            }
408            match index.checked_sub(1) {
409                Some(new_index) => index = new_index,
410                None => return None,
411            }
412        }
413    }
414
415    /// Split the string at the first occurrence of a character
416    pub const fn split_once(&self, char: char) -> Option<(Self, Self)> {
417        let str = self.as_str();
418        let mut index = 0;
419        // First find the bytes we are searching for
420        let (char_bytes, len) = char_to_bytes(char);
421        let (char_bytes, _) = char_bytes.split_at(len);
422        let bytes = str.as_bytes();
423
424        // Then walk forwards from the start of the string
425        while index < bytes.len() {
426            let byte = bytes[index];
427            // Look for char boundaries in the string and check if the bytes match
428            if let Some(char_boundary_len) = utf8_char_boundary_to_char_len(byte) {
429                // Split up the string into three sections: [before_char, in_char, after_char]
430                let (before_char, after_index) = bytes.split_at(index);
431                let (in_char, after_char) = after_index.split_at(char_boundary_len as usize);
432                if in_char.len() != char_boundary_len as usize {
433                    panic!("in_char.len() should always be equal to char_boundary_len as usize")
434                }
435                // Check if the bytes for the current char and the target char match
436                let mut in_char_eq = true;
437                let mut i = 0;
438                let min_len = if in_char.len() < char_bytes.len() {
439                    in_char.len()
440                } else {
441                    char_bytes.len()
442                };
443                while i < min_len {
444                    in_char_eq &= in_char[i] == char_bytes[i];
445                    i += 1;
446                }
447                // If they do, convert the bytes to strings and return the split strings
448                if in_char_eq {
449                    let Ok(before_char_str) = std::str::from_utf8(before_char) else {
450                        panic!("Invalid utf8; utf8_char_boundary_to_char_len should only return Some when the byte is a character boundary")
451                    };
452                    let Ok(after_char_str) = std::str::from_utf8(after_char) else {
453                        panic!("Invalid utf8; utf8_char_boundary_to_char_len should only return Some when the byte is a character boundary")
454                    };
455                    return Some((Self::new(before_char_str), Self::new(after_char_str)));
456                }
457            }
458            index += 1
459        }
460        None
461    }
462}
463
464impl std::fmt::Debug for ConstStr {
465    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
466        write!(f, "{:?}", self.as_str())
467    }
468}
469
470#[test]
471fn test_rsplit_once() {
472    let str = ConstStr::new("hello world");
473    assert_eq!(
474        str.rsplit_once(' '),
475        Some((ConstStr::new("hello"), ConstStr::new("world")))
476    );
477
478    let unicode_str = ConstStr::new("hi😀hello😀world😀world");
479    assert_eq!(
480        unicode_str.rsplit_once('😀'),
481        Some((ConstStr::new("hi😀hello😀world"), ConstStr::new("world")))
482    );
483    assert_eq!(unicode_str.rsplit_once('❌'), None);
484
485    for _ in 0..100 {
486        let random_str: String = (0..rand::random::<u8>() % 50)
487            .map(|_| rand::random::<char>())
488            .collect();
489        let konst = ConstStr::new(&random_str);
490        let mut seen_chars = std::collections::HashSet::new();
491        for char in random_str.chars().rev() {
492            let (char_bytes, len) = char_to_bytes(char);
493            let char_bytes = &char_bytes[..len];
494            assert_eq!(char_bytes, char.to_string().as_bytes());
495            if seen_chars.contains(&char) {
496                continue;
497            }
498            seen_chars.insert(char);
499            let (correct_left, correct_right) = random_str.rsplit_once(char).unwrap();
500            let (left, right) = konst.rsplit_once(char).unwrap();
501            println!("splitting {random_str:?} at {char:?}");
502            assert_eq!(left.as_str(), correct_left);
503            assert_eq!(right.as_str(), correct_right);
504        }
505    }
506}
507
508const CONTINUED_CHAR_MASK: u8 = 0b10000000;
509const BYTE_CHAR_BOUNDARIES: [u8; 4] = [0b00000000, 0b11000000, 0b11100000, 0b11110000];
510
511// Const version of https://doc.rust-lang.org/src/core/char/methods.rs.html#1765-1797
512const fn char_to_bytes(char: char) -> ([u8; 4], usize) {
513    let code = char as u32;
514    let len = char.len_utf8();
515    let mut bytes = [0; 4];
516    match len {
517        1 => {
518            bytes[0] = code as u8;
519        }
520        2 => {
521            bytes[0] = ((code >> 6) & 0x1F) as u8 | BYTE_CHAR_BOUNDARIES[1];
522            bytes[1] = (code & 0x3F) as u8 | CONTINUED_CHAR_MASK;
523        }
524        3 => {
525            bytes[0] = ((code >> 12) & 0x0F) as u8 | BYTE_CHAR_BOUNDARIES[2];
526            bytes[1] = ((code >> 6) & 0x3F) as u8 | CONTINUED_CHAR_MASK;
527            bytes[2] = (code & 0x3F) as u8 | CONTINUED_CHAR_MASK;
528        }
529        4 => {
530            bytes[0] = ((code >> 18) & 0x07) as u8 | BYTE_CHAR_BOUNDARIES[3];
531            bytes[1] = ((code >> 12) & 0x3F) as u8 | CONTINUED_CHAR_MASK;
532            bytes[2] = ((code >> 6) & 0x3F) as u8 | CONTINUED_CHAR_MASK;
533            bytes[3] = (code & 0x3F) as u8 | CONTINUED_CHAR_MASK;
534        }
535        _ => panic!(
536            "encode_utf8: need more than 4 bytes to encode the unicode character, but the buffer has 4 bytes"
537        ),
538    };
539    (bytes, len)
540}
541
542#[test]
543fn fuzz_char_to_bytes() {
544    use std::char;
545    for _ in 0..100 {
546        let char = rand::random::<char>();
547        let (bytes, len) = char_to_bytes(char);
548        let str = std::str::from_utf8(&bytes[..len]).unwrap();
549        assert_eq!(char.to_string(), str);
550    }
551}
552
553const fn utf8_char_boundary_to_char_len(byte: u8) -> Option<u8> {
554    match byte {
555        0b00000000..=0b01111111 => Some(1),
556        0b11000000..=0b11011111 => Some(2),
557        0b11100000..=0b11101111 => Some(3),
558        0b11110000..=0b11111111 => Some(4),
559        _ => None,
560    }
561}
562
563#[test]
564fn fuzz_utf8_byte_to_char_len() {
565    for _ in 0..100 {
566        let random_string: String = (0..rand::random::<u8>())
567            .map(|_| rand::random::<char>())
568            .collect();
569        let bytes = random_string.as_bytes();
570        let chars: std::collections::HashMap<_, _> = random_string.char_indices().collect();
571        for (i, byte) in bytes.iter().enumerate() {
572            match utf8_char_boundary_to_char_len(*byte) {
573                Some(char_len) => {
574                    let char = chars
575                        .get(&i)
576                        .unwrap_or_else(|| panic!("{byte:b} is not a character boundary"));
577                    assert_eq!(char.len_utf8(), char_len as usize);
578                }
579                None => {
580                    assert!(!chars.contains_key(&i), "{byte:b} is a character boundary");
581                }
582            }
583        }
584    }
585}
586
587/// Serialize a struct that is stored at the pointer passed in
588const fn serialize_const_struct(
589    ptr: *const (),
590    mut to: ConstVec<u8>,
591    layout: &StructLayout,
592) -> ConstVec<u8> {
593    let mut i = 0;
594    while i < layout.data.len() {
595        // Serialize the field at the offset pointer in the struct
596        let StructFieldLayout { offset, layout } = &layout.data[i];
597        let field = ptr.wrapping_byte_add(*offset as _);
598        to = serialize_const_ptr(field, to, layout);
599        i += 1;
600    }
601    to
602}
603
604/// Serialize an enum that is stored at the pointer passed in
605const fn serialize_const_enum(
606    ptr: *const (),
607    mut to: ConstVec<u8>,
608    layout: &EnumLayout,
609) -> ConstVec<u8> {
610    let mut discriminant = 0;
611
612    let byte_ptr = ptr as *const u8;
613    let mut offset = 0;
614    while offset < layout.discriminant.size {
615        // If the bytes are reversed, walk backwards from the end of the number when pushing bytes
616        let byte = if cfg!(target_endian = "big") {
617            unsafe {
618                byte_ptr
619                    .wrapping_byte_add((layout.discriminant.size - offset - 1) as _)
620                    .read()
621            }
622        } else {
623            unsafe { byte_ptr.wrapping_byte_add(offset as _).read() }
624        };
625        to = to.push(byte);
626        discriminant |= (byte as u32) << (offset * 8);
627        offset += 1;
628    }
629
630    let mut i = 0;
631    while i < layout.variants.len() {
632        // If the variant is the discriminated one, serialize it
633        let EnumVariant { tag, data, .. } = &layout.variants[i];
634        if discriminant == *tag {
635            let data_ptr = ptr.wrapping_byte_offset(layout.variants_offset as _);
636            to = serialize_const_struct(data_ptr, to, data);
637            break;
638        }
639        i += 1;
640    }
641    to
642}
643
644/// Serialize a primitive type that is stored at the pointer passed in
645const fn serialize_const_primitive(
646    ptr: *const (),
647    mut to: ConstVec<u8>,
648    layout: &PrimitiveLayout,
649) -> ConstVec<u8> {
650    let ptr = ptr as *const u8;
651    let mut offset = 0;
652    while offset < layout.size {
653        // If the bytes are reversed, walk backwards from the end of the number when pushing bytes
654        if cfg!(any(target_endian = "big", feature = "test-big-endian")) {
655            to = to.push(unsafe {
656                ptr.wrapping_byte_offset((layout.size - offset - 1) as _)
657                    .read()
658            });
659        } else {
660            to = to.push(unsafe { ptr.wrapping_byte_offset(offset as _).read() });
661        }
662        offset += 1;
663    }
664    to
665}
666
667/// Serialize a constant sized array that is stored at the pointer passed in
668const fn serialize_const_list(
669    ptr: *const (),
670    mut to: ConstVec<u8>,
671    layout: &ListLayout,
672) -> ConstVec<u8> {
673    let len = layout.len;
674    let mut i = 0;
675    while i < len {
676        let field = ptr.wrapping_byte_offset((i * layout.item_layout.size()) as _);
677        to = serialize_const_ptr(field, to, layout.item_layout);
678        i += 1;
679    }
680    to
681}
682
683/// Serialize a pointer to a type that is stored at the pointer passed in
684const fn serialize_const_ptr(ptr: *const (), to: ConstVec<u8>, layout: &Layout) -> ConstVec<u8> {
685    match layout {
686        Layout::Enum(layout) => serialize_const_enum(ptr, to, layout),
687        Layout::Struct(layout) => serialize_const_struct(ptr, to, layout),
688        Layout::List(layout) => serialize_const_list(ptr, to, layout),
689        Layout::Primitive(layout) => serialize_const_primitive(ptr, to, layout),
690    }
691}
692
693/// Serialize a type into a buffer
694///
695/// # Example
696///
697/// ```rust
698/// use const_serialize::{ConstVec, SerializeConst, serialize_const};
699///
700/// #[derive(Clone, Copy, Debug, PartialEq, SerializeConst)]
701/// struct Struct {
702///     a: u32,
703///     b: u8,
704///     c: u32,
705/// }
706///
707/// let mut buffer = ConstVec::new();
708/// buffer = serialize_const(&Struct {
709///     a: 0x11111111,
710///     b: 0x22,
711///     c: 0x33333333,
712/// }, buffer);
713/// let buf = buffer.read();
714/// assert_eq!(buf.as_ref(), &[0x11, 0x11, 0x11, 0x11, 0x22, 0x33, 0x33, 0x33, 0x33]);
715/// ```
716#[must_use = "The data is serialized into the returned buffer"]
717pub const fn serialize_const<T: SerializeConst>(data: &T, to: ConstVec<u8>) -> ConstVec<u8> {
718    let ptr = data as *const T as *const ();
719    serialize_const_ptr(ptr, to, &T::MEMORY_LAYOUT)
720}
721
722/// Deserialize a primitive type into the out buffer at the offset passed in. Returns a new version of the buffer with the data added.
723const fn deserialize_const_primitive<'a, const N: usize>(
724    mut from: ConstReadBuffer<'a>,
725    layout: &PrimitiveLayout,
726    out: (usize, [MaybeUninit<u8>; N]),
727) -> Option<(ConstReadBuffer<'a>, [MaybeUninit<u8>; N])> {
728    let (start, mut out) = out;
729    let mut offset = 0;
730    while offset < layout.size {
731        // If the bytes are reversed, walk backwards from the end of the number when filling in bytes
732        let (from_new, value) = match from.get() {
733            Some(data) => data,
734            None => return None,
735        };
736        from = from_new;
737        if cfg!(any(target_endian = "big", feature = "test-big-endian")) {
738            out[start + layout.size - offset - 1] = MaybeUninit::new(value);
739        } else {
740            out[start + offset] = MaybeUninit::new(value);
741        }
742        offset += 1;
743    }
744    Some((from, out))
745}
746
747/// Deserialize a struct type into the out buffer at the offset passed in. Returns a new version of the buffer with the data added.
748const fn deserialize_const_struct<'a, const N: usize>(
749    mut from: ConstReadBuffer<'a>,
750    layout: &StructLayout,
751    out: (usize, [MaybeUninit<u8>; N]),
752) -> Option<(ConstReadBuffer<'a>, [MaybeUninit<u8>; N])> {
753    let (start, mut out) = out;
754    let mut i = 0;
755    while i < layout.data.len() {
756        // Deserialize the field at the offset pointer in the struct
757        let StructFieldLayout { offset, layout } = &layout.data[i];
758        let (new_from, new_out) = match deserialize_const_ptr(from, layout, (start + *offset, out))
759        {
760            Some(data) => data,
761            None => return None,
762        };
763        from = new_from;
764        out = new_out;
765        i += 1;
766    }
767    Some((from, out))
768}
769
770/// Deserialize an enum type into the out buffer at the offset passed in. Returns a new version of the buffer with the data added.
771const fn deserialize_const_enum<'a, const N: usize>(
772    mut from: ConstReadBuffer<'a>,
773    layout: &EnumLayout,
774    out: (usize, [MaybeUninit<u8>; N]),
775) -> Option<(ConstReadBuffer<'a>, [MaybeUninit<u8>; N])> {
776    let (start, mut out) = out;
777    let mut discriminant = 0;
778
779    // First, deserialize the discriminant
780    let mut offset = 0;
781    while offset < layout.discriminant.size {
782        // If the bytes are reversed, walk backwards from the end of the number when filling in bytes
783        let (from_new, value) = match from.get() {
784            Some(data) => data,
785            None => return None,
786        };
787        from = from_new;
788        if cfg!(target_endian = "big") {
789            out[start + layout.size - offset - 1] = MaybeUninit::new(value);
790            discriminant |= (value as u32) << ((layout.discriminant.size - offset - 1) * 8);
791        } else {
792            out[start + offset] = MaybeUninit::new(value);
793            discriminant |= (value as u32) << (offset * 8);
794        }
795        offset += 1;
796    }
797
798    // Then, deserialize the variant
799    let mut i = 0;
800    let mut matched_variant = false;
801    while i < layout.variants.len() {
802        // If the variant is the discriminated one, deserialize it
803        let EnumVariant { tag, data, .. } = &layout.variants[i];
804        if discriminant == *tag {
805            let offset = layout.variants_offset;
806            let (new_from, new_out) =
807                match deserialize_const_struct(from, data, (start + offset, out)) {
808                    Some(data) => data,
809                    None => return None,
810                };
811            from = new_from;
812            out = new_out;
813            matched_variant = true;
814            break;
815        }
816        i += 1;
817    }
818    if !matched_variant {
819        return None;
820    }
821
822    Some((from, out))
823}
824
825/// Deserialize a list type into the out buffer at the offset passed in. Returns a new version of the buffer with the data added.
826const fn deserialize_const_list<'a, const N: usize>(
827    mut from: ConstReadBuffer<'a>,
828    layout: &ListLayout,
829    out: (usize, [MaybeUninit<u8>; N]),
830) -> Option<(ConstReadBuffer<'a>, [MaybeUninit<u8>; N])> {
831    let (start, mut out) = out;
832    let len = layout.len;
833    let item_layout = layout.item_layout;
834    let mut i = 0;
835    while i < len {
836        let (new_from, new_out) =
837            match deserialize_const_ptr(from, item_layout, (start + i * item_layout.size(), out)) {
838                Some(data) => data,
839                None => return None,
840            };
841        from = new_from;
842        out = new_out;
843        i += 1;
844    }
845    Some((from, out))
846}
847
848/// Deserialize a type into the out buffer at the offset passed in. Returns a new version of the buffer with the data added.
849const fn deserialize_const_ptr<'a, const N: usize>(
850    from: ConstReadBuffer<'a>,
851    layout: &Layout,
852    out: (usize, [MaybeUninit<u8>; N]),
853) -> Option<(ConstReadBuffer<'a>, [MaybeUninit<u8>; N])> {
854    match layout {
855        Layout::Enum(layout) => deserialize_const_enum(from, layout, out),
856        Layout::Struct(layout) => deserialize_const_struct(from, layout, out),
857        Layout::List(layout) => deserialize_const_list(from, layout, out),
858        Layout::Primitive(layout) => deserialize_const_primitive(from, layout, out),
859    }
860}
861
862/// Deserialize a type into the output buffer. Accepts `(type, ConstVec<u8>)` as input and returns `Option<(ConstReadBuffer, Instance of type)>`
863///
864/// # Example
865/// ```rust
866/// # use const_serialize::{deserialize_const, serialize_const, ConstVec, SerializeConst};
867/// #[derive(Clone, Copy, Debug, PartialEq, SerializeConst)]
868/// struct Struct {
869///     a: u32,
870///     b: u8,
871///     c: u32,
872///     d: u32,
873/// }
874///
875/// let mut buffer = ConstVec::new();
876/// buffer = serialize_const(&Struct {
877///     a: 0x11111111,
878///     b: 0x22,
879///     c: 0x33333333,
880///     d: 0x44444444,
881/// }, buffer);
882/// let buf = buffer.read();
883/// assert_eq!(deserialize_const!(Struct, buf).unwrap().1, Struct {
884///     a: 0x11111111,
885///     b: 0x22,
886///     c: 0x33333333,
887///     d: 0x44444444,
888/// });
889/// ```
890#[macro_export]
891macro_rules! deserialize_const {
892    ($type:ty, $buffer:expr) => {
893        unsafe {
894            const __SIZE: usize = std::mem::size_of::<$type>();
895            $crate::deserialize_const_raw::<__SIZE, $type>($buffer)
896        }
897    };
898}
899
900/// Deserialize a buffer into a type. This will return None if the buffer doesn't have enough data to fill the type.
901/// # Safety
902/// N must be `std::mem::size_of::<T>()`
903#[must_use = "The data is deserialized from the input buffer"]
904pub const unsafe fn deserialize_const_raw<const N: usize, T: SerializeConst>(
905    from: ConstReadBuffer,
906) -> Option<(ConstReadBuffer, T)> {
907    // Create uninitized memory with the size of the type
908    let out = [MaybeUninit::uninit(); N];
909    // Fill in the bytes into the buffer for the type
910    let (from, out) = match deserialize_const_ptr(from, &T::MEMORY_LAYOUT, (0, out)) {
911        Some(data) => data,
912        None => return None,
913    };
914    // Now that the memory is filled in, transmute it into the type
915    Some((from, unsafe {
916        std::mem::transmute_copy::<[MaybeUninit<u8>; N], T>(&out)
917    }))
918}
919
920/// Check if the serialized representation of two items are the same
921pub const fn serialize_eq<T: SerializeConst>(first: &T, second: &T) -> bool {
922    let first_serialized = ConstVec::<u8>::new();
923    let first_serialized = serialize_const(first, first_serialized);
924    let second_serialized = ConstVec::<u8>::new();
925    let second_serialized = serialize_const(second, second_serialized);
926    let first_buf = first_serialized.as_ref();
927    let second_buf = second_serialized.as_ref();
928    if first_buf.len() != second_buf.len() {
929        return false;
930    }
931    let mut i = 0;
932    while i < first_buf.len() {
933        if first_buf[i] != second_buf[i] {
934            return false;
935        }
936        i += 1;
937    }
938    true
939}