agentmux_srv\backend\utilfn/
nullenc.rs1#![allow(dead_code)]
2use std::collections::HashMap;
7
8const NULL_ENCODE_ESC_BYTE: u8 = b'\\';
11const NULL_ENCODE_SEP_BYTE: u8 = b'|';
12const NULL_ENCODE_EQ_BYTE: u8 = b'=';
13const NULL_ENCODE_ZERO_BYTE_ESC: u8 = b'0';
14const NULL_ENCODE_ESC_BYTE_ESC: u8 = b'\\';
15const NULL_ENCODE_SEP_BYTE_ESC: u8 = b's';
16const NULL_ENCODE_EQ_BYTE_ESC: u8 = b'e';
17
18pub fn null_encode_str(s: &str) -> Vec<u8> {
27 let bytes = s.as_bytes();
28 if !bytes.iter().any(|&b| b == 0 || b == NULL_ENCODE_ESC_BYTE || b == NULL_ENCODE_SEP_BYTE || b == NULL_ENCODE_EQ_BYTE) {
30 return bytes.to_vec();
31 }
32 let mut rtn = Vec::with_capacity(bytes.len() + 8);
33 for &b in bytes {
34 match b {
35 0 => {
36 rtn.push(NULL_ENCODE_ESC_BYTE);
37 rtn.push(NULL_ENCODE_ZERO_BYTE_ESC);
38 }
39 b if b == NULL_ENCODE_ESC_BYTE => {
40 rtn.push(NULL_ENCODE_ESC_BYTE);
41 rtn.push(NULL_ENCODE_ESC_BYTE_ESC);
42 }
43 b if b == NULL_ENCODE_SEP_BYTE => {
44 rtn.push(NULL_ENCODE_ESC_BYTE);
45 rtn.push(NULL_ENCODE_SEP_BYTE_ESC);
46 }
47 b if b == NULL_ENCODE_EQ_BYTE => {
48 rtn.push(NULL_ENCODE_ESC_BYTE);
49 rtn.push(NULL_ENCODE_EQ_BYTE_ESC);
50 }
51 _ => rtn.push(b),
52 }
53 }
54 rtn
55}
56
57pub fn null_decode_str(barr: &[u8]) -> Result<String, String> {
59 if !barr.contains(&NULL_ENCODE_ESC_BYTE) {
60 return Ok(String::from_utf8_lossy(barr).into_owned());
61 }
62 let mut rtn = Vec::with_capacity(barr.len());
63 let mut i = 0;
64 while i < barr.len() {
65 let cur = barr[i];
66 if cur == NULL_ENCODE_ESC_BYTE {
67 i += 1;
68 if i >= barr.len() {
69 return Err("invalid null encoding: escape at end of string".to_string());
70 }
71 let next = barr[i];
72 match next {
73 NULL_ENCODE_ZERO_BYTE_ESC => rtn.push(0),
74 NULL_ENCODE_ESC_BYTE_ESC => rtn.push(NULL_ENCODE_ESC_BYTE),
75 NULL_ENCODE_SEP_BYTE_ESC => rtn.push(NULL_ENCODE_SEP_BYTE),
76 NULL_ENCODE_EQ_BYTE_ESC => rtn.push(NULL_ENCODE_EQ_BYTE),
77 _ => return Err(format!("invalid null encoding: {}", next)),
78 }
79 } else {
80 rtn.push(cur);
81 }
82 i += 1;
83 }
84 Ok(String::from_utf8_lossy(&rtn).into_owned())
85}
86
87pub fn encode_string_map(m: &HashMap<String, String>) -> Vec<u8> {
90 let mut keys: Vec<&String> = m.keys().collect();
91 keys.sort();
92 let mut buf = Vec::new();
93 for (idx, key) in keys.iter().enumerate() {
94 let val = &m[*key];
95 buf.extend_from_slice(&null_encode_str(key));
96 buf.push(NULL_ENCODE_EQ_BYTE);
97 buf.extend_from_slice(&null_encode_str(val));
98 if idx < keys.len() - 1 {
99 buf.push(NULL_ENCODE_SEP_BYTE);
100 }
101 }
102 buf
103}
104
105pub fn decode_string_map(barr: &[u8]) -> Result<HashMap<String, String>, String> {
107 if barr.is_empty() {
108 return Ok(HashMap::new());
109 }
110 let mut rtn = HashMap::new();
111 for part in split_bytes(barr, NULL_ENCODE_SEP_BYTE) {
112 let kv: Vec<&[u8]> = splitn_bytes(part, NULL_ENCODE_EQ_BYTE, 2);
113 if kv.len() != 2 {
114 return Err(format!("invalid null encoding: {}", String::from_utf8_lossy(part)));
115 }
116 let key = null_decode_str(kv[0])?;
117 let val = null_decode_str(kv[1])?;
118 rtn.insert(key, val);
119 }
120 Ok(rtn)
121}
122
123pub fn encode_string_array(arr: &[String]) -> Vec<u8> {
126 let mut buf = Vec::new();
127 for (idx, s) in arr.iter().enumerate() {
128 buf.extend_from_slice(&null_encode_str(s));
129 if idx < arr.len() - 1 {
130 buf.push(NULL_ENCODE_SEP_BYTE);
131 }
132 }
133 buf
134}
135
136pub fn decode_string_array(barr: &[u8]) -> Result<Vec<String>, String> {
138 if barr.is_empty() {
139 return Ok(Vec::new());
140 }
141 let mut rtn = Vec::new();
142 for part in split_bytes(barr, NULL_ENCODE_SEP_BYTE) {
143 rtn.push(null_decode_str(part)?);
144 }
145 Ok(rtn)
146}
147
148pub fn encoded_string_array_has_first_val(encoded: &[u8], first_key: &str) -> bool {
150 let first_key_bytes = null_encode_str(first_key);
151 if !encoded.starts_with(&first_key_bytes) {
152 return false;
153 }
154 encoded.len() == first_key_bytes.len() || encoded[first_key_bytes.len()] == NULL_ENCODE_SEP_BYTE
155}
156
157pub fn encoded_string_array_get_first_val(encoded: &[u8]) -> String {
159 let sep_idx = encoded.iter().position(|&b| b == NULL_ENCODE_SEP_BYTE);
160 let slice = match sep_idx {
161 Some(idx) => &encoded[..idx],
162 None => encoded,
163 };
164 null_decode_str(slice).unwrap_or_default()
165}
166
167fn split_bytes(data: &[u8], delim: u8) -> Vec<&[u8]> {
170 let mut parts = Vec::new();
171 let mut start = 0;
172 for (i, &b) in data.iter().enumerate() {
173 if b == delim {
174 parts.push(&data[start..i]);
175 start = i + 1;
176 }
177 }
178 parts.push(&data[start..]);
179 parts
180}
181
182fn splitn_bytes(data: &[u8], delim: u8, n: usize) -> Vec<&[u8]> {
183 let mut parts = Vec::new();
184 let mut start = 0;
185 for (i, &b) in data.iter().enumerate() {
186 if b == delim && parts.len() < n - 1 {
187 parts.push(&data[start..i]);
188 start = i + 1;
189 }
190 }
191 parts.push(&data[start..]);
192 parts
193}