From 819daa375107be773527ea6e996e4334c366683d Mon Sep 17 00:00:00 2001 From: Xavier Lau Date: Sat, 26 Nov 2022 17:08:51 +0800 Subject: [PATCH] Optimize algorithm, generic (#31) --- README.md | 180 ++++++++++------------ fuzz/fuzz_targets/bytes_hex_conversion.rs | 4 +- src/lib.rs | 170 ++++++++++---------- src/test.rs | 136 +++++++++++++--- 4 files changed, 281 insertions(+), 209 deletions(-) diff --git a/README.md b/README.md index e85ffac..39e7fb7 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ ## Abilities #### `TryFromHex` trait -- Convert `Hex` to `Num` +- Convert hex to num - type `AsRef -> isize` - type `AsRef -> i8` - type `AsRef -> i16` @@ -29,21 +29,21 @@ - type `AsRef -> u128` #### `bytes` prefixed functions -- Convert `Bytes` to `Hex` +- Convert bytes to hex - type `AsRef<[u8]> -> String` #### `hex` prefixed functions -- Convert `HexBytes` to `Hex` +- Convert `HexBytes` to hex - type `&[u8] -> &str` - e.g. `b"0x..." -> "0x..."` -- Transform `Hex` from `Array` +- Transform hex from `Array` - type `&str -> [u8; N]` -- Convert `Hex` to `Bytes` - - type `AsRef -> Vec` -- Convert `Hex` to `Slice` - - type `AsRef -> &[u8]` -- Transform `Hex` to `T` - - type `AsRef -> T` +- Convert hex to bytes + - type `AsRef<[u8]> -> Vec` +- Convert hex to `Slice` + - type `AsRef<[u8]> -> &[u8]` +- Transform hex to `T` + - type `AsRef<[u8]> -> T` - e.g. `"0x..." -> [u8; 20] -> H160` #### `slice` prefixed functions @@ -72,123 +72,111 @@ - e.g. `"0x00" -> vec![0_u8]` ## Benchmark results -
Friday, November 25th, 2022
+
Friday, November 26th, 2022
```rs -array_bytes::bytes2hex time: [38.078 µs 38.126 µs 38.177 µs] - change: [-0.5147% -0.2140% +0.1154%] (p = 0.18 > 0.05) - No change in performance detected. -Found 14 outliers among 100 measurements (14.00%) - 1 (1.00%) low severe - 2 (2.00%) high mild - 11 (11.00%) high severe +array_bytes::bytes2hex time: [37.241 µs 37.321 µs 37.407 µs] + change: [-2.2373% -1.9757% -1.7126%] (p = 0.00 < 0.05) + Performance has improved. +Found 4 outliers among 100 measurements (4.00%) + 4 (4.00%) high mild -hex::encode time: [136.19 µs 136.65 µs 137.14 µs] - change: [-0.3002% -0.0359% +0.2459%] (p = 0.81 > 0.05) - No change in performance detected. -Found 9 outliers among 100 measurements (9.00%) - 2 (2.00%) low mild - 6 (6.00%) high mild +hex::encode time: [132.17 µs 132.42 µs 132.66 µs] + change: [-2.4772% -2.2353% -1.9952%] (p = 0.00 < 0.05) + Performance has improved. +Found 5 outliers among 100 measurements (5.00%) + 4 (4.00%) high mild 1 (1.00%) high severe -rustc_hex::to_hex time: [79.155 µs 79.268 µs 79.398 µs] - change: [-2.9058% -1.8791% -0.9713%] (p = 0.00 < 0.05) - Change within noise threshold. +rustc_hex::to_hex time: [77.565 µs 77.885 µs 78.236 µs] + change: [-1.7109% -1.4392% -1.1561%] (p = 0.00 < 0.05) + Performance has improved. Found 5 outliers among 100 measurements (5.00%) 4 (4.00%) high mild 1 (1.00%) high severe -faster_hex::hex_string time: [18.483 µs 18.524 µs 18.565 µs] - change: [-0.6456% -0.3159% -0.0313%] (p = 0.04 < 0.05) - Change within noise threshold. -Found 11 outliers among 100 measurements (11.00%) - 1 (1.00%) low mild - 8 (8.00%) high mild +faster_hex::hex_string time: [18.049 µs 18.091 µs 18.140 µs] + change: [-2.1506% -1.7957% -1.3953%] (p = 0.00 < 0.05) + Performance has improved. +Found 7 outliers among 100 measurements (7.00%) + 5 (5.00%) high mild 2 (2.00%) high severe faster_hex::hex_encode_fallback - time: [18.497 µs 18.528 µs 18.561 µs] - change: [-11.257% -4.9349% -0.9424%] (p = 0.08 > 0.05) - No change in performance detected. -Found 8 outliers among 100 measurements (8.00%) - 6 (6.00%) high mild - 2 (2.00%) high severe - -array_bytes::hex2bytes time: [224.34 µs 224.59 µs 224.86 µs] - change: [-1.7703% -1.2368% -0.7744%] (p = 0.00 < 0.05) - Change within noise threshold. -Found 4 outliers among 100 measurements (4.00%) - 3 (3.00%) high mild + time: [17.978 µs 18.018 µs 18.064 µs] + change: [-2.6657% -2.3283% -1.9846%] (p = 0.00 < 0.05) + Performance has improved. +Found 2 outliers among 100 measurements (2.00%) + 1 (1.00%) high mild 1 (1.00%) high severe +array_bytes::hex2bytes time: [119.27 µs 119.54 µs 119.81 µs] + change: [-2.5026% -2.2957% -2.0423%] (p = 0.00 < 0.05) + Performance has improved. +Found 14 outliers among 100 measurements (14.00%) + 11 (11.00%) high mild + 3 (3.00%) high severe + array_bytes::hex2bytes_unchecked - time: [222.78 µs 223.07 µs 223.39 µs] - change: [-0.5184% -0.1710% +0.1429%] (p = 0.32 > 0.05) - No change in performance detected. -Found 9 outliers among 100 measurements (9.00%) - 7 (7.00%) high mild + time: [82.136 µs 82.324 µs 82.531 µs] + change: [-55.176% -53.193% -52.029%] (p = 0.00 < 0.05) + Performance has improved. +Found 15 outliers among 100 measurements (15.00%) + 13 (13.00%) high mild 2 (2.00%) high severe -array_bytes::hex2slice time: [211.37 µs 211.49 µs 211.62 µs] - change: [-3.1739% -2.1127% -1.2688%] (p = 0.00 < 0.05) - Performance has improved. -Found 6 outliers among 100 measurements (6.00%) - 2 (2.00%) low severe - 4 (4.00%) high severe +array_bytes::hex2slice time: [112.94 µs 113.32 µs 113.78 µs] + change: [-1.6410% -1.1545% -0.6772%] (p = 0.00 < 0.05) + Change within noise threshold. +Found 3 outliers among 100 measurements (3.00%) + 2 (2.00%) high mild + 1 (1.00%) high severe array_bytes::hex2slice_unchecked - time: [212.00 µs 212.34 µs 212.71 µs] - change: [-0.8427% -0.5482% -0.2810%] (p = 0.00 < 0.05) - Change within noise threshold. -Found 9 outliers among 100 measurements (9.00%) - 1 (1.00%) low mild - 6 (6.00%) high mild - 2 (2.00%) high severe + time: [89.416 µs 89.650 µs 89.956 µs] + change: [-22.750% -22.423% -22.099%] (p = 0.00 < 0.05) + Performance has improved. +Found 14 outliers among 100 measurements (14.00%) + 8 (8.00%) high mild + 6 (6.00%) high severe -hex::decode time: [244.37 µs 244.78 µs 245.25 µs] - change: [-1.4130% -1.0496% -0.7133%] (p = 0.00 < 0.05) +hex::decode time: [239.97 µs 240.64 µs 241.33 µs] + change: [+0.3733% +0.6910% +1.0245%] (p = 0.00 < 0.05) Change within noise threshold. -Found 8 outliers among 100 measurements (8.00%) - 5 (5.00%) high mild - 3 (3.00%) high severe +Found 1 outliers among 100 measurements (1.00%) + 1 (1.00%) high mild -hex::decode_to_slice time: [166.67 µs 166.90 µs 167.16 µs] - change: [+0.1484% +0.3293% +0.5160%] (p = 0.00 < 0.05) - Change within noise threshold. -Found 3 outliers among 100 measurements (3.00%) +hex::decode_to_slice time: [162.75 µs 163.12 µs 163.61 µs] + change: [-0.4036% -0.0331% +0.3614%] (p = 0.86 > 0.05) + No change in performance detected. +Found 7 outliers among 100 measurements (7.00%) 3 (3.00%) high mild + 4 (4.00%) high severe -rustc_hex::from_hex time: [176.56 µs 177.79 µs 179.13 µs] - change: [+1.4009% +2.5404% +3.5866%] (p = 0.00 < 0.05) - Performance has regressed. -Found 5 outliers among 100 measurements (5.00%) - 2 (2.00%) low mild - 1 (1.00%) high mild - 2 (2.00%) high severe +rustc_hex::from_hex time: [166.34 µs 167.65 µs 169.03 µs] + change: [-1.5255% -0.5122% +0.5735%] (p = 0.33 > 0.05) + No change in performance detected. -faster_hex::hex_decode time: [39.127 µs 39.342 µs 39.582 µs] - change: [-0.0442% +0.3160% +0.6546%] (p = 0.08 > 0.05) +faster_hex::hex_decode time: [38.419 µs 38.613 µs 38.812 µs] + change: [-0.9090% -0.3666% +0.1714%] (p = 0.19 > 0.05) No change in performance detected. -Found 18 outliers among 100 measurements (18.00%) - 3 (3.00%) low severe - 8 (8.00%) high mild - 7 (7.00%) high severe +Found 4 outliers among 100 measurements (4.00%) + 4 (4.00%) high mild faster_hex::hex_decode_unchecked - time: [16.429 µs 16.479 µs 16.538 µs] - change: [+0.5738% +0.9176% +1.3113%] (p = 0.00 < 0.05) - Change within noise threshold. -Found 14 outliers among 100 measurements (14.00%) - 6 (6.00%) high mild - 8 (8.00%) high severe + time: [16.122 µs 16.166 µs 16.212 µs] + change: [-0.2496% +0.1886% +0.6435%] (p = 0.41 > 0.05) + No change in performance detected. +Found 1 outliers among 100 measurements (1.00%) + 1 (1.00%) high mild faster_hex::hex_decode_fallback - time: [16.422 µs 16.440 µs 16.460 µs] - change: [+0.3595% +0.6397% +0.9141%] (p = 0.00 < 0.05) + time: [16.001 µs 16.039 µs 16.081 µs] + change: [-1.1315% -0.7797% -0.4279%] (p = 0.00 < 0.05) Change within noise threshold. -Found 5 outliers among 100 measurements (5.00%) - 1 (1.00%) high mild - 4 (4.00%) high severe +Found 8 outliers among 100 measurements (8.00%) + 6 (6.00%) high mild + 2 (2.00%) high severe ```
diff --git a/fuzz/fuzz_targets/bytes_hex_conversion.rs b/fuzz/fuzz_targets/bytes_hex_conversion.rs index 8ac7d58..b81cafc 100644 --- a/fuzz/fuzz_targets/bytes_hex_conversion.rs +++ b/fuzz/fuzz_targets/bytes_hex_conversion.rs @@ -3,9 +3,9 @@ libfuzzer_sys::fuzz_target!(|data: &[u8]| { let _ = array_bytes::bytes2hex("", data); let _ = array_bytes::hex_bytes2hex_str(data); - let _ = array_bytes::hex2bytes(&String::from_utf8_lossy(data)); + let _ = array_bytes::hex2bytes(data); { let mut v = vec![0; data.len() / 2]; - let _ = array_bytes::hex2slice(&String::from_utf8_lossy(data), &mut v); + let _ = array_bytes::hex2slice(data, &mut v); } }); diff --git a/src/lib.rs b/src/lib.rs index b8630eb..13a23cc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,11 +22,6 @@ use alloc::{string::String, vec::Vec}; /// The main result of array-bytes. pub type Result = CoreResult; -/// Alias for `Vec`. -pub type Bytes = Vec; -/// Alias for `String`. -pub type Hex = String; - /// Simple and safe `T`/[`AsRef`] conversions that may fail in a controlled way under some /// circumstances. pub trait TryFromHex @@ -46,7 +41,7 @@ macro_rules! impl_num_from_hex { where H: AsRef, { - let hex = hex.as_ref().trim_start_matches("0x"); + let hex = strip_0x(hex.as_ref()); Self::from_str_radix(hex, 16).map_err(Error::ParseIntError) } @@ -163,7 +158,7 @@ where slice2array_unchecked(slice).into() } -/// [`Vec`] to `[T; M]`. +/// [`Vec`] to `[T; N]`. /// /// # Examples /// ``` @@ -276,23 +271,21 @@ pub unsafe fn hex_bytes2hex_str_unchecked(bytes: &[u8]) -> &str { mem::transmute(bytes) } -/// `AsRef<[u8]>` to [`Hex`]. +/// `AsRef<[u8]>` to [`String`]. /// /// # Examples /// ``` -/// use array_bytes::Hex; -/// /// assert_eq!( /// array_bytes::bytes2hex("0x", b"Love Jane Forever"), -/// Hex::from("0x4c6f7665204a616e6520466f7265766572") +/// String::from("0x4c6f7665204a616e6520466f7265766572") /// ); /// ``` -pub fn bytes2hex(prefix: &str, bytes: B) -> Hex +pub fn bytes2hex(prefix: &str, bytes: B) -> String where B: AsRef<[u8]>, { let bytes = bytes.as_ref(); - let mut hex = Hex::with_capacity(prefix.len() + bytes.len() * 2); + let mut hex = String::with_capacity(prefix.len() + bytes.len() * 2); prefix.chars().for_each(|byte| hex.push(byte)); bytes.iter().for_each(|byte| { @@ -314,7 +307,7 @@ where /// ``` pub fn hex2array(hex: H) -> Result<[u8; N]> where - H: AsRef, + H: AsRef<[u8]>, { vec2array(hex2bytes(hex.as_ref())?) } @@ -330,14 +323,16 @@ where /// ``` pub fn hex2array_unchecked(hex: H) -> [u8; N] where - H: AsRef, + H: AsRef<[u8]>, { hex2bytes_unchecked(hex).try_into().unwrap() } -/// [`AsRef`] to [`Bytes`]. +/// `AsRef<[u8]>` to [`Vec`]. /// -/// Return error while length is an odd number or any byte out of radix. +/// Return error if: +/// - length is odd +/// - encounter invalid hex ascii /// /// # Examples /// ``` @@ -346,40 +341,20 @@ where /// Ok(b"Love Jane Forever".to_vec()) /// ); /// ``` -pub fn hex2bytes(hex: H) -> Result +pub fn hex2bytes(hex: H) -> Result> where - H: AsRef, + H: AsRef<[u8]>, { - let hex = strip_0x(hex.as_ref()); + let hex = strip_0x_bytes(hex.as_ref()); if hex.len() % 2 != 0 { Err(Error::InvalidLength)?; } - let mut bytes = Bytes::new(); + let mut bytes = Vec::new(); for i in (0..hex.len()).step_by(2) { - let Some(str) = hex.get(i..i + 2) else { - Err(Error::InvalidCharacter { character: hex.as_bytes()[i] as _, index: i })? - }; - - match u8::from_str_radix(str, 16) { - Ok(byte_) => bytes.push(byte_), - Err(e) => { - if !is_hex_ascii(&hex.as_bytes()[i]) { - Err(Error::InvalidCharacter { character: hex.as_bytes()[i] as _, index: i })?; - } - if !is_hex_ascii(&hex.as_bytes()[i + 1]) { - Err(Error::InvalidCharacter { - character: hex.as_bytes()[i + 1] as _, - index: i + 1, - })?; - } - - // This will never happen, but for more safety - Err(Error::ParseIntError(e))?; - }, - } + bytes.push(hex2byte((&hex[i], i), (&hex[i + 1], i + 1))?); } Ok(bytes) @@ -394,21 +369,23 @@ where /// *b"Love Jane Forever" /// ); /// ``` -pub fn hex2bytes_unchecked(hex: H) -> Bytes +pub fn hex2bytes_unchecked(hex: H) -> Vec where - H: AsRef, + H: AsRef<[u8]>, { - let hex = strip_0x(hex.as_ref()); + let hex = strip_0x_bytes(hex.as_ref()); - (0..hex.len()).step_by(2).map(|i| u8::from_str_radix(&hex[i..i + 2], 16).unwrap()).collect() + (0..hex.len()).step_by(2).map(|i| hex2byte_unchecked(&hex[i], &hex[i + 1])).collect() } -/// [`AsRef`] to `&[u8]`. +/// `AsRef<[u8]>` to `&[u8]`. /// /// This function will modify the given slice's source and return the revised result. /// -/// Return error while the length is an odd number, any byte out of radix, or mismatch in the slice -/// size. +/// Return error if: +/// - length is odd +/// - encounter invalid hex ascii +/// - mismatched slice size /// /// # Examples /// ``` @@ -419,9 +396,9 @@ where /// ``` pub fn hex2slice(hex: H, slice: &mut [u8]) -> Result<&[u8]> where - H: AsRef, + H: AsRef<[u8]>, { - let hex = strip_0x(hex.as_ref()); + let hex = strip_0x_bytes(hex.as_ref()); if hex.len() % 2 != 0 { Err(Error::InvalidLength)?; @@ -434,27 +411,7 @@ where } for (byte, i) in slice.iter_mut().zip((0..hex.len()).step_by(2)) { - let Some(str) = hex.get(i..i + 2) else { - Err(Error::InvalidCharacter { character: hex.as_bytes()[i] as _, index: i })? - }; - - match u8::from_str_radix(str, 16) { - Ok(byte_) => *byte = byte_, - Err(e) => { - if !is_hex_ascii(&hex.as_bytes()[i]) { - Err(Error::InvalidCharacter { character: hex.as_bytes()[i] as _, index: i })?; - } - if !is_hex_ascii(&hex.as_bytes()[i + 1]) { - Err(Error::InvalidCharacter { - character: hex.as_bytes()[i + 1] as _, - index: i + 1, - })?; - } - - // This will never happen, but for more safety - Err(Error::ParseIntError(e))?; - }, - } + *byte = hex2byte((&hex[i], i), (&hex[i + 1], i + 1))?; } Ok(slice) @@ -471,19 +428,19 @@ where /// ``` pub fn hex2slice_unchecked(hex: H, slice: &mut [u8]) -> &[u8] where - H: AsRef, + H: AsRef<[u8]>, { - let hex = strip_0x(hex.as_ref()); + let hex = strip_0x_bytes(hex.as_ref()); - slice.iter_mut().zip((0..hex.len()).step_by(2)).for_each(|(byte, i)| { - *byte = u8::from_str_radix(&hex[i..i + 2], 16) - .expect("radix is always 16 which will never fail this; qed"); - }); + slice + .iter_mut() + .zip((0..hex.len()).step_by(2)) + .for_each(|(byte, i)| *byte = hex2byte_unchecked(&hex[i], &hex[i + 1])); slice } -/// Try to convert [`AsRef`] to `T` directly, where `T: From>`. +/// Try to convert `AsRef<[u8]>` to `T` directly, where `T: From>`. /// /// # Examples /// ``` @@ -502,8 +459,8 @@ where /// ``` pub fn hex_into(hex: H) -> Result where - H: AsRef, - T: From, + H: AsRef<[u8]>, + T: From>, { Ok(hex2bytes(hex.as_ref())?.into()) } @@ -527,13 +484,13 @@ where /// ``` pub fn hex_into_unchecked(hex: H) -> T where - H: AsRef, - T: From, + H: AsRef<[u8]>, + T: From>, { hex2bytes_unchecked(hex).into() } -/// Try to convert [`AsRef`] to `T` directly, where `T: From<[u8; N]>`. +/// Try to convert `AsRef<[u8]>` to `T` directly, where `T: From<[u8; N]>`. /// /// # Examples /// ``` @@ -552,7 +509,7 @@ where /// ``` pub fn hex_n_into(hex: H) -> Result where - H: AsRef, + H: AsRef<[u8]>, T: From<[u8; N]>, { Ok(hex2array(hex)?.into()) @@ -577,7 +534,7 @@ where /// ``` pub fn hex_n_into_unchecked(hex: H) -> T where - H: AsRef, + H: AsRef<[u8]>, T: From<[u8; N]>, { hex2array_unchecked(hex).into() @@ -615,7 +572,7 @@ where pub fn hex_deserialize_into<'de, D, T>(hex: D) -> CoreResult where D: Deserializer<'de>, - T: From, + T: From>, { Ok(hex2bytes_unchecked(<&str>::deserialize(hex)?).into()) } @@ -699,17 +656,16 @@ where T::try_from_hex(hex).map_err(|_| D::Error::custom(alloc::format!("Invalid hex str `{}`", hex))) } -/// Deserialize hex to [`Bytes`]. +/// Deserialize hex to [`Vec`]. /// /// # Examples /// ``` -/// use array_bytes::Bytes; /// use serde::Deserialize; /// /// #[derive(Debug, PartialEq, Deserialize)] /// struct LJF { /// #[serde(deserialize_with = "array_bytes::de_hex2bytes")] -/// ljf: Bytes, +/// ljf: Vec, /// } /// /// assert_eq!( @@ -723,7 +679,7 @@ where /// ); /// ``` #[cfg(feature = "serde")] -pub fn de_hex2bytes<'de, D>(hex: D) -> CoreResult +pub fn de_hex2bytes<'de, D>(hex: D) -> CoreResult, D::Error> where D: Deserializer<'de>, { @@ -740,9 +696,41 @@ fn strip_0x(hex: &str) -> &str { } } +fn strip_0x_bytes(hex: &[u8]) -> &[u8] { + if let Some(hex) = hex.strip_prefix(b"0x") { + hex + } else { + hex + } +} + fn is_hex_ascii(byte: &u8) -> bool { // Convert to lowercase. let byte = byte | 0b10_0000; matches!(byte, b'0'..=b'9' | b'a'..=b'f') } + +fn hex_ascii2digit(hex_ascii: &u8) -> Option { + // Convert to lowercase. + let hex_ascii = hex_ascii | 0b10_0000; + + match hex_ascii { + b'0'..=b'9' => Some(hex_ascii - b'0'), + b'a'..=b'f' => Some(hex_ascii - b'a' + 10), + _ => None, + } +} + +fn hex2byte(hex_ascii_1: (&u8, usize), hex_ascii_2: (&u8, usize)) -> Result { + let byte = hex_ascii2digit(hex_ascii_1.0) + .ok_or(Error::InvalidCharacter { character: *hex_ascii_1.0 as _, index: hex_ascii_1.1 })? + << 4 | hex_ascii2digit(hex_ascii_2.0) + .ok_or(Error::InvalidCharacter { character: *hex_ascii_2.0 as _, index: hex_ascii_2.1 })?; + + Ok(byte) +} + +fn hex2byte_unchecked(hex_ascii_1: &u8, hex_ascii_2: &u8) -> u8 { + hex_ascii2digit(hex_ascii_1).unwrap() << 4 | hex_ascii2digit(hex_ascii_2).unwrap() +} diff --git a/src/test.rs b/src/test.rs index d0af5a0..2809642 100644 --- a/src/test.rs +++ b/src/test.rs @@ -5,7 +5,7 @@ use crate::*; macro_rules! bytes { ($v:expr; $n:expr) => {{ - let mut v = Bytes::new(); + let mut v = Vec::new(); for _ in 0..$n { v.push($v); @@ -16,9 +16,9 @@ macro_rules! bytes { } #[derive(Debug, PartialEq)] -struct LJF(Bytes); -impl From for LJF { - fn from(bytes: Bytes) -> Self { +struct LJF(Vec); +impl From> for LJF { + fn from(bytes: Vec) -> Self { Self(bytes) } } @@ -74,11 +74,11 @@ fn vec_n_into_unchecked_should_work() { fn bytes2hex_should_work() { assert_eq!( bytes2hex("0x", b"Love Jane Forever"), - Hex::from("0x4c6f7665204a616e6520466f7265766572") + String::from("0x4c6f7665204a616e6520466f7265766572") ); assert_eq!( bytes2hex("", b"Love Jane Forever"), - Hex::from("4c6f7665204a616e6520466f7265766572") + String::from("4c6f7665204a616e6520466f7265766572") ); } @@ -120,7 +120,15 @@ fn hex_bytes2hex_str_unchecked_should_work() { #[test] fn hex2array_should_work() { assert_eq!(hex2array("0x4c6f7665204a616e6520466f7265766572"), Ok(*b"Love Jane Forever")); + assert_eq!( + hex2array("0x4c6f7665204a616e6520466f7265766572".as_bytes()), + Ok(*b"Love Jane Forever") + ); assert_eq!(hex2array("4c6f7665204a616e6520466f7265766572"), Ok(*b"Love Jane Forever")); + assert_eq!( + hex2array("4c6f7665204a616e6520466f7265766572".as_bytes()), + Ok(*b"Love Jane Forever") + ); } #[test] @@ -129,7 +137,15 @@ fn hex2bytes_should_work() { hex2bytes("0x4c6f7665204a616e6520466f7265766572"), Ok(b"Love Jane Forever".to_vec()) ); + assert_eq!( + hex2bytes("0x4c6f7665204a616e6520466f7265766572".as_bytes()), + Ok(b"Love Jane Forever".to_vec()) + ); assert_eq!(hex2bytes("4c6f7665204a616e6520466f7265766572"), Ok(b"Love Jane Forever".to_vec())); + assert_eq!( + hex2bytes("4c6f7665204a616e6520466f7265766572".as_bytes()), + Ok(b"Love Jane Forever".to_vec()) + ); assert_eq!(hex2bytes("我爱你"), Err(Error::InvalidLength)); assert_eq!(hex2bytes("0x我爱你"), Err(Error::InvalidLength)); @@ -141,7 +157,15 @@ fn hex2bytes_should_work() { #[test] fn hex2bytes_unchecked_should_work() { assert_eq!(hex2bytes_unchecked("0x4c6f7665204a616e6520466f7265766572"), *b"Love Jane Forever"); + assert_eq!( + hex2bytes_unchecked("0x4c6f7665204a616e6520466f7265766572".as_bytes()), + *b"Love Jane Forever" + ); assert_eq!(hex2bytes_unchecked("4c6f7665204a616e6520466f7265766572"), *b"Love Jane Forever"); + assert_eq!( + hex2bytes_unchecked("4c6f7665204a616e6520466f7265766572".as_bytes()), + *b"Love Jane Forever" + ); } #[test] @@ -155,6 +179,15 @@ fn hex2slice_should_work() { ); assert_eq!(bytes, *b"Love Jane Forever"); } + { + let mut bytes = [0; 17]; + + assert_eq!( + hex2slice("0x4c6f7665204a616e6520466f7265766572".as_bytes(), &mut bytes), + Ok(b"Love Jane Forever".as_slice()) + ); + assert_eq!(bytes, *b"Love Jane Forever"); + } { let mut bytes = [0; 17]; @@ -165,6 +198,15 @@ fn hex2slice_should_work() { ); assert_eq!(bytes, *b"Love Jane Forever"); } + { + let mut bytes = [0; 17]; + + assert_eq!( + hex2slice("4c6f7665204a616e6520466f7265766572".as_bytes(), &mut bytes), + Ok(b"Love Jane Forever".as_slice()) + ); + assert_eq!(bytes, *b"Love Jane Forever"); + } assert_eq!(hex2slice("0", &mut []), Err(Error::InvalidLength)); assert_eq!(hex2slice("0x0", &mut []), Err(Error::InvalidLength)); @@ -184,21 +226,43 @@ fn hex2slice_should_work() { #[test] fn hex2slice_unchecked_should_work() { - let mut bytes = [0; 17]; + { + let mut bytes = [0; 17]; - assert_eq!( - hex2slice_unchecked("0x4c6f7665204a616e6520466f7265766572", &mut bytes), - b"Love Jane Forever" - ); - assert_eq!(bytes, *b"Love Jane Forever"); + assert_eq!( + hex2slice_unchecked("0x4c6f7665204a616e6520466f7265766572", &mut bytes), + b"Love Jane Forever" + ); + assert_eq!(bytes, *b"Love Jane Forever"); + } + { + let mut bytes = [0; 17]; + + assert_eq!( + hex2slice_unchecked("0x4c6f7665204a616e6520466f7265766572".as_bytes(), &mut bytes), + b"Love Jane Forever" + ); + assert_eq!(bytes, *b"Love Jane Forever"); + } - let mut bytes = [0; 17]; + { + let mut bytes = [0; 17]; - assert_eq!( - hex2slice_unchecked("4c6f7665204a616e6520466f7265766572", &mut bytes), - b"Love Jane Forever" - ); - assert_eq!(bytes, *b"Love Jane Forever"); + assert_eq!( + hex2slice_unchecked("4c6f7665204a616e6520466f7265766572", &mut bytes), + b"Love Jane Forever" + ); + assert_eq!(bytes, *b"Love Jane Forever"); + } + { + let mut bytes = [0; 17]; + + assert_eq!( + hex2slice_unchecked("4c6f7665204a616e6520466f7265766572".as_bytes(), &mut bytes), + b"Love Jane Forever" + ); + assert_eq!(bytes, *b"Love Jane Forever"); + } } #[test] @@ -207,10 +271,18 @@ fn hex_into_should_work() { hex_into::<_, LJF>("0x4c6f7665204a616e6520466f7265766572"), Ok(LJF(b"Love Jane Forever".to_vec())) ); + assert_eq!( + hex_into::<_, LJF>("0x4c6f7665204a616e6520466f7265766572".as_bytes()), + Ok(LJF(b"Love Jane Forever".to_vec())) + ); assert_eq!( hex_into::<_, LJF>("4c6f7665204a616e6520466f7265766572"), Ok(LJF(b"Love Jane Forever".to_vec())) ); + assert_eq!( + hex_into::<_, LJF>("4c6f7665204a616e6520466f7265766572".as_bytes()), + Ok(LJF(b"Love Jane Forever".to_vec())) + ); } #[test] @@ -219,10 +291,18 @@ fn hex_n_into_should_work() { hex_n_into::<_, LJFN, 17>("0x4c6f7665204a616e6520466f7265766572"), Ok(LJFN(*b"Love Jane Forever")) ); + assert_eq!( + hex_n_into::<_, LJFN, 17>("0x4c6f7665204a616e6520466f7265766572".as_bytes()), + Ok(LJFN(*b"Love Jane Forever")) + ); assert_eq!( hex_n_into::<_, LJFN, 17>("4c6f7665204a616e6520466f7265766572"), Ok(LJFN(*b"Love Jane Forever")) ); + assert_eq!( + hex_n_into::<_, LJFN, 17>("4c6f7665204a616e6520466f7265766572".as_bytes()), + Ok(LJFN(*b"Love Jane Forever")) + ); } #[test] @@ -231,10 +311,18 @@ fn hex_into_unchecked_should_work() { hex_into_unchecked::<_, LJF>("0x4c6f7665204a616e6520466f7265766572"), LJF(b"Love Jane Forever".to_vec()) ); + assert_eq!( + hex_into_unchecked::<_, LJF>("0x4c6f7665204a616e6520466f7265766572".as_bytes()), + LJF(b"Love Jane Forever".to_vec()) + ); assert_eq!( hex_into_unchecked::<_, LJF>("4c6f7665204a616e6520466f7265766572"), LJF(b"Love Jane Forever".to_vec()) ); + assert_eq!( + hex_into_unchecked::<_, LJF>("4c6f7665204a616e6520466f7265766572".as_bytes()), + LJF(b"Love Jane Forever".to_vec()) + ); } #[test] @@ -243,10 +331,18 @@ fn hex_n_into_unchecked_should_work() { hex_n_into_unchecked::<_, LJFN, 17>("0x4c6f7665204a616e6520466f7265766572"), LJFN(*b"Love Jane Forever") ); + assert_eq!( + hex_n_into_unchecked::<_, LJFN, 17>("0x4c6f7665204a616e6520466f7265766572".as_bytes()), + LJFN(*b"Love Jane Forever") + ); assert_eq!( hex_n_into_unchecked::<_, LJFN, 17>("4c6f7665204a616e6520466f7265766572"), LJFN(*b"Love Jane Forever") ); + assert_eq!( + hex_n_into_unchecked::<_, LJFN, 17>("4c6f7665204a616e6520466f7265766572".as_bytes()), + LJFN(*b"Love Jane Forever") + ); } #[cfg(feature = "serde")] @@ -371,7 +467,7 @@ fn de_hex2bytes_should_work() { #[derive(Debug, PartialEq, Deserialize)] struct LJFN { #[serde(deserialize_with = "de_hex2bytes")] - ljf: Bytes, + ljf: Vec, } assert_eq!( @@ -413,7 +509,7 @@ fn random_input_should_work() { .enumerate() .map(|(i, piece)| { if i % 2 == 0 { - match piece.trim_start_matches("0x").len() { + match strip_0x(&piece).len() { 8 => hex2array_unchecked::<_, 8>(&piece).to_vec(), 32 => hex2array_unchecked::<_, 16>(&piece).to_vec(), 64 => hex2array_unchecked::<_, 32>(&piece).to_vec(),