ruint/support/
serde.rs

1//! Support for the [`serde`](https://crates.io/crates/serde) crate.
2
3#![cfg(feature = "serde")]
4#![cfg_attr(docsrs, doc(cfg(feature = "serde")))]
5
6use crate::{fmt::StackString, nbytes, Bits, Uint};
7use core::{fmt, str};
8use serde::{
9    de::{Error, Unexpected, Visitor},
10    Deserialize, Deserializer, Serialize, Serializer,
11};
12
13/// Canonical serialization for all human-readable instances of `Uint<0, 0>`,
14/// and minimal human-readable `Uint<BITS, LIMBS>::ZERO` for any bit size.
15const ZERO_STR: &str = "0x0";
16
17impl<const BITS: usize, const LIMBS: usize> Uint<BITS, LIMBS> {
18    fn serialize_human<const FULL: bool, S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
19        // Ideally `SIZE` is `2 + Self::BYTES * 2`.
20        if BITS <= 8 {
21            self.serialize_human_sized::<FULL, 8, _>(s)
22        } else {
23            self.serialize_human_sized::<FULL, BITS, _>(s)
24        }
25    }
26
27    fn serialize_human_sized<const FULL: bool, const SIZE: usize, S: Serializer>(
28        &self,
29        s: S,
30    ) -> Result<S::Ok, S::Error> {
31        let mut buffer = StackString::<SIZE>::new();
32        self.write_hex::<FULL>(&mut buffer);
33        s.serialize_str(buffer.as_str())
34    }
35
36    fn serialize_binary<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
37        s.serialize_bytes(&self.to_be_bytes_vec())
38    }
39
40    fn write_hex<const FULL: bool>(&self, s: &mut (impl fmt::Write + ?Sized)) {
41        if BITS == 0 {
42            s.write_str(ZERO_STR)
43        } else if FULL {
44            write!(s, "{:#0w$x}", *self, w = 2 + Self::BYTES * 2)
45        } else {
46            write!(s, "{:#x}", *self)
47        }
48        .unwrap();
49    }
50}
51
52/// Serialize a [`Uint`] value.
53///
54/// For human readable formats a `0x` prefixed lower case hex string is used.
55/// For binary formats a byte array is used. Leading zeros are included.
56impl<const BITS: usize, const LIMBS: usize> Serialize for Uint<BITS, LIMBS> {
57    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
58        if serializer.is_human_readable() {
59            self.serialize_human::<false, _>(serializer)
60        } else {
61            self.serialize_binary(serializer)
62        }
63    }
64}
65
66/// Deserialize human readable hex strings or byte arrays into [`Uint`].
67///
68/// Hex strings can be upper/lower/mixed case, have an optional `0x` prefix, and
69/// can be any length. They are interpreted big-endian.
70impl<'de, const BITS: usize, const LIMBS: usize> Deserialize<'de> for Uint<BITS, LIMBS> {
71    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
72        if deserializer.is_human_readable() {
73            deserializer.deserialize_any(HrVisitor)
74        } else {
75            deserializer.deserialize_bytes(ByteVisitor)
76        }
77    }
78}
79
80impl<const BITS: usize, const LIMBS: usize> Serialize for Bits<BITS, LIMBS> {
81    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
82        if serializer.is_human_readable() {
83            self.as_uint().serialize_human::<true, _>(serializer)
84        } else {
85            self.as_uint().serialize_binary(serializer)
86        }
87    }
88}
89
90impl<'de, const BITS: usize, const LIMBS: usize> Deserialize<'de> for Bits<BITS, LIMBS> {
91    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
92        Uint::deserialize(deserializer).map(Self::from)
93    }
94}
95
96/// Serde Visitor for human readable formats.
97///
98/// Accepts either a primitive number, a decimal or a hexadecimal string.
99struct HrVisitor<const BITS: usize, const LIMBS: usize>;
100
101impl<const BITS: usize, const LIMBS: usize> Visitor<'_> for HrVisitor<BITS, LIMBS> {
102    type Value = Uint<BITS, LIMBS>;
103
104    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
105        write!(formatter, "a {} byte hex string", nbytes(BITS))
106    }
107
108    fn visit_u64<E: Error>(self, v: u64) -> Result<Self::Value, E> {
109        Uint::try_from(v).map_err(|_| Error::invalid_value(Unexpected::Unsigned(v), &self))
110    }
111
112    fn visit_u128<E: Error>(self, v: u128) -> Result<Self::Value, E> {
113        // `Unexpected::Unsigned` cannot contain a `u128`
114        Uint::try_from(v).map_err(Error::custom)
115    }
116
117    fn visit_str<E: Error>(self, value: &str) -> Result<Self::Value, E> {
118        // Shortcut for common case
119        if value == ZERO_STR {
120            return Ok(Uint::<BITS, LIMBS>::ZERO);
121        }
122        // `ZERO_STR` is the only valid serialization of `Uint<0, 0>`, so if we
123        // have not shortcut, we are in an error case
124        if BITS == 0 {
125            return Err(Error::invalid_value(Unexpected::Str(value), &self));
126        }
127
128        value
129            .parse()
130            .map_err(|_| Error::invalid_value(Unexpected::Str(value), &self))
131    }
132}
133
134/// Serde Visitor for non-human readable formats
135struct ByteVisitor<const BITS: usize, const LIMBS: usize>;
136
137impl<const BITS: usize, const LIMBS: usize> Visitor<'_> for ByteVisitor<BITS, LIMBS> {
138    type Value = Uint<BITS, LIMBS>;
139
140    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
141        write!(formatter, "{BITS} bits of binary data in big endian order")
142    }
143
144    fn visit_bytes<E: Error>(self, value: &[u8]) -> Result<Self::Value, E> {
145        if value.len() != nbytes(BITS) {
146            return Err(Error::invalid_length(value.len(), &self));
147        }
148        Uint::try_from_be_slice(value).ok_or_else(|| {
149            Error::invalid_value(
150                Unexpected::Other(&format!("too large for Uint<{BITS}>")),
151                &self,
152            )
153        })
154    }
155}
156
157#[cfg(test)]
158mod tests {
159    use super::*;
160    use crate::{const_for, nlimbs};
161    use proptest::proptest;
162
163    #[allow(unused_imports)]
164    use alloc::vec::Vec;
165
166    #[test]
167    fn test_serde_human_readable() {
168        const_for!(BITS in SIZES {
169            const LIMBS: usize = nlimbs(BITS);
170            proptest!(|(value: Uint<BITS, LIMBS>)| {
171                let s = format!("U{BITS} => {value}");
172                let serialized = serde_json::to_string(&value).expect(&s);
173                let deserialized = serde_json::from_str::<Uint<BITS, LIMBS>>(&serialized).expect(&s);
174                assert_eq!(value, deserialized);
175            });
176            proptest!(|(value: Bits<BITS, LIMBS>)| {
177                let serialized = serde_json::to_string(&value).unwrap();
178                let deserialized = serde_json::from_str::<Bits<BITS, LIMBS>>(&serialized).unwrap();
179                assert_eq!(value, deserialized);
180            });
181        });
182    }
183
184    #[test]
185    fn test_human_readable_de() {
186        let jason = r#"[
187            1,
188            "0x1",
189            "0o1",
190            "0b1"
191        ]"#;
192        let numbers: Vec<Uint<1, 1>> = serde_json::from_str(jason).unwrap();
193        uint! {
194            assert_eq!(numbers, vec![1_U1, 1_U1, 1_U1, 1_U1]);
195        }
196
197        let jason = r#"[
198            "",
199            "0x",
200            "0o",
201            "0b"
202        ]"#;
203        let numbers: Vec<Uint<1, 1>> = serde_json::from_str(jason).unwrap();
204        uint! {
205            assert_eq!(numbers, vec![0_U1, 0_U1, 0_U1, 0_U1]);
206        }
207    }
208
209    #[test]
210    fn test_serde_machine_readable() {
211        const_for!(BITS in SIZES {
212            const LIMBS: usize = nlimbs(BITS);
213            proptest!(|(value: Uint<BITS, LIMBS>)| {
214                let serialized = bincode::serialize(&value).unwrap();
215                let deserialized = bincode::deserialize::<Uint<BITS, LIMBS>>(&serialized[..]).unwrap();
216                assert_eq!(value, deserialized);
217            });
218            proptest!(|(value: Bits<BITS, LIMBS>)| {
219                let serialized = bincode::serialize(&value).unwrap();
220                let deserialized = bincode::deserialize::<Bits<BITS, LIMBS>>(&serialized[..]).unwrap();
221                assert_eq!(value, deserialized);
222            });
223        });
224    }
225
226    #[test]
227    fn test_serde_invalid_size_error() {
228        // Test that if we add a character to a value that is already the max length for
229        // the given number of bits, we get an error.
230        const_for!(BITS in SIZES {
231            const LIMBS: usize = nlimbs(BITS);
232            let value = Uint::<BITS, LIMBS>::MAX;
233            let mut serialized = serde_json::to_string(&value).unwrap();
234
235            // ensure format of serialized value is correct ("0x...")
236            assert_eq!(&serialized[..3], "\"0x");
237            // last character should be a quote
238            assert_eq!(&serialized[serialized.len() - 1..], "\"");
239
240            // strip the last character, add a zero, and finish with a quote
241            serialized.pop();
242            serialized.push('0');
243            serialized.push('"');
244            let deserialized = serde_json::from_str::<Uint<BITS, LIMBS>>(&serialized);
245            assert!(deserialized.is_err(), "U{BITS} {serialized} => {deserialized:?}");
246        });
247    }
248}