pub enum HexParser { Liberal, WhitespaceAllowed, Strict, } pub enum HexFormatter { Lines(usize), Packed, } pub fn hexdigit(v: u8) -> char { char::from_digit(v as u32, 16).expect("hexadecimal digit value") } impl HexParser { pub fn decode(&self, s: &str) -> Option> { let mut result = Vec::new(); let mut buf: u8 = 0; let mut buf_full = false; for c in s.chars() { match c.to_digit(16) { None => match self { HexParser::Liberal => (), HexParser::WhitespaceAllowed => if !c.is_whitespace() { return None }, HexParser::Strict => return None, }, Some(nibble) => if buf_full { result.push(buf << 4 | (nibble as u8)); buf_full = false; } else { buf = nibble as u8; buf_full = true; }, } } if buf_full { None // odd number of hexits } else { Some(result) } } } impl HexFormatter { pub fn encode(&self, bs: &[u8]) -> String { match self { HexFormatter::Lines(max_line_length) => { let mut lines = Vec::new(); let mut line = String::new(); for b in bs { if line.len() + 2 > *max_line_length { lines.push(std::mem::take(&mut line)); } line.push(hexdigit(b >> 4)); line.push(hexdigit(b & 15)); } lines.push(std::mem::take(&mut line)); lines.join("\n") } HexFormatter::Packed => { let mut result = String::new(); for b in bs { result.push(hexdigit(b >> 4)); result.push(hexdigit(b & 15)); } result } } } } #[cfg(test)] mod test { use super::*; #[test] fn test_decode_packed() { let s = "01ab00ff"; assert_eq!(HexParser::Strict.decode(s), Some(vec![1, 171, 0, 255])); assert_eq!(HexParser::WhitespaceAllowed.decode(s), Some(vec![1, 171, 0, 255])); assert_eq!(HexParser::Liberal.decode(s), Some(vec![1, 171, 0, 255])); } #[test] fn test_decode_whitespace() { let s = "01ab 00ff"; assert_eq!(HexParser::Strict.decode(s), None); assert_eq!(HexParser::WhitespaceAllowed.decode(s), Some(vec![1, 171, 0, 255])); assert_eq!(HexParser::Liberal.decode(s), Some(vec![1, 171, 0, 255])); } #[test] fn test_decode_liberal() { let s = "01ab zz 00ff"; assert_eq!(HexParser::Strict.decode(s), None); assert_eq!(HexParser::WhitespaceAllowed.decode(s), None); assert_eq!(HexParser::Liberal.decode(s), Some(vec![1, 171, 0, 255])); } #[test] fn test_encode_lines() { assert_eq!(HexFormatter::Lines(10).encode(&vec![0x5a; 11]), "5a5a5a5a5a\n5a5a5a5a5a\n5a"); assert_eq!(HexFormatter::Lines(10).encode(&vec![0x5a; 10]), "5a5a5a5a5a\n5a5a5a5a5a"); assert_eq!(HexFormatter::Lines(10).encode(&vec![0x5a; 9]), "5a5a5a5a5a\n5a5a5a5a"); assert_eq!(HexFormatter::Lines(9).encode(&vec![0x5a; 11]), "5a5a5a5a\n5a5a5a5a\n5a5a5a"); assert_eq!(HexFormatter::Lines(9).encode(&vec![0x5a; 10]), "5a5a5a5a\n5a5a5a5a\n5a5a"); assert_eq!(HexFormatter::Lines(9).encode(&vec![0x5a; 9]), "5a5a5a5a\n5a5a5a5a\n5a"); assert_eq!(HexFormatter::Lines(8).encode(&vec![0x5a; 11]), "5a5a5a5a\n5a5a5a5a\n5a5a5a"); assert_eq!(HexFormatter::Lines(8).encode(&vec![0x5a; 10]), "5a5a5a5a\n5a5a5a5a\n5a5a"); assert_eq!(HexFormatter::Lines(8).encode(&vec![0x5a; 9]), "5a5a5a5a\n5a5a5a5a\n5a"); } #[test] fn test_encode_packed() { assert_eq!(HexFormatter::Packed.encode(&vec![0x5a; 11]), "5a5a5a5a5a5a5a5a5a5a5a"); assert_eq!(HexFormatter::Packed.encode(&vec![0x5a; 10]), "5a5a5a5a5a5a5a5a5a5a"); assert_eq!(HexFormatter::Packed.encode(&vec![0x5a; 9]), "5a5a5a5a5a5a5a5a5a"); } }