hexdecode/
lib.rs

1#![cfg_attr(test, feature(plugin))]
2#![cfg_attr(test, plugin(quickcheck_macros))]
3
4use std::io;
5
6/// A function for decoding a hex encoded string to Vec<u8>, leading zero agnostic
7pub fn decode<T: AsRef<[u8]>>(input: T) -> Result<Vec<u8>, io::Error> {
8    let data = input.as_ref();
9    if data.is_empty() {
10        return Ok(vec![]);
11    }
12    strip_preamble(data)
13        .into_iter()
14        .map(|u| hex(*u))
15        .collect::<Result<Vec<_>, io::Error>>()
16        .map(|v| v.into_iter().skip_while(|u| *u == 0).collect::<Vec<_>>())
17        .map(|mut v| {
18            v.reverse(); // reverse for trailing zero handling
19            v.chunks(2)
20                .map(|c| if c.len() == 1 { c[0] } else { c[1] * 16 + c[0] })
21                .rev() // rev back
22                .collect::<Vec<_>>()
23        })
24}
25
26// strip 0x
27fn strip_preamble(data: &[u8]) -> &[u8] {
28    if data.len() >= 2 && data[0] == 0x30 && data[1] == 0x78 {
29        &data[2..]
30    } else {
31        data
32    }
33}
34
35// convert ASCII
36fn hex(byte: u8) -> Result<u8, io::Error> {
37    if byte >= 0x30 && byte <= 0x39 {
38        return Ok(byte - 0x30);
39    }
40    if byte >= 0x41 && byte <= 0x46 {
41        return Ok(byte - 0x41 + 0xa);
42    }
43    if byte >= 0x61 && byte <= 0x66 {
44        return Ok(byte - 0x61 + 0xa);
45    }
46    Err(io::Error::new(
47        io::ErrorKind::InvalidInput,
48        "Did not supply a properly encoded hex value",
49    ))
50}
51
52#[cfg(test)]
53mod tests {
54    extern crate quickcheck;
55    use super::decode;
56    use quickcheck::TestResult;
57
58    #[test]
59    fn simple_case() {
60        let input = "0xc61bb3FA61A883Ed7723a2bF9D41D30B196fd999".to_lowercase();
61        let mut string = String::new();
62        string.push_str("0x");
63        for byte in decode(&input).unwrap() {
64            string.push_str(&format!("{:0>2x}", byte));
65        }
66        assert_eq!(input, string);
67    }
68
69    #[quickcheck]
70    fn decoding_is_identity(input: Vec<u8>) -> TestResult {
71        let mut encoded = String::with_capacity(input.len());
72        let mut zero_check = true;
73
74        for byte in &input {
75            encoded.push_str(&format!("{:0>2x}", byte));
76            if *byte != 0 {
77                zero_check = false;
78            }
79        }
80        if input.is_empty() {
81            let empty: Vec<u8> = vec![];
82            return TestResult::from_bool(empty == decode(encoded.as_bytes()).unwrap());
83        }
84        if zero_check {
85            return TestResult::from_bool(vec![0] == decode(encoded.as_bytes()).unwrap());
86        }
87        TestResult::from_bool(
88            input
89                .into_iter()
90                .skip_while(|u| *u == 0)
91                .collect::<Vec<_>>()
92                == decode(encoded.as_bytes()).unwrap(),
93        )
94    }
95
96    #[quickcheck]
97    fn decoding_is_identity_with_prefix(input: Vec<u8>) -> TestResult {
98        let mut encoded = String::with_capacity(input.len() + 2);
99        encoded.push_str("0x");
100        let mut zero_check = true;
101
102        for byte in &input {
103            encoded.push_str(&format!("{:0>2x}", byte));
104            if *byte != 0 {
105                zero_check = false;
106            }
107        }
108        if input.is_empty() {
109            let empty: Vec<u8> = vec![];
110            return TestResult::from_bool(empty == decode(encoded.as_bytes()).unwrap());
111        }
112        if zero_check {
113            return TestResult::from_bool(vec![0] == decode(encoded.as_bytes()).unwrap());
114        }
115        TestResult::from_bool(
116            input
117                .into_iter()
118                .skip_while(|u| *u == 0)
119                .collect::<Vec<_>>()
120                == decode(encoded.as_bytes()).unwrap(),
121        )
122    }
123}