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);