serde_hex/
lib.rs

1//! The `serde-hex` crate contains various utilities for Serialization/Deserialization
2//! of hexadecimal values using [`serde`](https://crates.io/crates/serde).
3//!
4//! The core utility of this crate is the `SerHex` trait.  Once implemented, `SerHex`
5//! allows for easy configuration of hexadecimal serialization/deserialization with
6//! `serde-derive`:
7//!
8//! ```rust
9//! #[macro_use]
10//! extern crate serde_derive;
11//! extern crate serde_hex;
12//! use serde_hex::{SerHex,StrictPfx};
13//!
14//! #[derive(Debug,Serialize,Deserialize)]
15//! struct Foo {
16//!    #[serde(with = "SerHex::<StrictPfx>")]
17//!    bar: [u8;32]
18//! }
19//!
20//! # fn main() {}
21//! ```
22//!
23//! The above example will cause serde to serialize `Bar` into a hexadecimal string
24//! with strict sizing (padded with leading zeroes), and prefixing (leading `0x`).
25//! The possible configurations allow for any combination of strict/compact
26//! representations, prefixing, and capitalizing (e.g.; `Compact`,
27//! `StrictCapPfx`, etc...).
28//!
29//! This crate provides implementations of `SerHex` for all unsigned integer types,
30//! as well as generic impls for arrays of types which implement `SerHex`.  The generic
31//! impls apply only to strict variants of the trait, and only for arrays of length 1
32//! through 64 (no impl is provided for arrays of length 0 since there isn't really
33//! a reasonable way to represent a zero-sized value in hex).
34//!
35//!
36//!
37#![warn(missing_docs)]
38
39extern crate array_init;
40extern crate serde;
41extern crate smallvec;
42
43#[macro_use]
44pub mod macros;
45pub mod config;
46pub mod types;
47pub mod utils;
48
49pub use config::*;
50pub use types::{Error, ParseHexError};
51
52use serde::{Deserialize, Deserializer, Serializer};
53use smallvec::SmallVec;
54use std::iter::FromIterator;
55use std::{error, io};
56
57/// Trait specifying custom serialization and deserialization logic from a
58/// hexadecimal string to some arbitrary type.  This trait can be used to apply
59/// custom parsing when using serde's `#[derive(Serialize,Deserialize)]`
60/// flag.  Just add `#[serde(with = "SerHex")]` above any fields which implement
61/// this trait.  Simplistic default implimentations for the the `serialize` and
62/// `deserialize` methods are provided based on `into_hex_raw` and `from_hex_raw` respectively.
63pub trait SerHex<C>: Sized
64where
65    C: HexConf,
66{
67    /// Error type of the implementation.
68    ///
69    /// Unless you have a compelling reason to do so, it is best to use the error
70    /// type exposed by `serde-hex`, since this is the error used for most provided
71    /// implementations (the generic array impls will work with any error that
72    /// implements [`From`](https://doc.rust-lang.org/std/convert/trait.From.html)
73    /// for the `serde-hex` error type).
74    type Error: error::Error;
75
76    /// Attept to convert `self` to hexadecimal, writing the resultant bytes to some buffer.
77    fn into_hex_raw<D>(&self, dst: D) -> Result<(), Self::Error>
78    where
79        D: io::Write;
80
81    /// Attempt to parse some buffer of hexadecimal bytes into an instance of `Self`.
82    fn from_hex_raw<S>(src: S) -> Result<Self, Self::Error>
83    where
84        S: AsRef<[u8]>;
85
86    /// Attempt to convert `self` into a hexadecimal string representation.
87    fn into_hex(&self) -> Result<String, Self::Error> {
88        let mut dst: Vec<u8> = Vec::with_capacity(32);
89        self.into_hex_raw(&mut dst)?;
90        Ok(String::from_utf8(dst).expect("invalid UTF-8 bytes in parsing"))
91    }
92
93    /// Attempt to convert a slice of hexadecimal bytes into an instance of `Self`.
94    fn from_hex<S>(src: S) -> Result<Self, Self::Error>
95    where
96        S: AsRef<[u8]>,
97    {
98        Self::from_hex_raw(src)
99    }
100
101    /// Attempt to serialize `self` into a hexadecimal string representation.
102    ///
103    /// *NOTE*: The default implementation attempts to avoid heap-allocation with a
104    /// [`SmallVec`](https://docs.rs/smallvec/) of size `[u8;64]`. This default will
105    /// prevent heap-alloc for non-prefixed serializations of `[u8;32]` or smaller.
106    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
107    where
108        S: Serializer,
109    {
110        use serde::ser::Error;
111        let mut dst = SmallVec::<[u8; 64]>::new();
112        self.into_hex_raw(&mut dst).map_err(S::Error::custom)?;
113        // if `dst` is not valid UTF-8 bytes, the underlying implementation
114        // is very broken, and you should be ashamed of yourelf.
115        debug_assert!(::std::str::from_utf8(dst.as_ref()).is_ok());
116        let s = unsafe { ::std::str::from_utf8_unchecked(dst.as_ref()) };
117        serializer.serialize_str(s)
118    }
119
120    /// Attempt to deserialize a hexadecimal string into an instance of `Self`.
121    fn deserialize<'de, D>(deserializer: D) -> Result<Self, D::Error>
122    where
123        D: Deserializer<'de>,
124    {
125        use serde::de::Error;
126        let buff: &[u8] = Deserialize::deserialize(deserializer)?;
127        let rslt = Self::from_hex_raw(buff).map_err(D::Error::custom)?;
128        Ok(rslt)
129    }
130}
131
132/// Variant of `SerHex` for serializing/deserializing `Option` types.
133///
134/// Any type `T` which implements `SerHex<C>` implements `SerHexOpt<C>`
135/// automatically.
136///
137/// ```rust
138/// # #[macro_use]
139/// # extern crate serde_derive;
140/// # extern crate serde_json;
141/// # extern crate serde_hex;
142/// # use serde_hex::{SerHexOpt,CompactPfx};
143/// #
144/// #[derive(Debug,PartialEq,Eq,Serialize,Deserialize)]
145/// struct MaybeNum {
146///     #[serde(with = "SerHexOpt::<CompactPfx>")]
147///     num: Option<u64>
148/// }
149///
150/// # fn main() {
151/// let s: MaybeNum = serde_json::from_str(r#"{"num":"0xff"}"#).unwrap();
152/// assert_eq!(s,MaybeNum { num: Some(255) });
153///
154/// let n: MaybeNum = serde_json::from_str(r#"{"num":null}"#).unwrap();
155/// assert_eq!(n,MaybeNum { num: None });
156/// # }
157/// ```
158///
159pub trait SerHexOpt<C>: Sized + SerHex<C>
160where
161    C: HexConf,
162{
163    /// Same as `SerHex::serialize`, except for `Option<Self>` instead of `Self`.
164    fn serialize<S>(option: &Option<Self>, serializer: S) -> Result<S::Ok, S::Error>
165    where
166        S: Serializer,
167    {
168        use serde::ser::Error;
169        if let Some(ref src) = *option {
170            let mut dst = SmallVec::<[u8; 64]>::new();
171            Self::into_hex_raw(src, &mut dst).map_err(S::Error::custom)?;
172            // if `dst` is not valid UTF-8 bytes, the underlying implementation
173            // is very broken, and you should be ashamed of yourelf.
174            debug_assert!(::std::str::from_utf8(dst.as_ref()).is_ok());
175            let s = unsafe { ::std::str::from_utf8_unchecked(dst.as_ref()) };
176            //serializer.serialize_str(s)
177            serializer.serialize_some(s)
178        } else {
179            serializer.serialize_none()
180        }
181    }
182
183    /// Same as `SerHex::deserialize`, except for `Option<Self>` instead of `Self`.
184    fn deserialize<'de, D>(deserializer: D) -> Result<Option<Self>, D::Error>
185    where
186        D: Deserializer<'de>,
187    {
188        use serde::de::Error;
189        let option: Option<&[u8]> = Deserialize::deserialize(deserializer)?;
190        if let Some(ref buff) = option {
191            let rslt = Self::from_hex_raw(buff).map_err(D::Error::custom)?;
192            Ok(Some(rslt))
193        } else {
194            Ok(None)
195        }
196    }
197}
198
199impl<T, C> SerHexOpt<C> for T
200where
201    T: Sized + SerHex<C>,
202    C: HexConf,
203{
204}
205
206/// Variant of `SerHex` for serializing/deserializing sequence types as
207/// contiguous hexadecimal strings.
208///
209/// *NOTE*: `Compact` configurations are not compatible with this trait.
210/// The size of each element must be consistent in order to avoid ambiguous
211/// encoding.
212///
213/// ```rust
214/// # #[macro_use]
215/// # extern crate serde_derive;
216/// # extern crate serde_json;
217/// # extern crate serde_hex;
218/// # use serde_hex::{SerHexSeq,StrictPfx};
219/// #
220/// #[derive(Debug,PartialEq,Eq,Serialize,Deserialize)]
221/// struct Bytes(#[serde(with = "SerHexSeq::<StrictPfx>")] Vec<u8>);
222///
223/// # fn main() {
224/// let bytes: Bytes = serde_json::from_str(r#""0xdeadbeef""#).unwrap();
225/// assert_eq!(bytes,Bytes(vec![0xde,0xad,0xbe,0xef]));
226/// # }
227/// ```
228///
229pub trait SerHexSeq<C>: Sized + SerHex<Strict> + SerHex<StrictCap>
230where
231    C: HexConf,
232{
233    /// expected size (in bytes) of a single element.  used to partition
234    /// the hexadecimal string into individual elements.
235    fn size() -> usize;
236
237    /// Same as `SerHex::serialize`, but for sequences of `Self`.
238    fn serialize<'a, S, T>(sequence: T, serializer: S) -> Result<S::Ok, S::Error>
239    where
240        S: Serializer,
241        T: IntoIterator<Item = &'a Self>,
242        Self: 'a,
243    {
244        use serde::ser::Error;
245        let mut dst = SmallVec::<[u8; 128]>::new();
246        if <C as HexConf>::withpfx() {
247            dst.extend_from_slice(b"0x");
248        }
249        if <C as HexConf>::withcap() {
250            for elem in sequence.into_iter() {
251                <Self as SerHex<StrictCap>>::into_hex_raw(elem, &mut dst)
252                    .map_err(S::Error::custom)?;
253            }
254        } else {
255            for elem in sequence.into_iter() {
256                <Self as SerHex<Strict>>::into_hex_raw(elem, &mut dst).map_err(S::Error::custom)?;
257            }
258        }
259        let s = unsafe { ::std::str::from_utf8_unchecked(dst.as_ref()) };
260        serializer.serialize_str(s)
261    }
262
263    /// Same as `SerHex::deserialize`, but for sequences of `Self`.
264    fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
265    where
266        D: Deserializer<'de>,
267        T: FromIterator<Self>,
268    {
269        use serde::de::Error;
270        let raw: &[u8] = Deserialize::deserialize(deserializer)?;
271        let src = if raw.starts_with(b"0x") {
272            &raw[2..]
273        } else {
274            &raw[..]
275        };
276        let hexsize = Self::size() * 2;
277        if src.len() % hexsize == 0 {
278            let mut buff = Vec::with_capacity(src.len() / hexsize);
279            for chunk in src.chunks(hexsize) {
280                let elem =
281                    <Self as SerHex<Strict>>::from_hex_raw(chunk).map_err(D::Error::custom)?;
282                buff.push(elem);
283            }
284            Ok(buff.into_iter().collect())
285        } else {
286            Err(D::Error::custom("bad hexadecimal sequence size"))
287        }
288    }
289}
290
291impl_serhex_uint!(u8, 1);
292impl_serhex_uint!(u16, 2);
293impl_serhex_uint!(u32, 4);
294impl_serhex_uint!(u64, 8);
295
296// implement strict variants of `SerHex` for arrays of `T` with
297// lengths of 1 through 64 (where `T` implements the strict variants
298// of `SerHex` as well).
299impl_serhex_strict_array!(
300    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
301    27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
302    51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64
303);