uor_addr/canonical/
hex.rs1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub struct HexOutputOverflow;
22
23pub fn encode_lower_into(input: &[u8], out: &mut [u8]) -> Result<usize, HexOutputOverflow> {
30 let need = input.len().checked_mul(2).ok_or(HexOutputOverflow)?;
31 if out.len() < need {
32 return Err(HexOutputOverflow);
33 }
34 const HEX: &[u8; 16] = b"0123456789abcdef";
35 for (i, &byte) in input.iter().enumerate() {
36 out[i * 2] = HEX[(byte >> 4) as usize];
37 out[i * 2 + 1] = HEX[(byte & 0x0f) as usize];
38 }
39 Ok(need)
40}
41
42#[cfg(test)]
43mod tests {
44 use super::*;
45
46 #[test]
47 fn empty_input_emits_nothing() {
48 let mut out = [0u8; 4];
49 assert_eq!(encode_lower_into(&[], &mut out).unwrap(), 0);
50 }
51
52 #[test]
53 fn each_byte_yields_two_lowercase_hex_chars() {
54 let mut out = [0u8; 4];
55 let n = encode_lower_into(&[0x00, 0xff], &mut out).unwrap();
56 assert_eq!(n, 4);
57 assert_eq!(&out[..n], b"00ff");
58 }
59
60 #[test]
61 fn sha256_zero_digest_round_trips_published_fixture() {
62 let digest: [u8; 32] = [
64 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f,
65 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b,
66 0x78, 0x52, 0xb8, 0x55,
67 ];
68 let mut out = [0u8; 64];
69 let n = encode_lower_into(&digest, &mut out).unwrap();
70 assert_eq!(n, 64);
71 assert_eq!(
72 &out[..n],
73 b"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
74 );
75 }
76
77 #[test]
78 fn rejects_undersized_output_buffer() {
79 let mut out = [0u8; 1];
80 assert_eq!(
81 encode_lower_into(&[0xab, 0xcd], &mut out),
82 Err(HexOutputOverflow)
83 );
84 }
85
86 #[test]
87 fn rejects_zero_length_output_for_nonempty_input() {
88 let mut out = [0u8; 0];
89 assert_eq!(encode_lower_into(&[0xff], &mut out), Err(HexOutputOverflow));
90 }
91}