grove
at @12
1//! Lowercase hex, for object ids and tokens.
2
3const DIGITS: &[u8; 16] = b"0123456789abcdef";
4
5pub fn encode(bytes: &[u8]) -> String {
6 let mut s = String::with_capacity(bytes.len() * 2);
7 for b in bytes {
8 s.push(DIGITS[(b >> 4) as usize] as char);
9 s.push(DIGITS[(b & 0xf) as usize] as char);
10 }
11 s
12}
13
14pub fn decode(s: &str) -> Option<Vec<u8>> {
15 if s.len() % 2 != 0 {
16 return None;
17 }
18 let nibble = |c: u8| match c {
19 b'0'..=b'9' => Some(c - b'0'),
20 b'a'..=b'f' => Some(c - b'a' + 10),
21 _ => None,
22 };
23 s.as_bytes().chunks(2).map(|p| Some(nibble(p[0])? << 4 | nibble(p[1])?)).collect()
24}
25
26#[cfg(test)]
27mod tests {
28 #[test]
29 fn round_trip() {
30 let bytes = [0u8, 1, 0x7f, 0x80, 0xff];
31 assert_eq!(super::encode(&bytes), "00017f80ff");
32 assert_eq!(super::decode("00017f80ff").unwrap(), bytes);
33 assert!(super::decode("0g").is_none());
34 assert!(super::decode("abc").is_none());
35 assert!(super::decode("AB").is_none());
36 }
37}