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 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
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(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 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 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 pub const fn len(&self) -> usize {
308 self.len as usize
309 }
310
311 pub const fn is_empty(&self) -> bool {
313 self.len == 0
314 }
315
316 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 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 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 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 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 loop {
376 let byte = bytes[index];
377 if let Some(char_boundary_len) = utf8_char_boundary_to_char_len(byte) {
379 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 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 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 pub const fn split_once(&self, char: char) -> Option<(Self, Self)> {
417 let str = self.as_str();
418 let mut index = 0;
419 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 while index < bytes.len() {
426 let byte = bytes[index];
427 if let Some(char_boundary_len) = utf8_char_boundary_to_char_len(byte) {
429 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 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 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
511const 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
587const 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 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
604const 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 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 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
644const 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 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
667const 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
683const 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#[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
722const 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 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
747const 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 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
770const 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 let mut offset = 0;
781 while offset < layout.discriminant.size {
782 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 let mut i = 0;
800 let mut matched_variant = false;
801 while i < layout.variants.len() {
802 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
825const 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
848const 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#[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#[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 let out = [MaybeUninit::uninit(); N];
909 let (from, out) = match deserialize_const_ptr(from, &T::MEMORY_LAYOUT, (0, out)) {
911 Some(data) => data,
912 None => return None,
913 };
914 Some((from, unsafe {
916 std::mem::transmute_copy::<[MaybeUninit<u8>; N], T>(&out)
917 }))
918}
919
920pub 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}