]> git.proxmox.com Git - proxmox.git/commitdiff
tools: refactor hex parsing
authorWolfgang Bumiller <w.bumiller@proxmox.com>
Thu, 19 Nov 2020 08:13:06 +0000 (09:13 +0100)
committerWolfgang Bumiller <w.bumiller@proxmox.com>
Thu, 19 Nov 2020 08:16:50 +0000 (09:16 +0100)
deduplicate parsing into a `hex_to_bin_exact`

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
proxmox/src/tools/mod.rs

index 9ce9dd752a54e3fb06a90cab8814f831ca27d5d3..ff3a720aca513f7cca5fcca782e48f4af561439a 100644 (file)
@@ -139,6 +139,37 @@ pub fn bin_to_hex(digest: &[u8]) -> String {
     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
@@ -151,78 +182,36 @@ pub fn bin_to_hex(digest: &[u8]) -> String {
 /// 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! {