123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124 |
- // Copyright (c) 2022-2023 Yuki Kishimoto
- // Distributed under the MIT software license
- //! Hex
- use core::fmt;
- /// Hex error
- #[derive(Debug, PartialEq, Eq)]
- pub enum Error {
- /// An invalid character was found
- InvalidHexCharacter {
- /// Char
- c: char,
- /// Char index
- index: usize,
- },
- /// A hex string's length needs to be even, as two digits correspond to
- /// one byte.
- OddLength,
- }
- impl std::error::Error for Error {}
- impl fmt::Display for Error {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Self::InvalidHexCharacter { c, index } => {
- write!(f, "Invalid character {} at position {}", c, index)
- }
- Self::OddLength => write!(f, "Odd number of digits"),
- }
- }
- }
- #[inline]
- fn from_digit(num: u8) -> char {
- if num < 10 {
- (b'0' + num) as char
- } else {
- (b'a' + num - 10) as char
- }
- }
- /// Hex encode
- pub fn encode<T>(data: T) -> String
- where
- T: AsRef<[u8]>,
- {
- let bytes: &[u8] = data.as_ref();
- let mut hex: String = String::with_capacity(2 * bytes.len());
- for byte in bytes.iter() {
- hex.push(from_digit(byte >> 4));
- hex.push(from_digit(byte & 0xF));
- }
- hex
- }
- const fn val(c: u8, idx: usize) -> Result<u8, Error> {
- match c {
- b'A'..=b'F' => Ok(c - b'A' + 10),
- b'a'..=b'f' => Ok(c - b'a' + 10),
- b'0'..=b'9' => Ok(c - b'0'),
- _ => Err(Error::InvalidHexCharacter {
- c: c as char,
- index: idx,
- }),
- }
- }
- /// Hex decode
- pub fn decode<T>(hex: T) -> Result<Vec<u8>, Error>
- where
- T: AsRef<[u8]>,
- {
- let hex = hex.as_ref();
- let len = hex.len();
- if len % 2 != 0 {
- return Err(Error::OddLength);
- }
- let mut bytes: Vec<u8> = Vec::with_capacity(len / 2);
- for i in (0..len).step_by(2) {
- let high = val(hex[i], i)?;
- let low = val(hex[i + 1], i + 1)?;
- bytes.push((high << 4) | low);
- }
- Ok(bytes)
- }
- #[cfg(test)]
- mod tests {
- use super::*;
- #[test]
- fn test_encode() {
- assert_eq!(encode("foobar"), "666f6f626172");
- }
- #[test]
- fn test_decode() {
- assert_eq!(
- decode("666f6f626172"),
- Ok(String::from("foobar").into_bytes())
- );
- }
- #[test]
- pub fn test_invalid_length() {
- assert_eq!(decode("1").unwrap_err(), Error::OddLength);
- assert_eq!(decode("666f6f6261721").unwrap_err(), Error::OddLength);
- }
- #[test]
- pub fn test_invalid_char() {
- assert_eq!(
- decode("66ag").unwrap_err(),
- Error::InvalidHexCharacter { c: 'g', index: 3 }
- );
- }
- }
|