Skip to main content

const_hex/
serde.rs

1//! Hex encoding with [`serde`](serde_core).
2//!
3//! # Examples
4//!
5//! ```
6//! # #[cfg(feature = "alloc")] {
7//! use serde::{Serialize, Deserialize};
8//!
9//! #[derive(Serialize, Deserialize)]
10//! struct Foo {
11//!     #[serde(with = "const_hex")]
12//!     bar: Vec<u8>,
13//! }
14//! # }
15//! ```
16
17use crate::FromHex;
18use core::fmt;
19use core::marker::PhantomData;
20use serde_core::de::{Error, Visitor};
21use serde_core::Deserializer;
22
23/// Serializes `data` as hex string using lowercase characters with a `0x` prefix.
24///
25/// Lowercase characters are used (e.g. `f9b4ca`). The resulting string's length
26/// is always even, each byte in data is always encoded using two hex digits.
27/// Thus, the resulting string contains exactly twice as many bytes as the input
28/// data plus two (for the prefix).
29#[inline]
30pub fn serialize<S, T>(data: T, serializer: S) -> Result<S::Ok, S::Error>
31where
32    S: serde_core::Serializer,
33    T: AsRef<[u8]>,
34{
35    serialize_inner::<S, false, true>(data.as_ref(), serializer)
36}
37
38/// Serializes `data` as hex string using uppercase characters.
39///
40/// Apart from the characters' casing, this works exactly like [`serialize`].
41#[inline]
42pub fn serialize_upper<S, T>(data: T, serializer: S) -> Result<S::Ok, S::Error>
43where
44    S: serde_core::Serializer,
45    T: AsRef<[u8]>,
46{
47    serialize_inner::<S, true, true>(data.as_ref(), serializer)
48}
49
50/// Inputs up to this many bytes are encoded into a stack buffer.
51const STACK_LEN: usize = 128;
52
53/// Encoded bytes buffered per formatter write for larger inputs.
54const STREAM_LEN: usize = 4096;
55
56/// Uses one `serialize_str` for small values and buffered `collect_str` for large ones.
57/// Serializers such as `serde_json` can stream without a full temporary string;
58/// serializers using Serde's default `collect_str` may still allocate one.
59fn serialize_inner<S, const UPPER: bool, const PREFIX: bool>(
60    data: &[u8],
61    serializer: S,
62) -> Result<S::Ok, S::Error>
63where
64    S: serde_core::Serializer,
65{
66    if data.len() <= STACK_LEN {
67        let mut buf = crate::impl_core::uninit_array::<u8, { STACK_LEN * 2 + 2 }>();
68        let prefix_len = PREFIX as usize * 2;
69        let len = prefix_len + data.len() * 2;
70        if PREFIX {
71            buf[0].write(b'0');
72            buf[1].write(b'x');
73        }
74        // SAFETY: `buf[prefix_len..len]` is exactly `data.len() * 2` bytes long.
75        unsafe { crate::imp::encode::<UPPER>(data, &mut buf[prefix_len..len]) };
76        // SAFETY: `buf[..len]` has been fully initialized above, and only ASCII
77        // characters, which are valid UTF-8, have been written.
78        let s = unsafe {
79            core::str::from_utf8_unchecked(crate::impl_core::slice_assume_init(&buf[..len]))
80        };
81        serializer.serialize_str(s)
82    } else {
83        serializer.collect_str(&BufferedHex::<UPPER, PREFIX>(data))
84    }
85}
86
87struct BufferedHex<'a, const UPPER: bool, const PREFIX: bool>(&'a [u8]);
88
89impl<const UPPER: bool, const PREFIX: bool> fmt::Display for BufferedHex<'_, UPPER, PREFIX> {
90    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91        use crate::output::{BufferedOutput, FormatterOutput, Output};
92
93        let mut formatter = FormatterOutput::new(f);
94        let mut output = BufferedOutput::<_, STREAM_LEN>::new(&mut formatter);
95        if PREFIX {
96            output.write(b"0x");
97        }
98        // SAFETY: BufferedOutput accepts any number of encoded bytes.
99        unsafe { crate::imp::encode::<UPPER>(self.0, &mut output) };
100        output.finish();
101        formatter.finish()
102    }
103}
104
105#[cfg(test)]
106mod tests {
107    use super::*;
108    use core::fmt::Write;
109
110    struct Sink {
111        writes: usize,
112        bytes: usize,
113        fail_at: usize,
114    }
115
116    impl fmt::Write for Sink {
117        fn write_str(&mut self, s: &str) -> fmt::Result {
118            self.writes += 1;
119            if self.writes >= self.fail_at {
120                return Err(fmt::Error);
121            }
122            self.bytes += s.len();
123            Ok(())
124        }
125    }
126
127    #[test]
128    fn buffered_hex_batches_writes() {
129        let mut sink = Sink {
130            writes: 0,
131            bytes: 0,
132            fail_at: usize::MAX,
133        };
134        write!(sink, "{}", BufferedHex::<false, true>(&[0xab; 4097])).unwrap();
135        assert_eq!(sink.bytes, 8196);
136        // A prefix can leave a partial SIMD chunk at the first boundary.
137        assert_eq!(sink.writes, 3);
138    }
139
140    #[test]
141    fn buffered_hex_propagates_flush_errors() {
142        // Exercise errors during an intermediate flush and the final flush.
143        for fail_at in 1..=3 {
144            let mut sink = Sink {
145                writes: 0,
146                bytes: 0,
147                fail_at,
148            };
149            assert!(write!(sink, "{}", BufferedHex::<false, true>(&[0xab; 4097])).is_err());
150            // FormatterOutput must not call the underlying writer again after failure.
151            assert_eq!(sink.writes, fail_at);
152        }
153    }
154}
155
156/// Deserializes a hex string into raw bytes.
157///
158/// Both, upper and lower case characters are valid in the input string and can
159/// even be mixed (e.g. `f9b4ca`, `F9B4CA` and `f9B4Ca` are all valid strings).
160#[inline]
161pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
162where
163    D: Deserializer<'de>,
164    T: FromHex,
165    <T as FromHex>::Error: fmt::Display,
166{
167    struct HexStrVisitor<T>(PhantomData<T>);
168
169    impl<T> Visitor<'_> for HexStrVisitor<T>
170    where
171        T: FromHex,
172        <T as FromHex>::Error: fmt::Display,
173    {
174        type Value = T;
175
176        fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177            f.write_str("a hex encoded string")
178        }
179
180        fn visit_bytes<E: Error>(self, data: &[u8]) -> Result<Self::Value, E> {
181            FromHex::from_hex(data).map_err(Error::custom)
182        }
183
184        fn visit_str<E: Error>(self, data: &str) -> Result<Self::Value, E> {
185            FromHex::from_hex(data.as_bytes()).map_err(Error::custom)
186        }
187    }
188
189    deserializer.deserialize_str(HexStrVisitor(PhantomData))
190}
191
192/// Hex encoding with [`serde`](serde_core).
193///
194/// # Examples
195///
196/// ```
197/// # #[cfg(feature = "alloc")] {
198/// use serde::{Serialize, Deserialize};
199///
200/// #[derive(Serialize, Deserialize)]
201/// struct Foo {
202///     #[serde(with = "const_hex::serde::no_prefix")]
203///     bar: Vec<u8>,
204/// }
205/// # }
206/// ```
207pub mod no_prefix {
208    /// Serializes `data` as hex string using lowercase characters.
209    ///
210    /// Lowercase characters are used (e.g. `f9b4ca`). The resulting string's length
211    /// is always even, each byte in data is always encoded using two hex digits.
212    /// Thus, the resulting string contains exactly twice as many bytes as the input
213    /// data.
214    #[inline]
215    pub fn serialize<S, T>(data: T, serializer: S) -> Result<S::Ok, S::Error>
216    where
217        S: serde_core::Serializer,
218        T: AsRef<[u8]>,
219    {
220        super::serialize_inner::<S, false, false>(data.as_ref(), serializer)
221    }
222
223    /// Serializes `data` as hex string using uppercase characters.
224    ///
225    /// Apart from the characters' casing, this works exactly like [`serialize`].
226    #[inline]
227    pub fn serialize_upper<S, T>(data: T, serializer: S) -> Result<S::Ok, S::Error>
228    where
229        S: serde_core::Serializer,
230        T: AsRef<[u8]>,
231    {
232        super::serialize_inner::<S, true, false>(data.as_ref(), serializer)
233    }
234
235    pub use super::deserialize;
236}