Skip to main content

bgpkit_parser/render/
hex.rs

1//! Hex encoding of record bytes.
2//!
3//! One record, one lowercase hex string — the paste format for
4//! byte-level tools (e.g. wirescope's hex input). Encoding operates on
5//! the record's *original* bytes (see
6//! [`BgpkitParser::into_filtered_raw_record_iter`](crate::BgpkitParser::into_filtered_raw_record_iter)), never on a
7//! re-encoding of the parsed model, so attribute ordering quirks (e.g.
8//! BGP-LS hash-map iteration order) cannot alter the output.
9
10/// Encode bytes as a single lowercase hex string (no separators).
11///
12/// Writes the two digits directly into the output buffer; no
13/// per-byte allocations.
14pub fn encode(bytes: &[u8]) -> String {
15    const HEX: &[u8; 16] = b"0123456789abcdef";
16    let mut out = String::with_capacity(bytes.len() * 2);
17    for &byte in bytes {
18        out.push(HEX[(byte >> 4) as usize] as char);
19        out.push(HEX[(byte & 0x0f) as usize] as char);
20    }
21    out
22}
23
24#[cfg(test)]
25mod tests {
26    use super::*;
27
28    #[test]
29    fn encodes_golden_values() {
30        assert_eq!(encode(&[]), "");
31        assert_eq!(encode(&[0x00]), "00");
32        assert_eq!(encode(&[0xde, 0xad, 0xbe, 0xef]), "deadbeef");
33        assert_eq!(encode(&[0xff, 0x10, 0x0a]), "ff100a");
34    }
35
36    #[test]
37    fn encodes_all_byte_values_lowercase() {
38        let all: Vec<u8> = (0..=255u8).collect();
39        let hex = encode(&all);
40        assert_eq!(hex.len(), 512);
41        assert!(!hex.contains(|c: char| c.is_uppercase()));
42        // spot checks across the range
43        assert_eq!(&hex[0..2], "00");
44        assert_eq!(&hex[9 * 2..10 * 2], "09");
45        assert_eq!(&hex[10 * 2..12 * 2], "0a0b");
46        assert_eq!(&hex[510..512], "ff");
47    }
48}