light_hasher/
to_byte_array.rs

1#[cfg(feature = "alloc")]
2use crate::String;
3use crate::{Hasher, HasherError, Poseidon};
4
5pub trait ToByteArray {
6    const NUM_FIELDS: usize;
7    const IS_PRIMITIVE: bool = false;
8    fn to_byte_array(&self) -> Result<[u8; 32], HasherError>;
9}
10
11macro_rules! impl_to_byte_array_for_integer_type {
12    ($int_ty:ty) => {
13        impl ToByteArray for $int_ty {
14            const IS_PRIMITIVE: bool = true;
15            const NUM_FIELDS: usize = 1;
16
17            /// Big endian representation of $int_ty.
18            fn to_byte_array(&self) -> Result<[u8; 32], HasherError> {
19                let bytes = self.to_be_bytes();
20                let mut result = [0; 32];
21                result[32 - core::mem::size_of::<$int_ty>()..].copy_from_slice(&bytes);
22                Ok(result)
23            }
24        }
25    };
26}
27
28impl<T: ToByteArray> ToByteArray for Option<T> {
29    const NUM_FIELDS: usize = 1;
30
31    /// Some(PrimitiveType) prefixed with 1 byte
32    /// Some(T) -> Poseidon::hash(T::to_byte_array())
33    /// None -> [0u8;32]
34    fn to_byte_array(&self) -> Result<[u8; 32], HasherError> {
35        if let Some(value) = &self {
36            let byte_array = if T::IS_PRIMITIVE {
37                let mut byte_array = value.to_byte_array()?;
38                // Prefix with 1 to indicate Some().
39                byte_array[32 - core::mem::size_of::<T>() - 1] = 1;
40                byte_array
41            } else {
42                let byte_array = value.to_byte_array()?;
43                Poseidon::hash(byte_array.as_slice())?
44            };
45            Ok(byte_array)
46        } else {
47            Ok([0; 32])
48        }
49    }
50}
51
52impl ToByteArray for bool {
53    const NUM_FIELDS: usize = 1;
54    const IS_PRIMITIVE: bool = true;
55
56    /// Big endian representation of bool.
57    fn to_byte_array(&self) -> Result<[u8; 32], HasherError> {
58        let mut bytes = [0u8; 32];
59        bytes[31] = *self as u8;
60        Ok(bytes)
61    }
62}
63
64impl_to_byte_array_for_integer_type!(i8);
65impl_to_byte_array_for_integer_type!(u8);
66impl_to_byte_array_for_integer_type!(i16);
67impl_to_byte_array_for_integer_type!(u16);
68impl_to_byte_array_for_integer_type!(i32);
69impl_to_byte_array_for_integer_type!(u32);
70impl_to_byte_array_for_integer_type!(i64);
71impl_to_byte_array_for_integer_type!(u64);
72impl_to_byte_array_for_integer_type!(i128);
73impl_to_byte_array_for_integer_type!(u128);
74
75// Macro for implementing ToByteArray for zero-copy types
76#[cfg(feature = "zero-copy")]
77macro_rules! impl_to_byte_array_for_zero_copy_type {
78    ($zero_copy_type:ty, $primitive_type:ty) => {
79        impl ToByteArray for $zero_copy_type {
80            const IS_PRIMITIVE: bool = true;
81            const NUM_FIELDS: usize = 1;
82
83            fn to_byte_array(&self) -> Result<[u8; 32], HasherError> {
84                let value: $primitive_type = (*self).into();
85                value.to_byte_array()
86            }
87        }
88    };
89}
90
91// ToByteArray implementations for zero-copy types
92#[cfg(feature = "zero-copy")]
93impl_to_byte_array_for_zero_copy_type!(zerocopy::little_endian::U16, u16);
94#[cfg(feature = "zero-copy")]
95impl_to_byte_array_for_zero_copy_type!(zerocopy::little_endian::U32, u32);
96#[cfg(feature = "zero-copy")]
97impl_to_byte_array_for_zero_copy_type!(zerocopy::little_endian::U64, u64);
98#[cfg(feature = "zero-copy")]
99impl_to_byte_array_for_zero_copy_type!(zerocopy::little_endian::I16, i16);
100#[cfg(feature = "zero-copy")]
101impl_to_byte_array_for_zero_copy_type!(zerocopy::little_endian::I32, i32);
102#[cfg(feature = "zero-copy")]
103impl_to_byte_array_for_zero_copy_type!(zerocopy::little_endian::I64, i64);
104
105/// Example usage:
106/// impl_to_byte_array_for_array! {
107///     MyCustomType,
108///     1 => [0],
109///     2 => [0, 1]
110/// }
111#[macro_export]
112macro_rules! impl_to_byte_array_for_array {
113    // First specify the type T, then for each array, specify the length and indices
114    ($t:ty, $( $len:literal => [$($index:tt),*] );* $(;)?) => {
115        $(
116            impl ToByteArray for [$t; $len] {
117                const NUM_FIELDS: usize = $len;
118                const IS_PRIMITIVE: bool = false;
119
120                fn to_byte_array(&self) -> Result<[u8; 32], HasherError> {
121                    let arrays = [$(self[$index].to_byte_array()?),*];
122                    let slices = [$(arrays[$index].as_slice()),*];
123                    Poseidon::hashv(&slices)
124                }
125            }
126        )*
127    }
128}
129
130// Implementation for [u8; N] arrays with N <= 31
131macro_rules! impl_to_byte_array_for_u8_array {
132    ($size:expr) => {
133        impl ToByteArray for [u8; $size] {
134            const NUM_FIELDS: usize = 1;
135
136            fn to_byte_array(&self) -> Result<[u8; 32], HasherError> {
137                let mut result = [0u8; 32];
138                result[32 - $size..].copy_from_slice(self.as_slice());
139                Ok(result)
140            }
141        }
142    };
143}
144
145// Implement for common array sizes until 31 so that it is less than field size.
146impl_to_byte_array_for_u8_array!(1);
147impl_to_byte_array_for_u8_array!(2);
148impl_to_byte_array_for_u8_array!(4);
149impl_to_byte_array_for_u8_array!(5);
150impl_to_byte_array_for_u8_array!(6);
151impl_to_byte_array_for_u8_array!(7);
152impl_to_byte_array_for_u8_array!(8);
153impl_to_byte_array_for_u8_array!(9);
154impl_to_byte_array_for_u8_array!(10);
155impl_to_byte_array_for_u8_array!(11);
156impl_to_byte_array_for_u8_array!(12);
157impl_to_byte_array_for_u8_array!(13);
158impl_to_byte_array_for_u8_array!(14);
159impl_to_byte_array_for_u8_array!(15);
160impl_to_byte_array_for_u8_array!(16);
161impl_to_byte_array_for_u8_array!(17);
162impl_to_byte_array_for_u8_array!(18);
163impl_to_byte_array_for_u8_array!(19);
164impl_to_byte_array_for_u8_array!(20);
165impl_to_byte_array_for_u8_array!(21);
166impl_to_byte_array_for_u8_array!(22);
167impl_to_byte_array_for_u8_array!(23);
168impl_to_byte_array_for_u8_array!(24);
169impl_to_byte_array_for_u8_array!(25);
170impl_to_byte_array_for_u8_array!(26);
171impl_to_byte_array_for_u8_array!(27);
172impl_to_byte_array_for_u8_array!(28);
173impl_to_byte_array_for_u8_array!(29);
174impl_to_byte_array_for_u8_array!(30);
175impl_to_byte_array_for_u8_array!(31);
176
177#[cfg(feature = "alloc")]
178impl ToByteArray for String {
179    const NUM_FIELDS: usize = 1;
180
181    /// Max allowed String length is 31 bytes.
182    /// For longer strings hash to field size or provide a custom implementation.
183    fn to_byte_array(&self) -> Result<[u8; 32], HasherError> {
184        let bytes = self.as_bytes();
185        let mut result = [0u8; 32];
186        let byte_len = bytes.len();
187        if byte_len > 31 {
188            return Err(HasherError::InvalidInputLength(31, bytes.len()));
189        }
190        result[32 - byte_len..].copy_from_slice(bytes);
191        Ok(result)
192    }
193}