espresso_types/v0/impls/block/
uint_bytes.rs

1//! Serialization (and deserialization) of primitive unsigned integer types to
2//! (and from) an arbitrary fixed-length byte array.
3//!
4use std::mem::size_of;
5
6use paste::paste;
7
8// Use an ugly macro because it's difficult or impossible to be generic over
9// primitive types such as `usize`, `u64`.
10macro_rules! uint_bytes_impl {
11        ($T:ty) => {
12            paste! {
13                /// Serialize `n` into `BYTE_LEN` bytes in little-endian form, padding with
14                /// 0 as needed.
15                ///
16                /// # Panics
17                /// If `n` cannot fit into `BYTE_LEN` bytes.
18                pub fn [<$T _to_bytes>]<const BYTE_LEN: usize>(n: $T) -> [u8; BYTE_LEN] {
19                    if size_of::<$T>() > BYTE_LEN {
20                        assert!(
21                            [<$T _fits>](n, BYTE_LEN),
22                            "n {n} cannot fit into {BYTE_LEN} bytes"
23                        );
24                        n.to_le_bytes()[..BYTE_LEN].try_into().unwrap() // panic is impossible
25                    } else {
26                        // convert `n` to bytes and pad with 0
27                        let mut result = [0; BYTE_LEN];
28                        result[..size_of::<$T>()].copy_from_slice(&n.to_le_bytes()[..]);
29                        result
30                    }
31                }
32
33                /// Deserialize `bytes` in little-endian form into a `$T`, padding with 0
34                /// as needed.
35                ///
36                /// # Panics
37                /// If `bytes.len()` is too large to fit into a `$T`.
38                pub fn [<$T _from_bytes>]<const BYTE_LEN: usize>(bytes: &[u8]) -> $T {
39                    assert!(bytes.len() <= BYTE_LEN, "bytes len {} exceeds BYTE_LEN {BYTE_LEN}", bytes.len());
40                    assert!(
41                        BYTE_LEN <= size_of::<$T>(),
42                        "BYTE_LEN {BYTE_LEN} cannot fit into {}",
43                        stringify!($T)
44                    );
45                    let mut [<$T _bytes>] = [0; size_of::<$T>()];
46                    [<$T _bytes>][..bytes.len()].copy_from_slice(bytes);
47                    $T::from_le_bytes([<$T _bytes>])
48                }
49
50                /// Return the largest `$T` value that can fit into `byte_len` bytes.
51                pub const fn [<$T _max_from_byte_len>](byte_len: usize) -> $T {
52                    if byte_len >= size_of::<$T>() {
53                        $T::MAX
54                    } else {
55                        // overflow cannot occur because `byte_len < size_of::<$T>()`
56                        (1 << (byte_len * 8)) - 1
57                    }
58                }
59
60                /// Can `n` fit into `byte_len` bytes?
61                pub const fn [<$T _fits>](n: $T, byte_len: usize) -> bool {
62                    n <= [<$T _max_from_byte_len>](byte_len)
63                }
64            }
65        };
66    }
67
68uint_bytes_impl!(usize);
69uint_bytes_impl!(u32);
70
71/// Impl [`serde`] for type `$T` with methods named `$to_bytes`, `$from_bytes`
72/// of the form
73/// ```ignore
74/// $T::$to_bytes(&self) -> $B
75/// $T::$from_bytes(bytes: &[u8]) -> Self
76/// ```
77/// where `$B` is any type that impls [`serde::Deserialize`] and has a method
78/// `as_ref` of the form
79/// ```ignore
80/// $B::as_ref(&self) -> &[u8]
81/// ```
82/// Typical examples of `$B` include array `[u8; N]`, slice `&[u8]`, or
83/// `Vec<u8>`.
84macro_rules! bytes_serde_impl {
85    ($T:ty, $to_bytes:ident, $B:ty, $from_bytes:ident) => {
86        impl Serialize for $T {
87            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
88            where
89                S: Serializer,
90            {
91                self.$to_bytes().serialize(serializer)
92            }
93        }
94
95        impl<'de> Deserialize<'de> for $T {
96            fn deserialize<D>(deserializer: D) -> Result<$T, D::Error>
97            where
98                D: Deserializer<'de>,
99            {
100                <$B as Deserialize>::deserialize(deserializer)
101                    .map(|bytes| <$T>::$from_bytes(bytes.as_ref()))
102            }
103        }
104    };
105}
106
107pub(super) use bytes_serde_impl;
108
109#[cfg(test)]
110mod test {
111    use std::mem::size_of;
112
113    use fluent_asserter::prelude::*;
114    use paste::paste;
115
116    macro_rules! uint_bytes_test_impl {
117            ($T:ty) => {
118                paste! {
119                    use super::{[<$T _max_from_byte_len>], [<$T _to_bytes>], [<$T _from_bytes>]};
120
121                    #[test]
122                    fn [<$T _max_from_byte_len_correctness>]() {
123                        // test byte lengths 0 to size_of::<$T>()
124                        let mut bytes = [0; size_of::<$T>()];
125                        assert_eq!([<$T _max_from_byte_len>](0), 0);
126                        for i in 0..bytes.len() {
127                            bytes[i] = 0xff;
128                            assert_eq!([<$T _max_from_byte_len>](i + 1).to_le_bytes(), bytes);
129                        }
130
131                        // test byte lengths size_of::<$T>() to twice that length
132                        for i in size_of::<$T>()..2 * size_of::<$T>() {
133                            assert_eq!([<$T _max_from_byte_len>](i + 1), $T::MAX);
134                        }
135                    }
136
137                    #[test]
138                    fn [<$T _to_bytes_correctness>]() {
139                        // byte length 0
140                        assert_eq!([<$T _to_bytes>](0), [0; 0]);
141                        assert_that_code!(|| [<$T _to_bytes>]::<0>(1)).panics();
142
143                        // byte length 1
144                        assert_eq!([<$T _to_bytes>](0), [0; 1]);
145                        assert_eq!([<$T _to_bytes>](255), [255; 1]);
146                        assert_that_code!(|| [<$T _to_bytes>]::<1>(256)).panics();
147
148                        // byte length 2
149                        assert_eq!([<$T _to_bytes>](0), [0; 2]);
150                        assert_eq!([<$T _to_bytes>](65535), [255; 2]);
151                        assert_that_code!(|| [<$T _to_bytes>]::<2>(65536)).panics();
152
153                        // byte length size_of::<$T>()
154                        assert_eq!([<$T _to_bytes>](0), [0; size_of::<$T>()]);
155                        assert_eq!([<$T _to_bytes>]($T::MAX), [255; size_of::<$T>()]);
156
157                        // byte length size_of::<$T>() + 1
158                        assert_eq!([<$T _to_bytes>](0), [0; size_of::<$T>() + 1]);
159                        let [<$T _max_bytes>] = {
160                            let mut bytes = [255; size_of::<$T>() + 1];
161                            bytes[bytes.len() - 1] = 0;
162                            bytes
163                        };
164                        assert_eq!([<$T _to_bytes>]($T::MAX), [<$T _max_bytes>]);
165                    }
166
167                    #[test]
168                    fn [<$T _from_bytes_correctness>]() {
169                        let bytes = [255; size_of::<$T>() + 1];
170
171                        // It would be nice to iterate through
172                        // `0..size_of::<$T>()` but this is not possible with
173                        // const generics for `[<$T _from_bytes>]`. We could
174                        // use `seq-macro` crate but it requires an integer
175                        // literal whereas our range includes `size_of::<$T>()`.
176                        //
177                        // Instead we just hard code four constants:
178                        // `0`, `1`, `size_of::<$T>() - 1`, `size_of::<$T>()`.
179                        assert_eq!(
180                            [<$T _from_bytes>]::<0>(&bytes[..0]),
181                            [<$T _max_from_byte_len>](0)
182                        );
183                        assert_eq!(
184                            [<$T _from_bytes>]::<1>(&bytes[..1]),
185                            [<$T _max_from_byte_len>](1)
186                        );
187                        assert_eq!(
188                            [<$T _from_bytes>]::<{size_of::<$T>() - 1}>(&bytes[..size_of::<$T>() - 1]),
189                            [<$T _max_from_byte_len>](size_of::<$T>() - 1)
190                        );
191                        assert_eq!(
192                            [<$T _from_bytes>]::<{size_of::<$T>()}>(&bytes[..size_of::<$T>()]),
193                            [<$T _max_from_byte_len>](size_of::<$T>())
194                        );
195
196                        assert_that_code!(|| [<$T _from_bytes>]::<{size_of::<$T>() + 1}>(&bytes[..])).panics();
197                    }
198
199                    #[test]
200                    fn [<$T _from_bytes_allows_smaller_byte_lens>]() {
201                        // This test same as `xxx_from_bytes_correctness` except
202                        // we set the const param `BYTE_LEN` to
203                        // `size_of::<$T>()` in all cases. Why? To ensure that
204                        // `xxx_from_bytes` allows its arg to have length
205                        // smaller than `BYTE_LEN`.
206                        let bytes = [255; size_of::<$T>() + 1];
207
208                        assert_eq!(
209                            [<$T _from_bytes>]::<{size_of::<$T>()}>(&bytes[..0]),
210                            [<$T _max_from_byte_len>](0)
211                        );
212                        assert_eq!(
213                            [<$T _from_bytes>]::<{size_of::<$T>()}>(&bytes[..1]),
214                            [<$T _max_from_byte_len>](1)
215                        );
216                        assert_eq!(
217                            [<$T _from_bytes>]::<{size_of::<$T>()}>(&bytes[..size_of::<$T>() - 1]),
218                            [<$T _max_from_byte_len>](size_of::<$T>() - 1)
219                        );
220                        assert_eq!(
221                            [<$T _from_bytes>]::<{size_of::<$T>()}>(&bytes[..size_of::<$T>()]),
222                            [<$T _max_from_byte_len>](size_of::<$T>())
223                        );
224
225                        assert_that_code!(|| [<$T _from_bytes>]::<{size_of::<$T>()}>(&bytes[..])).panics();
226                    }
227                }
228            };
229        }
230
231    uint_bytes_test_impl!(usize);
232    uint_bytes_test_impl!(u32);
233}