1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
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<Vec<u8>> {
        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;
                    },
            }
        }
        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");
    }
}