diff --git a/src/lib.rs b/src/lib.rs index bc6a69d..4c993cc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,9 +40,9 @@ #[cfg(feature = "alloc")] extern crate alloc; #[cfg(feature = "alloc")] -use alloc::{string::String, vec::Vec}; +use alloc::{string::String, vec, vec::Vec}; -use core::iter; +use core::{iter, u8}; mod error; pub use crate::error::FromHexError; @@ -172,16 +172,47 @@ pub trait FromHex: Sized { fn from_hex>(hex: T) -> Result; } -const fn val(c: u8, idx: usize) -> Result { - 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'), - _ => Err(FromHexError::InvalidHexCharacter { - c: c as char, +const __: u8 = u8::MAX; + +// Lookup table for ascii to hex decoding. +#[rustfmt::skip] +static DECODE_TABLE: [u8; 256] = [ + // 1 2 3 4 5 6 7 8 9 a b c d e f + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 0 + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 1 + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 2 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, __, __, __, __, __, __, // 3 + __, 10, 11, 12, 13, 14, 15, __, __, __, __, __, __, __, __, __, // 4 + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 5 + __, 10, 11, 12, 13, 14, 15, __, __, __, __, __, __, __, __, __, // 6 + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 7 + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 8 + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 9 + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // a + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // b + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // c + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // d + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // e + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // f +]; + +#[inline] +fn val(bytes: &[u8], idx: usize) -> Result { + let upper = DECODE_TABLE[bytes[0] as usize]; + let lower = DECODE_TABLE[bytes[1] as usize]; + if upper == u8::MAX { + return Err(FromHexError::InvalidHexCharacter { + c: bytes[0] as char, index: idx, - }), + }); } + if lower == u8::MAX { + return Err(FromHexError::InvalidHexCharacter { + c: bytes[1] as char, + index: idx + 1, + }); + } + Ok((upper << 4) | lower) } #[cfg(feature = "alloc")] @@ -194,10 +225,9 @@ impl FromHex for Vec { return Err(FromHexError::OddLength); } - hex.chunks(2) - .enumerate() - .map(|(i, pair)| Ok(val(pair[0], 2 * i)? << 4 | val(pair[1], 2 * i + 1)?)) - .collect() + let mut out = vec![0; hex.len() / 2]; + decode_to_slice(hex, &mut out)?; + Ok(out) } } @@ -309,6 +339,7 @@ pub fn decode>(data: T) -> Result, FromHexError> { /// assert_eq!(hex::decode_to_slice("6b697769", &mut bytes as &mut [u8]), Ok(())); /// assert_eq!(&bytes, b"kiwi"); /// ``` +#[inline] pub fn decode_to_slice>(data: T, out: &mut [u8]) -> Result<(), FromHexError> { let data = data.as_ref(); @@ -319,8 +350,8 @@ pub fn decode_to_slice>(data: T, out: &mut [u8]) -> Result<(), Fr return Err(FromHexError::InvalidStringLength); } - for (i, byte) in out.iter_mut().enumerate() { - *byte = val(data[2 * i], 2 * i)? << 4 | val(data[2 * i + 1], 2 * i + 1)?; + for (i, (data, byte)) in data.chunks_exact(2).zip(out).enumerate() { + *byte = val(data, 2 * i)?; } Ok(()) @@ -340,7 +371,7 @@ fn generate_iter(len: usize) -> impl Iterator { // the inverse of `val`. #[inline] #[must_use] -const fn byte2hex(byte: u8, table: &[u8; 16]) -> (u8, u8) { +fn byte2hex(byte: u8, table: &[u8; 16]) -> (u8, u8) { let high = table[((byte & 0xf0) >> 4) as usize]; let low = table[(byte & 0x0f) as usize]; diff --git a/tests/version-number.rs b/tests/version-number.rs index c4f4084..288592d 100644 --- a/tests/version-number.rs +++ b/tests/version-number.rs @@ -1,5 +1,3 @@ -#![allow(non_fmt_panic)] - #[test] fn test_readme_deps() { version_sync::assert_markdown_deps_updated!("README.md");