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/// Encodes into a single buffer and emits it with one `serialize_str` call.
54///
55/// `collect_str` would instead run the `Display` impl against the serializer's `fmt::Write`
56/// shim, which for `serde_json` means one string-escape scan and one `write_all` per SIMD
57/// chunk (and one per nibble for the scalar tail), i.e. tens of calls per value.
58fn serialize_inner<S, const UPPER: bool, const PREFIX: bool>(
59    data: &[u8],
60    serializer: S,
61) -> Result<S::Ok, S::Error>
62where
63    S: serde_core::Serializer,
64{
65    if data.len() <= STACK_LEN {
66        let mut buf = crate::impl_core::uninit_array::<u8, { STACK_LEN * 2 + 2 }>();
67        let prefix_len = PREFIX as usize * 2;
68        let len = prefix_len + data.len() * 2;
69        if PREFIX {
70            buf[0].write(b'0');
71            buf[1].write(b'x');
72        }
73        // SAFETY: `buf[prefix_len..len]` is exactly `data.len() * 2` bytes long.
74        unsafe { crate::imp::encode::<UPPER>(data, &mut buf[prefix_len..len]) };
75        // SAFETY: `buf[..len]` has been fully initialized above, and only ASCII
76        // characters, which are valid UTF-8, have been written.
77        let s = unsafe {
78            core::str::from_utf8_unchecked(crate::impl_core::slice_assume_init(&buf[..len]))
79        };
80        serializer.serialize_str(s)
81    } else {
82        #[cfg(feature = "alloc")]
83        {
84            serializer.serialize_str(&crate::encode_inner::<UPPER, PREFIX>(data))
85        }
86        #[cfg(not(feature = "alloc"))]
87        {
88            let display = crate::display(data);
89            match (UPPER, PREFIX) {
90                (false, false) => serializer.collect_str(&format_args!("{display:x}")),
91                (false, true) => serializer.collect_str(&format_args!("{display:#x}")),
92                (true, false) => serializer.collect_str(&format_args!("{display:X}")),
93                (true, true) => serializer.collect_str(&format_args!("{display:#X}")),
94            }
95        }
96    }
97}
98
99/// Deserializes a hex string into raw bytes.
100///
101/// Both, upper and lower case characters are valid in the input string and can
102/// even be mixed (e.g. `f9b4ca`, `F9B4CA` and `f9B4Ca` are all valid strings).
103#[inline]
104pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
105where
106    D: Deserializer<'de>,
107    T: FromHex,
108    <T as FromHex>::Error: fmt::Display,
109{
110    struct HexStrVisitor<T>(PhantomData<T>);
111
112    impl<T> Visitor<'_> for HexStrVisitor<T>
113    where
114        T: FromHex,
115        <T as FromHex>::Error: fmt::Display,
116    {
117        type Value = T;
118
119        fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120            f.write_str("a hex encoded string")
121        }
122
123        fn visit_bytes<E: Error>(self, data: &[u8]) -> Result<Self::Value, E> {
124            FromHex::from_hex(data).map_err(Error::custom)
125        }
126
127        fn visit_str<E: Error>(self, data: &str) -> Result<Self::Value, E> {
128            FromHex::from_hex(data.as_bytes()).map_err(Error::custom)
129        }
130    }
131
132    deserializer.deserialize_str(HexStrVisitor(PhantomData))
133}
134
135/// Hex encoding with [`serde`](serde_core).
136///
137/// # Examples
138///
139/// ```
140/// # #[cfg(feature = "alloc")] {
141/// use serde::{Serialize, Deserialize};
142///
143/// #[derive(Serialize, Deserialize)]
144/// struct Foo {
145///     #[serde(with = "const_hex::serde::no_prefix")]
146///     bar: Vec<u8>,
147/// }
148/// # }
149/// ```
150pub mod no_prefix {
151    /// Serializes `data` as hex string using lowercase characters.
152    ///
153    /// Lowercase characters are used (e.g. `f9b4ca`). The resulting string's length
154    /// is always even, each byte in data is always encoded using two hex digits.
155    /// Thus, the resulting string contains exactly twice as many bytes as the input
156    /// data.
157    #[inline]
158    pub fn serialize<S, T>(data: T, serializer: S) -> Result<S::Ok, S::Error>
159    where
160        S: serde_core::Serializer,
161        T: AsRef<[u8]>,
162    {
163        super::serialize_inner::<S, false, false>(data.as_ref(), serializer)
164    }
165
166    /// Serializes `data` as hex string using uppercase characters.
167    ///
168    /// Apart from the characters' casing, this works exactly like [`serialize`].
169    #[inline]
170    pub fn serialize_upper<S, T>(data: T, serializer: S) -> Result<S::Ok, S::Error>
171    where
172        S: serde_core::Serializer,
173        T: AsRef<[u8]>,
174    {
175        super::serialize_inner::<S, true, false>(data.as_ref(), serializer)
176    }
177
178    pub use super::deserialize;
179}