1pub const NAME_VALUE_SEPARATOR: char = '\u{0001}';
16pub const PROPERTY_SEPARATOR: char = '\u{0002}';
17
18use std::collections::HashMap;
19
20use cheetah_string::CheetahString;
21
22const HEX: &[u8; 16] = b"0123456789ABCDEF";
23
24pub fn bytes_to_string(bytes: &[u8]) -> String {
25 let mut value = String::with_capacity(bytes.len() * 2);
26 for byte in bytes {
27 value.push(HEX[(byte >> 4) as usize] as char);
28 value.push(HEX[(byte & 0x0f) as usize] as char);
29 }
30 value
31}
32
33pub fn string_to_bytes(value: &str) -> Option<Vec<u8>> {
34 if value.is_empty() || !value.len().is_multiple_of(2) {
35 return None;
36 }
37 value
38 .as_bytes()
39 .as_chunks::<2>()
40 .0
41 .iter()
42 .map(|pair| {
43 let high = hex_value(pair[0])?;
44 let low = hex_value(pair[1])?;
45 Some((high << 4) | low)
46 })
47 .collect()
48}
49
50pub fn write_int(buffer: &mut [char], position: usize, value: i32) {
51 for (offset, shift) in (0..8).zip((0..=28).rev().step_by(4)) {
52 buffer[position + offset] = HEX[((value >> shift) & 0x0f) as usize] as char;
53 }
54}
55
56pub fn write_short(buffer: &mut [char], position: usize, value: i16) {
57 for (offset, shift) in (0..4).zip((0..=12).rev().step_by(4)) {
58 buffer[position + offset] = HEX[((value >> shift) & 0x0f) as usize] as char;
59 }
60}
61
62pub fn message_properties_to_string(properties: &HashMap<CheetahString, CheetahString>) -> CheetahString {
63 let mut value = String::new();
64 for (name, property) in properties {
65 value.push_str(name);
66 value.push(NAME_VALUE_SEPARATOR);
67 value.push_str(property);
68 value.push(PROPERTY_SEPARATOR);
69 }
70 CheetahString::from_string(value)
71}
72
73pub fn string_to_message_properties(properties: Option<&CheetahString>) -> HashMap<CheetahString, CheetahString> {
74 let Some(properties) = properties else {
75 return HashMap::new();
76 };
77 properties
78 .split_char(PROPERTY_SEPARATOR)
79 .filter_map(|entry| {
80 let (name, value) = entry.split_once(NAME_VALUE_SEPARATOR)?;
81 (!name.is_empty()).then(|| (CheetahString::from_slice(name), CheetahString::from_slice(value)))
82 })
83 .collect()
84}
85
86fn hex_value(byte: u8) -> Option<u8> {
87 match byte {
88 b'0'..=b'9' => Some(byte - b'0'),
89 b'a'..=b'f' => Some(byte - b'a' + 10),
90 b'A'..=b'F' => Some(byte - b'A' + 10),
91 _ => None,
92 }
93}