1use crate::Error;
2
3#[inline]
5pub const fn encoded_len(n: usize) -> usize {
6 n * 2
7}
8
9#[inline]
17pub fn encode_to_slice(src: &[u8], dst_hex: &mut [u8], lowercase: bool) -> Result<usize, Error> {
18 let out_len = encoded_len(src.len());
19 if dst_hex.len() < out_len {
20 return Err(Error::OutputTooSmall);
21 }
22
23 let alphabet = if lowercase {
24 b"0123456789abcdef"
25 } else {
26 b"0123456789ABCDEF"
27 };
28
29 for (byte, out_pair) in src
33 .iter()
34 .copied()
35 .zip(dst_hex[..out_len].chunks_exact_mut(2))
36 {
37 out_pair[0] = alphabet[(byte >> 4) as usize];
38 out_pair[1] = alphabet[(byte & 0x0f) as usize];
39 }
40
41 Ok(out_len)
42}
43
44#[cfg(feature = "std")]
48#[inline]
49pub fn encode_to_string(src: &[u8], lowercase: bool) -> String {
50 let mut out = vec![0u8; encoded_len(src.len())];
51 let _ = encode_to_slice(src, &mut out, lowercase);
53
54 String::from_utf8(out).expect("hex output is always valid UTF-8")
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61
62 #[test]
63 fn test_encode_empty() {
64 let mut out = [0u8; 0];
65 let written = encode_to_slice(&[], &mut out, true).unwrap();
66 assert_eq!(written, 0);
67 }
68
69 #[test]
70 fn test_encode_lowercase() {
71 let input = [0xde, 0xad, 0xbe, 0xef];
72 let mut out = [0u8; 8];
73 encode_to_slice(&input, &mut out, true).unwrap();
74 assert_eq!(&out, b"deadbeef");
75 }
76
77 #[test]
78 fn test_encode_uppercase() {
79 let input = [0xde, 0xad, 0xbe, 0xef];
80 let mut out = [0u8; 8];
81 encode_to_slice(&input, &mut out, false).unwrap();
82 assert_eq!(&out, b"DEADBEEF");
83 }
84
85 #[test]
86 fn test_encode_output_too_small() {
87 let input = [0xde, 0xad];
88 let mut out = [0u8; 2];
89 let err = encode_to_slice(&input, &mut out, true).unwrap_err();
90 assert_eq!(err, Error::OutputTooSmall);
91 }
92}