From 97abf032d3430af4b2f6fa9b85806d99b2585b9d Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 28 Aug 2021 13:38:23 +0900 Subject: [PATCH 1/4] Adjust #[inline] on encoding functions --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index bc6a69d..460f2a0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -102,6 +102,7 @@ impl<'a> BytesToHexChars<'a> { impl<'a> Iterator for BytesToHexChars<'a> { type Item = char; + #[inline] fn next(&mut self) -> Option { match self.next.take() { Some(current) => Some(current), @@ -129,7 +130,6 @@ impl<'a> iter::ExactSizeIterator for BytesToHexChars<'a> { } } -#[inline] fn encode_to_iter>(table: &'static [u8; 16], source: &[u8]) -> T { BytesToHexChars::new(source, table).collect() } From 0129182ab8c57f9962c52367dc9665d69a3acab2 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 28 Aug 2021 13:50:31 +0900 Subject: [PATCH 2/4] Use encode_to_slice in encode --- src/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 460f2a0..28ff6c8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -257,7 +257,10 @@ from_hex_array_impl! { #[must_use] #[cfg(feature = "alloc")] pub fn encode>(data: T) -> String { - data.encode_hex() + let data = data.as_ref(); + let mut out = vec![0; data.len() * 2]; + encode_to_slice(data, &mut out).unwrap(); + String::from_utf8(out).unwrap() } /// Encodes `data` as hex string using uppercase characters. From 19b333d91cbbc58274cdcd948ef0b2f6078b5a2b Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 28 Aug 2021 13:51:30 +0900 Subject: [PATCH 3/4] Use chunks_exact_mut instead of generate_iter --- src/lib.rs | 31 +++---------------------------- 1 file changed, 3 insertions(+), 28 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 28ff6c8..9a456c3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -329,17 +329,6 @@ pub fn decode_to_slice>(data: T, out: &mut [u8]) -> Result<(), Fr Ok(()) } -// generates an iterator like this -// (0, 1) -// (2, 3) -// (4, 5) -// (6, 7) -// ... -#[inline] -fn generate_iter(len: usize) -> impl Iterator { - (0..len).step_by(2).zip((0..len).skip(1).step_by(2)) -} - // the inverse of `val`. #[inline] #[must_use] @@ -388,14 +377,10 @@ pub fn encode_to_slice>(input: T, output: &mut [u8]) -> Result<() return Err(FromHexError::InvalidStringLength); } - for (byte, (i, j)) in input - .as_ref() - .iter() - .zip(generate_iter(input.as_ref().len() * 2)) - { + for (byte, output) in input.as_ref().iter().zip(output.chunks_exact_mut(2)) { let (high, low) = byte2hex(*byte, HEX_CHARS_LOWER); - output[i] = high; - output[j] = low; + output[0] = high; + output[1] = low; } Ok(()) @@ -406,18 +391,8 @@ mod test { use super::*; #[cfg(feature = "alloc")] use alloc::string::ToString; - #[cfg(feature = "alloc")] - use alloc::vec; use pretty_assertions::assert_eq; - #[test] - #[cfg(feature = "alloc")] - fn test_gen_iter() { - let result = vec![(0, 1), (2, 3)]; - - assert_eq!(generate_iter(5).collect::>(), result); - } - #[test] fn test_encode_to_slice() { let mut output_1 = [0; 4 * 2]; From 8b9307bbb477b3b7af5ec7681c5a14528cda8f5a Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 28 Aug 2021 14:46:49 +0900 Subject: [PATCH 4/4] Add encode_to_slice_upper to improve performance of encode_upper This also fixes #45. --- src/lib.rs | 67 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 55 insertions(+), 12 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9a456c3..bdb029a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -276,7 +276,10 @@ pub fn encode>(data: T) -> String { #[must_use] #[cfg(feature = "alloc")] pub fn encode_upper>(data: T) -> String { - data.encode_hex_upper() + let data = data.as_ref(); + let mut out = vec![0; data.len() * 2]; + encode_to_slice_upper(data, &mut out).unwrap(); + String::from_utf8(out).unwrap() } /// Decodes a hex string into raw bytes. @@ -339,7 +342,25 @@ const fn byte2hex(byte: u8, table: &[u8; 16]) -> (u8, u8) { (high, low) } -/// Encodes some bytes into a mutable slice of bytes. +fn encode_to_slice_inner( + input: &[u8], + output: &mut [u8], + table: &[u8; 16], +) -> Result<(), FromHexError> { + if input.len() * 2 != output.len() { + return Err(FromHexError::InvalidStringLength); + } + + for (byte, output) in input.iter().zip(output.chunks_exact_mut(2)) { + let (high, low) = byte2hex(*byte, table); + output[0] = high; + output[1] = low; + } + + Ok(()) +} + +/// Encodes some bytes into a mutable slice of bytes using lowercase characters. /// /// The output buffer, has to be able to hold exactly `input.len() * 2` bytes, /// otherwise this function will return an error. @@ -373,17 +394,31 @@ const fn byte2hex(byte: u8, table: &[u8; 16]) -> (u8, u8) { /// # } /// ``` pub fn encode_to_slice>(input: T, output: &mut [u8]) -> Result<(), FromHexError> { - if input.as_ref().len() * 2 != output.len() { - return Err(FromHexError::InvalidStringLength); - } - - for (byte, output) in input.as_ref().iter().zip(output.chunks_exact_mut(2)) { - let (high, low) = byte2hex(*byte, HEX_CHARS_LOWER); - output[0] = high; - output[1] = low; - } + encode_to_slice_inner(input.as_ref(), output, HEX_CHARS_LOWER) +} - Ok(()) +/// Encodes some bytes into a mutable slice of bytes using uppercase characters. +/// +/// The output buffer, has to be able to hold exactly `input.len() * 2` bytes, +/// otherwise this function will return an error. +/// +/// # Example +/// +/// ``` +/// # use hex::FromHexError; +/// # fn main() -> Result<(), FromHexError> { +/// let mut bytes = [0u8; 4 * 2]; +/// +/// hex::encode_to_slice_upper(b"kiwi", &mut bytes)?; +/// assert_eq!(&bytes, b"6B697769"); +/// # Ok(()) +/// # } +/// ``` +pub fn encode_to_slice_upper>( + input: T, + output: &mut [u8], +) -> Result<(), FromHexError> { + encode_to_slice_inner(input.as_ref(), output, HEX_CHARS_UPPER) } #[cfg(test)] @@ -398,10 +433,14 @@ mod test { let mut output_1 = [0; 4 * 2]; encode_to_slice(b"kiwi", &mut output_1).unwrap(); assert_eq!(&output_1, b"6b697769"); + encode_to_slice_upper(b"kiwi", &mut output_1).unwrap(); + assert_eq!(&output_1, b"6B697769"); let mut output_2 = [0; 5 * 2]; encode_to_slice(b"kiwis", &mut output_2).unwrap(); assert_eq!(&output_2, b"6b69776973"); + encode_to_slice_upper(b"kiwis", &mut output_2).unwrap(); + assert_eq!(&output_2, b"6B69776973"); let mut output_3 = [0; 100]; @@ -409,6 +448,10 @@ mod test { encode_to_slice(b"kiwis", &mut output_3), Err(FromHexError::InvalidStringLength) ); + assert_eq!( + encode_to_slice_upper(b"kiwis", &mut output_3), + Err(FromHexError::InvalidStringLength) + ); } #[test]