From ed244d7cc615b2804d4e4602ba568fbf9945a110 Mon Sep 17 00:00:00 2001 From: Nicolas Sarlin Date: Fri, 2 Aug 2024 10:19:20 +0200 Subject: [PATCH 1/3] feat(versionable): Add support for statically sized arrays --- utils/tfhe-versionable/src/lib.rs | 49 ++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/utils/tfhe-versionable/src/lib.rs b/utils/tfhe-versionable/src/lib.rs index 3765339d85..7451b4676b 100644 --- a/utils/tfhe-versionable/src/lib.rs +++ b/utils/tfhe-versionable/src/lib.rs @@ -81,6 +81,12 @@ pub enum UnversionizeError { from_type: String, source: Box, }, + + /// The length of a statically sized array is wrong + ArrayLength { + expected_size: usize, + found_size: usize, + }, } impl Display for UnversionizeError { @@ -97,6 +103,15 @@ impl Display for UnversionizeError { Self::Conversion { from_type, source } => { write!(f, "Failed to convert from {from_type}: {source}") } + Self::ArrayLength { + expected_size, + found_size, + } => { + write!( + f, + "Expected array of size {expected_size}, found array of size {found_size}" + ) + } } } } @@ -106,6 +121,7 @@ impl Error for UnversionizeError { match self { UnversionizeError::Upgrade { source, .. } => Some(source.as_ref()), UnversionizeError::Conversion { source, .. } => Some(source.as_ref()), + UnversionizeError::ArrayLength { .. } => None, } } } @@ -262,7 +278,7 @@ impl VersionizeOwned for Box<[T]> { type VersionedOwned = T::VersionedVec; fn versionize_owned(self) -> Self::VersionedOwned { - T::versionize_vec(self.iter().cloned().collect()) + T::versionize_vec(self.to_vec()) } } @@ -312,6 +328,37 @@ impl Unversionize for Vec { } } +// Since serde doesn't support arbitrary length arrays with const generics, the array +// is converted to a slice/vec. +impl Versionize for [T; N] { + type Versioned<'vers> = T::VersionedSlice<'vers> where T: 'vers; + + fn versionize(&self) -> Self::Versioned<'_> { + T::versionize_slice(self) + } +} + +impl VersionizeOwned for [T; N] { + type VersionedOwned = T::VersionedVec; + + fn versionize_owned(self) -> Self::VersionedOwned { + T::versionize_vec(self.to_vec()) + } +} + +impl Unversionize for [T; N] { + fn unversionize(versioned: Self::VersionedOwned) -> Result { + let v = T::unversionize_vec(versioned)?; + let boxed_slice = v.into_boxed_slice(); + TryInto::>::try_into(boxed_slice) + .map(|array| *array) + .map_err(|slice| UnversionizeError::ArrayLength { + expected_size: N, + found_size: slice.len(), + }) + } +} + impl Versionize for String { type Versioned<'vers> = &'vers str; From 8a9570bc60d614861af4dc324c5dc9cbcab14a2f Mon Sep 17 00:00:00 2001 From: Nicolas Sarlin Date: Fri, 2 Aug 2024 15:10:23 +0200 Subject: [PATCH 2/3] feat(versionable): impl Versionize for `Wrapping` --- utils/tfhe-versionable/src/lib.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/utils/tfhe-versionable/src/lib.rs b/utils/tfhe-versionable/src/lib.rs index 7451b4676b..45f7d625a0 100644 --- a/utils/tfhe-versionable/src/lib.rs +++ b/utils/tfhe-versionable/src/lib.rs @@ -16,6 +16,7 @@ use std::convert::Infallible; use std::error::Error; use std::fmt::Display; use std::marker::PhantomData; +use std::num::Wrapping; use std::sync::Arc; pub use derived_traits::{Version, VersionsDispatch}; @@ -244,6 +245,30 @@ impl_scalar_versionize!(f64); impl_scalar_versionize!(char); +impl Versionize for Wrapping { + type Versioned<'vers> = Wrapping> where T: 'vers; + + fn versionize(&self) -> Self::Versioned<'_> { + Wrapping(self.0.versionize()) + } +} + +impl VersionizeOwned for Wrapping { + type VersionedOwned = Wrapping; + + fn versionize_owned(self) -> Self::VersionedOwned { + Wrapping(T::versionize_owned(self.0)) + } +} + +impl Unversionize for Wrapping { + fn unversionize(versioned: Self::VersionedOwned) -> Result { + Ok(Wrapping(T::unversionize(versioned.0)?)) + } +} + +impl NotVersioned for Wrapping {} + impl Versionize for Box { type Versioned<'vers> = T::Versioned<'vers> where T: 'vers; From a52fc90662f56f04b611c61d34eeb91bf7c10f03 Mon Sep 17 00:00:00 2001 From: Nicolas Sarlin Date: Fri, 2 Aug 2024 16:29:38 +0200 Subject: [PATCH 3/3] test(versionable): test `Versionize` with various rust types --- utils/tfhe-versionable/tests/types.rs | 74 +++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 utils/tfhe-versionable/tests/types.rs diff --git a/utils/tfhe-versionable/tests/types.rs b/utils/tfhe-versionable/tests/types.rs new file mode 100644 index 0000000000..1ef96a6ef2 --- /dev/null +++ b/utils/tfhe-versionable/tests/types.rs @@ -0,0 +1,74 @@ +//! Test the impl of Versionize for various types + +use std::collections::{HashMap, HashSet}; +use std::io::Cursor; +use std::marker::PhantomData; +use std::num::Wrapping; +use std::sync::Arc; + +use aligned_vec::{ABox, AVec}; +use num_complex::Complex; +use tfhe_versionable::{Unversionize, Versionize}; + +use backward_compat::MyStructVersions; + +#[derive(PartialEq, Clone, Debug, Versionize)] +#[versionize(MyStructVersions)] +pub struct MyStruct { + wrap: Wrapping, + base_box: Box, + sliced_box: Box<[u16; 50]>, + base_vec: Vec, + s: String, + opt: Option, + phantom: PhantomData, + arc: Arc, + complex: Complex, + aligned_box: ABox, + aligned_vec: AVec, + never: (), + tuple: (f32, f64), + set: HashSet, + map: HashMap, +} + +mod backward_compat { + use tfhe_versionable::VersionsDispatch; + + use super::MyStruct; + + #[derive(VersionsDispatch)] + #[allow(unused)] + pub enum MyStructVersions { + V0(MyStruct), + } +} + +#[test] +fn test_types() { + let stru = MyStruct { + wrap: Wrapping(false), + base_box: Box::new(42), + sliced_box: vec![11; 50].into_boxed_slice().try_into().unwrap(), + base_vec: vec![1234, 5678], + s: String::from("test"), + opt: Some(0xdeadbeef), + phantom: PhantomData, + arc: Arc::new(-1), + complex: Complex::new(-37, -45), + aligned_box: ABox::new(0, -98765), + aligned_vec: AVec::from_slice(0, &[1, 2, 3, 4]), + never: (), + tuple: (3.14, 2.71), + set: HashSet::from_iter([1, 2, 3].into_iter()), + map: HashMap::from_iter([('t', true), ('e', false), ('s', true)].into_iter()), + }; + + let mut ser = Vec::new(); + ciborium::ser::into_writer(&stru.versionize(), &mut ser).unwrap(); + + let unser = + MyStruct::unversionize(ciborium::de::from_reader(&mut Cursor::new(&ser)).unwrap()).unwrap(); + + assert_eq!(stru, unser); +}