1 // Copyright 2022 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5 // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6 // option. This file may not be copied, modified, or distributed
7 // except according to those terms.
8
9 use crate::GuidFromStrError;
10
byte_to_ascii_hex_lower(byte: u8) -> (u8, u8)11 pub(crate) const fn byte_to_ascii_hex_lower(byte: u8) -> (u8, u8) {
12 let mut l = byte & 0xf;
13 let mut h = byte >> 4;
14 if l <= 9 {
15 l += b'0';
16 } else {
17 l += b'a' - 10;
18 }
19 if h <= 9 {
20 h += b'0';
21 } else {
22 h += b'a' - 10;
23 }
24 (h, l)
25 }
26
27 /// Parse a hexadecimal ASCII character as a `u8`.
parse_byte_from_ascii_char(c: u8) -> Option<u8>28 const fn parse_byte_from_ascii_char(c: u8) -> Option<u8> {
29 match c {
30 b'0' => Some(0x0),
31 b'1' => Some(0x1),
32 b'2' => Some(0x2),
33 b'3' => Some(0x3),
34 b'4' => Some(0x4),
35 b'5' => Some(0x5),
36 b'6' => Some(0x6),
37 b'7' => Some(0x7),
38 b'8' => Some(0x8),
39 b'9' => Some(0x9),
40 b'a' | b'A' => Some(0xa),
41 b'b' | b'B' => Some(0xb),
42 b'c' | b'C' => Some(0xc),
43 b'd' | b'D' => Some(0xd),
44 b'e' | b'E' => Some(0xe),
45 b'f' | b'F' => Some(0xf),
46 _ => None,
47 }
48 }
49
50 /// Parse a pair of hexadecimal ASCII characters as a `u8`. For example,
51 /// `(b'1', b'a')` is parsed as `0x1a`.
parse_byte_from_ascii_char_pair(a: u8, b: u8) -> Option<u8>52 const fn parse_byte_from_ascii_char_pair(a: u8, b: u8) -> Option<u8> {
53 let Some(a) = parse_byte_from_ascii_char(a) else {
54 return None;
55 };
56
57 let Some(b) = parse_byte_from_ascii_char(b) else {
58 return None;
59 };
60
61 Some(a << 4 | b)
62 }
63
64 /// Parse a pair of hexadecimal ASCII characters at position `start` as
65 /// a `u8`.
parse_byte_from_ascii_str_at( s: &[u8], start: u8, ) -> Result<u8, GuidFromStrError>66 pub(crate) const fn parse_byte_from_ascii_str_at(
67 s: &[u8],
68 start: u8,
69 ) -> Result<u8, GuidFromStrError> {
70 // This `as` conversion is needed because this is a const
71 // function. It is always valid since `usize` is always bigger than
72 // a u8.
73 #![allow(clippy::as_conversions)]
74 let start_usize = start as usize;
75
76 if let Some(byte) =
77 parse_byte_from_ascii_char_pair(s[start_usize], s[start_usize + 1])
78 {
79 Ok(byte)
80 } else {
81 Err(GuidFromStrError::Hex(start))
82 }
83 }
84
85 #[cfg(test)]
86 mod tests {
87 use super::*;
88
89 #[test]
test_to_ascii()90 fn test_to_ascii() {
91 assert_eq!(byte_to_ascii_hex_lower(0x1f), (b'1', b'f'));
92 assert_eq!(byte_to_ascii_hex_lower(0xf1), (b'f', b'1'));
93 }
94
95 #[test]
test_parse()96 fn test_parse() {
97 assert_eq!(parse_byte_from_ascii_char_pair(b'1', b'a'), Some(0x1a));
98 assert_eq!(parse_byte_from_ascii_char_pair(b'8', b'f'), Some(0x8f));
99
100 assert_eq!(parse_byte_from_ascii_char_pair(b'g', b'a'), None);
101 assert_eq!(parse_byte_from_ascii_char_pair(b'a', b'g'), None);
102 }
103 }
104