1use crate::{
10 de::{
11 Deserialize, DeserializeSeed, Deserializer, Error, NamedProductAccess, ProductVisitor, SeqProductAccess,
12 SumAccess, SumVisitor, VariantAccess as _, VariantVisitor,
13 },
14 i256, impl_deserialize, impl_serialize,
15 sum_type::{OPTION_NONE_TAG, OPTION_SOME_TAG},
16 u256, AlgebraicType, AlgebraicValue, ArrayType, ProductType, ProductTypeElement, ProductValue, SumType,
17 SumTypeVariant, SumValue, WithTypespace,
18};
19use core::ops::{Index, Mul};
20use core::{mem, ops::Deref};
21use derive_more::{Add, Sub};
22use enum_as_inner::EnumAsInner;
23use std::sync::Arc;
24
25pub const fn align_to(base: usize, required_alignment: usize) -> usize {
29 if required_alignment == 0 {
30 base
33 } else {
34 let misalignment = base % required_alignment;
35 if misalignment == 0 {
36 base
37 } else {
38 let padding = required_alignment - misalignment;
39 base + padding
40 }
41 }
42}
43
44#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Add, Sub)]
46pub struct Size(pub u16);
47
48#[cfg(feature = "memory-usage")]
49impl spacetimedb_memory_usage::MemoryUsage for Size {}
50
51impl_serialize!([] Size, (self, ser) => self.0.serialize(ser));
53impl_deserialize!([] Size, de => u16::deserialize(de).map(Size));
54
55impl Size {
56 #[inline]
58 #[allow(clippy::len_without_is_empty)]
59 pub const fn len(self) -> usize {
60 self.0 as usize
61 }
62}
63
64impl Mul<usize> for Size {
65 type Output = Size;
66
67 #[inline]
68 fn mul(self, rhs: usize) -> Self::Output {
69 Size((self.len() * rhs) as u16)
70 }
71}
72
73#[derive(Copy, Clone, Debug, PartialEq, Eq)]
84pub struct Layout {
85 pub size: u16,
87 pub align: u16,
89 pub fixed: bool,
92}
93
94#[cfg(feature = "memory-usage")]
95impl spacetimedb_memory_usage::MemoryUsage for Layout {}
96
97pub trait HasLayout {
101 fn layout(&self) -> &Layout;
103
104 fn size(&self) -> usize {
110 self.layout().size as usize
111 }
112
113 fn align(&self) -> usize {
119 self.layout().align as usize
120 }
121}
122
123#[derive(Debug, PartialEq, Eq, Clone, EnumAsInner)]
138pub enum AlgebraicTypeLayout {
139 Sum(SumTypeLayout),
141 Product(ProductTypeLayout),
143 Primitive(PrimitiveType),
145 VarLen(VarLenType),
147}
148
149#[cfg(feature = "memory-usage")]
150impl spacetimedb_memory_usage::MemoryUsage for AlgebraicTypeLayout {
151 fn heap_usage(&self) -> usize {
152 match self {
153 AlgebraicTypeLayout::Sum(x) => x.heap_usage(),
154 AlgebraicTypeLayout::Product(x) => x.heap_usage(),
155 AlgebraicTypeLayout::Primitive(x) => x.heap_usage(),
156 AlgebraicTypeLayout::VarLen(x) => x.heap_usage(),
157 }
158 }
159}
160
161impl HasLayout for AlgebraicTypeLayout {
162 fn layout(&self) -> &Layout {
163 match self {
164 Self::Sum(ty) => ty.layout(),
165 Self::Product(ty) => ty.layout(),
166 Self::Primitive(ty) => ty.layout(),
167 Self::VarLen(ty) => ty.layout(),
168 }
169 }
170}
171
172#[allow(non_upper_case_globals)]
173impl AlgebraicTypeLayout {
174 pub const Bool: Self = Self::Primitive(PrimitiveType::Bool);
175 pub const I8: Self = Self::Primitive(PrimitiveType::I8);
176 pub const U8: Self = Self::Primitive(PrimitiveType::U8);
177 pub const I16: Self = Self::Primitive(PrimitiveType::I16);
178 pub const U16: Self = Self::Primitive(PrimitiveType::U16);
179 pub const I32: Self = Self::Primitive(PrimitiveType::I32);
180 pub const U32: Self = Self::Primitive(PrimitiveType::U32);
181 pub const I64: Self = Self::Primitive(PrimitiveType::I64);
182 pub const U64: Self = Self::Primitive(PrimitiveType::U64);
183 pub const I128: Self = Self::Primitive(PrimitiveType::I128);
184 pub const U128: Self = Self::Primitive(PrimitiveType::U128);
185 pub const I256: Self = Self::Primitive(PrimitiveType::I256);
186 pub const U256: Self = Self::Primitive(PrimitiveType::U256);
187 pub const F32: Self = Self::Primitive(PrimitiveType::F32);
188 pub const F64: Self = Self::Primitive(PrimitiveType::F64);
189 pub const String: Self = Self::VarLen(VarLenType::String);
190
191 fn is_compatible_with(&self, new: &Self) -> bool {
193 match (self, new) {
194 (Self::Sum(old), Self::Sum(new)) => old.is_compatible_with(new),
195 (Self::Product(old), Self::Product(new)) => old.view().is_compatible_with(new.view()),
196 (Self::Primitive(old), Self::Primitive(new)) => old == new,
197 (Self::VarLen(VarLenType::Array(old)), Self::VarLen(VarLenType::Array(new))) => {
198 let old = AlgebraicTypeLayout::from(old.elem_ty.deref().clone());
203 let new = AlgebraicTypeLayout::from(new.elem_ty.deref().clone());
204 old.is_compatible_with(&new)
205 }
206 (Self::VarLen(VarLenType::String), Self::VarLen(VarLenType::String)) => true,
207 _ => false,
208 }
209 }
210}
211
212type Collection<T> = Box<[T]>;
214
215pub const MIN_ROW_SIZE: Size = Size(2);
217
218pub const MIN_ROW_ALIGN: Size = Size(2);
220
221pub const fn row_size_for_bytes(required_bytes: usize) -> Size {
224 if required_bytes > MIN_ROW_SIZE.len() {
226 Size(align_to(required_bytes, MIN_ROW_ALIGN.len()) as u16)
227 } else {
228 MIN_ROW_SIZE
229 }
230}
231
232pub const fn row_size_for_type<T>() -> Size {
235 row_size_for_bytes(mem::size_of::<T>())
236}
237
238#[derive(Debug, PartialEq, Eq, Clone)]
242pub struct RowTypeLayout {
243 pub layout: Layout,
245 pub elements: Arc<[ProductTypeElementLayout]>,
253}
254
255#[cfg(feature = "memory-usage")]
256impl spacetimedb_memory_usage::MemoryUsage for RowTypeLayout {
257 fn heap_usage(&self) -> usize {
258 let Self { layout, elements } = self;
259 layout.heap_usage() + elements.heap_usage()
260 }
261}
262
263impl RowTypeLayout {
264 pub fn product(&self) -> ProductTypeLayoutView<'_> {
266 let elements = &*self.elements;
267 let layout = self.layout;
268 ProductTypeLayoutView { layout, elements }
269 }
270
271 pub fn size(&self) -> Size {
273 Size(self.product().size() as u16)
274 }
275
276 pub fn is_compatible_with(&self, new: &RowTypeLayout) -> bool {
278 self.layout == new.layout && self.product().is_compatible_with(new.product())
279 }
280}
281
282impl HasLayout for RowTypeLayout {
283 fn layout(&self) -> &Layout {
284 &self.layout
285 }
286}
287
288impl Index<usize> for RowTypeLayout {
289 type Output = AlgebraicTypeLayout;
290 fn index(&self, index: usize) -> &Self::Output {
291 &self.elements[index].ty
292 }
293}
294
295#[derive(Debug, PartialEq, Eq, Copy, Clone)]
297pub struct ProductTypeLayoutView<'a> {
298 pub layout: Layout,
300 pub elements: &'a [ProductTypeElementLayout],
302}
303
304impl HasLayout for ProductTypeLayoutView<'_> {
305 fn layout(&self) -> &Layout {
306 &self.layout
307 }
308}
309
310impl ProductTypeLayoutView<'_> {
311 fn is_compatible_with(self, new: Self) -> bool {
313 self.elements.len() == new.elements.len()
314 && self
315 .elements
316 .iter()
317 .zip(new.elements.iter())
318 .all(|(old, new)| old.ty.is_compatible_with(&new.ty))
319 }
320}
321
322#[derive(Debug, PartialEq, Eq, Clone)]
324pub struct ProductTypeLayout {
325 pub layout: Layout,
327 pub elements: Collection<ProductTypeElementLayout>,
329}
330
331impl ProductTypeLayout {
332 pub fn view(&self) -> ProductTypeLayoutView<'_> {
334 let elements = &*self.elements;
335 let layout = self.layout;
336 ProductTypeLayoutView { layout, elements }
337 }
338}
339
340#[cfg(feature = "memory-usage")]
341impl spacetimedb_memory_usage::MemoryUsage for ProductTypeLayout {
342 fn heap_usage(&self) -> usize {
343 let Self { layout, elements } = self;
344 layout.heap_usage() + elements.heap_usage()
345 }
346}
347
348impl HasLayout for ProductTypeLayout {
349 fn layout(&self) -> &Layout {
350 &self.layout
351 }
352}
353
354#[derive(Debug, PartialEq, Eq, Clone)]
356pub struct ProductTypeElementLayout {
357 pub offset: u16,
359
360 pub ty: AlgebraicTypeLayout,
362
363 pub name: Option<Box<str>>,
368}
369
370#[cfg(feature = "memory-usage")]
371impl spacetimedb_memory_usage::MemoryUsage for ProductTypeElementLayout {
372 fn heap_usage(&self) -> usize {
373 let Self { offset, ty, name } = self;
374 offset.heap_usage() + ty.heap_usage() + name.heap_usage()
375 }
376}
377
378#[derive(Debug, PartialEq, Eq, Clone)]
380pub struct SumTypeLayout {
381 pub layout: Layout,
383 pub variants: Collection<SumTypeVariantLayout>,
385 pub payload_offset: u16,
388}
389
390#[cfg(feature = "memory-usage")]
391impl spacetimedb_memory_usage::MemoryUsage for SumTypeLayout {
392 fn heap_usage(&self) -> usize {
393 let Self {
394 layout,
395 variants,
396 payload_offset,
397 } = self;
398 layout.heap_usage() + variants.heap_usage() + payload_offset.heap_usage()
399 }
400}
401
402impl HasLayout for SumTypeLayout {
403 fn layout(&self) -> &Layout {
404 &self.layout
405 }
406}
407
408#[derive(Debug, PartialEq, Eq, Clone)]
410pub struct SumTypeVariantLayout {
411 pub ty: AlgebraicTypeLayout,
413
414 pub name: Option<Box<str>>,
419}
420
421#[cfg(feature = "memory-usage")]
422impl spacetimedb_memory_usage::MemoryUsage for SumTypeVariantLayout {
423 fn heap_usage(&self) -> usize {
424 let Self { ty, name } = self;
425 ty.heap_usage() + name.heap_usage()
426 }
427}
428
429#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
432pub enum PrimitiveType {
433 Bool,
434 I8,
435 U8,
436 I16,
437 U16,
438 I32,
439 U32,
440 I64,
441 U64,
442 I128,
443 U128,
444 I256,
445 U256,
446 F32,
447 F64,
448}
449
450#[cfg(feature = "memory-usage")]
451impl spacetimedb_memory_usage::MemoryUsage for PrimitiveType {}
452
453impl PrimitiveType {
454 pub fn algebraic_type(&self) -> AlgebraicType {
455 match self {
456 PrimitiveType::Bool => AlgebraicType::Bool,
457 PrimitiveType::I8 => AlgebraicType::I8,
458 PrimitiveType::U8 => AlgebraicType::U8,
459 PrimitiveType::I16 => AlgebraicType::I16,
460 PrimitiveType::U16 => AlgebraicType::U16,
461 PrimitiveType::I32 => AlgebraicType::I32,
462 PrimitiveType::U32 => AlgebraicType::U32,
463 PrimitiveType::I64 => AlgebraicType::I64,
464 PrimitiveType::U64 => AlgebraicType::U64,
465 PrimitiveType::I128 => AlgebraicType::I128,
466 PrimitiveType::U128 => AlgebraicType::U128,
467 PrimitiveType::I256 => AlgebraicType::I256,
468 PrimitiveType::U256 => AlgebraicType::U256,
469 PrimitiveType::F32 => AlgebraicType::F32,
470 PrimitiveType::F64 => AlgebraicType::F64,
471 }
472 }
473}
474
475impl HasLayout for PrimitiveType {
476 fn layout(&self) -> &'static Layout {
477 match self {
478 Self::Bool | Self::I8 | Self::U8 => &Layout {
479 size: 1,
480 align: 1,
481 fixed: true,
482 },
483 Self::I16 | Self::U16 => &Layout {
484 size: 2,
485 align: 2,
486 fixed: true,
487 },
488 Self::I32 | Self::U32 | Self::F32 => &Layout {
489 size: 4,
490 align: 4,
491 fixed: true,
492 },
493 Self::I64 | Self::U64 | Self::F64 => &Layout {
494 size: 8,
495 align: 8,
496 fixed: true,
497 },
498 Self::I128 | Self::U128 => &Layout {
499 size: 16,
500 align: 16,
501 fixed: true,
502 },
503 Self::I256 | Self::U256 => &Layout {
504 size: 32,
505 align: 32,
506 fixed: true,
507 },
508 }
509 }
510}
511
512#[derive(Debug, PartialEq, Eq, Clone)]
515pub enum VarLenType {
516 String,
518 Array(ArrayType),
523}
524
525#[cfg(feature = "memory-usage")]
526impl spacetimedb_memory_usage::MemoryUsage for VarLenType {
527 fn heap_usage(&self) -> usize {
528 match self {
529 VarLenType::String => 0,
530 VarLenType::Array(x) => x.heap_usage(),
531 }
532 }
533}
534
535pub const VAR_LEN_REF_LAYOUT: Layout = Layout {
537 size: 4,
538 align: 2,
539 fixed: false,
540};
541
542impl HasLayout for VarLenType {
543 fn layout(&self) -> &Layout {
544 &VAR_LEN_REF_LAYOUT
545 }
546}
547
548impl From<AlgebraicType> for AlgebraicTypeLayout {
551 fn from(ty: AlgebraicType) -> Self {
552 match ty {
553 AlgebraicType::Sum(sum) => AlgebraicTypeLayout::Sum(sum.into()),
554 AlgebraicType::Product(prod) => AlgebraicTypeLayout::Product(prod.into()),
555
556 AlgebraicType::String => AlgebraicTypeLayout::VarLen(VarLenType::String),
557 AlgebraicType::Array(array) => AlgebraicTypeLayout::VarLen(VarLenType::Array(array)),
558
559 AlgebraicType::Bool => AlgebraicTypeLayout::Bool,
560 AlgebraicType::I8 => AlgebraicTypeLayout::I8,
561 AlgebraicType::U8 => AlgebraicTypeLayout::U8,
562 AlgebraicType::I16 => AlgebraicTypeLayout::I16,
563 AlgebraicType::U16 => AlgebraicTypeLayout::U16,
564 AlgebraicType::I32 => AlgebraicTypeLayout::I32,
565 AlgebraicType::U32 => AlgebraicTypeLayout::U32,
566 AlgebraicType::I64 => AlgebraicTypeLayout::I64,
567 AlgebraicType::U64 => AlgebraicTypeLayout::U64,
568 AlgebraicType::I128 => AlgebraicTypeLayout::I128,
569 AlgebraicType::U128 => AlgebraicTypeLayout::U128,
570 AlgebraicType::I256 => AlgebraicTypeLayout::I256,
571 AlgebraicType::U256 => AlgebraicTypeLayout::U256,
572 AlgebraicType::F32 => AlgebraicTypeLayout::F32,
573 AlgebraicType::F64 => AlgebraicTypeLayout::F64,
574
575 AlgebraicType::Ref(_) => todo!("Refs unsupported without typespace context"),
576 }
577 }
578}
579
580fn product_type_layout<C: FromIterator<ProductTypeElementLayout>>(ty: ProductType) -> (C, Layout) {
582 let mut current_offset: usize = 0;
583
584 let mut max_child_align = 1;
587
588 let mut fixed = true;
589 let elements = Vec::from(ty.elements)
590 .into_iter()
591 .map(|elem| {
592 let layout_type: AlgebraicTypeLayout = elem.algebraic_type.into();
593 fixed &= layout_type.layout().fixed;
594 let this_offset = align_to(current_offset, layout_type.align());
595 max_child_align = usize::max(max_child_align, layout_type.align());
596
597 current_offset = this_offset + layout_type.size();
598
599 ProductTypeElementLayout {
600 offset: this_offset as u16,
601 name: elem.name,
602 ty: layout_type,
603 }
604 })
605 .collect();
606
607 let layout = Layout {
608 align: max_child_align as u16,
609 size: align_to(current_offset, max_child_align) as u16,
610 fixed,
611 };
612
613 (elements, layout)
614}
615
616impl From<ProductType> for RowTypeLayout {
617 fn from(ty: ProductType) -> Self {
618 let (elements, mut layout) = product_type_layout(ty);
619 layout.size = row_size_for_bytes(layout.size as usize).0;
620 Self { layout, elements }
621 }
622}
623
624impl From<ProductType> for ProductTypeLayout {
625 fn from(ty: ProductType) -> Self {
626 let (elements, layout) = product_type_layout(ty);
627 Self { layout, elements }
628 }
629}
630
631impl From<SumType> for SumTypeLayout {
632 fn from(ty: SumType) -> Self {
633 let mut max_child_size = 0;
634
635 let mut max_child_align = 0;
638
639 let mut fixed = true;
640 let variants = Vec::from(ty.variants)
641 .into_iter()
642 .map(|variant| {
643 let layout_type: AlgebraicTypeLayout = variant.algebraic_type.into();
644 fixed &= layout_type.layout().fixed;
645
646 max_child_align = usize::max(max_child_align, layout_type.align());
647 max_child_size = usize::max(max_child_size, layout_type.size());
648
649 SumTypeVariantLayout {
650 ty: layout_type,
651 name: variant.name,
652 }
653 })
654 .collect::<Vec<_>>()
655 .into();
656
657 let align = u16::max(max_child_align as u16, 1);
659
660 let payload_size = align_to(max_child_size, max_child_align);
668
669 let size = align + payload_size as u16;
671 let payload_offset = align;
672 let layout = Layout { align, size, fixed };
673 Self {
674 layout,
675 payload_offset,
676 variants,
677 }
678 }
679}
680
681impl AlgebraicTypeLayout {
685 pub fn algebraic_type(&self) -> AlgebraicType {
692 match self {
693 Self::Primitive(prim) => prim.algebraic_type(),
694 Self::VarLen(VarLenType::String) => AlgebraicType::String,
695 Self::VarLen(VarLenType::Array(array)) => AlgebraicType::Array(array.clone()),
696 Self::Product(prod) => AlgebraicType::Product(prod.view().product_type()),
697 Self::Sum(sum) => AlgebraicType::Sum(sum.sum_type()),
698 }
699 }
700}
701
702impl ProductTypeLayoutView<'_> {
703 pub fn product_type(&self) -> ProductType {
704 ProductType {
705 elements: self
706 .elements
707 .iter()
708 .map(ProductTypeElementLayout::product_type_element)
709 .collect(),
710 }
711 }
712
713 pub fn algebraic_type(&self) -> AlgebraicType {
720 AlgebraicType::Product(self.product_type())
721 }
722}
723
724impl ProductTypeElementLayout {
725 fn product_type_element(&self) -> ProductTypeElement {
726 ProductTypeElement {
727 algebraic_type: self.ty.algebraic_type(),
728 name: self.name.clone(),
729 }
730 }
731}
732
733impl SumTypeLayout {
734 fn sum_type(&self) -> SumType {
735 SumType {
736 variants: self
737 .variants
738 .iter()
739 .map(SumTypeVariantLayout::sum_type_variant)
740 .collect(),
741 }
742 }
743
744 fn is_compatible_with(&self, new: &SumTypeLayout) -> bool {
748 self.variants.len() <= new.variants.len()
749 && self
750 .variants
751 .iter()
752 .zip(self.variants.iter())
753 .all(|(old, new)| old.ty.is_compatible_with(&new.ty))
754 }
755}
756
757impl SumTypeVariantLayout {
758 fn sum_type_variant(&self) -> SumTypeVariant {
759 SumTypeVariant {
760 algebraic_type: self.ty.algebraic_type(),
761 name: self.name.clone(),
762 }
763 }
764
765 pub fn has_name(&self, name: &str) -> bool {
767 self.name.as_deref() == Some(name)
768 }
769
770 pub fn is_unit(&self) -> bool {
772 self.ty.as_product().is_some_and(|ty| ty.elements.is_empty())
773 }
774}
775
776impl SumTypeLayout {
779 pub fn offset_of_variant_data(&self, _variant_tag: u8) -> usize {
780 self.payload_offset as usize
788 }
789
790 pub fn offset_of_tag(&self) -> usize {
791 0
798 }
799}
800
801impl<'de> DeserializeSeed<'de> for &AlgebraicTypeLayout {
802 type Output = AlgebraicValue;
803
804 fn deserialize<D: Deserializer<'de>>(self, de: D) -> Result<Self::Output, D::Error> {
805 match self {
806 AlgebraicTypeLayout::Sum(ty) => ty.deserialize(de).map(Into::into),
807 AlgebraicTypeLayout::Product(ty) => ty.view().deserialize(de).map(Into::into),
808 AlgebraicTypeLayout::Primitive(PrimitiveType::Bool) => bool::deserialize(de).map(Into::into),
809 AlgebraicTypeLayout::Primitive(PrimitiveType::I8) => i8::deserialize(de).map(Into::into),
810 AlgebraicTypeLayout::Primitive(PrimitiveType::U8) => u8::deserialize(de).map(Into::into),
811 AlgebraicTypeLayout::Primitive(PrimitiveType::I16) => i16::deserialize(de).map(Into::into),
812 AlgebraicTypeLayout::Primitive(PrimitiveType::U16) => u16::deserialize(de).map(Into::into),
813 AlgebraicTypeLayout::Primitive(PrimitiveType::I32) => i32::deserialize(de).map(Into::into),
814 AlgebraicTypeLayout::Primitive(PrimitiveType::U32) => u32::deserialize(de).map(Into::into),
815 AlgebraicTypeLayout::Primitive(PrimitiveType::I64) => i64::deserialize(de).map(Into::into),
816 AlgebraicTypeLayout::Primitive(PrimitiveType::U64) => u64::deserialize(de).map(Into::into),
817 AlgebraicTypeLayout::Primitive(PrimitiveType::I128) => i128::deserialize(de).map(Into::into),
818 AlgebraicTypeLayout::Primitive(PrimitiveType::U128) => u128::deserialize(de).map(Into::into),
819 AlgebraicTypeLayout::Primitive(PrimitiveType::I256) => i256::deserialize(de).map(Into::into),
820 AlgebraicTypeLayout::Primitive(PrimitiveType::U256) => u256::deserialize(de).map(Into::into),
821 AlgebraicTypeLayout::Primitive(PrimitiveType::F32) => f32::deserialize(de).map(Into::into),
822 AlgebraicTypeLayout::Primitive(PrimitiveType::F64) => f64::deserialize(de).map(Into::into),
823 AlgebraicTypeLayout::VarLen(VarLenType::Array(ty)) => {
824 WithTypespace::empty(ty).deserialize(de).map(AlgebraicValue::Array)
825 }
826 AlgebraicTypeLayout::VarLen(VarLenType::String) => <Box<str>>::deserialize(de).map(Into::into),
827 }
828 }
829}
830
831impl<'de> DeserializeSeed<'de> for ProductTypeLayoutView<'_> {
832 type Output = ProductValue;
833
834 fn deserialize<D: Deserializer<'de>>(self, de: D) -> Result<Self::Output, D::Error> {
835 de.deserialize_product(self)
836 }
837}
838
839impl<'de> ProductVisitor<'de> for ProductTypeLayoutView<'_> {
840 type Output = ProductValue;
841
842 fn product_name(&self) -> Option<&str> {
843 None
844 }
845 fn product_len(&self) -> usize {
846 self.elements.len()
847 }
848
849 fn visit_seq_product<A: SeqProductAccess<'de>>(self, mut tup: A) -> Result<Self::Output, A::Error> {
850 let mut elems: Vec<AlgebraicValue> = Vec::with_capacity(self.product_len());
851 for (i, elem_ty) in self.elements.iter().enumerate() {
852 let Some(elem_val) = tup.next_element_seed(&elem_ty.ty)? else {
853 return Err(A::Error::invalid_product_length(i, &self));
854 };
855 elems.push(elem_val);
856 }
857 Ok(elems.into())
858 }
859
860 fn visit_named_product<A: NamedProductAccess<'de>>(self, _: A) -> Result<Self::Output, A::Error> {
861 unreachable!()
862 }
863}
864
865impl<'de> DeserializeSeed<'de> for &SumTypeLayout {
866 type Output = SumValue;
867
868 fn deserialize<D: Deserializer<'de>>(self, deserializer: D) -> Result<Self::Output, D::Error> {
869 deserializer.deserialize_sum(self)
870 }
871}
872
873impl<'de> SumVisitor<'de> for &SumTypeLayout {
874 type Output = SumValue;
875
876 fn sum_name(&self) -> Option<&str> {
877 None
878 }
879
880 fn is_option(&self) -> bool {
881 match &*self.variants {
882 [first, second]
883 if second.is_unit() && first.has_name(OPTION_SOME_TAG)
885 && second.has_name(OPTION_NONE_TAG) =>
886 {
887 true
888 }
889 _ => false,
890 }
891 }
892
893 fn visit_sum<A: SumAccess<'de>>(self, data: A) -> Result<Self::Output, A::Error> {
894 let (tag, data) = data.variant(self)?;
895 let variant_ty = &self.variants[tag as usize].ty;
897
898 let value = Box::new(data.deserialize_seed(variant_ty)?);
899 Ok(SumValue { tag, value })
900 }
901}
902
903impl VariantVisitor<'_> for &SumTypeLayout {
904 type Output = u8;
905
906 fn variant_names(&self) -> impl '_ + Iterator<Item = &str> {
907 self.variants.iter().filter_map(|v| v.name.as_deref())
909 }
910
911 fn visit_tag<E: Error>(self, tag: u8) -> Result<Self::Output, E> {
912 self.variants
914 .get(tag as usize)
915 .ok_or_else(|| E::unknown_variant_tag(tag, &self))?;
916
917 Ok(tag)
918 }
919
920 fn visit_name<E: Error>(self, name: &str) -> Result<Self::Output, E> {
921 self.variants
923 .iter()
924 .position(|var| var.has_name(name))
925 .map(|pos| pos as u8)
926 .ok_or_else(|| E::unknown_variant_name(name, &self))
927 }
928}
929
930#[cfg(test)]
931mod test {
932 use super::*;
933 use crate::proptest::generate_algebraic_type;
934 use itertools::Itertools as _;
935 use proptest::collection::vec;
936 use proptest::prelude::*;
937
938 #[test]
939 fn align_to_expected() {
940 fn assert_alignment(offset: usize, alignment: usize, expected: usize) {
941 assert_eq!(
942 align_to(offset, alignment),
943 expected,
944 "align_to({}, {}): expected {} but found {}",
945 offset,
946 alignment,
947 expected,
948 align_to(offset, alignment)
949 );
950 }
951
952 for align in [1usize, 2, 4, 8, 16, 32, 64] {
953 assert_alignment(0, align, 0);
954
955 for offset in 1..=align {
956 assert_alignment(offset, align, align);
957 }
958 for offset in (align + 1)..=(align * 2) {
959 assert_alignment(offset, align, align * 2);
960 }
961 }
962 }
963
964 fn assert_size_align(ty: AlgebraicType, size: usize, align: usize) {
965 let layout = AlgebraicTypeLayout::from(ty);
966 assert_eq!(layout.size(), size);
967 assert_eq!(layout.align(), align);
968 }
969
970 #[test]
971 fn known_product_expected_size_align() {
972 for (ty, size, align) in [
973 (AlgebraicType::product::<[AlgebraicType; 0]>([]), 0, 1),
974 (AlgebraicType::product([AlgebraicType::U8]), 1, 1),
975 (AlgebraicType::product([AlgebraicType::I8]), 1, 1),
976 (AlgebraicType::product([AlgebraicType::Bool]), 1, 1),
977 (AlgebraicType::product([AlgebraicType::U8, AlgebraicType::U8]), 2, 1),
978 (AlgebraicType::product([AlgebraicType::U8, AlgebraicType::U16]), 4, 2),
979 (
980 AlgebraicType::product([AlgebraicType::U8, AlgebraicType::U8, AlgebraicType::U16]),
981 4,
982 2,
983 ),
984 (
985 AlgebraicType::product([AlgebraicType::U8, AlgebraicType::U16, AlgebraicType::U8]),
986 6,
987 2,
988 ),
989 (
990 AlgebraicType::product([AlgebraicType::U16, AlgebraicType::U8, AlgebraicType::U8]),
991 4,
992 2,
993 ),
994 (AlgebraicType::product([AlgebraicType::U8, AlgebraicType::U32]), 8, 4),
995 (AlgebraicType::product([AlgebraicType::U8, AlgebraicType::U64]), 16, 8),
996 (AlgebraicType::product([AlgebraicType::U8, AlgebraicType::U128]), 32, 16),
997 (AlgebraicType::product([AlgebraicType::U16, AlgebraicType::U8]), 4, 2),
998 (AlgebraicType::product([AlgebraicType::U32, AlgebraicType::U8]), 8, 4),
999 (AlgebraicType::product([AlgebraicType::U64, AlgebraicType::U8]), 16, 8),
1000 (AlgebraicType::product([AlgebraicType::U128, AlgebraicType::U8]), 32, 16),
1001 (AlgebraicType::product([AlgebraicType::U16, AlgebraicType::U16]), 4, 2),
1002 (AlgebraicType::product([AlgebraicType::U32, AlgebraicType::U32]), 8, 4),
1003 (AlgebraicType::product([AlgebraicType::U64, AlgebraicType::U64]), 16, 8),
1004 (
1005 AlgebraicType::product([AlgebraicType::U128, AlgebraicType::U128]),
1006 32,
1007 16,
1008 ),
1009 (AlgebraicType::product([AlgebraicType::String]), 4, 2),
1010 (
1011 AlgebraicType::product([AlgebraicType::String, AlgebraicType::U16]),
1012 6,
1013 2,
1014 ),
1015 (AlgebraicType::product([AlgebraicType::I8, AlgebraicType::I8]), 2, 1),
1016 (AlgebraicType::product([AlgebraicType::I8, AlgebraicType::I16]), 4, 2),
1017 (AlgebraicType::product([AlgebraicType::I8, AlgebraicType::I32]), 8, 4),
1018 (AlgebraicType::product([AlgebraicType::I8, AlgebraicType::I64]), 16, 8),
1019 (AlgebraicType::product([AlgebraicType::I8, AlgebraicType::I128]), 32, 16),
1020 (AlgebraicType::product([AlgebraicType::I16, AlgebraicType::I8]), 4, 2),
1021 (AlgebraicType::product([AlgebraicType::I32, AlgebraicType::I8]), 8, 4),
1022 (AlgebraicType::product([AlgebraicType::I64, AlgebraicType::I8]), 16, 8),
1023 (AlgebraicType::product([AlgebraicType::I128, AlgebraicType::I8]), 32, 16),
1024 (AlgebraicType::product([AlgebraicType::I16, AlgebraicType::I16]), 4, 2),
1025 (AlgebraicType::product([AlgebraicType::I32, AlgebraicType::I32]), 8, 4),
1026 (AlgebraicType::product([AlgebraicType::I64, AlgebraicType::I64]), 16, 8),
1027 (
1028 AlgebraicType::product([AlgebraicType::I128, AlgebraicType::I128]),
1029 32,
1030 16,
1031 ),
1032 (
1033 AlgebraicType::product([AlgebraicType::I256, AlgebraicType::U256]),
1034 64,
1035 32,
1036 ),
1037 (
1038 AlgebraicType::product([AlgebraicType::String, AlgebraicType::I16]),
1039 6,
1040 2,
1041 ),
1042 ] {
1043 assert_size_align(ty, size, align);
1044 }
1045 }
1046
1047 #[test]
1048 fn known_sum_expected_size_align() {
1049 for (ty, size, align) in [
1050 (AlgebraicType::sum([AlgebraicType::U8]), 2, 1),
1051 (AlgebraicType::sum([AlgebraicType::I8]), 2, 1),
1052 (AlgebraicType::sum([AlgebraicType::Bool]), 2, 1),
1053 (AlgebraicType::sum([AlgebraicType::U8, AlgebraicType::U8]), 2, 1),
1054 (AlgebraicType::sum([AlgebraicType::U8, AlgebraicType::U16]), 4, 2),
1055 (AlgebraicType::sum([AlgebraicType::U8, AlgebraicType::U32]), 8, 4),
1056 (AlgebraicType::sum([AlgebraicType::U8, AlgebraicType::U64]), 16, 8),
1057 (AlgebraicType::sum([AlgebraicType::U8, AlgebraicType::U128]), 32, 16),
1058 (AlgebraicType::sum([AlgebraicType::U16, AlgebraicType::U8]), 4, 2),
1059 (AlgebraicType::sum([AlgebraicType::U32, AlgebraicType::U8]), 8, 4),
1060 (AlgebraicType::sum([AlgebraicType::U64, AlgebraicType::U8]), 16, 8),
1061 (AlgebraicType::sum([AlgebraicType::U128, AlgebraicType::U8]), 32, 16),
1062 (AlgebraicType::sum([AlgebraicType::U16, AlgebraicType::U16]), 4, 2),
1063 (AlgebraicType::sum([AlgebraicType::U32, AlgebraicType::U32]), 8, 4),
1064 (AlgebraicType::sum([AlgebraicType::U64, AlgebraicType::U64]), 16, 8),
1065 (AlgebraicType::sum([AlgebraicType::U128, AlgebraicType::U128]), 32, 16),
1066 (AlgebraicType::sum([AlgebraicType::String]), 6, 2),
1067 (AlgebraicType::sum([AlgebraicType::String, AlgebraicType::U16]), 6, 2),
1068 (AlgebraicType::sum([AlgebraicType::I8, AlgebraicType::I8]), 2, 1),
1069 (AlgebraicType::sum([AlgebraicType::I8, AlgebraicType::I16]), 4, 2),
1070 (AlgebraicType::sum([AlgebraicType::I8, AlgebraicType::I32]), 8, 4),
1071 (AlgebraicType::sum([AlgebraicType::I8, AlgebraicType::I64]), 16, 8),
1072 (AlgebraicType::sum([AlgebraicType::I8, AlgebraicType::I128]), 32, 16),
1073 (AlgebraicType::sum([AlgebraicType::I16, AlgebraicType::I8]), 4, 2),
1074 (AlgebraicType::sum([AlgebraicType::I32, AlgebraicType::I8]), 8, 4),
1075 (AlgebraicType::sum([AlgebraicType::I64, AlgebraicType::I8]), 16, 8),
1076 (AlgebraicType::sum([AlgebraicType::I128, AlgebraicType::I8]), 32, 16),
1077 (AlgebraicType::sum([AlgebraicType::I16, AlgebraicType::I16]), 4, 2),
1078 (AlgebraicType::sum([AlgebraicType::I32, AlgebraicType::I32]), 8, 4),
1079 (AlgebraicType::sum([AlgebraicType::I64, AlgebraicType::I64]), 16, 8),
1080 (AlgebraicType::sum([AlgebraicType::I128, AlgebraicType::I128]), 32, 16),
1081 (AlgebraicType::sum([AlgebraicType::I256, AlgebraicType::I128]), 64, 32),
1082 (AlgebraicType::sum([AlgebraicType::I256, AlgebraicType::U256]), 64, 32),
1083 (AlgebraicType::sum([AlgebraicType::String, AlgebraicType::I16]), 6, 2),
1084 ] {
1085 assert_size_align(ty, size, align);
1086 }
1087 }
1088
1089 proptest! {
1090 fn variant_order_irrelevant_for_layout(
1091 variants in vec(generate_algebraic_type(), 0..5)
1092 ) {
1093 use crate::SumTypeVariant;
1094
1095 let len = variants.len();
1096 let sum_permutations = variants
1098 .into_iter()
1099 .permutations(len)
1100 .map(|vars| vars.into_iter().map(SumTypeVariant::from).collect::<Box<[_]>>())
1101 .map(AlgebraicType::sum);
1102 let mut sum_layout_perms = sum_permutations
1104 .map(AlgebraicTypeLayout::from)
1105 .map(|ty| *ty.layout());
1106 prop_assert!(sum_layout_perms.all_equal());
1108 }
1109
1110 #[test]
1111 fn size_always_multiple_of_align(ty in generate_algebraic_type()) {
1112 let layout = AlgebraicTypeLayout::from(ty);
1113
1114 if layout.size() == 0 {
1115 assert_eq!(layout.align(), 1);
1116 } else {
1117 assert_eq!(layout.size() % layout.align(), 0);
1118 }
1119 }
1120 }
1121
1122 #[test]
1123 fn infinite_recursion_in_is_compatible_with_with_array_type() {
1124 let ty = AlgebraicTypeLayout::from(AlgebraicType::array(AlgebraicType::U64));
1125 assert!(ty.is_compatible_with(&ty));
1131 }
1132}