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