const LUT: &[u8; 16] = b"0123456789abcdef";
#[must_use]
pub fn to_hex(bytes: &[u8]) -> String {
let mut out = String::with_capacity(bytes.len() * 2);
for &b in bytes {
out.push(LUT[(b >> 4) as usize] as char);
out.push(LUT[(b & 0x0f) as usize] as char);
}
out
}
pub trait ToHex
where
Self: Sized,
{
fn to_hex(self) -> String;
}
impl<T> ToHex for T
where
T: AsRef<[u8]>,
{
fn to_hex(self) -> String {
to_hex(self.as_ref())
}
}
#[cfg(test)]
mod tests {
use super::{to_hex, ToHex};
#[test]
fn empty_slice() {
let bytes: [u8; 0] = [];
assert_eq!(to_hex(&bytes), ""); }
#[test]
fn single_byte_zero() {
assert_eq!(to_hex(&[0]), "00"); }
#[test]
fn single_byte_max() {
assert_eq!(to_hex(&[255]), "ff"); }
#[test]
fn single_byte_mid_range() {
assert_eq!(to_hex(&[42]), "2a"); }
#[test]
fn multiple_bytes() {
let bytes = [0x00, 0x01, 0x0A, 0xFF];
assert_eq!(to_hex(&bytes), "00010aff"); }
#[test]
fn all_zeros() {
let bytes = [0u8; 10]; assert_eq!(to_hex(&bytes), "00000000000000000000"); }
#[test]
fn all_ones() {
let bytes = [0xFFu8; 5]; assert_eq!(to_hex(&bytes), "ffffffffff"); }
#[test]
fn mixed_bytes() {
let bytes = [0x1A, 0x2B, 0x3C, 0x4D, 0x5E];
assert_eq!(to_hex(&bytes), "1a2b3c4d5e"); }
#[test]
fn large_input() {
let bytes: Vec<u8> = vec![0xAA; 100]; let expected: String = "aa".repeat(100); assert_eq!(to_hex(&bytes), expected); }
#[test]
fn ensures_lowercase() {
let bytes = [0xAB, 0xCD];
assert_eq!(to_hex(&bytes), "abcd"); }
#[test]
fn test_trait() {
let bytes = b"\xde\xad\xbe\xef";
let hex = "deadbeef";
assert_eq!(hex, bytes.to_hex());
}
}