encodings/
hex.rs

1// Copyright (c) 2013-2019 The Rust Project Developers.
2use std::fmt;
3
4/// A trait for converting a value to hexadecimal encoding
5pub trait ToHex {
6    /// Converts the value of `self` to a hex value, returning the owned
7    /// string.
8    fn to_hex(&self) -> String;
9}
10
11const CHARS: &[u8] = b"0123456789abcdef";
12
13// const HEX_CHARS_LOWER: &[u8; 16] = b"0123456789abcdef";
14// const HEX_CHARS_UPPER: &[u8; 16] = b"0123456789ABCDEF";
15
16impl ToHex for [u8] {
17    fn to_hex(&self) -> String {
18        let mut v = Vec::with_capacity(self.len() * 2);
19        for &byte in self {
20            v.push(CHARS[(byte >> 4) as usize]);
21            v.push(CHARS[(byte & 0xf) as usize]);
22        }
23
24        unsafe { String::from_utf8_unchecked(v) }
25    }
26}
27
28pub trait FromHex: Sized {
29    type Error;
30
31    fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error>;
32}
33
34/// Errors that can occur when decoding a hex encoded string
35#[derive(Copy, Clone, Debug)]
36pub enum FromHexError {
37    /// The input contained a character not part of the hex format
38    InvalidHexCharacter {
39        c: char,
40        index: usize,
41    },
42    /// The input had an invalid length
43    InvalidHexLength,
44    OddLength,
45}
46
47impl std::error::Error for FromHexError {
48    fn description(&self) -> &str {
49        match *self {
50            Self::InvalidHexCharacter { .. } => "invalid character",
51            Self::OddLength => "odd number of digits",
52            Self::InvalidHexLength => "invalid hex length",
53        }
54    }
55}
56
57impl fmt::Display for FromHexError {
58    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
59        match *self {
60            Self::InvalidHexCharacter { c, index } => {
61                write!(f, "Invalid character '{}' at position {}", c, index)
62            }
63            Self::OddLength => write!(f, "Odd number of digits"),
64            Self::InvalidHexLength => write!(f, "Invalid hex length"),
65        }
66    }
67}
68
69fn val(c: u8, idx: usize) -> Result<u8, FromHexError> {
70    match c {
71        b'A'..=b'F' => Ok(c - b'A' + 10),
72        b'a'..=b'f' => Ok(c - b'a' + 10),
73        b'0'..=b'9' => Ok(c - b'0'),
74        _ => Err(FromHexError::InvalidHexCharacter {
75            c: c as char,
76            index: idx,
77        }),
78    }
79}
80
81impl FromHex for Vec<u8> {
82    type Error = FromHexError;
83
84    fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, FromHexError> {
85        let hex = hex.as_ref();
86        if hex.len() % 2 != 0 {
87            return Err(FromHexError::OddLength);
88        }
89
90        hex.chunks(2)
91            .enumerate()
92            .map(|(i, pair)| Ok(val(pair[0], 2 * i)? << 4 | val(pair[1], 2 * i + 1)?))
93            .collect()
94    }
95}