From 260b341383a083c4e3491a65fb071d5124e28fa9 Mon Sep 17 00:00:00 2001 From: diogo Date: Sat, 23 May 2020 15:27:50 +0100 Subject: update --- README.md | 1 + src/lib.rs | 171 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 158 insertions(+), 14 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..73c5212 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# rhex diff --git a/src/lib.rs b/src/lib.rs index c31c3a0..e5597e3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,45 +1,159 @@ +///Rust crate to decode and encode hexadecimal. +/// +///#Examples +///``` +///let hex_string = "1a2b3c"; +///let data = rhex::decode_hex(&hex_string).unwrap(); +///``` + #[derive(Debug)] +///Possible errors that occur during decoding pub enum HexDecodeError { InvalidByteCount, InvalidHexCharacter, } -fn hex_char_byte_to_value(c : u8) -> Result { +struct EncodeHexIterSlice<'data_lifetime> { + //bytes to encode as hex + data: &'data_lifetime [u8], + //each byte generates 2 hex characters but we only return one at a time + next_char : Option +} + +impl<'data_lifetime> EncodeHexIterSlice<'data_lifetime> { + fn new(data : &'data_lifetime [u8]) -> Self { + EncodeHexIterSlice{ + data, + next_char : None + } + } +} + +impl<'data_lifetime> Iterator for EncodeHexIterSlice<'data_lifetime> { + type Item = char; + fn next(&mut self) -> Option { + //if the we still have a pending character return it + if let Some(c) = self.next_char.take() { + return Some(c); + } + if self.data.len() == 0 { + return None; + } + + let next_byte = &self.data[0]; + self.data = &self.data[1..]; + + let high = byte_to_hex_char((next_byte & 0xF0) >> 4); + let low = byte_to_hex_char(next_byte & 0x0F); + self.next_char = Some(low); + Some(high) + } +} + +struct DecodeHexIterSlice<'data_lifetime> { + //ascii encoded hex characters + //if any of the bytes is not valid ascii hex the iterator will return None + data: &'data_lifetime [u8], +} + +impl<'data_lifetime> DecodeHexIterSlice<'data_lifetime> { + fn new(data: &'data_lifetime [u8]) -> Self { + DecodeHexIterSlice { data } + } +} + +impl<'data_lifetime> Iterator for DecodeHexIterSlice<'data_lifetime> { + type Item = u8; + fn next(&mut self) -> Option { + if self.data.len() < 2 { + return None; + } + let v1_opt = hex_char_byte_to_value(self.data[0]); + let v2_opt = hex_char_byte_to_value(self.data[1]); + if v1_opt.is_err() || v2_opt.is_err() { + self.data = &[]; + return None; + } + self.data = &self.data[2..]; + let v1 = v1_opt.unwrap(); + let v2 = v2_opt.unwrap(); + Some(v1 * 16 + v2) + } +} + +fn hex_char_byte_to_value(c: u8) -> Result { if !c.is_ascii_hexdigit() { return Err(HexDecodeError::InvalidHexCharacter); } - Ok( (c as char).to_digit(16).unwrap() as u8) + Ok((c as char).to_digit(16).unwrap() as u8) } -fn byte_to_hex_char(b : u8) -> char { +fn byte_to_hex_char(b: u8) -> char { assert!(b <= 15); match b { digit if digit < 10 => ('0' as u8 + digit) as char, - letter => ('a' as u8 + letter - 10) as char + letter => ('a' as u8 + letter - 10) as char, } } -fn append_byte_hex_to_string(byte : u8, string : &mut String) { +fn append_byte_hex_to_string(byte: u8, string: &mut String) { let high = (byte & 0xF0) >> 4; let low = byte & 0x0F; string.push(byte_to_hex_char(high)); string.push(byte_to_hex_char(low)); } -pub fn decode_hex>(string : T) -> Result, HexDecodeError> { +///Returns an iterator that decodes the hex characters into bytes +///if any error occures it returns None so no error code is reported +/// +///``` +///let hex_string = "12ffbc"; +///for byte in rhex::decode_hex_iter(&hex_string) { +/// //do something +///} +///``` +pub fn decode_hex_iter<'a, T>(string: &'a T) -> impl Iterator + 'a +where + T: AsRef<[u8]> + ?Sized +{ + DecodeHexIterSlice::new(string.as_ref()) +} + +///Returns an iterator of char's that are the hexadecimal representation of the data. +///The chars returned by the iterar are all lower case. +/// +///``` +///let data = &[0x11, 0xbc, 0x22]; +///for c in rhex::encode_hex_iter(&data) { +/// //do something with c +///} +///``` +pub fn encode_hex_iter<'a, T>(data : &'a T) -> impl Iterator + 'a +where T: AsRef<[u8]> + ?Sized +{ + EncodeHexIterSlice::new(data.as_ref()) +} + +///Returns a Vec containing all the decoded hex data. +/// +///``` +///let hex_string = "ffaa11"; +///let data = rhex::decode_hex(&hex_string).unwrap(); +///``` +pub fn decode_hex>(string: T) -> Result, HexDecodeError> { let chars = string.as_ref(); if chars.len() % 2 != 0 { return Err(HexDecodeError::InvalidByteCount); } let mut data = Vec::new(); let mut index = 0; - let mut first_digit : u8 = 0; + let mut first_digit: u8 = 0; for c in chars.iter() { if index % 2 != 0 { let second_digit = hex_char_byte_to_value(*c)?; let value = first_digit * 16 + second_digit; data.push(value); - }else { + } else { first_digit = hex_char_byte_to_value(*c)?; } index += 1; @@ -47,7 +161,14 @@ pub fn decode_hex>(string : T) -> Result, HexDecodeError Ok(data) } -pub fn encode_hex>(data : T) -> String { +///Returns a new string contaning the hexadecimal representation of the data. +/// +///``` +///let data = &[0x11, 0x12, 0x13]; +///let string = rhex::encode_hex(&data); +///assert_eq!(string, "111213"); +///``` +pub fn encode_hex>(data: T) -> String { let mut string = String::new(); let data_ref = data.as_ref(); for b in data_ref.iter() { @@ -63,14 +184,14 @@ mod tests { #[test] fn decode_zero_bytes_test() { let string = ""; - let data = decode_hex(string).unwrap(); + let data = decode_hex(&string).unwrap(); assert_eq!(0, data.len()); } #[test] fn decode_one_byte_test() { let string = "ff"; - let data = decode_hex(string).unwrap(); + let data = decode_hex(&string).unwrap(); assert_eq!(1, data.len()); assert_eq!(0xff, data[0]); } @@ -78,7 +199,7 @@ mod tests { #[test] fn decode_multiple_bytes_test() { let string = "ffaabbccdd1122"; - let data = decode_hex(string).unwrap(); + let data = decode_hex(&string).unwrap(); assert_eq!(7, data.len()); assert_eq!(&[0xff, 0xaa, 0xbb, 0xcc, 0xdd, 0x11, 0x22], data.as_slice()); } @@ -86,7 +207,7 @@ mod tests { #[test] fn decode_invalid_character_test() { let string = "ffaabz"; - match decode_hex(string) { + match decode_hex(&string) { Err(HexDecodeError::InvalidHexCharacter) => {} _ => panic!("Invalid result returned"), } @@ -95,7 +216,7 @@ mod tests { #[test] fn decode_invalid_character_length() { let string = "ffaab"; - match decode_hex(string) { + match decode_hex(&string) { Err(HexDecodeError::InvalidByteCount) => {} _ => panic!("Invalid result returned"), } @@ -121,4 +242,26 @@ mod tests { let string = encode_hex(&data); assert_eq!(string, "aabbccddee"); } + + #[test] + fn owned_string_decode() { + //this test just needs to compile + let string = String::from("aabbccddee"); + let _ = decode_hex(&string); + let _ = decode_hex(string); + } + + #[test] + fn iter_decode_test() { + let string = "aabbcc"; + let data: Vec = decode_hex_iter(&string).collect(); + assert_eq!(&[0xaa, 0xbb, 0xcc], data.as_slice()); + } + + #[test] + fn iter_encode_test() { + let data = &[0xff, 0xaa, 0x12, 0x45]; + let string : String = encode_hex_iter(&data).collect(); + assert_eq!(string, "ffaa1245"); + } } -- cgit