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 raw_identifier::RawIdentifier,
16 sum_type::{OPTION_NONE_TAG, OPTION_SOME_TAG},
17 u256, AlgebraicType, AlgebraicValue, ArrayType, ProductType, ProductTypeElement, ProductValue, SumType,
18 SumTypeVariant, SumValue, WithTypespace,
19};
20use core::ops::{Index, Mul};
21use core::{mem, ops::Deref};
22use derive_more::{Add, Sub};
23use enum_as_inner::EnumAsInner;
24use std::sync::Arc;
25
26pub const fn align_to(base: usize, required_alignment: usize) -> usize {
30 if required_alignment == 0 {
31 base
34 } else {
35 let misalignment = base % required_alignment;
36 if misalignment == 0 {
37 base
38 } else {
39 let padding = required_alignment - misalignment;
40 base + padding
41 }
42 }
43}
44
45#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Add, Sub)]
47pub struct Size(pub u16);
48
49#[cfg(feature = "memory-usage")]
50impl spacetimedb_memory_usage::MemoryUsage for Size {}
51
52impl_serialize!([] Size, (self, ser) => self.0.serialize(ser));
54impl_deserialize!([] Size, de => u16::deserialize(de).map(Size));
55
56impl Size {
57 #[inline]
59 #[allow(clippy::len_without_is_empty)]
60 pub const fn len(self) -> usize {
61 self.0 as usize
62 }
63}
64
65impl Mul<usize> for Size {
66 type Output = Size;
67
68 #[inline]
69 fn mul(self, rhs: usize) -> Self::Output {
70 Size((self.len() * rhs) as u16)
71 }
72}
73
74#[derive(Copy, Clone, Debug, PartialEq, Eq)]
85pub struct Layout {
86 pub size: u16,
88 pub align: u16,
90 pub fixed: bool,
93}
94
95#[cfg(feature = "memory-usage")]
96impl spacetimedb_memory_usage::MemoryUsage for Layout {}
97
98pub trait HasLayout {
102 fn layout(&self) -> &Layout;
104
105 fn size(&self) -> usize {
111 self.layout().size as usize
112 }
113
114 fn align(&self) -> usize {
120 self.layout().align as usize
121 }
122}
123
124#[derive(Debug, PartialEq, Eq, Clone, EnumAsInner)]
139pub enum AlgebraicTypeLayout {
140 Sum(SumTypeLayout),
142 Product(ProductTypeLayout),
144 Primitive(PrimitiveType),
146 VarLen(VarLenType),
148}
149
150#[cfg(feature = "memory-usage")]
151impl spacetimedb_memory_usage::MemoryUsage for AlgebraicTypeLayout {
152 fn heap_usage(&self) -> usize {
153 match self {
154 AlgebraicTypeLayout::Sum(x) => x.heap_usage(),
155 AlgebraicTypeLayout::Product(x) => x.heap_usage(),
156 AlgebraicTypeLayout::Primitive(x) => x.heap_usage(),
157 AlgebraicTypeLayout::VarLen(x) => x.heap_usage(),
158 }
159 }
160}
161
162impl HasLayout for AlgebraicTypeLayout {
163 fn layout(&self) -> &Layout {
164 match self {
165 Self::Sum(ty) => ty.layout(),
166 Self::Product(ty) => ty.layout(),
167 Self::Primitive(ty) => ty.layout(),
168 Self::VarLen(ty) => ty.layout(),
169 }
170 }
171}
172
173#[allow(non_upper_case_globals)]
174impl AlgebraicTypeLayout {
175 pub const Bool: Self = Self::Primitive(PrimitiveType::Bool);
176 pub const I8: Self = Self::Primitive(PrimitiveType::I8);
177 pub const U8: Self = Self::Primitive(PrimitiveType::U8);
178 pub const I16: Self = Self::Primitive(PrimitiveType::I16);
179 pub const U16: Self = Self::Primitive(PrimitiveType::U16);
180 pub const I32: Self = Self::Primitive(PrimitiveType::I32);
181 pub const U32: Self = Self::Primitive(PrimitiveType::U32);
182 pub const I64: Self = Self::Primitive(PrimitiveType::I64);
183 pub const U64: Self = Self::Primitive(PrimitiveType::U64);
184 pub const I128: Self = Self::Primitive(PrimitiveType::I128);
185 pub const U128: Self = Self::Primitive(PrimitiveType::U128);
186 pub const I256: Self = Self::Primitive(PrimitiveType::I256);
187 pub const U256: Self = Self::Primitive(PrimitiveType::U256);
188 pub const F32: Self = Self::Primitive(PrimitiveType::F32);
189 pub const F64: Self = Self::Primitive(PrimitiveType::F64);
190 pub const String: Self = Self::VarLen(VarLenType::String);
191
192 fn ensure_compatible_with(&self, new: &Self) -> Result<(), Box<IncompatibleTypeLayoutError>> {
196 match (self, new) {
197 (Self::Sum(old), Self::Sum(new)) => old.ensure_compatible_with(new),
198 (Self::Product(old), Self::Product(new)) => old.view().ensure_compatible_with(new.view()),
199 (Self::Primitive(old), Self::Primitive(new)) => {
200 if old == new {
201 Ok(())
202 } else {
203 Err(Box::new(IncompatibleTypeLayoutError::DifferentPrimitiveTypes {
204 old: old.algebraic_type(),
205 new: new.algebraic_type(),
206 }))
207 }
208 }
209 (Self::VarLen(VarLenType::Array(old)), Self::VarLen(VarLenType::Array(new))) => {
210 let old = AlgebraicTypeLayout::from(old.elem_ty.deref().clone());
215 let new = AlgebraicTypeLayout::from(new.elem_ty.deref().clone());
216 old.ensure_compatible_with(&new).map_err(|err| {
217 Box::new(IncompatibleTypeLayoutError::IncompatibleArrayElements {
218 old: old.algebraic_type(),
219 new: new.algebraic_type(),
220 err,
221 })
222 })
223 }
224 (Self::VarLen(VarLenType::String), Self::VarLen(VarLenType::String)) => Ok(()),
225 _ => Err(Box::new(IncompatibleTypeLayoutError::DifferentKind {
226 old: self.algebraic_type(),
227 new: new.algebraic_type(),
228 })),
229 }
230 }
231}
232
233type Collection<T> = Box<[T]>;
235
236pub const MIN_ROW_SIZE: Size = Size(2);
238
239pub const MIN_ROW_ALIGN: Size = Size(2);
241
242pub const fn row_size_for_bytes(required_bytes: usize) -> Size {
245 if required_bytes > MIN_ROW_SIZE.len() {
247 Size(align_to(required_bytes, MIN_ROW_ALIGN.len()) as u16)
248 } else {
249 MIN_ROW_SIZE
250 }
251}
252
253pub const fn row_size_for_type<T>() -> Size {
256 row_size_for_bytes(mem::size_of::<T>())
257}
258
259#[derive(Debug, PartialEq, Eq, Clone)]
263pub struct RowTypeLayout {
264 pub layout: Layout,
266 pub elements: Arc<[ProductTypeElementLayout]>,
274}
275
276#[cfg(feature = "memory-usage")]
277impl spacetimedb_memory_usage::MemoryUsage for RowTypeLayout {
278 fn heap_usage(&self) -> usize {
279 let Self { layout, elements } = self;
280 layout.heap_usage() + elements.heap_usage()
281 }
282}
283
284impl RowTypeLayout {
285 pub fn product(&self) -> ProductTypeLayoutView<'_> {
287 let elements = &*self.elements;
288 let layout = self.layout;
289 ProductTypeLayoutView { layout, elements }
290 }
291
292 pub fn size(&self) -> Size {
294 Size(self.product().size() as u16)
295 }
296
297 pub fn ensure_compatible_with(&self, new: &RowTypeLayout) -> Result<(), Box<IncompatibleTypeLayoutError>> {
302 if self.layout != new.layout {
303 return Err(Box::new(IncompatibleTypeLayoutError::LayoutsNotEqual {
304 old: self.layout,
305 new: new.layout,
306 }));
307 }
308 self.product().ensure_compatible_with(new.product())
309 }
310}
311
312#[derive(thiserror::Error, Debug, Clone)]
319pub enum IncompatibleTypeLayoutError {
320 #[error("Layout of new type {new:?} does not match layout of old type {old:?}")]
321 LayoutsNotEqual { old: Layout, new: Layout },
322 #[error("Product type elements at index {index} are incompatible: {err}")]
323 IncompatibleProductElements {
324 index: usize,
325 err: Box<IncompatibleTypeLayoutError>,
326 },
327 #[error("Sum type elements in variant {index} are incompatible: {err}")]
328 IncompatibleSumVariants {
329 index: usize,
330 err: Box<IncompatibleTypeLayoutError>,
331 },
332 #[error("New product type {new:?} has {} elements while old product type {old:?} has {} elements", .new.elements.len(), .old.elements.len())]
333 DifferentElementCounts { old: ProductType, new: ProductType },
334 #[error("New sum type {new:?} has {} variants, which is fewer than old sum type {old:?} with {} variants", .new.variants.len(), .old.variants.len())]
335 RemovedVariants { old: SumType, new: SumType },
336 #[error("New primitive type {new:?} is not the same as old primitive type {old:?}")]
337 DifferentPrimitiveTypes { old: AlgebraicType, new: AlgebraicType },
338 #[error("New array element type {new:?} is incompatible with old array element type {old:?}: {err}")]
339 IncompatibleArrayElements {
340 new: AlgebraicType,
341 old: AlgebraicType,
342 err: Box<IncompatibleTypeLayoutError>,
343 },
344 #[error("New type {new:?} is not the same kind (sum, product, primitive, etc.) as old type {old:?}")]
345 DifferentKind { old: AlgebraicType, new: AlgebraicType },
346}
347
348pub enum IncompatibleTypeReason {
349 LayoutsNotEqual,
350}
351
352impl HasLayout for RowTypeLayout {
353 fn layout(&self) -> &Layout {
354 &self.layout
355 }
356}
357
358impl Index<usize> for RowTypeLayout {
359 type Output = AlgebraicTypeLayout;
360 fn index(&self, index: usize) -> &Self::Output {
361 &self.elements[index].ty
362 }
363}
364
365#[derive(Debug, PartialEq, Eq, Copy, Clone)]
367pub struct ProductTypeLayoutView<'a> {
368 pub layout: Layout,
370 pub elements: &'a [ProductTypeElementLayout],
372}
373
374impl HasLayout for ProductTypeLayoutView<'_> {
375 fn layout(&self) -> &Layout {
376 &self.layout
377 }
378}
379
380impl ProductTypeLayoutView<'_> {
381 fn ensure_compatible_with(self, new: Self) -> Result<(), Box<IncompatibleTypeLayoutError>> {
390 if self.elements.len() != new.elements.len() {
391 return Err(Box::new(IncompatibleTypeLayoutError::DifferentElementCounts {
392 old: self.product_type(),
393 new: new.product_type(),
394 }));
395 }
396 for (index, (old, new)) in self.elements.iter().zip(new.elements.iter()).enumerate() {
397 if let Err(err) = old.ty.ensure_compatible_with(&new.ty) {
398 return Err(Box::new(IncompatibleTypeLayoutError::IncompatibleProductElements {
399 index,
400 err,
401 }));
402 }
403 }
404 Ok(())
405 }
406}
407
408#[derive(Debug, PartialEq, Eq, Clone)]
410pub struct ProductTypeLayout {
411 pub layout: Layout,
413 pub elements: Collection<ProductTypeElementLayout>,
415}
416
417impl ProductTypeLayout {
418 pub fn view(&self) -> ProductTypeLayoutView<'_> {
420 let elements = &*self.elements;
421 let layout = self.layout;
422 ProductTypeLayoutView { layout, elements }
423 }
424}
425
426#[cfg(feature = "memory-usage")]
427impl spacetimedb_memory_usage::MemoryUsage for ProductTypeLayout {
428 fn heap_usage(&self) -> usize {
429 let Self { layout, elements } = self;
430 layout.heap_usage() + elements.heap_usage()
431 }
432}
433
434impl HasLayout for ProductTypeLayout {
435 fn layout(&self) -> &Layout {
436 &self.layout
437 }
438}
439
440#[derive(Debug, PartialEq, Eq, Clone)]
442pub struct ProductTypeElementLayout {
443 pub offset: u16,
445
446 pub ty: AlgebraicTypeLayout,
448
449 pub name: Option<RawIdentifier>,
454}
455
456#[cfg(feature = "memory-usage")]
457impl spacetimedb_memory_usage::MemoryUsage for ProductTypeElementLayout {
458 fn heap_usage(&self) -> usize {
459 let Self { offset, ty, name } = self;
460 offset.heap_usage() + ty.heap_usage() + name.heap_usage()
461 }
462}
463
464#[derive(Debug, PartialEq, Eq, Clone)]
466pub struct SumTypeLayout {
467 pub layout: Layout,
469 pub variants: Collection<SumTypeVariantLayout>,
471 pub payload_offset: u16,
474}
475
476#[cfg(feature = "memory-usage")]
477impl spacetimedb_memory_usage::MemoryUsage for SumTypeLayout {
478 fn heap_usage(&self) -> usize {
479 let Self {
480 layout,
481 variants,
482 payload_offset,
483 } = self;
484 layout.heap_usage() + variants.heap_usage() + payload_offset.heap_usage()
485 }
486}
487
488impl HasLayout for SumTypeLayout {
489 fn layout(&self) -> &Layout {
490 &self.layout
491 }
492}
493
494#[derive(Debug, PartialEq, Eq, Clone)]
496pub struct SumTypeVariantLayout {
497 pub ty: AlgebraicTypeLayout,
499
500 pub name: Option<RawIdentifier>,
505}
506
507#[cfg(feature = "memory-usage")]
508impl spacetimedb_memory_usage::MemoryUsage for SumTypeVariantLayout {
509 fn heap_usage(&self) -> usize {
510 let Self { ty, name } = self;
511 ty.heap_usage() + name.heap_usage()
512 }
513}
514
515#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
518pub enum PrimitiveType {
519 Bool,
520 I8,
521 U8,
522 I16,
523 U16,
524 I32,
525 U32,
526 I64,
527 U64,
528 I128,
529 U128,
530 I256,
531 U256,
532 F32,
533 F64,
534}
535
536#[cfg(feature = "memory-usage")]
537impl spacetimedb_memory_usage::MemoryUsage for PrimitiveType {}
538
539impl PrimitiveType {
540 pub fn algebraic_type(&self) -> AlgebraicType {
541 match self {
542 PrimitiveType::Bool => AlgebraicType::Bool,
543 PrimitiveType::I8 => AlgebraicType::I8,
544 PrimitiveType::U8 => AlgebraicType::U8,
545 PrimitiveType::I16 => AlgebraicType::I16,
546 PrimitiveType::U16 => AlgebraicType::U16,
547 PrimitiveType::I32 => AlgebraicType::I32,
548 PrimitiveType::U32 => AlgebraicType::U32,
549 PrimitiveType::I64 => AlgebraicType::I64,
550 PrimitiveType::U64 => AlgebraicType::U64,
551 PrimitiveType::I128 => AlgebraicType::I128,
552 PrimitiveType::U128 => AlgebraicType::U128,
553 PrimitiveType::I256 => AlgebraicType::I256,
554 PrimitiveType::U256 => AlgebraicType::U256,
555 PrimitiveType::F32 => AlgebraicType::F32,
556 PrimitiveType::F64 => AlgebraicType::F64,
557 }
558 }
559}
560
561impl HasLayout for PrimitiveType {
562 fn layout(&self) -> &'static Layout {
563 match self {
564 Self::Bool | Self::I8 | Self::U8 => &Layout {
565 size: 1,
566 align: 1,
567 fixed: true,
568 },
569 Self::I16 | Self::U16 => &Layout {
570 size: 2,
571 align: 2,
572 fixed: true,
573 },
574 Self::I32 | Self::U32 | Self::F32 => &Layout {
575 size: 4,
576 align: 4,
577 fixed: true,
578 },
579 Self::I64 | Self::U64 | Self::F64 => &Layout {
580 size: 8,
581 align: 8,
582 fixed: true,
583 },
584 Self::I128 | Self::U128 => &Layout {
585 size: 16,
586 align: 16,
587 fixed: true,
588 },
589 Self::I256 | Self::U256 => &Layout {
590 size: 32,
591 align: 32,
592 fixed: true,
593 },
594 }
595 }
596}
597
598#[derive(Debug, PartialEq, Eq, Clone)]
601pub enum VarLenType {
602 String,
604 Array(ArrayType),
609}
610
611#[cfg(feature = "memory-usage")]
612impl spacetimedb_memory_usage::MemoryUsage for VarLenType {
613 fn heap_usage(&self) -> usize {
614 match self {
615 VarLenType::String => 0,
616 VarLenType::Array(x) => x.heap_usage(),
617 }
618 }
619}
620
621pub const VAR_LEN_REF_LAYOUT: Layout = Layout {
623 size: 4,
624 align: 2,
625 fixed: false,
626};
627
628impl HasLayout for VarLenType {
629 fn layout(&self) -> &Layout {
630 &VAR_LEN_REF_LAYOUT
631 }
632}
633
634impl From<AlgebraicType> for AlgebraicTypeLayout {
637 fn from(ty: AlgebraicType) -> Self {
638 match ty {
639 AlgebraicType::Sum(sum) => AlgebraicTypeLayout::Sum(sum.into()),
640 AlgebraicType::Product(prod) => AlgebraicTypeLayout::Product(prod.into()),
641
642 AlgebraicType::String => AlgebraicTypeLayout::VarLen(VarLenType::String),
643 AlgebraicType::Array(array) => AlgebraicTypeLayout::VarLen(VarLenType::Array(array)),
644
645 AlgebraicType::Bool => AlgebraicTypeLayout::Bool,
646 AlgebraicType::I8 => AlgebraicTypeLayout::I8,
647 AlgebraicType::U8 => AlgebraicTypeLayout::U8,
648 AlgebraicType::I16 => AlgebraicTypeLayout::I16,
649 AlgebraicType::U16 => AlgebraicTypeLayout::U16,
650 AlgebraicType::I32 => AlgebraicTypeLayout::I32,
651 AlgebraicType::U32 => AlgebraicTypeLayout::U32,
652 AlgebraicType::I64 => AlgebraicTypeLayout::I64,
653 AlgebraicType::U64 => AlgebraicTypeLayout::U64,
654 AlgebraicType::I128 => AlgebraicTypeLayout::I128,
655 AlgebraicType::U128 => AlgebraicTypeLayout::U128,
656 AlgebraicType::I256 => AlgebraicTypeLayout::I256,
657 AlgebraicType::U256 => AlgebraicTypeLayout::U256,
658 AlgebraicType::F32 => AlgebraicTypeLayout::F32,
659 AlgebraicType::F64 => AlgebraicTypeLayout::F64,
660
661 AlgebraicType::Ref(_) => todo!("Refs unsupported without typespace context"),
662 }
663 }
664}
665
666fn product_type_layout<C: FromIterator<ProductTypeElementLayout>>(ty: ProductType) -> (C, Layout) {
668 let mut current_offset: usize = 0;
669
670 let mut max_child_align = 1;
673
674 let mut fixed = true;
675 let elements = Vec::from(ty.elements)
676 .into_iter()
677 .map(|elem| {
678 let layout_type: AlgebraicTypeLayout = elem.algebraic_type.into();
679 fixed &= layout_type.layout().fixed;
680 let this_offset = align_to(current_offset, layout_type.align());
681 max_child_align = usize::max(max_child_align, layout_type.align());
682
683 current_offset = this_offset + layout_type.size();
684
685 ProductTypeElementLayout {
686 offset: this_offset as u16,
687 name: elem.name,
688 ty: layout_type,
689 }
690 })
691 .collect();
692
693 let layout = Layout {
694 align: max_child_align as u16,
695 size: align_to(current_offset, max_child_align) as u16,
696 fixed,
697 };
698
699 (elements, layout)
700}
701
702impl From<ProductType> for RowTypeLayout {
703 fn from(ty: ProductType) -> Self {
704 let (elements, mut layout) = product_type_layout(ty);
705 layout.size = row_size_for_bytes(layout.size as usize).0;
706 Self { layout, elements }
707 }
708}
709
710impl From<ProductType> for ProductTypeLayout {
711 fn from(ty: ProductType) -> Self {
712 let (elements, layout) = product_type_layout(ty);
713 Self { layout, elements }
714 }
715}
716
717impl From<SumType> for SumTypeLayout {
718 fn from(ty: SumType) -> Self {
719 let mut max_child_size = 0;
720
721 let mut max_child_align = 0;
724
725 let mut fixed = true;
726 let variants = Vec::from(ty.variants)
727 .into_iter()
728 .map(|variant| {
729 let layout_type: AlgebraicTypeLayout = variant.algebraic_type.into();
730 fixed &= layout_type.layout().fixed;
731
732 max_child_align = usize::max(max_child_align, layout_type.align());
733 max_child_size = usize::max(max_child_size, layout_type.size());
734
735 SumTypeVariantLayout {
736 ty: layout_type,
737 name: variant.name,
738 }
739 })
740 .collect::<Vec<_>>()
741 .into();
742
743 let align = u16::max(max_child_align as u16, 1);
745
746 let payload_size = align_to(max_child_size, max_child_align);
754
755 let size = align + payload_size as u16;
757 let payload_offset = align;
758 let layout = Layout { align, size, fixed };
759 Self {
760 layout,
761 payload_offset,
762 variants,
763 }
764 }
765}
766
767impl AlgebraicTypeLayout {
771 pub fn algebraic_type(&self) -> AlgebraicType {
778 match self {
779 Self::Primitive(prim) => prim.algebraic_type(),
780 Self::VarLen(VarLenType::String) => AlgebraicType::String,
781 Self::VarLen(VarLenType::Array(array)) => AlgebraicType::Array(array.clone()),
782 Self::Product(prod) => AlgebraicType::Product(prod.view().product_type()),
783 Self::Sum(sum) => AlgebraicType::Sum(sum.sum_type()),
784 }
785 }
786}
787
788impl ProductTypeLayoutView<'_> {
789 pub fn product_type(&self) -> ProductType {
790 ProductType {
791 elements: self
792 .elements
793 .iter()
794 .map(ProductTypeElementLayout::product_type_element)
795 .collect(),
796 }
797 }
798
799 pub fn algebraic_type(&self) -> AlgebraicType {
806 AlgebraicType::Product(self.product_type())
807 }
808}
809
810impl ProductTypeElementLayout {
811 fn product_type_element(&self) -> ProductTypeElement {
812 ProductTypeElement {
813 algebraic_type: self.ty.algebraic_type(),
814 name: self.name.clone(),
815 }
816 }
817}
818
819impl SumTypeLayout {
820 fn sum_type(&self) -> SumType {
821 SumType {
822 variants: self
823 .variants
824 .iter()
825 .map(SumTypeVariantLayout::sum_type_variant)
826 .collect(),
827 }
828 }
829
830 fn ensure_compatible_with(&self, new: &SumTypeLayout) -> Result<(), Box<IncompatibleTypeLayoutError>> {
841 if self.variants.len() > new.variants.len() {
842 return Err(Box::new(IncompatibleTypeLayoutError::RemovedVariants {
843 old: self.sum_type(),
844 new: new.sum_type(),
845 }));
846 }
847 for (index, (old, new)) in self.variants.iter().zip(self.variants.iter()).enumerate() {
848 if let Err(err) = old.ty.ensure_compatible_with(&new.ty) {
849 return Err(Box::new(IncompatibleTypeLayoutError::IncompatibleSumVariants {
850 index,
851 err,
852 }));
853 }
854 }
855 Ok(())
856 }
857}
858
859impl SumTypeVariantLayout {
860 fn sum_type_variant(&self) -> SumTypeVariant {
861 SumTypeVariant {
862 algebraic_type: self.ty.algebraic_type(),
863 name: self.name.clone(),
864 }
865 }
866
867 pub fn has_name(&self, name: &str) -> bool {
869 self.name.as_deref() == Some(name)
870 }
871
872 pub fn is_unit(&self) -> bool {
874 self.ty.as_product().is_some_and(|ty| ty.elements.is_empty())
875 }
876}
877
878impl SumTypeLayout {
881 pub fn offset_of_variant_data(&self, _variant_tag: u8) -> usize {
882 self.payload_offset as usize
890 }
891
892 pub fn offset_of_tag(&self) -> usize {
893 0
900 }
901}
902
903impl<'de> DeserializeSeed<'de> for &AlgebraicTypeLayout {
904 type Output = AlgebraicValue;
905
906 fn deserialize<D: Deserializer<'de>>(self, de: D) -> Result<Self::Output, D::Error> {
907 match self {
908 AlgebraicTypeLayout::Sum(ty) => ty.deserialize(de).map(Into::into),
909 AlgebraicTypeLayout::Product(ty) => ty.view().deserialize(de).map(Into::into),
910 AlgebraicTypeLayout::Primitive(PrimitiveType::Bool) => bool::deserialize(de).map(Into::into),
911 AlgebraicTypeLayout::Primitive(PrimitiveType::I8) => i8::deserialize(de).map(Into::into),
912 AlgebraicTypeLayout::Primitive(PrimitiveType::U8) => u8::deserialize(de).map(Into::into),
913 AlgebraicTypeLayout::Primitive(PrimitiveType::I16) => i16::deserialize(de).map(Into::into),
914 AlgebraicTypeLayout::Primitive(PrimitiveType::U16) => u16::deserialize(de).map(Into::into),
915 AlgebraicTypeLayout::Primitive(PrimitiveType::I32) => i32::deserialize(de).map(Into::into),
916 AlgebraicTypeLayout::Primitive(PrimitiveType::U32) => u32::deserialize(de).map(Into::into),
917 AlgebraicTypeLayout::Primitive(PrimitiveType::I64) => i64::deserialize(de).map(Into::into),
918 AlgebraicTypeLayout::Primitive(PrimitiveType::U64) => u64::deserialize(de).map(Into::into),
919 AlgebraicTypeLayout::Primitive(PrimitiveType::I128) => i128::deserialize(de).map(Into::into),
920 AlgebraicTypeLayout::Primitive(PrimitiveType::U128) => u128::deserialize(de).map(Into::into),
921 AlgebraicTypeLayout::Primitive(PrimitiveType::I256) => i256::deserialize(de).map(Into::into),
922 AlgebraicTypeLayout::Primitive(PrimitiveType::U256) => u256::deserialize(de).map(Into::into),
923 AlgebraicTypeLayout::Primitive(PrimitiveType::F32) => f32::deserialize(de).map(Into::into),
924 AlgebraicTypeLayout::Primitive(PrimitiveType::F64) => f64::deserialize(de).map(Into::into),
925 AlgebraicTypeLayout::VarLen(VarLenType::Array(ty)) => {
926 WithTypespace::empty(ty).deserialize(de).map(AlgebraicValue::Array)
927 }
928 AlgebraicTypeLayout::VarLen(VarLenType::String) => <Box<str>>::deserialize(de).map(Into::into),
929 }
930 }
931}
932
933impl<'de> DeserializeSeed<'de> for ProductTypeLayoutView<'_> {
934 type Output = ProductValue;
935
936 fn deserialize<D: Deserializer<'de>>(self, de: D) -> Result<Self::Output, D::Error> {
937 de.deserialize_product(self)
938 }
939}
940
941impl<'de> ProductVisitor<'de> for ProductTypeLayoutView<'_> {
942 type Output = ProductValue;
943
944 fn product_name(&self) -> Option<&str> {
945 None
946 }
947 fn product_len(&self) -> usize {
948 self.elements.len()
949 }
950
951 fn visit_seq_product<A: SeqProductAccess<'de>>(self, mut tup: A) -> Result<Self::Output, A::Error> {
952 let mut elems: Vec<AlgebraicValue> = Vec::with_capacity(self.product_len());
953 for (i, elem_ty) in self.elements.iter().enumerate() {
954 let Some(elem_val) = tup.next_element_seed(&elem_ty.ty)? else {
955 return Err(A::Error::invalid_product_length(i, &self));
956 };
957 elems.push(elem_val);
958 }
959 Ok(elems.into())
960 }
961
962 fn visit_named_product<A: NamedProductAccess<'de>>(self, _: A) -> Result<Self::Output, A::Error> {
963 unreachable!()
964 }
965}
966
967impl<'de> DeserializeSeed<'de> for &SumTypeLayout {
968 type Output = SumValue;
969
970 fn deserialize<D: Deserializer<'de>>(self, deserializer: D) -> Result<Self::Output, D::Error> {
971 deserializer.deserialize_sum(self)
972 }
973}
974
975impl<'de> SumVisitor<'de> for &SumTypeLayout {
976 type Output = SumValue;
977
978 fn sum_name(&self) -> Option<&str> {
979 None
980 }
981
982 fn is_option(&self) -> bool {
983 match &*self.variants {
984 [first, second]
985 if second.is_unit() && first.has_name(OPTION_SOME_TAG)
987 && second.has_name(OPTION_NONE_TAG) =>
988 {
989 true
990 }
991 _ => false,
992 }
993 }
994
995 fn visit_sum<A: SumAccess<'de>>(self, data: A) -> Result<Self::Output, A::Error> {
996 let (tag, data) = data.variant(self)?;
997 let variant_ty = &self.variants[tag as usize].ty;
999
1000 let value = data.deserialize_seed(variant_ty)?;
1001 Ok(SumValue::new(tag, value))
1002 }
1003}
1004
1005impl VariantVisitor<'_> for &SumTypeLayout {
1006 type Output = u8;
1007
1008 fn variant_names(&self) -> impl '_ + Iterator<Item = &str> {
1009 self.variants.iter().filter_map(|v| v.name.as_deref())
1011 }
1012
1013 fn visit_tag<E: Error>(self, tag: u8) -> Result<Self::Output, E> {
1014 self.variants
1016 .get(tag as usize)
1017 .ok_or_else(|| E::unknown_variant_tag(tag, &self))?;
1018
1019 Ok(tag)
1020 }
1021
1022 fn visit_name<E: Error>(self, name: &str) -> Result<Self::Output, E> {
1023 self.variants
1025 .iter()
1026 .position(|var| var.has_name(name))
1027 .map(|pos| pos as u8)
1028 .ok_or_else(|| E::unknown_variant_name(name, &self))
1029 }
1030}
1031
1032#[cfg(test)]
1033mod test {
1034 use super::*;
1035 use crate::proptest::generate_algebraic_type;
1036 use itertools::Itertools as _;
1037 use proptest::collection::vec;
1038 use proptest::prelude::*;
1039
1040 #[test]
1041 fn align_to_expected() {
1042 fn assert_alignment(offset: usize, alignment: usize, expected: usize) {
1043 assert_eq!(
1044 align_to(offset, alignment),
1045 expected,
1046 "align_to({}, {}): expected {} but found {}",
1047 offset,
1048 alignment,
1049 expected,
1050 align_to(offset, alignment)
1051 );
1052 }
1053
1054 for align in [1usize, 2, 4, 8, 16, 32, 64] {
1055 assert_alignment(0, align, 0);
1056
1057 for offset in 1..=align {
1058 assert_alignment(offset, align, align);
1059 }
1060 for offset in (align + 1)..=(align * 2) {
1061 assert_alignment(offset, align, align * 2);
1062 }
1063 }
1064 }
1065
1066 fn assert_size_align(ty: AlgebraicType, size: usize, align: usize) {
1067 let layout = AlgebraicTypeLayout::from(ty);
1068 assert_eq!(layout.size(), size);
1069 assert_eq!(layout.align(), align);
1070 }
1071
1072 #[test]
1073 fn known_product_expected_size_align() {
1074 for (ty, size, align) in [
1075 (AlgebraicType::product::<[AlgebraicType; 0]>([]), 0, 1),
1076 (AlgebraicType::product([AlgebraicType::U8]), 1, 1),
1077 (AlgebraicType::product([AlgebraicType::I8]), 1, 1),
1078 (AlgebraicType::product([AlgebraicType::Bool]), 1, 1),
1079 (AlgebraicType::product([AlgebraicType::U8, AlgebraicType::U8]), 2, 1),
1080 (AlgebraicType::product([AlgebraicType::U8, AlgebraicType::U16]), 4, 2),
1081 (
1082 AlgebraicType::product([AlgebraicType::U8, AlgebraicType::U8, AlgebraicType::U16]),
1083 4,
1084 2,
1085 ),
1086 (
1087 AlgebraicType::product([AlgebraicType::U8, AlgebraicType::U16, AlgebraicType::U8]),
1088 6,
1089 2,
1090 ),
1091 (
1092 AlgebraicType::product([AlgebraicType::U16, AlgebraicType::U8, AlgebraicType::U8]),
1093 4,
1094 2,
1095 ),
1096 (AlgebraicType::product([AlgebraicType::U8, AlgebraicType::U32]), 8, 4),
1097 (AlgebraicType::product([AlgebraicType::U8, AlgebraicType::U64]), 16, 8),
1098 (AlgebraicType::product([AlgebraicType::U8, AlgebraicType::U128]), 32, 16),
1099 (AlgebraicType::product([AlgebraicType::U16, AlgebraicType::U8]), 4, 2),
1100 (AlgebraicType::product([AlgebraicType::U32, AlgebraicType::U8]), 8, 4),
1101 (AlgebraicType::product([AlgebraicType::U64, AlgebraicType::U8]), 16, 8),
1102 (AlgebraicType::product([AlgebraicType::U128, AlgebraicType::U8]), 32, 16),
1103 (AlgebraicType::product([AlgebraicType::U16, AlgebraicType::U16]), 4, 2),
1104 (AlgebraicType::product([AlgebraicType::U32, AlgebraicType::U32]), 8, 4),
1105 (AlgebraicType::product([AlgebraicType::U64, AlgebraicType::U64]), 16, 8),
1106 (
1107 AlgebraicType::product([AlgebraicType::U128, AlgebraicType::U128]),
1108 32,
1109 16,
1110 ),
1111 (AlgebraicType::product([AlgebraicType::String]), 4, 2),
1112 (
1113 AlgebraicType::product([AlgebraicType::String, AlgebraicType::U16]),
1114 6,
1115 2,
1116 ),
1117 (AlgebraicType::product([AlgebraicType::I8, AlgebraicType::I8]), 2, 1),
1118 (AlgebraicType::product([AlgebraicType::I8, AlgebraicType::I16]), 4, 2),
1119 (AlgebraicType::product([AlgebraicType::I8, AlgebraicType::I32]), 8, 4),
1120 (AlgebraicType::product([AlgebraicType::I8, AlgebraicType::I64]), 16, 8),
1121 (AlgebraicType::product([AlgebraicType::I8, AlgebraicType::I128]), 32, 16),
1122 (AlgebraicType::product([AlgebraicType::I16, AlgebraicType::I8]), 4, 2),
1123 (AlgebraicType::product([AlgebraicType::I32, AlgebraicType::I8]), 8, 4),
1124 (AlgebraicType::product([AlgebraicType::I64, AlgebraicType::I8]), 16, 8),
1125 (AlgebraicType::product([AlgebraicType::I128, AlgebraicType::I8]), 32, 16),
1126 (AlgebraicType::product([AlgebraicType::I16, AlgebraicType::I16]), 4, 2),
1127 (AlgebraicType::product([AlgebraicType::I32, AlgebraicType::I32]), 8, 4),
1128 (AlgebraicType::product([AlgebraicType::I64, AlgebraicType::I64]), 16, 8),
1129 (
1130 AlgebraicType::product([AlgebraicType::I128, AlgebraicType::I128]),
1131 32,
1132 16,
1133 ),
1134 (
1135 AlgebraicType::product([AlgebraicType::I256, AlgebraicType::U256]),
1136 64,
1137 32,
1138 ),
1139 (
1140 AlgebraicType::product([AlgebraicType::String, AlgebraicType::I16]),
1141 6,
1142 2,
1143 ),
1144 ] {
1145 assert_size_align(ty, size, align);
1146 }
1147 }
1148
1149 #[test]
1150 fn known_sum_expected_size_align() {
1151 for (ty, size, align) in [
1152 (AlgebraicType::sum([AlgebraicType::U8]), 2, 1),
1153 (AlgebraicType::sum([AlgebraicType::I8]), 2, 1),
1154 (AlgebraicType::sum([AlgebraicType::Bool]), 2, 1),
1155 (AlgebraicType::sum([AlgebraicType::U8, AlgebraicType::U8]), 2, 1),
1156 (AlgebraicType::sum([AlgebraicType::U8, AlgebraicType::U16]), 4, 2),
1157 (AlgebraicType::sum([AlgebraicType::U8, AlgebraicType::U32]), 8, 4),
1158 (AlgebraicType::sum([AlgebraicType::U8, AlgebraicType::U64]), 16, 8),
1159 (AlgebraicType::sum([AlgebraicType::U8, AlgebraicType::U128]), 32, 16),
1160 (AlgebraicType::sum([AlgebraicType::U16, AlgebraicType::U8]), 4, 2),
1161 (AlgebraicType::sum([AlgebraicType::U32, AlgebraicType::U8]), 8, 4),
1162 (AlgebraicType::sum([AlgebraicType::U64, AlgebraicType::U8]), 16, 8),
1163 (AlgebraicType::sum([AlgebraicType::U128, AlgebraicType::U8]), 32, 16),
1164 (AlgebraicType::sum([AlgebraicType::U16, AlgebraicType::U16]), 4, 2),
1165 (AlgebraicType::sum([AlgebraicType::U32, AlgebraicType::U32]), 8, 4),
1166 (AlgebraicType::sum([AlgebraicType::U64, AlgebraicType::U64]), 16, 8),
1167 (AlgebraicType::sum([AlgebraicType::U128, AlgebraicType::U128]), 32, 16),
1168 (AlgebraicType::sum([AlgebraicType::String]), 6, 2),
1169 (AlgebraicType::sum([AlgebraicType::String, AlgebraicType::U16]), 6, 2),
1170 (AlgebraicType::sum([AlgebraicType::I8, AlgebraicType::I8]), 2, 1),
1171 (AlgebraicType::sum([AlgebraicType::I8, AlgebraicType::I16]), 4, 2),
1172 (AlgebraicType::sum([AlgebraicType::I8, AlgebraicType::I32]), 8, 4),
1173 (AlgebraicType::sum([AlgebraicType::I8, AlgebraicType::I64]), 16, 8),
1174 (AlgebraicType::sum([AlgebraicType::I8, AlgebraicType::I128]), 32, 16),
1175 (AlgebraicType::sum([AlgebraicType::I16, AlgebraicType::I8]), 4, 2),
1176 (AlgebraicType::sum([AlgebraicType::I32, AlgebraicType::I8]), 8, 4),
1177 (AlgebraicType::sum([AlgebraicType::I64, AlgebraicType::I8]), 16, 8),
1178 (AlgebraicType::sum([AlgebraicType::I128, AlgebraicType::I8]), 32, 16),
1179 (AlgebraicType::sum([AlgebraicType::I16, AlgebraicType::I16]), 4, 2),
1180 (AlgebraicType::sum([AlgebraicType::I32, AlgebraicType::I32]), 8, 4),
1181 (AlgebraicType::sum([AlgebraicType::I64, AlgebraicType::I64]), 16, 8),
1182 (AlgebraicType::sum([AlgebraicType::I128, AlgebraicType::I128]), 32, 16),
1183 (AlgebraicType::sum([AlgebraicType::I256, AlgebraicType::I128]), 64, 32),
1184 (AlgebraicType::sum([AlgebraicType::I256, AlgebraicType::U256]), 64, 32),
1185 (AlgebraicType::sum([AlgebraicType::String, AlgebraicType::I16]), 6, 2),
1186 ] {
1187 assert_size_align(ty, size, align);
1188 }
1189 }
1190
1191 proptest! {
1192 fn variant_order_irrelevant_for_layout(
1193 variants in vec(generate_algebraic_type(), 0..5)
1194 ) {
1195 use crate::SumTypeVariant;
1196
1197 let len = variants.len();
1198 let sum_permutations = variants
1200 .into_iter()
1201 .permutations(len)
1202 .map(|vars| vars.into_iter().map(SumTypeVariant::from).collect::<Box<[_]>>())
1203 .map(AlgebraicType::sum);
1204 let mut sum_layout_perms = sum_permutations
1206 .map(AlgebraicTypeLayout::from)
1207 .map(|ty| *ty.layout());
1208 prop_assert!(sum_layout_perms.all_equal());
1210 }
1211
1212 #[test]
1213 fn size_always_multiple_of_align(ty in generate_algebraic_type()) {
1214 let layout = AlgebraicTypeLayout::from(ty);
1215
1216 if layout.size() == 0 {
1217 assert_eq!(layout.align(), 1);
1218 } else {
1219 assert_eq!(layout.size() % layout.align(), 0);
1220 }
1221 }
1222 }
1223
1224 #[test]
1225 fn infinite_recursion_in_ensure_compatible_with_with_array_type() {
1226 let ty = AlgebraicTypeLayout::from(AlgebraicType::array(AlgebraicType::U64));
1227 assert!(ty.ensure_compatible_with(&ty).is_ok());
1233 }
1234}