From 20836836cdaa8db32936db9f6023f8421343452c Mon Sep 17 00:00:00 2001 From: benesjan Date: Thu, 9 Jan 2025 19:25:36 +0000 Subject: [PATCH] feat: Packable trait --- .../crates/types/src/traits.nr | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/traits.nr b/noir-projects/noir-protocol-circuits/crates/types/src/traits.nr index 4034448eda53..8bc896fab236 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/traits.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/traits.nr @@ -155,6 +155,35 @@ impl FromField for U128 { } // docs:start:serialize +/// Trait for serializing Noir types into arrays of Fields in a constraint-efficient manner. +/// +/// This trait prioritizes minimal constraint costs during serialization, even if it +/// results in larger array sizes. Each value is typically converted directly into one +/// or more Fields without any packing or compression. This trait (and Deserialize) are +/// typically used to communicate between Noir and TypeScript (via oracles and function +/// arguments). +/// +/// # Type Parameters +/// * `N` - The length of the output Field array, known at compile time +/// +/// # Implementation Notes +/// - Focus on simple, direct conversions to minimize constraints +/// - Each component of a type typically maps to its own Field +/// - Do not attempt to pack or compress values (use Packable trait for that) +/// +/// # Example +/// ``` +/// impl Serialize for str { +/// fn serialize(self) -> [Field; N] { +/// let bytes = self.as_bytes(); +/// let mut fields = [0; N]; +/// for i in 0..bytes.len() { +/// fields[i] = bytes[i] as Field; // Each byte gets its own Field +/// } +/// fields +/// } +/// } +/// ``` #[derive_via(derive_serialize)] pub trait Serialize { fn serialize(self) -> [Field; N]; @@ -173,6 +202,30 @@ impl Serialize for str { } // docs:start:deserialize +/// Trait for deserializing Noir types from arrays of Fields in a constraint-efficient manner. +/// +/// This trait prioritizes minimal constraint costs during deserialization, even if it +/// results in larger array sizes. Each Field is typically converted directly into one +/// or more values without any packing or compression. This trait (and Serialize) are +/// typically used to communicate between Noir and TypeScript (via oracles and function +/// arguments). +/// +/// # Type Parameters +/// * `N` - The length of the input Field array, known at compile time +/// +/// # Implementation Notes +/// - Focus on simple, direct conversions to minimize constraints +/// - Each Field typically maps to its own component of a type +/// - Do not attempt to unpack or decompress values (use Packable trait for that) +/// +/// # Example +/// ``` +/// impl Deserialize for str { +/// fn deserialize(fields: [Field; N]) -> Self { +/// str::from(fields.map(|value| value as u8)) +/// } +/// } +/// ``` #[derive_via(derive_deserialize)] pub trait Deserialize { fn deserialize(fields: [Field; N]) -> Self; @@ -184,3 +237,28 @@ impl Deserialize for str { str::from(fields.map(|value| value as u8)) } } + +/// Trait for efficiently packing and unpacking Noir types into and from arrays of Fields. +/// +/// The `Packable` trait allows types to be serialized and deserialized with a focus on +/// minimizing the size of the resulting Field array, even at the cost of additional +/// constraints. This is in contrast to the `Serialize` and `Deserialize` traits, which +/// prioritize minimizing constraint costs but may result in larger arrays. +/// +/// Packing and unpacking often involve compression or aggregation of values, enabling +/// more compact representations of complex types. +/// +/// # Type Parameters +/// * `N` - The length of the Field array, known at compile time. +/// +/// # Implementation Notes +/// - Use efficient algorithms to reduce the size of the Field array during packing. +/// - Ensure data integrity and correctness during both packing and unpacking. +/// - This trait is useful for cases where storage efficiency is critical. +pub trait Packable { + /// Packs the current value into a compact array of `Field` elements. + fn pack(self) -> [Field; N]; + + /// Unpacks a compact array of `Field` elements into the original value. + fn unpack(fields: [Field; N]) -> Self; +}