serializer/llm/
machine_zerocopy.rs1use crate::llm::types::{DxDocument, DxLlmValue, DxSection};
6use std::collections::HashMap;
7use thiserror::Error;
8
9#[derive(Debug, Error)]
11pub enum ZeroCopyError {
12 #[error("Invalid magic number")]
14 InvalidMagic,
15 #[error("Unsupported version: {0}")]
17 UnsupportedVersion(u8),
18 #[error("Invalid data: {0}")]
20 InvalidData(String),
21 #[error("Out of bounds access")]
23 OutOfBounds,
24}
25
26const MAGIC: &[u8; 4] = b"DXZC";
27const VERSION: u8 = 1;
28
29pub struct ZeroCopyMachine {
31 data: Vec<u8>,
32}
33
34impl ZeroCopyMachine {
35 pub fn from_document(doc: &DxDocument) -> Self {
37 let mut data = Vec::new();
38
39 data.extend_from_slice(MAGIC);
41 data.push(VERSION);
42
43 let mut string_table: HashMap<&str, u32> = HashMap::new();
45 let mut string_data = Vec::new();
46
47 for key in doc.context.keys() {
49 if !string_table.contains_key(key.as_str()) {
50 let id = string_table.len() as u32;
51 string_table.insert(key, id);
52 string_data.push(key.as_bytes());
53 }
54 }
55 for value in doc.context.values() {
56 collect_strings_from_value(value, &mut string_table, &mut string_data);
57 }
58 for section in doc.sections.values() {
59 for col in §ion.schema {
60 if !string_table.contains_key(col.as_str()) {
61 let id = string_table.len() as u32;
62 string_table.insert(col, id);
63 string_data.push(col.as_bytes());
64 }
65 }
66 for row in §ion.rows {
67 for value in row {
68 collect_strings_from_value(value, &mut string_table, &mut string_data);
69 }
70 }
71 }
72
73 data.extend_from_slice(&(string_table.len() as u32).to_le_bytes());
75 for s in &string_data {
76 data.extend_from_slice(&(s.len() as u32).to_le_bytes());
77 data.extend_from_slice(s);
78 }
79
80 data.extend_from_slice(&(doc.context.len() as u32).to_le_bytes());
82 for (key, value) in &doc.context {
83 let key_id = string_table[key.as_str()];
84 data.extend_from_slice(&key_id.to_le_bytes());
85 write_value(&mut data, value, &string_table);
86 }
87
88 data.extend_from_slice(&(doc.sections.len() as u32).to_le_bytes());
90 for (id, section) in &doc.sections {
91 data.push(*id as u8);
92 data.extend_from_slice(&(section.schema.len() as u32).to_le_bytes());
93 for col in §ion.schema {
94 let col_id = string_table[col.as_str()];
95 data.extend_from_slice(&col_id.to_le_bytes());
96 }
97 data.extend_from_slice(&(section.rows.len() as u32).to_le_bytes());
98 for row in §ion.rows {
99 for value in row {
100 write_value(&mut data, value, &string_table);
101 }
102 }
103 }
104
105 Self { data }
106 }
107
108 pub fn as_bytes(&self) -> &[u8] {
110 &self.data
111 }
112
113 pub fn to_document(&self) -> Result<DxDocument, ZeroCopyError> {
115 let mut pos = 0;
116 let data = &self.data;
117
118 if data.len() < 5 || &data[0..4] != MAGIC {
120 return Err(ZeroCopyError::InvalidMagic);
121 }
122 pos += 4;
123
124 let version = data[pos];
125 if version != VERSION {
126 return Err(ZeroCopyError::UnsupportedVersion(version));
127 }
128 pos += 1;
129
130 let string_count = read_u32(data, &mut pos)?;
132 let mut strings = Vec::with_capacity(string_count as usize);
133 for _ in 0..string_count {
134 let len = read_u32(data, &mut pos)? as usize;
135 if pos + len > data.len() {
136 return Err(ZeroCopyError::OutOfBounds);
137 }
138 let s = std::str::from_utf8(&data[pos..pos + len])
139 .map_err(|_| ZeroCopyError::InvalidData("Invalid UTF-8".to_string()))?
140 .to_string();
141 strings.push(s);
142 pos += len;
143 }
144
145 let mut doc = DxDocument::new();
146
147 let context_count = read_u32(data, &mut pos)?;
149 for _ in 0..context_count {
150 let key_id = read_u32(data, &mut pos)? as usize;
151 if key_id >= strings.len() {
152 return Err(ZeroCopyError::OutOfBounds);
153 }
154 let key = strings[key_id].clone();
155 let value = read_value(data, &mut pos, &strings)?;
156 doc.context.insert(key, value);
157 }
158
159 let sections_count = read_u32(data, &mut pos)?;
161 for _ in 0..sections_count {
162 if pos >= data.len() {
163 return Err(ZeroCopyError::OutOfBounds);
164 }
165 let id = data[pos] as char;
166 pos += 1;
167
168 let schema_count = read_u32(data, &mut pos)? as usize;
169 let mut schema = Vec::with_capacity(schema_count);
170 for _ in 0..schema_count {
171 let col_id = read_u32(data, &mut pos)? as usize;
172 if col_id >= strings.len() {
173 return Err(ZeroCopyError::OutOfBounds);
174 }
175 schema.push(strings[col_id].clone());
176 }
177
178 let mut section = DxSection::new(schema.clone());
179 let rows_count = read_u32(data, &mut pos)?;
180 for _ in 0..rows_count {
181 let mut row = Vec::with_capacity(schema.len());
182 for _ in 0..schema.len() {
183 row.push(read_value(data, &mut pos, &strings)?);
184 }
185 section.rows.push(row);
186 }
187
188 doc.sections.insert(id, section);
189 }
190
191 Ok(doc)
192 }
193
194 pub fn access(&self) -> Result<ZeroCopyDocument<'_>, ZeroCopyError> {
196 ZeroCopyDocument::new(&self.data)
197 }
198}
199
200pub struct ZeroCopyDocument<'a> {
202 _data: &'a [u8],
203}
204
205impl<'a> ZeroCopyDocument<'a> {
206 fn new(data: &'a [u8]) -> Result<Self, ZeroCopyError> {
207 if data.len() < 5 || &data[0..4] != MAGIC {
208 return Err(ZeroCopyError::InvalidMagic);
209 }
210 if data[4] != VERSION {
211 return Err(ZeroCopyError::UnsupportedVersion(data[4]));
212 }
213 Ok(Self { _data: data })
214 }
215
216 pub fn to_document(&self) -> Result<DxDocument, ZeroCopyError> {
218 ZeroCopyMachine {
219 data: self._data.to_vec(),
220 }
221 .to_document()
222 }
223}
224
225fn collect_strings_from_value<'a>(
226 value: &'a DxLlmValue,
227 table: &mut HashMap<&'a str, u32>,
228 data: &mut Vec<&'a [u8]>,
229) {
230 match value {
231 DxLlmValue::Str(s) => {
232 if !table.contains_key(s.as_str()) {
233 let id = table.len() as u32;
234 table.insert(s, id);
235 data.push(s.as_bytes());
236 }
237 }
238 DxLlmValue::Ref(s) => {
239 if !table.contains_key(s.as_str()) {
240 let id = table.len() as u32;
241 table.insert(s, id);
242 data.push(s.as_bytes());
243 }
244 }
245 DxLlmValue::Arr(items) => {
246 for item in items {
247 collect_strings_from_value(item, table, data);
248 }
249 }
250 DxLlmValue::Obj(fields) => {
251 for (k, v) in fields {
252 if !table.contains_key(k.as_str()) {
253 let id = table.len() as u32;
254 table.insert(k, id);
255 data.push(k.as_bytes());
256 }
257 collect_strings_from_value(v, table, data);
258 }
259 }
260 _ => {}
261 }
262}
263
264fn write_value(data: &mut Vec<u8>, value: &DxLlmValue, string_table: &HashMap<&str, u32>) {
265 match value {
266 DxLlmValue::Str(s) => {
267 data.push(0);
268 let id = string_table[s.as_str()];
269 data.extend_from_slice(&id.to_le_bytes());
270 }
271 DxLlmValue::Num(n) => {
272 data.push(1);
273 data.extend_from_slice(&n.to_le_bytes());
274 }
275 DxLlmValue::Bool(b) => {
276 data.push(2);
277 data.push(if *b { 1 } else { 0 });
278 }
279 DxLlmValue::Null => {
280 data.push(3);
281 }
282 _ => {
283 data.push(3); }
285 }
286}
287
288fn read_u32(data: &[u8], pos: &mut usize) -> Result<u32, ZeroCopyError> {
289 if *pos + 4 > data.len() {
290 return Err(ZeroCopyError::OutOfBounds);
291 }
292 let bytes = [data[*pos], data[*pos + 1], data[*pos + 2], data[*pos + 3]];
293 *pos += 4;
294 Ok(u32::from_le_bytes(bytes))
295}
296
297fn read_value(
298 data: &[u8],
299 pos: &mut usize,
300 strings: &[String],
301) -> Result<DxLlmValue, ZeroCopyError> {
302 if *pos >= data.len() {
303 return Err(ZeroCopyError::OutOfBounds);
304 }
305
306 let type_tag = data[*pos];
307 *pos += 1;
308
309 match type_tag {
310 0 => {
311 let id = read_u32(data, pos)? as usize;
312 if id >= strings.len() {
313 return Err(ZeroCopyError::OutOfBounds);
314 }
315 Ok(DxLlmValue::Str(strings[id].clone()))
316 }
317 1 => {
318 if *pos + 8 > data.len() {
319 return Err(ZeroCopyError::OutOfBounds);
320 }
321 let mut bytes = [0u8; 8];
322 bytes.copy_from_slice(&data[*pos..*pos + 8]);
323 *pos += 8;
324 Ok(DxLlmValue::Num(f64::from_le_bytes(bytes)))
325 }
326 2 => {
327 if *pos >= data.len() {
328 return Err(ZeroCopyError::OutOfBounds);
329 }
330 let b = data[*pos] != 0;
331 *pos += 1;
332 Ok(DxLlmValue::Bool(b))
333 }
334 3 => Ok(DxLlmValue::Null),
335 _ => Err(ZeroCopyError::InvalidData(format!(
336 "Unknown type tag: {}",
337 type_tag
338 ))),
339 }
340}