diff --git a/implementations/rust/preserves/src/hex.rs b/implementations/rust/preserves/src/hex.rs new file mode 100644 index 0000000..2d0f1d3 --- /dev/null +++ b/implementations/rust/preserves/src/hex.rs @@ -0,0 +1,126 @@ +pub enum HexParser { + Liberal, + WhitespaceAllowed, + Strict, +} + +pub enum HexFormatter { + Lines(usize), + Packed, +} + +pub fn hexdigit(v: u8) -> char { + (if v < 10 {('0' as u8) + v} else {('a' as u8) + v - 10}) as char +} + +pub fn unhex(c: char) -> Option { + if c >= '0' && c <= '9' { + return Some((c as u8) - ('0' as u8)); + } + if c >= 'a' && c <= 'f' { + return Some((c as u8) - ('a' as u8) + 10); + } + if c >= 'A' && c <= 'F' { + return Some((c as u8) - ('A' as u8) + 10); + } + None +} + +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 unhex(c) { + 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); + buf_full = false; + } else { + buf = nibble; + buf_full = true; + }, + } + } + 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"); + } +} diff --git a/implementations/rust/preserves/src/lib.rs b/implementations/rust/preserves/src/lib.rs index 85e4b46..38aeb2e 100644 --- a/implementations/rust/preserves/src/lib.rs +++ b/implementations/rust/preserves/src/lib.rs @@ -1,6 +1,7 @@ pub mod de; -pub mod ser; pub mod error; +pub mod hex; +pub mod ser; pub mod set; pub mod symbol; pub mod value;