serde_hex/
utils.rs

1//! various helper functions.
2use std::borrow::Borrow;
3use std::io;
4use types::{Error, ParseHexError};
5
6/// convert a byte from a hex string to its numeric value.
7/// use the `tobyte` function to convert a pair of hex characters
8/// to their actual byte representation.
9#[inline]
10pub fn intoval(c: u8) -> Result<u8, ParseHexError> {
11    // ------------------------------------------------------
12    // NOTE: the below logic is a nearly exact copy of an
13    // equivalent function in the `hex` crate.
14    // ------------------------------------------------------
15    //   repository - https://github.com/KokaKiwi/rust-hex
16    //   crates.io  - https://crates.io/crates/hex
17    // ------------------------------------------------------
18    // copyright at time of shameless theft:
19    //   Copyright (c) 2013-2014 The Rust Project Developers.
20    //   Copyright (c) 2015-2016 The rust-hex Developers
21    // ------------------------------------------------------
22    // licensing at time of shameless theft:
23    //   MIT/APACHE (at your option)
24    // ------------------------------------------------------
25    match c {
26        b'A'...b'F' => Ok(c - b'A' + 10),
27        b'a'...b'f' => Ok(c - b'a' + 10),
28        b'0'...b'9' => Ok(c - b'0'),
29        _ => {
30            let val = c as char;
31            Err(ParseHexError::Char { val })
32        }
33    }
34}
35
36/// convert a byte value in range `0x00` to `0x0f` to its
37/// corresponding lowercase hexadecimal character.
38#[inline]
39pub fn fromval(val: u8) -> u8 {
40    match val {
41        0xa...0xf => val - 0xa + b'a',
42        0x0...0x9 => val + b'0',
43        _ => panic!("value outside range 0x0...0xf"),
44    }
45}
46
47/// convert a byte value in range `0x00` to `0x0f` to its
48/// corresponding uppercase hexadecimal character.
49#[inline]
50pub fn fromvalcaps(val: u8) -> u8 {
51    match val {
52        0xA...0xF => val - 0xa + b'A',
53        0x0...0x9 => val + b'0',
54        _ => panic!("value outside range 0x0...0xf"),
55    }
56}
57
58/// attemt to convert a pair of bytes from a hexadecimal string into their
59/// underlying byte representation.
60#[inline]
61pub fn intobyte(a: u8, b: u8) -> Result<u8, ParseHexError> {
62    Ok(intoval(a)? << 4 | intoval(b)?)
63}
64
65/// attempt to convert a byte value into a pair of hexadecimal values.
66#[inline]
67fn frombyte(val: u8) -> (u8, u8) {
68    (fromval(val >> 4), fromval(val & 0x0f))
69}
70
71/// attempt to convert a byte value into a pair of uppercase hexadecimal values.
72#[inline]
73pub fn frombytecaps(val: u8) -> (u8, u8) {
74    (fromvalcaps(val >> 4), fromvalcaps(val & 0x0f))
75}
76
77/// Helper function which takes a mutable slice of expected byte-length and
78/// attempts to parse an immutable slice of bytes as hexadecimal characters.
79/// Returns an error if `src` is not exactly twice the size of `buf`, or if
80/// any non-hexadecimal characters are found.
81pub fn fromhex(buf: &mut [u8], src: &[u8]) -> Result<(), ParseHexError> {
82    let expect = buf.len() * 2;
83    let actual = src.len();
84    if expect == actual {
85        for (idx, pair) in src.chunks(2).enumerate() {
86            buf[idx] = intobyte(pair[0], pair[1])?;
87        }
88        Ok(())
89    } else {
90        Err(ParseHexError::Size { expect, actual })
91    }
92}
93
94/// write hex to buffer.
95///
96/// # panics
97///
98/// panics if `buff` is not exactly twice the size of `src`.
99pub fn intohex(buf: &mut [u8], src: &[u8]) {
100    if buf.len() == src.len() * 2 {
101        for (i, byte) in src.iter().enumerate() {
102            let (a, b) = frombyte(*byte);
103            let idx = i * 2;
104            buf[idx] = a;
105            buf[idx + 1] = b;
106        }
107    } else {
108        panic!("invalid buffer sizes");
109    }
110}
111
112/// write uppercase hex to buffer.
113///
114/// # panics
115///
116/// panics if `buff` is not exactly twice the size of `src`.
117pub fn intohexcaps(buf: &mut [u8], src: &[u8]) {
118    if buf.len() == src.len() * 2 {
119        for (i, byte) in src.iter().enumerate() {
120            let (a, b) = frombytecaps(*byte);
121            let idx = i * 2;
122            buf[idx] = a;
123            buf[idx + 1] = b;
124        }
125    } else {
126        panic!("invalid buffer sizes");
127    }
128}
129
130/// Helper function which attempts to convert an immutable set of bytes into
131/// hexadecimal characters and write them to some destination.
132pub fn writehex<S, B, D>(src: S, mut dst: D) -> Result<(), Error>
133where
134    S: IntoIterator<Item = B>,
135    B: Borrow<u8>,
136    D: io::Write,
137{
138    for byte in src.into_iter() {
139        let (a, b) = frombyte(*byte.borrow());
140        dst.write_all(&[a, b])?;
141    }
142    Ok(())
143}
144
145/// Helper function which attempts to convert an immutable set of bytes into
146/// capital hexadecimal characters and write them to some destination.
147pub fn writehexcaps<S, B, D>(src: S, mut dst: D) -> Result<(), Error>
148where
149    S: IntoIterator<Item = B>,
150    B: Borrow<u8>,
151    D: io::Write,
152{
153    for byte in src.into_iter() {
154        let (a, b) = frombytecaps(*byte.borrow());
155        dst.write_all(&[a, b])?;
156    }
157    Ok(())
158}
159
160#[cfg(test)]
161mod tests {
162    #[test]
163    fn hex_bytes() {
164        use utils::{frombyte, intobyte};
165        for i in 0..255u8 {
166            let h = frombyte(i);
167            let b = intobyte(h.0, h.1).unwrap();
168            assert_eq!(i, b);
169        }
170        let hex = ["ff", "aa", "f0", "a0", "0f", "0a", "00", "99", "90", "09"];
171        for s in hex.iter() {
172            let s: &[u8] = s.as_ref();
173            let v = intobyte(s[0], s[1]).unwrap();
174            let (a, b) = frombyte(v);
175            assert_eq!(s, &[a, b]);
176        }
177    }
178
179    #[test]
180    fn hex_strings() {
181        use utils::{fromhex, intohex};
182        let hv = [
183            "ff",
184            "aa",
185            "f0f0",
186            "a0a0",
187            "1234",
188            "5678",
189            "0000",
190            "0123456789abfdef",
191        ];
192        for hs in hv.iter() {
193            let src: &[u8] = hs.as_ref();
194            let mut buff = vec![0u8; src.len() / 2];
195            let mut rslt = vec![0u8; buff.len() * 2];
196            fromhex(&mut buff, src).unwrap();
197            intohex(&mut rslt, &buff);
198            assert_eq!(src, AsRef::<[u8]>::as_ref(&rslt));
199        }
200    }
201}