Skip to main content

rocketmq_model/
codec.rs

1// Copyright 2023 The RocketMQ Rust Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15pub 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}