const_serialize/
enum.rs

1use crate::*;
2
3/// Serialize an enum that is stored at the pointer passed in
4pub(crate) const unsafe fn serialize_const_enum(
5    ptr: *const (),
6    mut to: ConstVec<u8>,
7    layout: &EnumLayout,
8) -> ConstVec<u8> {
9    let byte_ptr = ptr as *const u8;
10    let discriminant = layout.discriminant.read(byte_ptr);
11
12    let mut i = 0;
13    while i < layout.variants.len() {
14        // If the variant is the discriminated one, serialize it
15        let EnumVariant {
16            tag, name, data, ..
17        } = &layout.variants[i];
18        if discriminant == *tag {
19            to = write_map(to, 1);
20            to = write_map_key(to, name);
21            let data_ptr = ptr.wrapping_byte_offset(layout.variants_offset as _);
22            to = serialize_const_struct(data_ptr, to, data);
23            break;
24        }
25        i += 1;
26    }
27    to
28}
29
30/// The layout for an enum. The enum layout is just a discriminate size and a tag layout.
31#[derive(Debug, Copy, Clone)]
32pub struct EnumLayout {
33    pub(crate) size: usize,
34    discriminant: PrimitiveLayout,
35    variants_offset: usize,
36    variants: &'static [EnumVariant],
37}
38
39impl EnumLayout {
40    /// Create a new enum layout
41    pub const fn new(
42        size: usize,
43        discriminant: PrimitiveLayout,
44        variants: &'static [EnumVariant],
45    ) -> Self {
46        let mut max_align = 1;
47        let mut i = 0;
48        while i < variants.len() {
49            let EnumVariant { align, .. } = &variants[i];
50            if *align > max_align {
51                max_align = *align;
52            }
53            i += 1;
54        }
55
56        let variants_offset_raw = discriminant.size;
57        let padding = (max_align - (variants_offset_raw % max_align)) % max_align;
58        let variants_offset = variants_offset_raw + padding;
59
60        assert!(variants_offset % max_align == 0);
61
62        Self {
63            size,
64            discriminant,
65            variants_offset,
66            variants,
67        }
68    }
69}
70
71/// The layout for an enum variant. The enum variant layout is just a struct layout with a tag and alignment.
72#[derive(Debug, Copy, Clone)]
73pub struct EnumVariant {
74    name: &'static str,
75    // Note: tags may not be sequential
76    tag: u32,
77    data: StructLayout,
78    align: usize,
79}
80
81impl EnumVariant {
82    /// Create a new enum variant layout
83    pub const fn new(name: &'static str, tag: u32, data: StructLayout, align: usize) -> Self {
84        Self {
85            name,
86            tag,
87            data,
88            align,
89        }
90    }
91}
92
93/// Deserialize an enum type into the out buffer at the offset passed in. Returns a new version of the buffer with the data added.
94pub(crate) const fn deserialize_const_enum<'a>(
95    from: &'a [u8],
96    layout: &EnumLayout,
97    out: &mut [MaybeUninit<u8>],
98) -> Option<&'a [u8]> {
99    // First, deserialize the map
100    let Ok((map, remaining)) = take_map(from) else {
101        return None;
102    };
103
104    // Then get the only field which is the tag
105    let Ok((deserilized_name, from)) = take_str(map.bytes) else {
106        return None;
107    };
108
109    // Then, deserialize the variant
110    let mut i = 0;
111    let mut matched_variant = false;
112    while i < layout.variants.len() {
113        // If the variant is the discriminated one, deserialize it
114        let EnumVariant {
115            name, data, tag, ..
116        } = &layout.variants[i];
117        if str_eq(deserilized_name, name) {
118            layout.discriminant.write(*tag, out);
119            let Some((_, out)) = out.split_at_mut_checked(layout.variants_offset) else {
120                return None;
121            };
122            if deserialize_const_struct(from, data, out).is_none() {
123                return None;
124            }
125            matched_variant = true;
126            break;
127        }
128        i += 1;
129    }
130    if !matched_variant {
131        return None;
132    }
133
134    Some(remaining)
135}