diff options
| -rw-r--r-- | README.md | 1 | ||||
| -rw-r--r-- | src/lib.rs | 171 |
2 files changed, 158 insertions, 14 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..73c5212 --- /dev/null +++ b/README.md | |||
| @@ -0,0 +1 @@ | |||
| # rhex | |||
| @@ -1,45 +1,159 @@ | |||
| 1 | ///Rust crate to decode and encode hexadecimal. | ||
| 2 | /// | ||
| 3 | ///#Examples | ||
| 4 | ///``` | ||
| 5 | ///let hex_string = "1a2b3c"; | ||
| 6 | ///let data = rhex::decode_hex(&hex_string).unwrap(); | ||
| 7 | ///``` | ||
| 8 | |||
| 1 | #[derive(Debug)] | 9 | #[derive(Debug)] |
| 10 | ///Possible errors that occur during decoding | ||
| 2 | pub enum HexDecodeError { | 11 | pub enum HexDecodeError { |
| 3 | InvalidByteCount, | 12 | InvalidByteCount, |
| 4 | InvalidHexCharacter, | 13 | InvalidHexCharacter, |
| 5 | } | 14 | } |
| 6 | 15 | ||
| 7 | fn hex_char_byte_to_value(c : u8) -> Result<u8, HexDecodeError> { | 16 | struct EncodeHexIterSlice<'data_lifetime> { |
| 17 | //bytes to encode as hex | ||
| 18 | data: &'data_lifetime [u8], | ||
| 19 | //each byte generates 2 hex characters but we only return one at a time | ||
| 20 | next_char : Option<char> | ||
| 21 | } | ||
| 22 | |||
| 23 | impl<'data_lifetime> EncodeHexIterSlice<'data_lifetime> { | ||
| 24 | fn new(data : &'data_lifetime [u8]) -> Self { | ||
| 25 | EncodeHexIterSlice{ | ||
| 26 | data, | ||
| 27 | next_char : None | ||
| 28 | } | ||
| 29 | } | ||
| 30 | } | ||
| 31 | |||
| 32 | impl<'data_lifetime> Iterator for EncodeHexIterSlice<'data_lifetime> { | ||
| 33 | type Item = char; | ||
| 34 | fn next(&mut self) -> Option<Self::Item> { | ||
| 35 | //if the we still have a pending character return it | ||
| 36 | if let Some(c) = self.next_char.take() { | ||
| 37 | return Some(c); | ||
| 38 | } | ||
| 39 | if self.data.len() == 0 { | ||
| 40 | return None; | ||
| 41 | } | ||
| 42 | |||
| 43 | let next_byte = &self.data[0]; | ||
| 44 | self.data = &self.data[1..]; | ||
| 45 | |||
| 46 | let high = byte_to_hex_char((next_byte & 0xF0) >> 4); | ||
| 47 | let low = byte_to_hex_char(next_byte & 0x0F); | ||
| 48 | self.next_char = Some(low); | ||
| 49 | Some(high) | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | struct DecodeHexIterSlice<'data_lifetime> { | ||
| 54 | //ascii encoded hex characters | ||
| 55 | //if any of the bytes is not valid ascii hex the iterator will return None | ||
| 56 | data: &'data_lifetime [u8], | ||
| 57 | } | ||
| 58 | |||
| 59 | impl<'data_lifetime> DecodeHexIterSlice<'data_lifetime> { | ||
| 60 | fn new(data: &'data_lifetime [u8]) -> Self { | ||
| 61 | DecodeHexIterSlice { data } | ||
| 62 | } | ||
| 63 | } | ||
| 64 | |||
| 65 | impl<'data_lifetime> Iterator for DecodeHexIterSlice<'data_lifetime> { | ||
| 66 | type Item = u8; | ||
| 67 | fn next(&mut self) -> Option<Self::Item> { | ||
| 68 | if self.data.len() < 2 { | ||
| 69 | return None; | ||
| 70 | } | ||
| 71 | let v1_opt = hex_char_byte_to_value(self.data[0]); | ||
| 72 | let v2_opt = hex_char_byte_to_value(self.data[1]); | ||
| 73 | if v1_opt.is_err() || v2_opt.is_err() { | ||
| 74 | self.data = &[]; | ||
| 75 | return None; | ||
| 76 | } | ||
| 77 | self.data = &self.data[2..]; | ||
| 78 | let v1 = v1_opt.unwrap(); | ||
| 79 | let v2 = v2_opt.unwrap(); | ||
| 80 | Some(v1 * 16 + v2) | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | fn hex_char_byte_to_value(c: u8) -> Result<u8, HexDecodeError> { | ||
| 8 | if !c.is_ascii_hexdigit() { | 85 | if !c.is_ascii_hexdigit() { |
| 9 | return Err(HexDecodeError::InvalidHexCharacter); | 86 | return Err(HexDecodeError::InvalidHexCharacter); |
| 10 | } | 87 | } |
| 11 | Ok( (c as char).to_digit(16).unwrap() as u8) | 88 | Ok((c as char).to_digit(16).unwrap() as u8) |
| 12 | } | 89 | } |
| 13 | 90 | ||
| 14 | fn byte_to_hex_char(b : u8) -> char { | 91 | fn byte_to_hex_char(b: u8) -> char { |
| 15 | assert!(b <= 15); | 92 | assert!(b <= 15); |
| 16 | match b { | 93 | match b { |
| 17 | digit if digit < 10 => ('0' as u8 + digit) as char, | 94 | digit if digit < 10 => ('0' as u8 + digit) as char, |
| 18 | letter => ('a' as u8 + letter - 10) as char | 95 | letter => ('a' as u8 + letter - 10) as char, |
| 19 | } | 96 | } |
| 20 | } | 97 | } |
| 21 | 98 | ||
| 22 | fn append_byte_hex_to_string(byte : u8, string : &mut String) { | 99 | fn append_byte_hex_to_string(byte: u8, string: &mut String) { |
| 23 | let high = (byte & 0xF0) >> 4; | 100 | let high = (byte & 0xF0) >> 4; |
| 24 | let low = byte & 0x0F; | 101 | let low = byte & 0x0F; |
| 25 | string.push(byte_to_hex_char(high)); | 102 | string.push(byte_to_hex_char(high)); |
| 26 | string.push(byte_to_hex_char(low)); | 103 | string.push(byte_to_hex_char(low)); |
| 27 | } | 104 | } |
| 28 | 105 | ||
| 29 | pub fn decode_hex<T : AsRef<[u8]>>(string : T) -> Result<Vec<u8>, HexDecodeError> { | 106 | ///Returns an iterator that decodes the hex characters into bytes |
| 107 | ///if any error occures it returns None so no error code is reported | ||
| 108 | /// | ||
| 109 | ///``` | ||
| 110 | ///let hex_string = "12ffbc"; | ||
| 111 | ///for byte in rhex::decode_hex_iter(&hex_string) { | ||
| 112 | /// //do something | ||
| 113 | ///} | ||
| 114 | ///``` | ||
| 115 | pub fn decode_hex_iter<'a, T>(string: &'a T) -> impl Iterator<Item = u8> + 'a | ||
| 116 | where | ||
| 117 | T: AsRef<[u8]> + ?Sized | ||
| 118 | { | ||
| 119 | DecodeHexIterSlice::new(string.as_ref()) | ||
| 120 | } | ||
| 121 | |||
| 122 | ///Returns an iterator of char's that are the hexadecimal representation of the data. | ||
| 123 | ///The chars returned by the iterar are all lower case. | ||
| 124 | /// | ||
| 125 | ///``` | ||
| 126 | ///let data = &[0x11, 0xbc, 0x22]; | ||
| 127 | ///for c in rhex::encode_hex_iter(&data) { | ||
| 128 | /// //do something with c | ||
| 129 | ///} | ||
| 130 | ///``` | ||
| 131 | pub fn encode_hex_iter<'a, T>(data : &'a T) -> impl Iterator<Item = char> + 'a | ||
| 132 | where T: AsRef<[u8]> + ?Sized | ||
| 133 | { | ||
| 134 | EncodeHexIterSlice::new(data.as_ref()) | ||
| 135 | } | ||
| 136 | |||
| 137 | ///Returns a Vec containing all the decoded hex data. | ||
| 138 | /// | ||
| 139 | ///``` | ||
| 140 | ///let hex_string = "ffaa11"; | ||
| 141 | ///let data = rhex::decode_hex(&hex_string).unwrap(); | ||
| 142 | ///``` | ||
| 143 | pub fn decode_hex<T: AsRef<[u8]>>(string: T) -> Result<Vec<u8>, HexDecodeError> { | ||
| 30 | let chars = string.as_ref(); | 144 | let chars = string.as_ref(); |
| 31 | if chars.len() % 2 != 0 { | 145 | if chars.len() % 2 != 0 { |
| 32 | return Err(HexDecodeError::InvalidByteCount); | 146 | return Err(HexDecodeError::InvalidByteCount); |
| 33 | } | 147 | } |
| 34 | let mut data = Vec::new(); | 148 | let mut data = Vec::new(); |
| 35 | let mut index = 0; | 149 | let mut index = 0; |
| 36 | let mut first_digit : u8 = 0; | 150 | let mut first_digit: u8 = 0; |
| 37 | for c in chars.iter() { | 151 | for c in chars.iter() { |
| 38 | if index % 2 != 0 { | 152 | if index % 2 != 0 { |
| 39 | let second_digit = hex_char_byte_to_value(*c)?; | 153 | let second_digit = hex_char_byte_to_value(*c)?; |
| 40 | let value = first_digit * 16 + second_digit; | 154 | let value = first_digit * 16 + second_digit; |
| 41 | data.push(value); | 155 | data.push(value); |
| 42 | }else { | 156 | } else { |
| 43 | first_digit = hex_char_byte_to_value(*c)?; | 157 | first_digit = hex_char_byte_to_value(*c)?; |
| 44 | } | 158 | } |
| 45 | index += 1; | 159 | index += 1; |
| @@ -47,7 +161,14 @@ pub fn decode_hex<T : AsRef<[u8]>>(string : T) -> Result<Vec<u8>, HexDecodeError | |||
| 47 | Ok(data) | 161 | Ok(data) |
| 48 | } | 162 | } |
| 49 | 163 | ||
| 50 | pub fn encode_hex<T : AsRef<[u8]>>(data : T) -> String { | 164 | ///Returns a new string contaning the hexadecimal representation of the data. |
| 165 | /// | ||
| 166 | ///``` | ||
| 167 | ///let data = &[0x11, 0x12, 0x13]; | ||
| 168 | ///let string = rhex::encode_hex(&data); | ||
| 169 | ///assert_eq!(string, "111213"); | ||
| 170 | ///``` | ||
| 171 | pub fn encode_hex<T: AsRef<[u8]>>(data: T) -> String { | ||
| 51 | let mut string = String::new(); | 172 | let mut string = String::new(); |
| 52 | let data_ref = data.as_ref(); | 173 | let data_ref = data.as_ref(); |
| 53 | for b in data_ref.iter() { | 174 | for b in data_ref.iter() { |
| @@ -63,14 +184,14 @@ mod tests { | |||
| 63 | #[test] | 184 | #[test] |
| 64 | fn decode_zero_bytes_test() { | 185 | fn decode_zero_bytes_test() { |
| 65 | let string = ""; | 186 | let string = ""; |
| 66 | let data = decode_hex(string).unwrap(); | 187 | let data = decode_hex(&string).unwrap(); |
| 67 | assert_eq!(0, data.len()); | 188 | assert_eq!(0, data.len()); |
| 68 | } | 189 | } |
| 69 | 190 | ||
| 70 | #[test] | 191 | #[test] |
| 71 | fn decode_one_byte_test() { | 192 | fn decode_one_byte_test() { |
| 72 | let string = "ff"; | 193 | let string = "ff"; |
| 73 | let data = decode_hex(string).unwrap(); | 194 | let data = decode_hex(&string).unwrap(); |
| 74 | assert_eq!(1, data.len()); | 195 | assert_eq!(1, data.len()); |
| 75 | assert_eq!(0xff, data[0]); | 196 | assert_eq!(0xff, data[0]); |
| 76 | } | 197 | } |
| @@ -78,7 +199,7 @@ mod tests { | |||
| 78 | #[test] | 199 | #[test] |
| 79 | fn decode_multiple_bytes_test() { | 200 | fn decode_multiple_bytes_test() { |
| 80 | let string = "ffaabbccdd1122"; | 201 | let string = "ffaabbccdd1122"; |
| 81 | let data = decode_hex(string).unwrap(); | 202 | let data = decode_hex(&string).unwrap(); |
| 82 | assert_eq!(7, data.len()); | 203 | assert_eq!(7, data.len()); |
| 83 | assert_eq!(&[0xff, 0xaa, 0xbb, 0xcc, 0xdd, 0x11, 0x22], data.as_slice()); | 204 | assert_eq!(&[0xff, 0xaa, 0xbb, 0xcc, 0xdd, 0x11, 0x22], data.as_slice()); |
| 84 | } | 205 | } |
| @@ -86,7 +207,7 @@ mod tests { | |||
| 86 | #[test] | 207 | #[test] |
| 87 | fn decode_invalid_character_test() { | 208 | fn decode_invalid_character_test() { |
| 88 | let string = "ffaabz"; | 209 | let string = "ffaabz"; |
| 89 | match decode_hex(string) { | 210 | match decode_hex(&string) { |
| 90 | Err(HexDecodeError::InvalidHexCharacter) => {} | 211 | Err(HexDecodeError::InvalidHexCharacter) => {} |
| 91 | _ => panic!("Invalid result returned"), | 212 | _ => panic!("Invalid result returned"), |
| 92 | } | 213 | } |
| @@ -95,7 +216,7 @@ mod tests { | |||
| 95 | #[test] | 216 | #[test] |
| 96 | fn decode_invalid_character_length() { | 217 | fn decode_invalid_character_length() { |
| 97 | let string = "ffaab"; | 218 | let string = "ffaab"; |
| 98 | match decode_hex(string) { | 219 | match decode_hex(&string) { |
| 99 | Err(HexDecodeError::InvalidByteCount) => {} | 220 | Err(HexDecodeError::InvalidByteCount) => {} |
| 100 | _ => panic!("Invalid result returned"), | 221 | _ => panic!("Invalid result returned"), |
| 101 | } | 222 | } |
| @@ -121,4 +242,26 @@ mod tests { | |||
| 121 | let string = encode_hex(&data); | 242 | let string = encode_hex(&data); |
| 122 | assert_eq!(string, "aabbccddee"); | 243 | assert_eq!(string, "aabbccddee"); |
| 123 | } | 244 | } |
| 245 | |||
| 246 | #[test] | ||
| 247 | fn owned_string_decode() { | ||
| 248 | //this test just needs to compile | ||
| 249 | let string = String::from("aabbccddee"); | ||
| 250 | let _ = decode_hex(&string); | ||
| 251 | let _ = decode_hex(string); | ||
| 252 | } | ||
| 253 | |||
| 254 | #[test] | ||
| 255 | fn iter_decode_test() { | ||
| 256 | let string = "aabbcc"; | ||
| 257 | let data: Vec<u8> = decode_hex_iter(&string).collect(); | ||
| 258 | assert_eq!(&[0xaa, 0xbb, 0xcc], data.as_slice()); | ||
| 259 | } | ||
| 260 | |||
| 261 | #[test] | ||
| 262 | fn iter_encode_test() { | ||
| 263 | let data = &[0xff, 0xaa, 0x12, 0x45]; | ||
| 264 | let string : String = encode_hex_iter(&data).collect(); | ||
| 265 | assert_eq!(string, "ffaa1245"); | ||
| 266 | } | ||
| 124 | } | 267 | } |
