AsHex(digest).to_string()
}
+/// Convert an ascii character into a hex nibble.
+fn hex_nibble_to_byte(b: u8) -> Result<u8, Error> {
+ Ok(match b {
+ b'0'..=b'9' => b - b'0',
+ b'a'..=b'f' => b - b'a' + 0xA,
+ b'A'..=b'F' => b - b'A' + 0xA,
+ _ => bail!("not a hexadecimal digit: {}", char::from(b)),
+ })
+}
+
+/// Parse hexadecimal digits into a byte array.
+pub fn hex_to_bin_exact(hex: &str, out: &mut [u8]) -> Result<(), Error> {
+ let bytes = hex.as_bytes();
+
+ if bytes.len() != out.len() * 2 {
+ bail!(
+ "hexadecimal string has invalid length ({}, expected {})",
+ bytes.len(),
+ out.len() * 2,
+ );
+ }
+
+ for i in 0..out.len() {
+ let h = hex_nibble_to_byte(bytes[i * 2])?;
+ let l = hex_nibble_to_byte(bytes[i * 2 + 1])?;
+ out[i] = (h << 4) | l;
+ }
+
+ Ok(())
+}
+
/// Convert a string of hexadecimal digits to a byte vector. Any non-digits are treated as an
/// error, so when there is possible whitespace in the string it must be stripped by the caller
/// first. Also, only full bytes are allowed, so the input must consist of an even number of
/// assert_eq!(&data, &[0xaa, 0xbb, 0x01, 0x23]);
/// ```
pub fn hex_to_bin(hex: &str) -> Result<Vec<u8>, Error> {
- let mut result = vec![];
-
- let bytes = hex.as_bytes();
-
- if (bytes.len() % 2) != 0 {
+ if (hex.len() % 2) != 0 {
bail!("hex_to_bin: got wrong input length.");
}
- let val = |c| {
- if c >= b'0' && c <= b'9' {
- return Ok(c - b'0');
- }
- if c >= b'a' && c <= b'f' {
- return Ok(c - b'a' + 10);
- }
- if c >= b'A' && c <= b'F' {
- return Ok(c - b'A' + 10);
- }
- bail!("found illegal hex character.");
- };
-
- for pair in bytes.chunks(2) {
- let h = val(pair[0])?;
- let l = val(pair[1])?;
- result.push((h << 4) | l);
- }
-
- Ok(result)
+ let mut out = unsafe { vec::uninitialized(hex.len() / 2) };
+ hex_to_bin_exact(hex, &mut out)?;
+ Ok(out)
}
// FIXME: This should be renamed to contain the digest algorithm, so that the array's size makes
// sense.
pub fn hex_to_digest(hex: &str) -> Result<[u8; 32], Error> {
let mut digest = [0u8; 32];
-
- let bytes = hex.as_bytes();
-
- if bytes.len() != 64 {
- bail!("got wrong digest length.");
- }
-
- let val = |c| {
- if c >= b'0' && c <= b'9' {
- return Ok(c - b'0');
- }
- if c >= b'a' && c <= b'f' {
- return Ok(c - b'a' + 10);
- }
- if c >= b'A' && c <= b'F' {
- return Ok(c - b'A' + 10);
- }
- bail!("found illegal hex character.");
- };
-
- let mut pos = 0;
- for pair in bytes.chunks(2) {
- if pos >= digest.len() {
- bail!("hex digest too long.");
- }
- let h = val(pair[0])?;
- let l = val(pair[1])?;
- digest[pos] = (h << 4) | l;
- pos += 1;
- }
-
- if pos != digest.len() {
- bail!("hex digest too short.");
- }
-
+ hex_to_bin_exact(hex, &mut digest)?;
Ok(digest)
}
+#[test]
+fn test_hex() {
+ let mut out = [0u8; 5];
+ hex_to_bin_exact("abCA01239f", &mut out).expect("failed to parse hex digit");
+ assert_eq!(out, *b"\xab\xca\x01\x23\x9f");
+ let v = hex_to_bin("abCA01239f").expect("failed to parse hex digit");
+ assert_eq!(v, out);
+
+ hex_to_bin_exact("abca01239", &mut out).expect_err("parsed invalid hex string");
+ hex_to_bin_exact("abca01239fa", &mut out).expect_err("parsed invalid hex string");
+ hex_to_bin_exact("abca0x239f", &mut out).expect_err("parsed invalid hex string");
+}
+
/// Returns the hosts node name (UTS node name)
pub fn nodename() -> &'static str {
lazy_static! {