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}