diff --git a/crates/fayalite/src/array.rs b/crates/fayalite/src/array.rs index c953aea..7f24a9d 100644 --- a/crates/fayalite/src/array.rs +++ b/crates/fayalite/src/array.rs @@ -12,7 +12,8 @@ use crate::{ sim::value::{SimValue, SimValuePartialEq}, source_location::SourceLocation, ty::{ - CanonicalType, MatchVariantWithoutScope, StaticType, Type, TypeProperties, TypeWithDeref, + CanonicalType, MatchVariantWithoutScope, OpaqueSimValueSlice, OpaqueSimValueWriter, + OpaqueSimValueWritten, StaticType, Type, TypeProperties, TypeWithDeref, serde_impls::SerdeCanonicalType, }, util::ConstUsize, @@ -48,6 +49,7 @@ impl ArrayType { is_storable, is_castable_from_bits, bit_width, + sim_only_value_types, } = element; let Some(bit_width) = bit_width.checked_mul(len) else { panic!("array too big"); @@ -194,42 +196,71 @@ impl Type for ArrayType { SourceLocation::builtin() } - fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { + fn sim_value_from_bits( + &self, + bits: &BitSlice, + sim_only_values: &[DynSimOnlyValue], + ) -> Self::SimValue { assert_eq!(bits.len(), self.type_properties.bit_width); let element = self.element(); - let element_bit_width = element.canonical().bit_width(); + let element_props = element.canonical().type_properties(); + let element_bit_width = element_props.bit_width; + let element_sim_only_types_count = element_props.sim_only_value_types_ref().len(); TryFrom::try_from(Vec::from_iter((0..self.len()).map(|i| { - SimValue::from_bitslice(element, &bits[i * element_bit_width..][..element_bit_width]) + SimValue::from_bitslice_and_sim_only_values( + element, + &bits[i * element_props.bit_width..][..element_props.bit_width], + &sim_only_values[i * element_sim_only_types_count][..element_sim_only_types_count], + ) }))) .ok() .expect("used correct length") } - fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { + fn sim_value_clone_from_bits( + &self, + value: &mut Self::SimValue, + bits: &BitSlice, + sim_only_values: &[DynSimOnlyValue], + ) { assert_eq!(bits.len(), self.type_properties.bit_width); let element_ty = self.element(); - let element_bit_width = element_ty.canonical().bit_width(); + let element_props = element_ty.canonical().type_properties(); + let element_bit_width = element_props.bit_width; + let element_sim_only_types_count = element_props.sim_only_value_types_ref().len(); let value: &mut [SimValue] = value.as_mut(); assert_eq!(self.len(), value.len()); for (i, element_value) in value.iter_mut().enumerate() { assert_eq!(SimValue::ty(element_value), element_ty); - SimValue::bits_mut(element_value) + let (bits_mut, sim_only_values_mut) = SimValue::opaque_mut(element_value).parts_mut(); + bits_mut .bits_mut() .copy_from_bitslice(&bits[i * element_bit_width..][..element_bit_width]); + sim_only_values_mut.clone_from_slice( + &sim_only_values[i * element_sim_only_types_count..] + [..element_sim_only_types_count], + ); } } - fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { - assert_eq!(bits.len(), self.type_properties.bit_width); + fn sim_value_to_opaque<'w>( + &self, + value: &Self::SimValue, + mut writer: OpaqueSimValueWriter<'w>, + ) -> OpaqueSimValueWritten<'w> { let element_ty = self.element(); - let element_bit_width = element_ty.canonical().bit_width(); - let value: &[SimValue] = value.as_ref(); + let element_props = element_ty.canonical().type_properties(); + let element_bit_width = element_props.bit_width; + let element_sim_only_types_count = element_props.sim_only_value_types_ref().len(); + let value = AsRef::<[SimValue]>::as_ref(&value); assert_eq!(self.len(), value.len()); - for (i, element_value) in value.iter().enumerate() { + for element_value in value { assert_eq!(SimValue::ty(element_value), element_ty); - bits[i * element_bit_width..][..element_bit_width] - .copy_from_bitslice(SimValue::bits(element_value).bits()); + writer.fill_prefix_with(element_bit_width, element_sim_only_types_count, |writer| { + writer.fill_cloned_from_slice(SimValue::opaque(element_value).as_slice()) + }); } + writer.fill_cloned_from_slice(OpaqueSimValueSlice::empty()) } } diff --git a/crates/fayalite/src/bundle.rs b/crates/fayalite/src/bundle.rs index 30a70d5..cf08eb9 100644 --- a/crates/fayalite/src/bundle.rs +++ b/crates/fayalite/src/bundle.rs @@ -241,16 +241,30 @@ impl Type for Bundle { fn source_location() -> SourceLocation { SourceLocation::builtin() } - fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { + fn sim_value_from_bits( + &self, + bits: &BitSlice, + sim_only_values: &[DynSimOnlyValue], + ) -> Self::SimValue { assert_eq!(bits.len(), self.type_properties().bit_width); OpaqueSimValue::from_bitslice(bits) } - fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { + fn sim_value_clone_from_bits( + &self, + value: &mut Self::SimValue, + bits: &BitSlice, + sim_only_values: &[DynSimOnlyValue], + ) { assert_eq!(bits.len(), self.type_properties().bit_width); assert_eq!(value.bit_width(), self.type_properties().bit_width); value.bits_mut().bits_mut().copy_from_bitslice(bits); } - fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { + fn sim_value_to_bits<'w>( + &self, + value: &Self::SimValue, + bits: &mut BitSlice, + sim_only_values: DynSimOnlyValuesWriter<'w>, + ) -> DynSimOnlyValuesWritten<'w> { assert_eq!(bits.len(), self.type_properties().bit_width); assert_eq!(value.bit_width(), self.type_properties().bit_width); bits.copy_from_bitslice(value.bits().bits()); @@ -495,19 +509,33 @@ macro_rules! impl_tuples { fn source_location() -> SourceLocation { SourceLocation::builtin() } - fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { + fn sim_value_from_bits( + &self, + bits: &BitSlice, + sim_only_values: &[DynSimOnlyValue], + ) -> Self::SimValue { #![allow(unused_mut, unused_variables)] let mut v = BundleSimValueFromBits::new(*self, bits); $(let $var = v.field_from_bits();)* ($($var,)*) } - fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { + fn sim_value_clone_from_bits( + &self, + value: &mut Self::SimValue, + bits: &BitSlice, + sim_only_values: &[DynSimOnlyValue], + ) { #![allow(unused_mut, unused_variables)] let mut v = BundleSimValueFromBits::new(*self, bits); let ($($var,)*) = value; $(v.field_clone_from_bits($var);)* } - fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { + fn sim_value_to_bits<'w>( + &self, + value: &Self::SimValue, + bits: &mut BitSlice, + sim_only_values: DynSimOnlyValuesWriter<'w>, + ) -> DynSimOnlyValuesWritten<'w> { #![allow(unused_mut, unused_variables)] let mut v = BundleSimValueToBits::new(*self, bits); let ($($var,)*) = value; @@ -723,7 +751,11 @@ impl Type for PhantomData { fn source_location() -> SourceLocation { SourceLocation::builtin() } - fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { + fn sim_value_from_bits( + &self, + bits: &BitSlice, + sim_only_values: &[DynSimOnlyValue], + ) -> Self::SimValue { assert!(bits.is_empty()); *self } diff --git a/crates/fayalite/src/clock.rs b/crates/fayalite/src/clock.rs index 66b0e20..dee0891 100644 --- a/crates/fayalite/src/clock.rs +++ b/crates/fayalite/src/clock.rs @@ -39,17 +39,31 @@ impl Type for Clock { retval } - fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { + fn sim_value_from_bits( + &self, + bits: &BitSlice, + sim_only_values: &[DynSimOnlyValue], + ) -> Self::SimValue { assert_eq!(bits.len(), 1); bits[0] } - fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { + fn sim_value_clone_from_bits( + &self, + value: &mut Self::SimValue, + bits: &BitSlice, + sim_only_values: &[DynSimOnlyValue], + ) { assert_eq!(bits.len(), 1); *value = bits[0]; } - fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { + fn sim_value_to_bits<'w>( + &self, + value: &Self::SimValue, + bits: &mut BitSlice, + sim_only_values: DynSimOnlyValuesWriter<'w>, + ) -> DynSimOnlyValuesWritten<'w> { assert_eq!(bits.len(), 1); bits.set(0, *value); } diff --git a/crates/fayalite/src/enum_.rs b/crates/fayalite/src/enum_.rs index 283e4ff..a402f6f 100644 --- a/crates/fayalite/src/enum_.rs +++ b/crates/fayalite/src/enum_.rs @@ -381,16 +381,30 @@ impl Type for Enum { fn source_location() -> SourceLocation { SourceLocation::builtin() } - fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { + fn sim_value_from_bits( + &self, + bits: &BitSlice, + sim_only_values: &[DynSimOnlyValue], + ) -> Self::SimValue { assert_eq!(bits.len(), self.type_properties().bit_width); OpaqueSimValue::from_bitslice(bits) } - fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { + fn sim_value_clone_from_bits( + &self, + value: &mut Self::SimValue, + bits: &BitSlice, + sim_only_values: &[DynSimOnlyValue], + ) { assert_eq!(bits.len(), self.type_properties().bit_width); assert_eq!(value.bit_width(), self.type_properties().bit_width); value.bits_mut().bits_mut().copy_from_bitslice(bits); } - fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { + fn sim_value_to_bits<'w>( + &self, + value: &Self::SimValue, + bits: &mut BitSlice, + sim_only_values: DynSimOnlyValuesWriter<'w>, + ) -> DynSimOnlyValuesWritten<'w> { assert_eq!(bits.len(), self.type_properties().bit_width); assert_eq!(value.bit_width(), self.type_properties().bit_width); bits.copy_from_bitslice(value.bits().bits()); diff --git a/crates/fayalite/src/int.rs b/crates/fayalite/src/int.rs index c491cdc..694adcf 100644 --- a/crates/fayalite/src/int.rs +++ b/crates/fayalite/src/int.rs @@ -678,16 +678,30 @@ macro_rules! impl_int { fn source_location() -> SourceLocation { SourceLocation::builtin() } - fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { + fn sim_value_from_bits( + &self, + bits: &BitSlice, + sim_only_values: &[DynSimOnlyValue], + ) -> Self::SimValue { assert_eq!(bits.len(), self.width()); $value::new(Arc::new(bits.to_bitvec())) } - fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { + fn sim_value_clone_from_bits( + &self, + value: &mut Self::SimValue, + bits: &BitSlice, + sim_only_values: &[DynSimOnlyValue], + ) { assert_eq!(bits.len(), self.width()); assert_eq!(value.width(), self.width()); value.bits_mut().copy_from_bitslice(bits); } - fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { + fn sim_value_to_bits<'w>( + &self, + value: &Self::SimValue, + bits: &mut BitSlice, + sim_only_values: DynSimOnlyValuesWriter<'w>, + ) -> DynSimOnlyValuesWritten<'w> { assert_eq!(bits.len(), self.width()); assert_eq!(value.width(), self.width()); bits.copy_from_bitslice(value.bits()); @@ -1252,15 +1266,29 @@ impl Type for Bool { fn source_location() -> SourceLocation { SourceLocation::builtin() } - fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { + fn sim_value_from_bits( + &self, + bits: &BitSlice, + sim_only_values: &[DynSimOnlyValue], + ) -> Self::SimValue { assert_eq!(bits.len(), 1); bits[0] } - fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { + fn sim_value_clone_from_bits( + &self, + value: &mut Self::SimValue, + bits: &BitSlice, + sim_only_values: &[DynSimOnlyValue], + ) { assert_eq!(bits.len(), 1); *value = bits[0]; } - fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { + fn sim_value_to_bits<'w>( + &self, + value: &Self::SimValue, + bits: &mut BitSlice, + sim_only_values: DynSimOnlyValuesWriter<'w>, + ) -> DynSimOnlyValuesWritten<'w> { assert_eq!(bits.len(), 1); bits.set(0, *value); } diff --git a/crates/fayalite/src/int/uint_in_range.rs b/crates/fayalite/src/int/uint_in_range.rs index ae80a93..c9e92a9 100644 --- a/crates/fayalite/src/int/uint_in_range.rs +++ b/crates/fayalite/src/int/uint_in_range.rs @@ -70,15 +70,29 @@ impl Type for UIntInRangeMaskType { SourceLocation::builtin() } - fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { + fn sim_value_from_bits( + &self, + bits: &BitSlice, + sim_only_values: &[DynSimOnlyValue], + ) -> Self::SimValue { Bool.sim_value_from_bits(bits) } - fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { + fn sim_value_clone_from_bits( + &self, + value: &mut Self::SimValue, + bits: &BitSlice, + sim_only_values: &[DynSimOnlyValue], + ) { Bool.sim_value_clone_from_bits(value, bits); } - fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { + fn sim_value_to_bits<'w>( + &self, + value: &Self::SimValue, + bits: &mut BitSlice, + sim_only_values: DynSimOnlyValuesWriter<'w>, + ) -> DynSimOnlyValuesWritten<'w> { Bool.sim_value_to_bits(value, bits); } } @@ -353,17 +367,31 @@ macro_rules! define_uint_in_range_type { SourceLocation::builtin() } - fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { + fn sim_value_from_bits( + &self, + bits: &BitSlice, + sim_only_values: &[DynSimOnlyValue], + ) -> Self::SimValue { let mut retval = 0usize; retval.view_bits_mut::()[..bits.len()].clone_from_bitslice(bits); retval } - fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { + fn sim_value_clone_from_bits( + &self, + value: &mut Self::SimValue, + bits: &BitSlice, + sim_only_values: &[DynSimOnlyValue], + ) { *value = self.sim_value_from_bits(bits); } - fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { + fn sim_value_to_bits<'w>( + &self, + value: &Self::SimValue, + bits: &mut BitSlice, + sim_only_values: DynSimOnlyValuesWriter<'w>, + ) -> DynSimOnlyValuesWritten<'w> { bits.clone_from_bitslice(&value.view_bits::()[..bits.len()]); } } diff --git a/crates/fayalite/src/phantom_const.rs b/crates/fayalite/src/phantom_const.rs index 44b36ca..e6be72d 100644 --- a/crates/fayalite/src/phantom_const.rs +++ b/crates/fayalite/src/phantom_const.rs @@ -8,7 +8,10 @@ use crate::{ }, int::Bool, intern::{Intern, Interned, InternedCompare, LazyInterned, LazyInternedTrait, Memoize}, - sim::value::{SimValue, SimValuePartialEq, ToSimValue, ToSimValueWithType}, + sim::value::{ + DynSimOnlyValue, DynSimOnlyValuesWriter, DynSimOnlyValuesWritten, SimValue, + SimValuePartialEq, ToSimValue, ToSimValueWithType, assert_matching_sim_values, + }, source_location::SourceLocation, ty::{ CanonicalType, StaticType, Type, TypeProperties, impl_match_variant_as_self, @@ -284,18 +287,32 @@ impl Type for PhantomConst { SourceLocation::builtin() } - fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { - assert!(bits.is_empty()); + fn sim_value_from_bits( + &self, + bits: &BitSlice, + sim_only_values: &[DynSimOnlyValue], + ) -> Self::SimValue { + assert_matching_sim_values(0, &[], bits, sim_only_values); *self } - fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { - assert!(bits.is_empty()); + fn sim_value_clone_from_bits( + &self, + value: &mut Self::SimValue, + bits: &BitSlice, + sim_only_values: &[DynSimOnlyValue], + ) { + assert_matching_sim_values(0, &[], bits, sim_only_values); assert_eq!(*value, *self); } - fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { - assert!(bits.is_empty()); + fn sim_value_to_bits<'w>( + &self, + value: &Self::SimValue, + bits: &mut BitSlice, + sim_only_values: DynSimOnlyValuesWriter<'w>, + ) -> DynSimOnlyValuesWritten<'w> { + assert_matching_sim_values(0, &[], bits, sim_only_values); assert_eq!(*value, *self); } } diff --git a/crates/fayalite/src/reset.rs b/crates/fayalite/src/reset.rs index f3392a2..ef6366d 100644 --- a/crates/fayalite/src/reset.rs +++ b/crates/fayalite/src/reset.rs @@ -69,17 +69,31 @@ macro_rules! reset_type { retval } - fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { + fn sim_value_from_bits( + &self, + bits: &BitSlice, + sim_only_values: &[DynSimOnlyValue], + ) -> Self::SimValue { assert_eq!(bits.len(), 1); bits[0] } - fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { + fn sim_value_clone_from_bits( + &self, + value: &mut Self::SimValue, + bits: &BitSlice, + sim_only_values: &[DynSimOnlyValue], + ) { assert_eq!(bits.len(), 1); *value = bits[0]; } - fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { + fn sim_value_to_bits<'w>( + &self, + value: &Self::SimValue, + bits: &mut BitSlice, + sim_only_values: DynSimOnlyValuesWriter<'w>, + ) -> DynSimOnlyValuesWritten<'w> { assert_eq!(bits.len(), 1); bits.set(0, *value); } diff --git a/crates/fayalite/src/sim/value.rs b/crates/fayalite/src/sim/value.rs index 70cb943..02190d2 100644 --- a/crates/fayalite/src/sim/value.rs +++ b/crates/fayalite/src/sim/value.rs @@ -8,61 +8,81 @@ use crate::{ enum_::{Enum, EnumType}, expr::{CastBitsTo, Expr, ToExpr}, int::{Bool, IntType, KnownSize, SInt, SIntType, SIntValue, Size, UInt, UIntType, UIntValue}, + intern::Interned, reset::{AsyncReset, Reset, SyncReset}, - ty::{CanonicalType, StaticType, Type}, + source_location::SourceLocation, + ty::{ + CanonicalType, OpaqueSimValue, OpaqueSimValueSlice, OpaqueSimValueWriter, StaticType, Type, + TypeProperties, impl_match_variant_as_self, + }, util::{ ConstUsize, alternating_cell::{AlternatingCell, AlternatingCellMethods}, }, }; use bitvec::{slice::BitSlice, vec::BitVec}; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use hashbrown::{HashMap, hash_map::Entry}; +use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as _, ser::Error as _}; use std::{ - fmt, + borrow::Cow, + fmt::{self, Write}, + hash::{BuildHasher, Hash, Hasher, RandomState}, + marker::PhantomData, ops::{Deref, DerefMut}, - sync::Arc, + sync::{Arc, Mutex}, +}; + +pub(crate) mod sim_only_value_unsafe; + +pub use sim_only_value_unsafe::{ + DynSimOnlyValue, DynSimOnlyValueType, SimOnlyValue, SimOnlyValueTrait, SimOnlyValueType, }; #[derive(Copy, Clone, Eq, PartialEq)] enum ValidFlags { BothValid = 0, OnlyValueValid = 1, - OnlyBitsValid = 2, + OnlyOpaqueValid = 2, } #[derive(Clone)] struct SimValueInner { value: T::SimValue, - bits: UIntValue, + opaque: OpaqueSimValue, valid_flags: ValidFlags, ty: T, + sim_only_values_len: usize, } impl SimValueInner { - fn fill_bits(&mut self) { + fn fill_opaque(&mut self) { match self.valid_flags { - ValidFlags::BothValid | ValidFlags::OnlyBitsValid => {} + ValidFlags::BothValid | ValidFlags::OnlyOpaqueValid => {} ValidFlags::OnlyValueValid => { - self.ty.sim_value_to_bits(&self.value, self.bits.bits_mut()); + OpaqueSimValueWriter::rewrite_with( + self.sim_only_values_len, + &mut self.opaque, + |writer| self.ty.sim_value_to_opaque(&self.value, writer), + ); self.valid_flags = ValidFlags::BothValid; } } } - fn into_bits(mut self) -> UIntValue { - self.fill_bits(); - self.bits + fn into_opaque(mut self) -> OpaqueSimValue { + self.fill_opaque(); + self.opaque } - fn bits_mut(&mut self) -> &mut UIntValue { - self.fill_bits(); - self.valid_flags = ValidFlags::OnlyBitsValid; - &mut self.bits + fn opaque_mut(&mut self) -> &mut OpaqueSimValue { + self.fill_opaque(); + self.valid_flags = ValidFlags::OnlyOpaqueValid; + &mut self.opaque } fn fill_value(&mut self) { match self.valid_flags { ValidFlags::BothValid | ValidFlags::OnlyValueValid => {} - ValidFlags::OnlyBitsValid => { + ValidFlags::OnlyOpaqueValid => { self.ty - .sim_value_clone_from_bits(&mut self.value, self.bits.bits()); + .sim_value_clone_from_opaque(&mut self.value, self.opaque.as_slice()); self.valid_flags = ValidFlags::BothValid; } } @@ -82,12 +102,14 @@ impl AlternatingCellMethods for SimValueInner { fn unique_to_shared(&mut self) { match self.valid_flags { ValidFlags::BothValid => return, - ValidFlags::OnlyValueValid => { - self.ty.sim_value_to_bits(&self.value, self.bits.bits_mut()) - } - ValidFlags::OnlyBitsValid => self + ValidFlags::OnlyValueValid => OpaqueSimValueWriter::rewrite_with( + self.sim_only_values_len, + &mut self.opaque, + |writer| self.ty.sim_value_to_opaque(&self.value, writer), + ), + ValidFlags::OnlyOpaqueValid => self .ty - .sim_value_clone_from_bits(&mut self.value, self.bits.bits()), + .sim_value_clone_from_opaque(&mut self.value, self.opaque.as_slice()), } self.valid_flags = ValidFlags::BothValid; } @@ -143,13 +165,15 @@ impl Clone for SimValue { impl SimValue { #[track_caller] - pub fn from_bits(ty: T, bits: UIntValue) -> Self { - assert_eq!(ty.canonical().bit_width(), bits.width()); + pub fn from_opaque(ty: T, opaque: OpaqueSimValue) -> Self { + let type_properties = ty.canonical().type_properties(); + type_properties.assert_matching_opaque_sim_value(opaque.as_slice()); let inner = SimValueInner { - value: ty.sim_value_from_bits(bits.bits()), - bits, + value: ty.sim_value_from_opaque(opaque.as_slice()), + opaque, valid_flags: ValidFlags::BothValid, ty, + sim_only_values_len: type_properties.sim_only_values_len, }; Self { inner: AlternatingCell::new_shared(inner), @@ -157,14 +181,30 @@ impl SimValue { } #[track_caller] pub fn from_bitslice(ty: T, bits: &BitSlice) -> Self { - Self::from_bits(ty, UIntValue::new(Arc::new(bits.to_bitvec()))) + Self::from_bitslice_and_sim_only_values(ty, bits, Vec::new()) + } + #[track_caller] + pub fn from_bitslice_and_sim_only_values( + ty: T, + bits: &BitSlice, + sim_only_values: Vec, + ) -> Self { + Self::from_opaque( + ty, + OpaqueSimValue::from_bitslice_and_sim_only_values(bits, sim_only_values), + ) } pub fn from_value(ty: T, value: T::SimValue) -> Self { + let type_properties = ty.canonical().type_properties(); let inner = SimValueInner { - bits: UIntValue::new_dyn(Arc::new(BitVec::repeat(false, ty.canonical().bit_width()))), + opaque: OpaqueSimValue::from_bits_and_sim_only_values( + UIntValue::new_dyn(Arc::new(BitVec::repeat(false, type_properties.bit_width))), + Vec::with_capacity(type_properties.sim_only_values_len), + ), value, valid_flags: ValidFlags::OnlyValueValid, ty, + sim_only_values_len: type_properties.sim_only_values_len, }; Self { inner: AlternatingCell::new_unique(inner), @@ -173,18 +213,24 @@ impl SimValue { pub fn ty(this: &Self) -> T { this.inner.share().ty } - pub fn into_bits(this: Self) -> UIntValue { - this.inner.into_inner().into_bits() + pub fn into_opaque(this: Self) -> OpaqueSimValue { + this.inner.into_inner().into_opaque() } - pub fn into_ty_and_bits(this: Self) -> (T, UIntValue) { + pub fn into_ty_and_opaque(this: Self) -> (T, OpaqueSimValue) { let inner = this.inner.into_inner(); - (inner.ty, inner.into_bits()) + (inner.ty, inner.into_opaque()) + } + pub fn opaque(this: &Self) -> &OpaqueSimValue { + &this.inner.share().opaque + } + pub fn opaque_mut(this: &mut Self) -> &mut OpaqueSimValue { + &mut this.inner.unique().opaque } pub fn bits(this: &Self) -> &UIntValue { - &this.inner.share().bits + Self::opaque(this).bits() } pub fn bits_mut(this: &mut Self) -> &mut UIntValue { - this.inner.unique().bits_mut() + Self::opaque_mut(this).bits_mut() } pub fn into_value(this: Self) -> T::SimValue { this.inner.into_inner().into_value() @@ -197,59 +243,59 @@ impl SimValue { } #[track_caller] pub fn from_canonical(v: SimValue) -> Self { - let (ty, bits) = SimValue::into_ty_and_bits(v); - Self::from_bits(T::from_canonical(ty), bits) + let (ty, opaque) = SimValue::into_ty_and_opaque(v); + Self::from_opaque(T::from_canonical(ty), opaque) } pub fn into_canonical(this: Self) -> SimValue { - let (ty, bits) = Self::into_ty_and_bits(this); - SimValue::from_bits(ty.canonical(), bits) + let (ty, opaque) = Self::into_ty_and_opaque(this); + SimValue::from_opaque(ty.canonical(), opaque) } pub fn canonical(this: &Self) -> SimValue { - SimValue::from_bits(Self::ty(this).canonical(), Self::bits(this).clone()) + SimValue::from_opaque(Self::ty(this).canonical(), Self::opaque(this).clone()) } #[track_caller] pub fn from_dyn_int(v: SimValue) -> Self where T: IntType, { - let (ty, bits) = SimValue::into_ty_and_bits(v); - SimValue::from_bits(T::from_dyn_int(ty), bits) + let (ty, opaque) = SimValue::into_ty_and_opaque(v); + SimValue::from_opaque(T::from_dyn_int(ty), opaque) } pub fn into_dyn_int(this: Self) -> SimValue where T: IntType, { - let (ty, bits) = Self::into_ty_and_bits(this); - SimValue::from_bits(ty.as_dyn_int(), bits) + let (ty, opaque) = Self::into_ty_and_opaque(this); + SimValue::from_opaque(ty.as_dyn_int(), opaque) } pub fn to_dyn_int(this: &Self) -> SimValue where T: IntType, { - SimValue::from_bits(Self::ty(this).as_dyn_int(), Self::bits(&this).clone()) + SimValue::from_opaque(Self::ty(this).as_dyn_int(), Self::opaque(&this).clone()) } #[track_caller] pub fn from_bundle(v: SimValue) -> Self where T: BundleType, { - let (ty, bits) = SimValue::into_ty_and_bits(v); - SimValue::from_bits(T::from_canonical(CanonicalType::Bundle(ty)), bits) + let (ty, opaque) = SimValue::into_ty_and_opaque(v); + SimValue::from_opaque(T::from_canonical(CanonicalType::Bundle(ty)), opaque) } pub fn into_bundle(this: Self) -> SimValue where T: BundleType, { - let (ty, bits) = Self::into_ty_and_bits(this); - SimValue::from_bits(Bundle::from_canonical(ty.canonical()), bits) + let (ty, opaque) = Self::into_ty_and_opaque(this); + SimValue::from_opaque(Bundle::from_canonical(ty.canonical()), opaque) } pub fn to_bundle(this: &Self) -> SimValue where T: BundleType, { - SimValue::from_bits( + SimValue::from_opaque( Bundle::from_canonical(Self::ty(this).canonical()), - Self::bits(&this).clone(), + Self::opaque(&this).clone(), ) } #[track_caller] @@ -257,23 +303,23 @@ impl SimValue { where T: EnumType, { - let (ty, bits) = SimValue::into_ty_and_bits(v); - SimValue::from_bits(T::from_canonical(CanonicalType::Enum(ty)), bits) + let (ty, opaque) = SimValue::into_ty_and_opaque(v); + SimValue::from_opaque(T::from_canonical(CanonicalType::Enum(ty)), opaque) } pub fn into_enum(this: Self) -> SimValue where T: EnumType, { - let (ty, bits) = Self::into_ty_and_bits(this); - SimValue::from_bits(Enum::from_canonical(ty.canonical()), bits) + let (ty, opaque) = Self::into_ty_and_opaque(this); + SimValue::from_opaque(Enum::from_canonical(ty.canonical()), opaque) } pub fn to_enum(this: &Self) -> SimValue where T: EnumType, { - SimValue::from_bits( + SimValue::from_opaque( Enum::from_canonical(Self::ty(this).canonical()), - Self::bits(&this).clone(), + Self::opaque(&this).clone(), ) } } @@ -308,7 +354,11 @@ impl ToExpr for SimValue { #[track_caller] fn to_expr(&self) -> Expr { let inner = self.inner.share(); - inner.bits.cast_bits_to(inner.ty) + assert!( + inner.sim_only_value_types.is_empty(), + "can't convert sim-only values to Expr" + ); + inner.opaque.bits().cast_bits_to(inner.ty) } } @@ -443,12 +493,15 @@ impl ToSimValueWithType for BitVec { #[track_caller] fn arc_into_sim_value_with_type(self: Arc, ty: T) -> SimValue { - SimValue::from_bits(ty, UIntValue::new_dyn(self)) + SimValue::from_opaque(ty, OpaqueSimValue::from_bits(UIntValue::new_dyn(self))) } #[track_caller] fn arc_to_sim_value_with_type(self: &Arc, ty: T) -> SimValue { - SimValue::from_bits(ty, UIntValue::new_dyn(self.clone())) + SimValue::from_opaque( + ty, + OpaqueSimValue::from_bits(UIntValue::new_dyn(self.clone())), + ) } } @@ -792,16 +845,18 @@ impl ToSimValueWithType for bool { | CanonicalType::Array(_) | CanonicalType::Enum(_) | CanonicalType::Bundle(_) - | CanonicalType::PhantomConst(_) => { + | CanonicalType::PhantomConst(_) + | CanonicalType::DynSimOnlyValueType(_) => { panic!("can't create SimValue from bool: expected value of type: {ty:?}"); } CanonicalType::Bool(_) | CanonicalType::AsyncReset(_) | CanonicalType::SyncReset(_) | CanonicalType::Reset(_) - | CanonicalType::Clock(_) => { - SimValue::from_bits(ty, UIntValue::new(Arc::new(BitVec::repeat(*self, 1)))) - } + | CanonicalType::Clock(_) => SimValue::from_opaque( + ty, + OpaqueSimValue::from_bits(UIntValue::new(Arc::new(BitVec::repeat(*self, 1)))), + ), } } } @@ -911,3 +966,445 @@ macro_rules! impl_to_sim_value_for_int_value { impl_to_sim_value_for_int_value!(UIntValue, UInt, UIntType); impl_to_sim_value_for_int_value!(SIntValue, SInt, SIntType); + +#[derive(Default)] +struct DynSimOnlyValueTypeSerdeTableRest { + from_serde: HashMap, + serde_id_random_state: RandomState, + buffer: String, +} + +impl DynSimOnlyValueTypeSerdeTableRest { + #[cold] + fn add_new(&mut self, ty: DynSimOnlyValueType) -> DynSimOnlyValueTypeSerdeId { + let mut try_number = 0u64; + let mut hasher = self.serde_id_random_state.build_hasher(); + // extract more bits of randomness from TypeId -- its Hash impl only hashes 64-bits + write!(self.buffer, "{:?}", ty.type_id()).expect("shouldn't ever fail"); + self.buffer.hash(&mut hasher); + loop { + let mut hasher = hasher.clone(); + try_number.hash(&mut hasher); + try_number += 1; + let retval = DynSimOnlyValueTypeSerdeId(std::array::from_fn(|i| { + let mut hasher = hasher.clone(); + i.hash(&mut hasher); + hasher.finish() as u32 + })); + match self.from_serde.entry(retval) { + Entry::Occupied(_) => continue, + Entry::Vacant(e) => { + e.insert(ty); + return retval; + } + } + } + } +} + +#[derive(Default)] +struct DynSimOnlyValueTypeSerdeTable { + to_serde: HashMap, + rest: DynSimOnlyValueTypeSerdeTableRest, +} + +static DYN_SIM_ONLY_VALUE_TYPE_SERDE_TABLE: Mutex> = + Mutex::new(None); + +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)] +#[serde(transparent)] +struct DynSimOnlyValueTypeSerdeId([u32; 4]); + +impl From for DynSimOnlyValueTypeSerdeId { + fn from(ty: DynSimOnlyValueType) -> Self { + let mut locked = DYN_SIM_ONLY_VALUE_TYPE_SERDE_TABLE + .lock() + .expect("shouldn't be poison"); + let DynSimOnlyValueTypeSerdeTable { to_serde, rest } = locked.get_or_insert_default(); + match to_serde.entry(ty) { + Entry::Occupied(occupied_entry) => *occupied_entry.get(), + Entry::Vacant(vacant_entry) => *vacant_entry.insert(rest.add_new(ty)), + } + } +} + +impl DynSimOnlyValueTypeSerdeId { + fn ty(self) -> Option { + let locked = DYN_SIM_ONLY_VALUE_TYPE_SERDE_TABLE + .lock() + .expect("shouldn't be poison"); + Some(*locked.as_ref()?.rest.from_serde.get(&self)?) + } +} + +#[derive(Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)] +struct DynSimOnlyValueTypeSerde<'a> { + random_id: DynSimOnlyValueTypeSerdeId, + #[serde(borrow)] + type_name: Cow<'a, str>, +} + +impl Serialize for DynSimOnlyValueType { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + DynSimOnlyValueTypeSerde { + random_id: (*self).into(), + type_name: Cow::Borrowed(self.type_name()), + } + .serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for DynSimOnlyValueType { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let deserialized = DynSimOnlyValueTypeSerde::deserialize(deserializer)?; + let retval = deserialized + .random_id + .ty() + .filter(|ty| ty.type_name() == deserialized.type_name); + retval.ok_or_else(|| D::Error::custom("doesn't match any DynSimOnlyValueType that was serialized this time this program was run")) + } +} + +impl DynSimOnlyValueType { + pub const fn type_properties(self) -> TypeProperties { + TypeProperties { + is_passive: true, + is_storable: true, + is_castable_from_bits: false, + bit_width: 0, + sim_only_values_len: 1, + } + } +} + +impl Type for DynSimOnlyValueType { + type BaseType = DynSimOnlyValueType; + type MaskType = Bool; + type SimValue = DynSimOnlyValue; + + impl_match_variant_as_self!(); + + fn mask_type(&self) -> Self::MaskType { + Bool + } + + fn canonical(&self) -> CanonicalType { + CanonicalType::DynSimOnlyValueType(*self) + } + + fn from_canonical(canonical_type: CanonicalType) -> Self { + let CanonicalType::DynSimOnlyValueType(v) = canonical_type else { + panic!("expected DynSimOnlyValueType"); + }; + v + } + + fn source_location() -> SourceLocation { + SourceLocation::builtin() + } + + fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue { + self.type_properties() + .assert_matching_opaque_sim_value(opaque); + opaque.sim_only_values()[0].clone() + } + + fn sim_value_clone_from_opaque( + &self, + value: &mut Self::SimValue, + opaque: OpaqueSimValueSlice<'_>, + ) { + self.type_properties() + .assert_matching_opaque_sim_value(opaque); + value.clone_from(&opaque.sim_only_values()[0]); + } + + fn sim_value_to_opaque<'w>( + &self, + value: &Self::SimValue, + writer: OpaqueSimValueWriter<'w>, + ) -> crate::ty::OpaqueSimValueWritten<'w> { + writer.fill_cloned_from_slice(OpaqueSimValueSlice::from_parts( + BitSlice::empty(), + std::array::from_ref(value), + )) + } +} + +impl Type for SimOnlyValueType { + type BaseType = DynSimOnlyValueType; + type MaskType = Bool; + type SimValue = SimOnlyValue; + + impl_match_variant_as_self!(); + + fn mask_type(&self) -> Self::MaskType { + Bool + } + + fn canonical(&self) -> CanonicalType { + DynSimOnlyValueType::from(*self).canonical() + } + + fn from_canonical(canonical_type: CanonicalType) -> Self { + DynSimOnlyValueType::from_canonical(canonical_type) + .downcast() + .expect("got wrong SimOnlyValueType") + } + + fn source_location() -> SourceLocation { + SourceLocation::builtin() + } + + fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue { + DynSimOnlyValueType::from(*self) + .type_properties() + .assert_matching_opaque_sim_value(opaque); + SimOnlyValue::new( + opaque.sim_only_values()[0] + .downcast_ref::() + .expect("type mismatch") + .clone(), + ) + } + + fn sim_value_clone_from_opaque( + &self, + value: &mut Self::SimValue, + opaque: OpaqueSimValueSlice<'_>, + ) { + DynSimOnlyValueType::from(*self) + .type_properties() + .assert_matching_opaque_sim_value(opaque); + (**value).clone_from( + &opaque.sim_only_values()[0] + .downcast_ref::() + .expect("already checked type"), + ) + } + + fn sim_value_to_opaque<'w>( + &self, + value: &Self::SimValue, + writer: OpaqueSimValueWriter<'w>, + ) -> crate::ty::OpaqueSimValueWritten<'w> { + SimOnlyValue::with_dyn_ref(value, |value| { + writer.fill_cloned_from_slice(OpaqueSimValueSlice::from_parts( + BitSlice::empty(), + std::array::from_ref(value), + )) + }) + } +} + +impl fmt::Debug for SimOnlyValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Self::with_dyn_ref(self, |this| fmt::Debug::fmt(this, f)) + } +} + +#[derive(Serialize, Deserialize)] +#[serde(rename = "SimOnlyValue")] +struct SerdeSimOnlyValue<'a> { + ty: DynSimOnlyValueType, + #[serde(borrow)] + value: Cow<'a, str>, +} + +impl Serialize for DynSimOnlyValue { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + SerdeSimOnlyValue { + ty: self.ty(), + value: Cow::Owned(self.serialize_to_json_string().map_err(S::Error::custom)?), + } + .serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for DynSimOnlyValue { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let SerdeSimOnlyValue { ty, value } = Deserialize::deserialize(deserializer)?; + ty.deserialize_from_json_string(&value) + .map_err(D::Error::custom) + } +} + +impl ToSimValueWithType for DynSimOnlyValue { + #[track_caller] + fn to_sim_value_with_type(&self, ty: DynSimOnlyValueType) -> SimValue { + assert_eq!(self.ty(), ty, "mismatched type"); + SimValue::from_value(ty, self.clone()) + } + #[track_caller] + fn into_sim_value_with_type(self, ty: DynSimOnlyValueType) -> SimValue { + assert_eq!(self.ty(), ty, "mismatched type"); + SimValue::from_value(ty, self) + } +} + +impl ToSimValueWithType> for SimOnlyValue { + fn to_sim_value_with_type(&self, ty: SimOnlyValueType) -> SimValue> { + SimValue::from_value(ty, self.clone()) + } + fn into_sim_value_with_type(self, ty: SimOnlyValueType) -> SimValue> { + SimValue::from_value(ty, self) + } +} + +impl ToSimValue for DynSimOnlyValue { + type Type = DynSimOnlyValueType; + + fn to_sim_value(&self) -> SimValue { + SimValue::from_value(self.ty(), self.clone()) + } + + fn into_sim_value(self) -> SimValue { + SimValue::from_value(self.ty(), self) + } +} + +impl ToSimValue for SimOnlyValue { + type Type = SimOnlyValueType; + + fn to_sim_value(&self) -> SimValue { + SimValue::from_value(Default::default(), self.clone()) + } + + fn into_sim_value(self) -> SimValue { + SimValue::from_value(Default::default(), self) + } +} + +#[derive(Debug)] +pub struct DynSimOnlyValuesWriter<'a> { + types: &'static [DynSimOnlyValueType], + values: &'a mut Vec, + values_offset: usize, +} + +#[derive(Debug)] +pub struct DynSimOnlyValuesWritten<'a> { + _phantom: PhantomData<&'a ()>, +} + +impl<'a> DynSimOnlyValuesWriter<'a> { + pub fn rewrite_with( + types: Interned<[DynSimOnlyValueType]>, + values: &mut Vec, + f: F, + ) where + F: for<'b> FnOnce(DynSimOnlyValuesWriter<'b>) -> DynSimOnlyValuesWritten<'b>, // 'b is used as a brand + { + Self::rewrite_with_static(Interned::into_inner(types), values, f); + } + pub fn rewrite_with_static( + types: &'static [DynSimOnlyValueType], + values: &mut Vec, + f: F, + ) where + F: for<'b> FnOnce(DynSimOnlyValuesWriter<'b>) -> DynSimOnlyValuesWritten<'b>, // 'b is used as a brand + { + let DynSimOnlyValuesWritten { + _phantom: PhantomData, + } = f(DynSimOnlyValuesWriter::rewrite_helper(types, values)); + } + pub(crate) fn rewrite_helper( + types: &'static [DynSimOnlyValueType], + values: &'a mut Vec, + ) -> Self { + assert!(values.len() <= types.len(), "too many values"); + values.reserve_exact(types.len() - values.len()); + Self { + types, + values, + values_offset: 0, + } + } + pub fn types(&self) -> &'static [DynSimOnlyValueType] { + self.types + } + pub fn len(&self) -> usize { + self.types.len() + } + pub fn is_empty(&self) -> bool { + self.types.is_empty() + } + pub fn fill_cloned_from_slice(self, values: &[DynSimOnlyValue]) -> DynSimOnlyValuesWritten<'a> { + assert_matching_sim_only_values(self.types, values); + let (clone_from_src, clone_src) = + values.split_at((self.values.len() - self.values_offset).min(values.len())); + self.values[self.values_offset..][..clone_from_src.len()].clone_from_slice(clone_from_src); + self.values.extend_from_slice(clone_src); + DynSimOnlyValuesWritten { + _phantom: PhantomData, + } + } + pub fn fill_prefix_with(&mut self, prefix_len: usize, f: F) + where + F: for<'b> FnOnce(DynSimOnlyValuesWriter<'b>) -> DynSimOnlyValuesWritten<'b>, // 'b is used as a brand + { + let DynSimOnlyValuesWritten { + _phantom: PhantomData, + } = f(self.fill_prefix_setup(prefix_len)); + self.fill_prefix_finish(prefix_len); + } + /// the first part of [`fill_prefix_with`] + pub(crate) fn fill_prefix_setup<'b>( + &'b mut self, + prefix_len: usize, + ) -> DynSimOnlyValuesWriter<'b> { + let prefix_types = &self.types[..prefix_len]; + DynSimOnlyValuesWriter::<'b> { + types: prefix_types, + values: self.values, + values_offset: self.values_offset, + } + } + /// the last part of [`fill_prefix_with`] + pub(crate) fn fill_prefix_finish(&mut self, prefix_len: usize) { + assert!(self.values.len() >= self.values_offset + prefix_len); + self.values_offset += prefix_len; + self.types = &self.types[prefix_len..]; + } +} + +#[track_caller] +#[inline] +pub(crate) fn assert_matching_sim_values( + bit_width: usize, + sim_only_value_types: &[DynSimOnlyValueType], + bits: &BitSlice, + sim_only_values: &[DynSimOnlyValue], +) { + assert_matching_sim_only_values(sim_only_value_types, sim_only_values); + assert_eq!(bit_width, bits.len()); +} + +#[track_caller] +#[inline] +pub(crate) fn assert_matching_sim_only_values( + sim_only_value_types: &[DynSimOnlyValueType], + sim_only_values: &[DynSimOnlyValue], +) { + assert!( + sim_only_value_types + .iter() + .copied() + .eq(sim_only_values.iter().map(DynSimOnlyValue::ty)), + "sim-only values don't match expected types: {:?} != {:?}", + sim_only_value_types, + Vec::from_iter(sim_only_values.iter().map(DynSimOnlyValue::ty)), + ); +} diff --git a/crates/fayalite/src/sim/value/sim_only_value_unsafe.rs b/crates/fayalite/src/sim/value/sim_only_value_unsafe.rs new file mode 100644 index 0000000..ea2ba35 --- /dev/null +++ b/crates/fayalite/src/sim/value/sim_only_value_unsafe.rs @@ -0,0 +1,351 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +//! `unsafe` parts of [`DynSimOnlyValue`] + +use serde::{Serialize, de::DeserializeOwned}; +use std::{ + alloc::{Layout, alloc, dealloc, handle_alloc_error}, + any::TypeId, + fmt, + hash::{Hash, Hasher}, + marker::PhantomData, + mem::ManuallyDrop, + ptr::{self, NonNull}, +}; + +struct SimOnlyValueVTable { + layout: Layout, + // TODO: replace with TypeId once TypeId::of is const-stable + type_id: fn() -> TypeId, + type_name: fn() -> &'static str, + drop_in_place: unsafe fn(this: NonNull<()>), + eq: unsafe fn(this: NonNull<()>, other: NonNull<()>) -> bool, + hash: unsafe fn(this: NonNull<()>, hasher: &mut dyn Hasher), + debug_fmt: unsafe fn(this: NonNull<()>, f: &mut fmt::Formatter<'_>) -> fmt::Result, + serialize_to_json_string: unsafe fn(this: NonNull<()>) -> serde_json::Result, + deserialize_into_uninit_from_json_string: + unsafe fn(this: NonNull<()>, json_str: &str) -> serde_json::Result<()>, + clone_into_uninit: unsafe fn(target: NonNull<()>, src: NonNull<()>), + clone_from: unsafe fn(this: NonNull<()>, src: NonNull<()>), +} + +pub trait SimOnlyValueTrait: + 'static + Eq + Hash + fmt::Debug + Serialize + DeserializeOwned + Clone +{ +} + +impl SimOnlyValueTrait + for T +{ +} + +unsafe trait GetSimOnlyValueVTable: SimOnlyValueTrait { + const VTABLE: &'static SimOnlyValueVTable; +} + +unsafe impl GetSimOnlyValueVTable for T { + const VTABLE: &'static SimOnlyValueVTable = &SimOnlyValueVTable { + layout: Layout::new::(), + type_id: TypeId::of::, + type_name: std::any::type_name::, + drop_in_place: |this| unsafe { + this.cast::().drop_in_place(); + }, + eq: |this, other| unsafe { this.cast::().as_ref() == other.cast::().as_ref() }, + hash: |this, mut hasher| unsafe { this.cast::().as_ref().hash(&mut hasher) }, + debug_fmt: |this, f| unsafe { fmt::Debug::fmt(this.cast::().as_ref(), f) }, + serialize_to_json_string: |this| unsafe { + serde_json::to_string(this.cast::().as_ref()) + }, + deserialize_into_uninit_from_json_string: |this, json_str| unsafe { + serde_json::from_str(json_str).map(|v| this.cast::().write(v)) + }, + clone_into_uninit: |target, src| unsafe { + target + .cast::() + .write(Clone::clone(src.cast::().as_ref())); + }, + clone_from: |this, src| unsafe { + Clone::clone_from(this.cast::().as_mut(), src.cast::().as_ref()); + }, + }; +} + +#[derive(Copy, Clone)] +pub struct DynSimOnlyValueType { + vtable: &'static SimOnlyValueVTable, +} + +struct DynSimOnlyValueUninit { + ty: DynSimOnlyValueType, + value: NonNull<()>, +} + +impl DynSimOnlyValueUninit { + fn new(ty: DynSimOnlyValueType) -> Self { + let layout = ty.vtable.layout; + let value = if layout.size() == 0 { + ptr::without_provenance_mut(layout.align()) + } else { + unsafe { alloc(layout).cast() } + }; + let Some(value) = NonNull::new(value) else { + handle_alloc_error(layout) + }; + Self { ty, value } + } + unsafe fn assume_init(self) -> DynSimOnlyValue { + let this = ManuallyDrop::new(self); + DynSimOnlyValue { + ty: this.ty, + value: this.value, + } + } +} + +impl Drop for DynSimOnlyValueUninit { + fn drop(&mut self) { + let layout = self.ty.vtable.layout; + if layout.size() != 0 { + unsafe { + dealloc(self.value.as_ptr().cast(), layout); + } + } + } +} + +impl DynSimOnlyValueType { + pub const fn of() -> Self { + Self { + vtable: ::VTABLE, + } + } + pub fn type_id(self) -> TypeId { + (self.vtable.type_id)() + } + pub fn type_name(self) -> &'static str { + (self.vtable.type_name)() + } + pub fn is(self) -> bool { + self.type_id() == TypeId::of::() + } + pub fn downcast(self) -> Option> { + self.is::().then_some(SimOnlyValueType::default()) + } + pub fn deserialize_from_json_string( + self, + json_str: &str, + ) -> serde_json::Result { + let retval = DynSimOnlyValueUninit::new(self); + unsafe { + (self.vtable.deserialize_into_uninit_from_json_string)(retval.value, json_str)?; + Ok(retval.assume_init()) + } + } +} + +impl PartialEq for DynSimOnlyValueType { + fn eq(&self, other: &Self) -> bool { + if ptr::eq(self.vtable, other.vtable) { + true + } else if self.vtable.layout != other.vtable.layout { + false + } else { + (self.vtable.type_id)() == (other.vtable.type_id)() + } + } +} + +impl Eq for DynSimOnlyValueType {} + +impl Hash for DynSimOnlyValueType { + fn hash(&self, state: &mut H) { + (self.vtable.type_id)().hash(state); + } +} + +impl fmt::Debug for DynSimOnlyValueType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "SimOnlyValueType<{}>", (self.vtable.type_name)()) + } +} + +impl From> for DynSimOnlyValueType { + fn from(value: SimOnlyValueType) -> Self { + let SimOnlyValueType(PhantomData) = value; + Self { + vtable: ::VTABLE, + } + } +} + +#[derive(Clone, Eq, PartialEq, Hash, Debug)] +pub struct SimOnlyValueType(PhantomData T>); + +impl Copy for SimOnlyValueType {} + +impl Default for SimOnlyValueType { + fn default() -> Self { + Self(PhantomData) + } +} + +#[derive(Clone, Eq, PartialEq, Hash, Default, PartialOrd, Ord)] +pub struct SimOnlyValue(Box); + +impl SimOnlyValue { + pub fn with_dyn_ref R, R>(&self, f: F) -> R { + let dyn_ref = ManuallyDrop::new(DynSimOnlyValue { + ty: SimOnlyValueType::::default().into(), + value: NonNull::::from_ref(&self.0).cast(), + }); + f(&dyn_ref) + } + pub fn from_box(v: Box) -> Self { + Self(v) + } + pub fn new(v: T) -> Self { + Self(Box::new(v)) + } + pub fn into_inner(this: Self) -> T { + *this.0 + } + pub fn into_inner_box(this: Self) -> Box { + this.0 + } + pub fn into_dyn(this: Self) -> DynSimOnlyValue { + DynSimOnlyValue::from(this) + } +} + +impl std::ops::Deref for SimOnlyValue { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl std::ops::DerefMut for SimOnlyValue { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +pub struct DynSimOnlyValue { + ty: DynSimOnlyValueType, + value: NonNull<()>, +} + +struct DebugDynSimOnlyValueInner<'a>(&'a DynSimOnlyValue); + +impl<'a> fmt::Debug for DebugDynSimOnlyValueInner<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + unsafe { (self.0.ty.vtable.debug_fmt)(self.0.value, f) } + } +} + +impl fmt::Debug for DynSimOnlyValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "SimOnlyValue<{}>", self.ty.type_name())?; + f.debug_tuple("") + .field(&DebugDynSimOnlyValueInner(self)) + .finish() + } +} + +impl PartialEq for DynSimOnlyValue { + fn eq(&self, other: &Self) -> bool { + self.ty == other.ty && unsafe { (self.ty.vtable.eq)(self.value, other.value) } + } +} + +impl Eq for DynSimOnlyValue {} + +impl Hash for DynSimOnlyValue { + fn hash(&self, state: &mut H) { + self.ty.hash(state); + unsafe { (self.ty.vtable.hash)(self.value, state) }; + } +} + +impl Clone for DynSimOnlyValue { + fn clone(&self) -> Self { + let retval = DynSimOnlyValueUninit::new(self.ty); + unsafe { + (self.ty.vtable.clone_into_uninit)(retval.value, self.value); + retval.assume_init() + } + } + fn clone_from(&mut self, source: &Self) { + if self.ty == source.ty { + unsafe { (self.ty.vtable.clone_from)(self.value, source.value) }; + } else { + *self = source.clone(); + } + } +} + +impl Drop for DynSimOnlyValue { + fn drop(&mut self) { + unsafe { + ptr::read(self).drop_in_place_and_keep_alloc(); + } + } +} + +impl From> for DynSimOnlyValue { + fn from(value: SimOnlyValue) -> Self { + unsafe { + Self { + ty: SimOnlyValueType::::default().into(), + value: NonNull::new_unchecked(Box::into_raw(value.0)).cast::<()>(), + } + } + } +} + +impl DynSimOnlyValue { + pub fn ty(&self) -> DynSimOnlyValueType { + self.ty + } + pub fn type_id(&self) -> TypeId { + self.ty.type_id() + } + pub fn is(&self) -> bool { + self.ty.is::() + } + pub fn downcast(self) -> Result, DynSimOnlyValue> { + let Some(_) = self.ty.downcast::() else { + return Err(self); + }; + Ok(SimOnlyValue(unsafe { + Box::from_raw(ManuallyDrop::new(self).value.as_ptr().cast::()) + })) + } + pub fn downcast_ref(&self) -> Option<&T> { + self.ty + .downcast::() + .map(|_| unsafe { &*self.value.as_ptr().cast::() }) + } + pub fn downcast_mut(&mut self) -> Option<&mut T> { + self.ty + .downcast::() + .map(|_| unsafe { &mut *self.value.as_ptr().cast::() }) + } + pub fn serialize_to_json_string(&self) -> serde_json::Result { + unsafe { (self.ty.vtable.serialize_to_json_string)(self.value) } + } + fn forget_and_keep_alloc(self) -> DynSimOnlyValueUninit { + let this = ManuallyDrop::new(self); + DynSimOnlyValueUninit { + ty: this.ty, + value: this.value, + } + } + fn drop_in_place_and_keep_alloc(self) -> DynSimOnlyValueUninit { + let retval = self.forget_and_keep_alloc(); + unsafe { (retval.ty.vtable.drop_in_place)(retval.value) }; + retval + } +} diff --git a/crates/fayalite/src/ty.rs b/crates/fayalite/src/ty.rs index 787869d..02df46a 100644 --- a/crates/fayalite/src/ty.rs +++ b/crates/fayalite/src/ty.rs @@ -11,13 +11,16 @@ use crate::{ intern::{Intern, Interned}, phantom_const::PhantomConst, reset::{AsyncReset, Reset, SyncReset}, - sim::value::{SimValue, ToSimValueWithType}, + sim::value::{ + DynSimOnlyValue, DynSimOnlyValueType, SimValue, ToSimValueWithType, + assert_matching_sim_values, + }, source_location::SourceLocation, util::ConstUsize, }; use bitvec::slice::BitSlice; use serde::{Deserialize, Deserializer, Serialize, Serializer, de::DeserializeOwned}; -use std::{fmt, hash::Hash, iter::FusedIterator, ops::Index, sync::Arc}; +use std::{fmt, hash::Hash, iter::FusedIterator, marker::PhantomData, mem, ops::Index, sync::Arc}; pub(crate) mod serde_impls; @@ -28,6 +31,20 @@ pub struct TypeProperties { pub is_storable: bool, pub is_castable_from_bits: bool, pub bit_width: usize, + pub sim_only_values_len: usize, +} + +impl TypeProperties { + #[track_caller] + pub(crate) fn assert_matching_opaque_sim_value(self, opaque: OpaqueSimValueSlice<'_>) { + assert_eq!(self.bit_width, opaque.bit_width()); + assert_eq!(self.sim_only_values_len, opaque.sim_only_values().len()); + } + #[track_caller] + pub(crate) fn assert_matching_opaque_sim_value_writer(self, writer: &OpaqueSimValueWriter<'_>) { + assert_eq!(self.bit_width, writer.bit_width()); + assert_eq!(self.sim_only_values_len, writer.sim_only_values_len()); + } } #[derive(Copy, Clone, Hash, PartialEq, Eq)] @@ -43,6 +60,7 @@ pub enum CanonicalType { Reset(Reset), Clock(Clock), PhantomConst(PhantomConst), + DynSimOnlyValueType(DynSimOnlyValueType), } impl fmt::Debug for CanonicalType { @@ -59,6 +77,7 @@ impl fmt::Debug for CanonicalType { Self::Reset(v) => v.fmt(f), Self::Clock(v) => v.fmt(f), Self::PhantomConst(v) => v.fmt(f), + Self::DynSimOnlyValueType(v) => v.fmt(f), } } } @@ -95,6 +114,7 @@ impl CanonicalType { CanonicalType::Reset(v) => v.type_properties(), CanonicalType::Clock(v) => v.type_properties(), CanonicalType::PhantomConst(v) => v.type_properties(), + CanonicalType::DynSimOnlyValueType(v) => v.type_properties(), } } pub fn is_passive(self) -> bool { @@ -177,6 +197,12 @@ impl CanonicalType { }; lhs.can_connect(rhs) } + CanonicalType::DynSimOnlyValueType(lhs) => { + let CanonicalType::DynSimOnlyValueType(rhs) = rhs else { + return false; + }; + lhs.can_connect(rhs) + } } } pub(crate) fn as_serde_unexpected_str(self) -> &'static str { @@ -287,6 +313,7 @@ impl_base_type!(SyncReset); impl_base_type!(Reset); impl_base_type!(Clock); impl_base_type!(PhantomConst); +impl_base_type!(DynSimOnlyValueType); impl_base_type_serde!(Bool, "a Bool"); impl_base_type_serde!(Enum, "an Enum"); @@ -348,9 +375,17 @@ pub trait Type: fn canonical(&self) -> CanonicalType; fn from_canonical(canonical_type: CanonicalType) -> Self; fn source_location() -> SourceLocation; - fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue; - fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice); - fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice); + fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue; + fn sim_value_clone_from_opaque( + &self, + value: &mut Self::SimValue, + opaque: OpaqueSimValueSlice<'_>, + ); + fn sim_value_to_opaque<'w>( + &self, + value: &Self::SimValue, + writer: OpaqueSimValueWriter<'w>, + ) -> OpaqueSimValueWritten<'w>; } pub trait BaseType: @@ -405,6 +440,7 @@ impl Type for CanonicalType { CanonicalType::Reset(v) => v.mask_type().canonical(), CanonicalType::Clock(v) => v.mask_type().canonical(), CanonicalType::PhantomConst(v) => v.mask_type().canonical(), + CanonicalType::DynSimOnlyValueType(v) => v.mask_type().canonical(), } } fn canonical(&self) -> CanonicalType { @@ -416,28 +452,46 @@ impl Type for CanonicalType { fn source_location() -> SourceLocation { SourceLocation::builtin() } - fn sim_value_from_bits(&self, bits: &BitSlice) -> Self::SimValue { - assert_eq!(bits.len(), self.bit_width()); - OpaqueSimValue::from_bitslice(bits) + fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue { + self.type_properties() + .assert_matching_opaque_sim_value(opaque); + opaque.to_owned() } - fn sim_value_clone_from_bits(&self, value: &mut Self::SimValue, bits: &BitSlice) { - assert_eq!(bits.len(), self.bit_width()); - assert_eq!(value.bit_width(), self.bit_width()); - value.bits_mut().bits_mut().copy_from_bitslice(bits); + fn sim_value_clone_from_opaque( + &self, + value: &mut Self::SimValue, + opaque: OpaqueSimValueSlice<'_>, + ) { + self.type_properties() + .assert_matching_opaque_sim_value(opaque); + value.clone_from_slice(opaque); } - fn sim_value_to_bits(&self, value: &Self::SimValue, bits: &mut BitSlice) { - assert_eq!(bits.len(), self.bit_width()); - assert_eq!(value.bit_width(), self.bit_width()); - bits.copy_from_bitslice(value.bits().bits()); + fn sim_value_to_opaque<'w>( + &self, + value: &Self::SimValue, + writer: OpaqueSimValueWriter<'w>, + ) -> OpaqueSimValueWritten<'w> { + self.type_properties() + .assert_matching_opaque_sim_value_writer(&writer); + writer.fill_cloned_from_slice(value.as_slice()) } } #[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)] pub struct OpaqueSimValue { bits: UIntValue, + #[serde(skip_serializing_if = "Vec::is_empty", default)] + sim_only_values: Vec, } impl OpaqueSimValue { + pub fn is_empty(&self) -> bool { + let Self { + bits, + sim_only_values, + } = self; + bits.bits().is_empty() && sim_only_values.is_empty() + } pub fn bit_width(&self) -> usize { self.bits.width() } @@ -451,13 +505,86 @@ impl OpaqueSimValue { self.bits } pub fn from_bits(bits: UIntValue) -> Self { - Self { bits } + Self { + bits, + sim_only_values: Vec::new(), + } } pub fn from_bitslice(v: &BitSlice) -> Self { + Self::from_bitslice_and_sim_only_values(v, Vec::new()) + } + pub fn from_bitslice_and_sim_only_values( + bits: &BitSlice, + sim_only_values: Vec, + ) -> Self { Self { - bits: UIntValue::new(Arc::new(v.to_bitvec())), + bits: UIntValue::new(Arc::new(bits.to_bitvec())), + sim_only_values, } } + pub fn from_bits_and_sim_only_values( + bits: UIntValue, + sim_only_values: Vec, + ) -> Self { + Self { + bits, + sim_only_values, + } + } + pub fn into_parts(self) -> (UIntValue, Vec) { + let Self { + bits, + sim_only_values, + } = self; + (bits, sim_only_values) + } + pub fn parts_mut(&mut self) -> (&mut UIntValue, &mut Vec) { + let Self { + bits, + sim_only_values, + } = self; + (bits, sim_only_values) + } + pub fn sim_only_values(&self) -> &[DynSimOnlyValue] { + &self.sim_only_values + } + pub fn sim_only_values_mut(&mut self) -> &mut Vec { + &mut self.sim_only_values + } + pub fn as_slice(&self) -> OpaqueSimValueSlice<'_> { + OpaqueSimValueSlice { + bits: self.bits.bits(), + sim_only_values: &self.sim_only_values, + } + } + pub fn slice( + &self, + bits_range: BitsRange, + sim_only_values_range: SOVRange, + ) -> OpaqueSimValueSlice<'_> + where + BitSlice: Index, + [DynSimOnlyValue]: Index, + { + self.as_slice().slice(bits_range, sim_only_values_range) + } + pub fn rewrite_with(&mut self, types: Interned<[DynSimOnlyValueType]>, f: F) + where + F: for<'b> FnOnce(OpaqueSimValueWriter<'b>) -> OpaqueSimValueWritten<'b>, // 'b is used as a brand + { + OpaqueSimValueWriter::rewrite_with(types, self, f); + } + pub fn rewrite_with_static(&mut self, types: &'static [DynSimOnlyValueType], f: F) + where + F: for<'b> FnOnce(OpaqueSimValueWriter<'b>) -> OpaqueSimValueWritten<'b>, // 'b is used as a brand + { + OpaqueSimValueWriter::rewrite_with_static(types, self, f); + } + pub fn clone_from_slice(&mut self, slice: OpaqueSimValueSlice<'_>) { + self.bits.bits_mut().clone_from_bitslice(slice.bits()); + self.sim_only_values + .clone_from_slice(slice.sim_only_values()); + } } impl> ToSimValueWithType for OpaqueSimValue { @@ -469,6 +596,163 @@ impl> ToSimValueWithType for OpaqueSimValu } } +#[derive(Copy, Clone, Debug)] +pub struct OpaqueSimValueSlice<'a> { + bits: &'a BitSlice, + sim_only_values: &'a [DynSimOnlyValue], +} + +impl<'a> Default for OpaqueSimValueSlice<'a> { + fn default() -> Self { + Self::empty() + } +} + +impl<'a> OpaqueSimValueSlice<'a> { + pub fn from_parts(bits: &'a BitSlice, sim_only_values: &'a [DynSimOnlyValue]) -> Self { + Self { + bits, + sim_only_values, + } + } + pub fn empty() -> Self { + Self { + bits: BitSlice::empty(), + sim_only_values: &[], + } + } + pub fn is_empty(self) -> bool { + let Self { + bits, + sim_only_values, + } = self; + bits.is_empty() && sim_only_values.is_empty() + } + pub fn bit_width(self) -> usize { + self.bits.len() + } + pub fn bits(self) -> &'a BitSlice { + self.bits + } + pub fn sim_only_values(self) -> &'a [DynSimOnlyValue] { + self.sim_only_values + } + pub fn to_owned(self) -> OpaqueSimValue { + OpaqueSimValue::from_bitslice_and_sim_only_values(self.bits, self.sim_only_values.to_vec()) + } + pub fn slice( + self, + bits_range: BitsRange, + sim_only_values_range: SOVRange, + ) -> OpaqueSimValueSlice<'a> + where + BitSlice: Index, + [DynSimOnlyValue]: Index, + { + Self { + bits: &self.bits[bits_range], + sim_only_values: &self.sim_only_values[sim_only_values_range], + } + } +} + +#[derive(Debug)] +pub struct OpaqueSimValueWriter<'a> { + bits: &'a mut BitSlice, + sim_only_values: &'a mut Vec, + sim_only_values_range: std::ops::Range, +} + +#[derive(Debug)] +pub struct OpaqueSimValueWritten<'a> { + _phantom: PhantomData<&'a ()>, +} + +impl<'a> OpaqueSimValueWriter<'a> { + pub fn rewrite_with(sim_only_values_len: usize, value: &mut OpaqueSimValue, f: F) + where + F: for<'b> FnOnce(OpaqueSimValueWriter<'b>) -> OpaqueSimValueWritten<'b>, // 'b is used as a brand + { + let OpaqueSimValueWritten { + _phantom: PhantomData, + } = f(OpaqueSimValueWriter::rewrite_helper( + sim_only_values_len, + value, + )); + } + pub(crate) fn rewrite_helper( + sim_only_values_len: usize, + value: &'a mut OpaqueSimValue, + ) -> Self { + let (bits_mut, sim_only_values) = value.parts_mut(); + sim_only_values.truncate(sim_only_values_len); + sim_only_values.reserve_exact(sim_only_values_len - sim_only_values.len()); + Self { + bits: bits_mut.bits_mut(), + sim_only_values, + sim_only_values_range: 0..sim_only_values_len, + } + } + pub fn bit_width(&self) -> usize { + self.bits.len() + } + pub fn sim_only_values_len(&self) -> usize { + self.sim_only_values_range.len() + } + pub fn is_empty(&self) -> bool { + let Self { + bits, + sim_only_values: _, + sim_only_values_range, + } = self; + bits.is_empty() && sim_only_values_range.is_empty() + } + pub fn fill_cloned_from_slice( + self, + slice: OpaqueSimValueSlice<'_>, + ) -> OpaqueSimValueWritten<'a> { + let Self { + bits, + sim_only_values, + sim_only_values_range, + } = self; + assert_eq!(slice.bit_width(), bits.len()); + assert_eq!(slice.sim_only_values().len(), sim_only_values_range.len()); + bits.copy_from_bitslice(slice.bits); + let (clone_from_src, clone_src) = slice.sim_only_values.split_at( + (sim_only_values.len() - sim_only_values_range.start).min(slice.sim_only_values.len()), + ); + sim_only_values[sim_only_values_range][..clone_from_src.len()] + .clone_from_slice(clone_from_src); + sim_only_values.extend_from_slice(clone_src); + OpaqueSimValueWritten { + _phantom: PhantomData, + } + } + pub fn fill_prefix_with( + &mut self, + prefix_bit_width: usize, + prefix_sim_only_values_len: usize, + f: F, + ) where + F: for<'b> FnOnce(OpaqueSimValueWriter<'b>) -> OpaqueSimValueWritten<'b>, // 'b is used as a brand + { + assert!(prefix_bit_width <= self.bit_width()); + assert!(prefix_sim_only_values_len <= self.sim_only_values_len()); + let next_start = self.sim_only_values_range.start + prefix_sim_only_values_len; + let OpaqueSimValueWritten { + _phantom: PhantomData, + } = f(OpaqueSimValueWriter { + bits: &mut self.bits[..prefix_bit_width], + sim_only_values: self.sim_only_values, + sim_only_values_range: self.sim_only_values_range.start..next_start, + }); + assert!(self.sim_only_values.len() >= next_start); + self.bits = &mut mem::take(&mut self.bits)[prefix_bit_width..]; + self.sim_only_values_range.start = next_start; + } +} + pub trait StaticType: Type + Default { const TYPE: Self; const MASK_TYPE: Self::MaskType; diff --git a/crates/fayalite/src/util.rs b/crates/fayalite/src/util.rs index 4670a1f..e550464 100644 --- a/crates/fayalite/src/util.rs +++ b/crates/fayalite/src/util.rs @@ -9,6 +9,7 @@ mod misc; mod scoped_ref; pub(crate) mod streaming_read_utf8; mod test_hasher; +mod type_id_serde; // allow easily switching the hasher crate-wide for testing #[cfg(feature = "unstable-test-hasher")] @@ -39,6 +40,9 @@ pub use misc::{ iter_eq_by, }; +#[doc(inline)] +pub use type_id_serde::TypeIdSerde; + pub mod job_server; pub mod prefix_sum; pub mod ready_valid; diff --git a/crates/fayalite/src/util/type_id_serde.rs b/crates/fayalite/src/util/type_id_serde.rs new file mode 100644 index 0000000..35408fb --- /dev/null +++ b/crates/fayalite/src/util/type_id_serde.rs @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use hashbrown::{HashMap, hash_map::Entry}; +use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error}; +use std::{ + any::TypeId, + fmt::Write, + hash::{BuildHasher, Hash, Hasher, RandomState}, + sync::Mutex, +}; + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +/// [`TypeId`] that implements [`Serialize`] and [`Deserialize`] for use only within a single running instance of a +/// single program. If you try to transfer the serialized value from one program to another, it should fail to +/// deserialize with extremely high probability since we use 128-bit random numbers from [`RandomState`] (though +/// they may be deterministic on some targets). Note that those 128-bit random numbers are *not* the same number as is +/// stored in the inner [`TypeId`]. +pub struct TypeIdSerde(pub TypeId); + +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)] +#[serde(rename = "TypeIdSerde")] +struct TypeIdSerdeId([u32; 4]); + +#[derive(Default)] +struct TypeIdSerdeIdMapInner { + serde_id_to_type_id: HashMap, + serde_id_random_state: RandomState, + buffer: String, +} + +impl TypeIdSerdeIdMapInner { + #[cold] + fn new_serde_id(&mut self, type_id: TypeId) -> TypeIdSerdeId { + let mut try_number = 0u64; + let mut hasher = self.serde_id_random_state.build_hasher(); + // extract more bits of randomness from TypeId -- its Hash impl only hashes 64-bits + self.buffer.clear(); + write!(self.buffer, "{type_id:?}").expect("shouldn't ever fail"); + self.buffer.hash(&mut hasher); + loop { + let mut hasher = hasher.clone(); + try_number.hash(&mut hasher); + try_number += 1; + let retval = TypeIdSerdeId(std::array::from_fn(|i| { + let mut hasher = hasher.clone(); + i.hash(&mut hasher); + hasher.finish() as u32 + })); + match self.serde_id_to_type_id.entry(retval) { + Entry::Occupied(_) => continue, + Entry::Vacant(e) => { + e.insert(type_id); + return retval; + } + } + } + } +} + +#[derive(Default)] +struct TypeIdSerdeIdMap { + type_id_to_serde_id: HashMap, + inner: TypeIdSerdeIdMapInner, +} + +static TYPE_ID_SERDE_ID_MAP: Mutex> = Mutex::new(None); + +impl Serialize for TypeIdSerde { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut type_id_serde_id_map = TYPE_ID_SERDE_ID_MAP.lock().unwrap(); + let map = type_id_serde_id_map.get_or_insert_default(); + let serde_id = match map.type_id_to_serde_id.entry(self.0) { + Entry::Occupied(e) => *e.get(), + Entry::Vacant(e) => *e.insert(map.inner.new_serde_id(self.0)), + }; + drop(type_id_serde_id_map); + serde_id.serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for TypeIdSerde { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let serde_id = TypeIdSerdeId::deserialize(deserializer)?; + let mut type_id_serde_id_map = TYPE_ID_SERDE_ID_MAP.lock().unwrap(); + Ok(TypeIdSerde(*type_id_serde_id_map + .as_mut() + .and_then(|map| map.inner.serde_id_to_type_id.get(&serde_id)) + .ok_or_else(|| { + D::Error::custom( + "TypeIdSerde value from a different invocation of this program which is not allowed", + ) + })?)) + } +}