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#[derive(Debug, Copy, Clone)]
15pub struct StructFieldLayout {
16 offset: usize,
17 layout: Layout,
18}
19
20impl StructFieldLayout {
21 pub const fn new(offset: usize, layout: Layout) -> Self {
23 Self { offset, layout }
24 }
25}
26
27#[derive(Debug, Copy, Clone)]
29pub struct StructLayout {
30 size: usize,
31 data: &'static [StructFieldLayout],
32}
33
34impl StructLayout {
35 pub const fn new(size: usize, data: &'static [StructFieldLayout]) -> Self {
37 Self { size, data }
38 }
39}
40
41#[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 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#[derive(Debug, Copy, Clone)]
84pub struct EnumVariant {
85 tag: u32,
87 data: StructLayout,
88 align: usize,
89}
90
91impl EnumVariant {
92 pub const fn new(tag: u32, data: StructLayout, align: usize) -> Self {
94 Self { tag, data, align }
95 }
96}
97
98#[derive(Debug, Copy, Clone)]
100pub struct ListLayout {
101 len: usize,
102 item_layout: &'static Layout,
103}
104
105impl ListLayout {
106 pub const fn new(len: usize, item_layout: &'static Layout) -> Self {
108 Self { len, item_layout }
109 }
110}
111
112#[derive(Debug, Copy, Clone)]
114pub struct PrimitiveLayout {
115 size: usize,
116}
117
118impl PrimitiveLayout {
119 pub const fn new(size: usize) -> Self {
121 Self { size }
122 }
123}
124
125#[derive(Debug, Copy, Clone)]
127pub enum Layout {
128 Enum(EnumLayout),
130 Struct(StructLayout),
132 List(ListLayout),
134 Primitive(PrimitiveLayout),
136}
137
138impl Layout {
139 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
150pub unsafe trait SerializeConst: Sized {
155 const MEMORY_LAYOUT: Layout;
157 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#[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 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 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 pub const fn len(&self) -> usize {
306 self.len as usize
307 }
308
309 pub const fn is_empty(&self) -> bool {
311 self.len == 0
312 }
313
314 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 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 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 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 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 loop {
374 let byte = bytes[index];
375 if let Some(char_boundary_len) = utf8_char_boundary_to_char_len(byte) {
377 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 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 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 pub const fn split_once(&self, char: char) -> Option<(Self, Self)> {
415 let str = self.as_str();
416 let mut index = 0;
417 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 while index < bytes.len() {
424 let byte = bytes[index];
425 if let Some(char_boundary_len) = utf8_char_boundary_to_char_len(byte) {
427 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 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 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
509const 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
585const 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 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
602const 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 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 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
642const 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 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
665const 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
681const 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#[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
720const 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 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
745const 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 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
768const 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 let mut offset = 0;
779 while offset < layout.discriminant.size {
780 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 let mut i = 0;
798 let mut matched_variant = false;
799 while i < layout.variants.len() {
800 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
823const 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
846const 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#[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#[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 let out = [MaybeUninit::uninit(); N];
907 let (from, out) = match deserialize_const_ptr(from, &T::MEMORY_LAYOUT, (0, out)) {
909 Some(data) => data,
910 None => return None,
911 };
912 Some((from, unsafe {
914 std::mem::transmute_copy::<[MaybeUninit<u8>; N], T>(&out)
915 }))
916}
917
918pub 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}