testnumbat_wasm/types/general/
h256.rs

1use crate::{abi::TypeAbi, types::BoxedBytes};
2use alloc::{boxed::Box, string::String, vec::Vec};
3use core::fmt::Debug;
4
5const ERR_BAD_H256_LENGTH: &[u8] = b"bad H256 length";
6const ZERO_32: &[u8] = &[0u8; 32];
7
8/// Type that holds 32 bytes of data.
9/// Data is kept on the heap to keep wasm size low and avoid copies.
10#[derive(Hash, Eq, PartialEq, Clone, Debug)]
11pub struct H256(Box<[u8; 32]>);
12
13impl From<[u8; 32]> for H256 {
14    /// Constructs a hash type from the given bytes array of fixed length.
15    ///
16    /// # Note
17    ///
18    /// The given bytes are interpreted in big endian order.
19    #[inline]
20    fn from(arr: [u8; 32]) -> Self {
21        H256(Box::new(arr))
22    }
23}
24
25impl<'a> From<&'a [u8; 32]> for H256 {
26    /// Constructs a hash type from the given reference
27    /// to the bytes array of fixed length.
28    ///
29    /// # Note
30    ///
31    /// The given bytes are interpreted in big endian order.
32    #[inline]
33    fn from(bytes: &'a [u8; 32]) -> Self {
34        H256(Box::new(*bytes))
35    }
36}
37
38impl<'a> From<&'a mut [u8; 32]> for H256 {
39    /// Constructs a hash type from the given reference
40    /// to the mutable bytes array of fixed length.
41    ///
42    /// # Note
43    ///
44    /// The given bytes are interpreted in big endian order.
45    #[inline]
46    fn from(bytes: &'a mut [u8; 32]) -> Self {
47        H256(Box::new(*bytes))
48    }
49}
50
51impl From<Box<[u8; 32]>> for H256 {
52    #[inline]
53    fn from(bytes: Box<[u8; 32]>) -> Self {
54        H256(bytes)
55    }
56}
57
58impl H256 {
59    pub fn from_slice(slice: &[u8]) -> Self {
60        let mut arr = [0u8; 32];
61        let len = core::cmp::min(slice.len(), 32);
62        arr[..len].copy_from_slice(&slice[..len]);
63        H256(Box::new(arr))
64    }
65}
66
67impl From<H256> for [u8; 32] {
68    #[inline]
69    fn from(s: H256) -> Self {
70        *(s.0)
71    }
72}
73
74impl AsRef<[u8]> for H256 {
75    #[inline]
76    fn as_ref(&self) -> &[u8] {
77        self.as_bytes()
78    }
79}
80
81impl AsMut<[u8]> for H256 {
82    #[inline]
83    fn as_mut(&mut self) -> &mut [u8] {
84        self.0.as_mut()
85    }
86}
87
88impl H256 {
89    /// Returns a new zero-initialized fixed hash.
90    /// Allocates directly in heap.
91    /// Minimal resulting wasm code (14 bytes if not inlined).
92    pub fn zero() -> Self {
93        use alloc::alloc::{alloc_zeroed, Layout};
94        unsafe {
95            let ptr = alloc_zeroed(Layout::new::<[u8; 32]>()) as *mut [u8; 32];
96            H256(Box::from_raw(ptr))
97        }
98    }
99
100    /// Returns the size of this hash in bytes.
101    #[inline]
102    pub fn len_bytes() -> usize {
103        32
104    }
105
106    /// Extracts a byte slice containing the entire fixed hash.
107    #[inline]
108    pub fn as_bytes(&self) -> &[u8] {
109        self.0.as_ref()
110    }
111
112    #[inline]
113    pub fn as_array(&self) -> &[u8; 32] {
114        self.0.as_ref()
115    }
116
117    #[inline]
118    pub fn copy_to_array(&self, target: &mut [u8; 32]) {
119        target.copy_from_slice(&self.0[..]);
120    }
121
122    #[inline]
123    pub fn to_vec(&self) -> Vec<u8> {
124        self.0[..].to_vec()
125    }
126
127    /// Pointer to the data on the heap.
128    #[inline]
129    pub fn as_ptr(&self) -> *const u8 {
130        self.0.as_ptr()
131    }
132
133    /// Returns an unsafe mutable pointer to the data on the heap.
134    /// Used by the API to populate data.
135    #[inline]
136    pub fn as_mut_ptr(&mut self) -> *mut u8 {
137        self.0.as_mut_ptr()
138    }
139
140    /// True if all 32 bytes of the hash are zero.
141    pub fn is_zero(&self) -> bool {
142        self.as_bytes() == ZERO_32
143    }
144
145    /// Transmutes self to an (in principle) variable length boxed bytes object.
146    /// Both BoxedBytes and H256 keep the data on the heap, so only the pointer to that data needs to be transmuted.
147    /// Does not reallocate or copy data, the data on the heap remains untouched.
148    pub fn into_boxed_bytes(self) -> BoxedBytes {
149        let raw = Box::into_raw(self.0) as *mut u8;
150        unsafe {
151            let bytes_box = Box::<[u8]>::from_raw(core::slice::from_raw_parts_mut(raw, 32));
152            bytes_box.into()
153        }
154    }
155}
156
157use testnumbat_codec::*;
158
159impl NestedEncode for H256 {
160    fn dep_encode<O: NestedEncodeOutput>(&self, dest: &mut O) -> Result<(), EncodeError> {
161        dest.write(&self.0[..]);
162        Ok(())
163    }
164
165    fn dep_encode_or_exit<O: NestedEncodeOutput, ExitCtx: Clone>(
166        &self,
167        dest: &mut O,
168        _: ExitCtx,
169        _: fn(ExitCtx, EncodeError) -> !,
170    ) {
171        dest.write(&self.0[..]);
172    }
173}
174
175impl TopEncode for H256 {
176    fn top_encode<O: TopEncodeOutput>(&self, output: O) -> Result<(), EncodeError> {
177        output.set_slice_u8(&self.0[..]);
178        Ok(())
179    }
180
181    fn top_encode_or_exit<O: TopEncodeOutput, ExitCtx: Clone>(
182        &self,
183        output: O,
184        _: ExitCtx,
185        _: fn(ExitCtx, EncodeError) -> !,
186    ) {
187        output.set_slice_u8(&self.0[..]);
188    }
189}
190
191impl NestedDecode for H256 {
192    fn dep_decode<I: NestedDecodeInput>(input: &mut I) -> Result<Self, DecodeError> {
193        let mut res = H256::zero();
194        input.read_into(res.as_mut())?;
195        Ok(res)
196    }
197
198    fn dep_decode_or_exit<I: NestedDecodeInput, ExitCtx: Clone>(
199        input: &mut I,
200        c: ExitCtx,
201        exit: fn(ExitCtx, DecodeError) -> !,
202    ) -> Self {
203        let mut res = H256::zero();
204        input.read_into_or_exit(res.as_mut(), c, exit);
205        res
206    }
207}
208
209impl H256 {
210    // Transmutes directly from a (variable-sized) boxed byte slice.
211    // Will exit early if the input length is not 32.
212    // Designed to be used especially in deserializer implementations.
213    pub fn decode_from_boxed_bytes_or_exit<ExitCtx: Clone>(
214        input: Box<[u8]>,
215        c: ExitCtx,
216        exit: fn(ExitCtx, DecodeError) -> !,
217    ) -> Self {
218        if input.len() != 32 {
219            exit(c, DecodeError::from(ERR_BAD_H256_LENGTH));
220        }
221        let raw = Box::into_raw(input);
222        let array_box = unsafe { Box::<[u8; 32]>::from_raw(raw as *mut [u8; 32]) };
223        H256(array_box)
224    }
225}
226
227impl TopDecode for H256 {
228    fn top_decode<I: TopDecodeInput>(input: I) -> Result<Self, DecodeError> {
229        match <[u8; 32]>::top_decode_boxed(input) {
230            Ok(array_box) => Ok(H256(array_box)),
231            Err(_) => Err(DecodeError::from(ERR_BAD_H256_LENGTH)),
232        }
233    }
234
235    fn top_decode_or_exit<I: TopDecodeInput, ExitCtx: Clone>(
236        input: I,
237        c: ExitCtx,
238        exit: fn(ExitCtx, DecodeError) -> !,
239    ) -> Self {
240        H256::decode_from_boxed_bytes_or_exit(input.into_boxed_slice_u8(), c, exit)
241    }
242}
243
244impl TypeAbi for H256 {
245    fn type_name() -> String {
246        "H256".into()
247    }
248}
249
250#[cfg(test)]
251mod h256_tests {
252    use super::*;
253    use alloc::vec::Vec;
254    use testnumbat_codec::test_util::{check_top_encode, check_top_encode_decode};
255
256    #[test]
257    fn test_h256_from_array() {
258        let addr = H256::from([4u8; 32]);
259        check_top_encode_decode(addr, &[4u8; 32]);
260    }
261
262    #[test]
263    fn test_opt_h256() {
264        let addr = H256::from([4u8; 32]);
265        let mut expected: Vec<u8> = Vec::new();
266        expected.push(1u8);
267        expected.extend_from_slice(&[4u8; 32]);
268        check_top_encode_decode(Some(addr), expected.as_slice());
269    }
270
271    #[test]
272    fn test_ser_h256_ref() {
273        let addr = H256::from([4u8; 32]);
274        let expected_bytes: &[u8] = &[4u8; 32 * 3];
275
276        let tuple = (&addr, &&&addr, addr.clone());
277        let serialized_bytes = check_top_encode(&tuple);
278        assert_eq!(serialized_bytes.as_slice(), expected_bytes);
279    }
280
281    #[test]
282    fn test_is_zero() {
283        assert!(H256::zero().is_zero());
284    }
285
286    #[test]
287    fn test_size_of() {
288        use core::mem::size_of;
289        assert_eq!(size_of::<H256>(), size_of::<usize>());
290        assert_eq!(size_of::<Option<H256>>(), size_of::<usize>());
291    }
292
293    #[test]
294    fn test_into_boxed_bytes() {
295        let array = b"32_bytes________________________";
296        let h256 = H256::from(array);
297        let bb = h256.into_boxed_bytes();
298        assert_eq!(bb.as_slice(), &array[..]);
299    }
300}