use std::str::Bytes;
pub trait ToHex {
fn to_hex(&self) -> String;
}
impl ToHex for str {
fn to_hex(&self) -> String {
if self.is_empty() {
return String::new();
}
let mut result = allocate_string(self.bytes().len());
result.fill_from(&mut self.bytes());
result
}
}
fn allocate_string(input_data_length: usize) -> String {
let mut result = String::new();
let hexes_size = input_data_length * 2;
let spaces_size = input_data_length - 1;
result.reserve(hexes_size + spaces_size);
result
}
trait Fill {
fn fill_from(&mut self, byte_iter: &mut Bytes<'_>);
}
impl Fill for String {
fn fill_from(&mut self, byte_iter: &mut Bytes<'_>) {
for _ in 0..(byte_iter.len() - 1) {
self.push_str(&format!("{:0>2x} ", byte_iter.next().unwrap()));
}
self.push_str(&format!("{:0>2x}", byte_iter.next().unwrap()));
}
}
#[cfg(test)]
mod test {
use super::*;
macro_rules! positive_tests {
($($name:ident: $value:expr,)*) => {
$(
#[test]
fn $name() {
let (input_data, expected) = $value;
let actual = input_data.to_hex();
assert_eq!(actual, expected)
}
)*
}
}
positive_tests! {
empty_string: (&String::new(), ""),
digits: ("0123456789", "30 31 32 33 34 35 36 37 38 39"),
lowercase_ascii: (
"abcdefghijklmnopqrstuvwxyz",
"61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a"),
uppercase_ascii: (
"ABCDEFGHIJKLMNOPQRSTUVWXYZ",
"41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a"),
utf8_1byte_first_code_point: ("\u{0000}", "00"),
utf8_1byte_last_code_point: ("\u{007F}", "7f"),
utf8_2bytes_first_code_point: ("\u{0080}", "c2 80"),
utf8_2bytes_last_code_point: ("\u{07FF}", "df bf"),
utf8_3bytes_first_code_point: ("\u{0800}", "e0 a0 80"),
utf8_3bytes_last_code_point: ("\u{FFFF}", "ef bf bf"),
utf8_4bytes_first_code_point: ("\u{10000}", "f0 90 80 80"),
utf8_4bytes_last_code_point: ("\u{10FFFF}", "f4 8f bf bf"),
}
#[test]
fn returned_string_size() {
let response = "a".to_hex();
assert!(response.len() <= response.capacity());
}
}