Skip to content

Commit

Permalink
chore(versionable): update examples
Browse files Browse the repository at this point in the history
Mostly test in the main that the derived code actually works
  • Loading branch information
nsarlin-zama committed Sep 18, 2024
1 parent 4d1a7de commit 68821b1
Show file tree
Hide file tree
Showing 6 changed files with 245 additions and 50 deletions.
38 changes: 38 additions & 0 deletions utils/tfhe-versionable/examples/associated_bounds.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/// In this example, we use a generic that is not versionable itself. Only its associated types
/// should be versioned.
use tfhe_versionable::{Versionize, VersionsDispatch};

trait WithAssociated {
type Assoc;
type OtherAssoc;
}

struct Marker;

impl WithAssociated for Marker {
type Assoc = u64;

type OtherAssoc = u32;
}

#[derive(VersionsDispatch)]
#[allow(unused)]
enum MyStructVersions<T: WithAssociated> {
V0(MyStruct<T>),
}

#[derive(Versionize)]
#[versionize(MyStructVersions)]
struct MyStruct<T: WithAssociated> {
val: T::Assoc,
other_val: T::OtherAssoc,
}

fn main() {
let ms = MyStruct::<Marker> {
val: 27,
other_val: 54,
};

ms.versionize();
}
90 changes: 56 additions & 34 deletions utils/tfhe-versionable/examples/bounds.rs
Original file line number Diff line number Diff line change
@@ -1,52 +1,74 @@
//! This example shows how to use the `bound` attribute to add a specific bound that is needed to be
//! able to derive `Versionize`
//! Example of a simple struct with an Upgrade impl that requires a specific bound.
//! In that case, the previous versions of the type used a string as a representation, but it has
//! been changed to a Generic. For the upgrade to work, we need to be able to create this generic
//! from a String.
use serde::de::DeserializeOwned;
use serde::Serialize;
use tfhe_versionable::{
Unversionize, UnversionizeError, Versionize, VersionizeOwned, VersionsDispatch,
};
use std::error::Error;
use std::io::Cursor;
use std::str::FromStr;

// Example of a simple struct with a manual Versionize impl that requires a specific bound
struct MyStruct<T> {
val: T,
}
use tfhe_versionable::{Unversionize, Upgrade, Version, Versionize, VersionsDispatch};

impl<T: Serialize + DeserializeOwned> Versionize for MyStruct<T> {
type Versioned<'vers> = &'vers T where T: 'vers;
/// The previous version of our application
mod v0 {
use tfhe_versionable::{Versionize, VersionsDispatch};

fn versionize(&self) -> Self::Versioned<'_> {
&self.val
#[derive(Versionize)]
#[versionize(MyStructVersions)]
pub(super) struct MyStruct {
pub(super) val: String,
}
}

impl<T: Serialize + DeserializeOwned + ToOwned<Owned = T>> VersionizeOwned for MyStruct<T> {
type VersionedOwned = T;

fn versionize_owned(self) -> Self::VersionedOwned {
self.val.to_owned()
#[derive(VersionsDispatch)]
#[allow(unused)]
pub(super) enum MyStructVersions {
V0(MyStruct),
}
}

impl<T: Serialize + DeserializeOwned + ToOwned<Owned = T>> Unversionize for MyStruct<T> {
fn unversionize(versioned: Self::VersionedOwned) -> Result<Self, UnversionizeError> {
Ok(MyStruct { val: versioned })
}
#[derive(Version)]
struct MyStructV0 {
val: String,
}

// The additional bound can be specified on the parent struct using this attribute. This is similar
// to what serde does. You can also use #[versionize(OuterVersions, bound(unversionize = "T:
// ToOwned<Owned = T>"))] if the bound is only needed for the Unversionize impl.
#[derive(Versionize)]
#[versionize(OuterVersions, bound = "T: ToOwned<Owned = T>")]
struct Outer<T> {
inner: MyStruct<T>,
#[versionize(MyStructVersions)]
struct MyStruct<T> {
val: T,
}

impl<T: FromStr> Upgrade<MyStruct<T>> for MyStructV0
where
<T as FromStr>::Err: Error + Send + Sync + 'static,
{
type Error = <T as FromStr>::Err;

fn upgrade(self) -> Result<MyStruct<T>, Self::Error> {
let val = T::from_str(&self.val)?;

Ok(MyStruct { val })
}
}

#[derive(VersionsDispatch)]
#[allow(unused)]
enum OuterVersions<T: ToOwned<Owned = T>> {
V0(Outer<T>),
enum MyStructVersions<T> {
V0(MyStructV0),
V1(MyStruct<T>),
}

fn main() {}
fn main() {
let val = 64;
let stru_v0 = v0::MyStruct {
val: format!("{val}"),
};

let mut ser = Vec::new();
ciborium::ser::into_writer(&stru_v0.versionize(), &mut ser).unwrap();

let unvers =
MyStruct::<u64>::unversionize(ciborium::de::from_reader(&mut Cursor::new(&ser)).unwrap())
.unwrap();

assert_eq!(unvers.val, val);
}
11 changes: 9 additions & 2 deletions utils/tfhe-versionable/examples/not_versioned.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! This example shows how to create a type that should not be versioned, even if it is
//! included in other versioned types
//! included in other versioned types. Of course it means that if this type is modified in the
//! future, the parent struct should be updated.
use serde::{Deserialize, Serialize};
use tfhe_versionable::{NotVersioned, Versionize, VersionsDispatch};
Expand All @@ -21,4 +22,10 @@ enum MyStructVersions {
V0(MyStruct),
}

fn main() {}
fn main() {
let ms = MyStruct {
inner: MyStructNotVersioned { val: 1234 },
};

let _versioned = ms.versionize();
}
50 changes: 46 additions & 4 deletions utils/tfhe-versionable/examples/recursive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
use std::convert::Infallible;

use tfhe_versionable::{Upgrade, Version, Versionize, VersionsDispatch};
use tfhe_versionable::{Unversionize, Upgrade, Version, Versionize, VersionsDispatch};

// The inner struct is independently versioned
#[derive(Versionize)]
#[versionize(MyStructInnerVersions)]
struct MyStructInner<T: Default> {
Expand All @@ -13,7 +14,7 @@ struct MyStructInner<T: Default> {

#[derive(Version)]
struct MyStructInnerV0 {
attr: u32,
builtin: u32,
}

impl<T: Default> Upgrade<MyStructInner<T>> for MyStructInnerV0 {
Expand All @@ -22,7 +23,7 @@ impl<T: Default> Upgrade<MyStructInner<T>> for MyStructInnerV0 {
fn upgrade(self) -> Result<MyStructInner<T>, Self::Error> {
Ok(MyStructInner {
attr: T::default(),
builtin: 0,
builtin: self.builtin,
})
}
}
Expand All @@ -34,6 +35,7 @@ enum MyStructInnerVersions<T: Default> {
V1(MyStructInner<T>),
}

// An upgrade of the inner struct does not require an upgrade of the outer struct
#[derive(Versionize)]
#[versionize(MyStructVersions)]
struct MyStruct<T: Default> {
Expand All @@ -46,4 +48,44 @@ enum MyStructVersions<T: Default> {
V0(MyStruct<T>),
}

fn main() {}
mod v0 {
use tfhe_versionable::{Versionize, VersionsDispatch};

#[derive(Versionize)]
#[versionize(MyStructInnerVersions)]
pub(super) struct MyStructInner {
pub(super) builtin: u32,
}

#[derive(VersionsDispatch)]
#[allow(unused)]
pub(super) enum MyStructInnerVersions {
V0(MyStructInner),
}

#[derive(Versionize)]
#[versionize(MyStructVersions)]
pub(super) struct MyStruct {
pub(super) inner: MyStructInner,
}

#[derive(VersionsDispatch)]
#[allow(unused)]
pub(super) enum MyStructVersions {
V0(MyStruct),
}
}

fn main() {
let builtin = 654;
let inner = v0::MyStructInner { builtin: 654 };
let ms = v0::MyStruct { inner };

let serialized = bincode::serialize(&ms.versionize()).unwrap();

// This can be called in future versions of your application, when more variants have been added
let unserialized =
MyStruct::<u64>::unversionize(bincode::deserialize(&serialized).unwrap()).unwrap();

assert_eq!(unserialized.inner.builtin, builtin);
}
30 changes: 25 additions & 5 deletions utils/tfhe-versionable/examples/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,34 @@ enum MyStructVersions<T: Default> {
V1(MyStruct<T>),
}

mod v0 {
// This module simulates an older version of our app where we initiated the versioning process.
// In real life code this would likely be only present in your git history.
use tfhe_versionable::{Versionize, VersionsDispatch};

#[derive(Versionize)]
#[versionize(MyStructVersions)]
pub(super) struct MyStruct {
pub(super) builtin: u32,
}

#[derive(VersionsDispatch)]
#[allow(unused)]
pub(super) enum MyStructVersions {
V0(MyStruct),
}
}

fn main() {
let ms = MyStruct {
attr: 37u64,
builtin: 1234,
};
// In the past we saved a value
let value = 1234;
let ms = v0::MyStruct { builtin: value };

let serialized = bincode::serialize(&ms.versionize()).unwrap();

// This can be called in future versions of your application, when more variants have been added
let _unserialized = MyStruct::<u64>::unversionize(bincode::deserialize(&serialized).unwrap());
let unserialized =
MyStruct::<u64>::unversionize(bincode::deserialize(&serialized).unwrap()).unwrap();

assert_eq!(unserialized.builtin, value);
}
76 changes: 71 additions & 5 deletions utils/tfhe-versionable/examples/vec.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
/// The `VersionizeVec` and `UnversionizeVec` traits are also automatically derived
/// So that Vec can be versioned as well
use tfhe_versionable::{Versionize, VersionsDispatch};
//! The `VersionizeVec` and `UnversionizeVec` traits are also automatically derived
//! So that Vec can be versioned as well. Because of the recursivity, each element of the vec
//! has its own version tag. For built-in rust types and anything that derives `NotVersioned`,
//! the versioning of the whole vec is skipped.
use std::convert::Infallible;

use tfhe_versionable::{Unversionize, Upgrade, Version, Versionize, VersionsDispatch};

#[derive(Version)]
struct MyStructInnerV0 {
val: u64,
}

#[derive(Versionize)]
#[versionize(MyStructInnerVersions)]
Expand All @@ -9,10 +19,22 @@ struct MyStructInner<T> {
gen: T,
}

impl<T: Default> Upgrade<MyStructInner<T>> for MyStructInnerV0 {
type Error = Infallible;

fn upgrade(self) -> Result<MyStructInner<T>, Self::Error> {
Ok(MyStructInner {
val: self.val,
gen: T::default(),
})
}
}

#[derive(VersionsDispatch)]
#[allow(unused)]
enum MyStructInnerVersions<T> {
V0(MyStructInner<T>),
V0(MyStructInnerV0),
V1(MyStructInner<T>),
}

#[derive(Versionize)]
Expand All @@ -27,4 +49,48 @@ enum MyVecVersions<T> {
V0(MyVec<T>),
}

fn main() {}
mod v0 {
use tfhe_versionable::{Versionize, VersionsDispatch};

#[derive(Versionize)]
#[versionize(MyStructInnerVersions)]
pub(super) struct MyStructInner {
pub(super) val: u64,
}

#[derive(VersionsDispatch)]
#[allow(unused)]
pub(super) enum MyStructInnerVersions {
V0(MyStructInner),
}

#[derive(Versionize)]
#[versionize(MyVecVersions)]
pub(super) struct MyVec {
pub(super) vec: Vec<MyStructInner>,
}

#[derive(VersionsDispatch)]
#[allow(unused)]
pub(super) enum MyVecVersions {
V0(MyVec),
}
}

fn main() {
let values: [u64; 6] = [12, 23, 34, 45, 56, 67];
let vec = values
.iter()
.map(|val| v0::MyStructInner { val: *val })
.collect();
let mv = v0::MyVec { vec };

let serialized = bincode::serialize(&mv.versionize()).unwrap();

let unserialized =
MyVec::<u64>::unversionize(bincode::deserialize(&serialized).unwrap()).unwrap();

let unser_values: Vec<u64> = unserialized.vec.iter().map(|inner| inner.val).collect();

assert_eq!(unser_values, values);
}

0 comments on commit 68821b1

Please sign in to comment.