1use super::entity::*;
4use reddb_file::native_store::{
5 write_native_varu32 as write_varu32, write_native_varu64 as write_varu64,
6};
7use reddb_file::{
8 embedded::{RdbFileError, RdbFileResult},
9 native_store::*,
10 native_value::{write_native_value, NativeReader},
11};
12use std::collections::HashMap;
13
14fn persistent_name(name: &str) -> &str {
15 super::system_collection_names::canonical_or_self(name)
16}
17
18pub fn write_entity_binary(buf: &mut Vec<u8>, entity: &UnifiedEntity, format_version: u32) {
20 write_varu64(buf, entity.id.raw());
22
23 match &entity.kind {
25 EntityKind::TableRow { table, row_id } => {
26 let table = persistent_name(table);
27 buf.push(0);
28 write_varu32(buf, table.len() as u32);
29 buf.extend_from_slice(table.as_bytes());
30 write_varu64(buf, *row_id);
31 }
32 EntityKind::GraphNode(ref node) => {
33 buf.push(1);
34 write_varu32(buf, node.label.len() as u32);
35 buf.extend_from_slice(node.label.as_bytes());
36 write_varu32(buf, node.node_type.len() as u32);
37 buf.extend_from_slice(node.node_type.as_bytes());
38 }
39 EntityKind::GraphEdge(ref edge) => {
40 buf.push(2);
41 write_varu32(buf, edge.label.len() as u32);
42 buf.extend_from_slice(edge.label.as_bytes());
43 write_varu32(buf, edge.from_node.len() as u32);
44 buf.extend_from_slice(edge.from_node.as_bytes());
45 write_varu32(buf, edge.to_node.len() as u32);
46 buf.extend_from_slice(edge.to_node.as_bytes());
47 buf.extend_from_slice(&edge.weight.to_le_bytes());
48 }
49 EntityKind::Vector { collection } => {
50 let collection = persistent_name(collection);
51 buf.push(3);
52 write_varu32(buf, collection.len() as u32);
53 buf.extend_from_slice(collection.as_bytes());
54 }
55 EntityKind::TimeSeriesPoint(ref ts) => {
56 let series = persistent_name(&ts.series);
57 buf.push(4);
58 write_varu32(buf, series.len() as u32);
59 buf.extend_from_slice(series.as_bytes());
60 write_varu32(buf, ts.metric.len() as u32);
61 buf.extend_from_slice(ts.metric.as_bytes());
62 }
63 EntityKind::QueueMessage { queue, position } => {
64 let queue = persistent_name(queue);
65 buf.push(5);
66 write_varu32(buf, queue.len() as u32);
67 buf.extend_from_slice(queue.as_bytes());
68 write_varu64(buf, *position);
69 }
70 }
71
72 match &entity.data {
74 EntityData::Row(row) => {
75 if row.named.is_some()
76 || row
77 .schema
78 .as_ref()
79 .is_some_and(|schema| schema.len() == row.columns.len())
80 {
81 buf.push(6);
84 let mut entries: Vec<_> = if let Some(named) = &row.named {
85 named
86 .iter()
87 .map(|(name, value)| (name.as_str(), value))
88 .collect()
89 } else {
90 row.schema
91 .as_ref()
92 .expect("validated schema")
93 .iter()
94 .map(String::as_str)
95 .zip(&row.columns)
96 .collect()
97 };
98 write_varu32(buf, entries.len() as u32);
99 entries.sort_by(|a, b| a.0.cmp(b.0));
100 for (key, value) in entries {
101 write_varu32(buf, key.len() as u32);
102 buf.extend_from_slice(key.as_bytes());
103 write_native_value(buf, value);
104 }
105 } else {
106 buf.push(0);
107 write_varu32(buf, row.columns.len() as u32);
108 for col in &row.columns {
109 write_native_value(buf, col);
110 }
111 }
112 }
113 EntityData::Node(node) => {
114 buf.push(1);
115 write_varu32(buf, node.properties.len() as u32);
116 let mut entries: Vec<_> = node.properties.iter().collect();
117 entries.sort_by(|a, b| a.0.as_str().cmp(b.0.as_str()));
118 for (key, value) in entries {
119 write_varu32(buf, key.len() as u32);
120 buf.extend_from_slice(key.as_bytes());
121 write_native_value(buf, value);
122 }
123 }
124 EntityData::Edge(edge) => {
125 buf.push(2);
126 buf.extend_from_slice(&edge.weight.to_le_bytes());
127 write_varu32(buf, edge.properties.len() as u32);
128 let mut entries: Vec<_> = edge.properties.iter().collect();
129 entries.sort_by(|a, b| a.0.as_str().cmp(b.0.as_str()));
130 for (key, value) in entries {
131 write_varu32(buf, key.len() as u32);
132 buf.extend_from_slice(key.as_bytes());
133 write_native_value(buf, value);
134 }
135 }
136 EntityData::Vector(vec) => {
137 buf.push(3);
138 write_varu32(buf, vec.dense.len() as u32);
139 for f in &vec.dense {
140 buf.extend_from_slice(&f.to_le_bytes());
141 }
142 if format_version >= STORE_VERSION_V6 {
143 buf.push(u8::from(vec.content.is_some()));
144 if let Some(content) = &vec.content {
145 write_varu32(buf, content.len() as u32);
146 buf.extend_from_slice(content.as_bytes());
147 }
148 }
149 }
150 EntityData::TimeSeries(ts) => {
151 buf.push(4);
152 write_varu32(buf, ts.metric.len() as u32);
153 buf.extend_from_slice(ts.metric.as_bytes());
154 write_varu64(buf, ts.timestamp_ns);
155 buf.extend_from_slice(&ts.value.to_le_bytes());
156 if format_version >= STORE_VERSION_V11 {
157 match ts.series_id {
163 Some(series_id) => {
164 write_varu32(buf, 1);
165 write_varu64(buf, series_id);
166 }
167 None => {
168 write_varu32(buf, 0);
169 write_varu32(buf, ts.tags.len() as u32);
170 let mut tag_entries: Vec<_> = ts.tags.iter().collect();
171 tag_entries.sort_by(|a, b| a.0.as_str().cmp(b.0.as_str()));
172 for (key, value) in tag_entries {
173 write_varu32(buf, key.len() as u32);
174 buf.extend_from_slice(key.as_bytes());
175 write_varu32(buf, value.len() as u32);
176 buf.extend_from_slice(value.as_bytes());
177 }
178 }
179 }
180 } else if format_version >= STORE_VERSION_V5 {
181 write_varu32(buf, ts.tags.len() as u32);
182 let mut tag_entries: Vec<_> = ts.tags.iter().collect();
183 tag_entries.sort_by(|a, b| a.0.as_str().cmp(b.0.as_str()));
184 for (key, value) in tag_entries {
185 write_varu32(buf, key.len() as u32);
186 buf.extend_from_slice(key.as_bytes());
187 write_varu32(buf, value.len() as u32);
188 buf.extend_from_slice(value.as_bytes());
189 }
190 }
191 if format_version >= STORE_VERSION_V12 {
192 write_varu32(buf, ts.fields.len() as u32);
193 let mut field_entries: Vec<_> = ts.fields.iter().collect();
194 field_entries.sort_by(|a, b| a.0.as_str().cmp(b.0.as_str()));
195 for (key, value) in field_entries {
196 write_varu32(buf, key.len() as u32);
197 buf.extend_from_slice(key.as_bytes());
198 write_native_value(buf, value);
199 }
200 }
201 }
202 EntityData::QueueMessage(msg) => {
203 buf.push(5);
204 write_native_value(buf, &msg.payload);
205 write_varu64(buf, msg.enqueued_at_ns);
206 write_varu32(buf, msg.attempts);
207 }
208 }
209
210 write_varu64(buf, entity.created_at);
212 write_varu64(buf, entity.updated_at);
213
214 write_varu32(buf, entity.embeddings().len() as u32);
216 for emb in entity.embeddings() {
217 write_varu32(buf, emb.name.len() as u32);
218 buf.extend_from_slice(emb.name.as_bytes());
219 write_varu32(buf, emb.vector.len() as u32);
220 for f in &emb.vector {
221 buf.extend_from_slice(&f.to_le_bytes());
222 }
223 write_varu32(buf, emb.model.len() as u32);
224 buf.extend_from_slice(emb.model.as_bytes());
225 }
226
227 write_varu32(buf, entity.cross_refs().len() as u32);
229 for cross_ref in entity.cross_refs() {
230 write_varu64(buf, cross_ref.source.raw());
231 write_varu64(buf, cross_ref.target.raw());
232 buf.push(cross_ref.ref_type.to_byte());
233 if format_version >= STORE_VERSION_V2 {
234 let target_collection = persistent_name(&cross_ref.target_collection);
235 write_varu32(buf, target_collection.len() as u32);
236 buf.extend_from_slice(target_collection.as_bytes());
237 buf.extend_from_slice(&cross_ref.weight.to_le_bytes());
238 write_varu64(buf, cross_ref.created_at);
239 }
240 }
241
242 write_varu64(buf, entity.sequence_id);
244
245 if format_version >= STORE_VERSION_V4 {
246 if let EntityData::QueueMessage(message) = &entity.data {
247 buf.push(u8::from(message.priority.is_some()));
248 if let Some(priority) = message.priority {
249 buf.extend_from_slice(&priority.to_le_bytes());
250 }
251 write_varu32(buf, message.max_attempts);
252 buf.push(u8::from(message.acked));
253 }
254 }
255
256 if format_version >= STORE_VERSION_V8 {
257 buf.push(u8::from(entity.has_explicit_logical_id()));
258 if entity.has_explicit_logical_id() {
259 write_varu64(buf, entity.logical_id().raw());
260 }
261 }
262 if format_version >= STORE_VERSION_V9 {
263 write_varu64(buf, entity.xmin);
264 write_varu64(buf, entity.xmax);
265 }
266}
267
268pub fn read_entity_binary(
271 bytes: &[u8],
272 position: &mut usize,
273 format_version: u32,
274) -> RdbFileResult<UnifiedEntity> {
275 read_entity_from_reader(&mut NativeReader::new(bytes, position), format_version)
276}
277
278pub fn read_entity_binary_with_budget(
279 bytes: &[u8],
280 position: &mut usize,
281 format_version: u32,
282 budget: &mut reddb_file::native_value::DecodeBudget,
283) -> RdbFileResult<UnifiedEntity> {
284 budget.elements::<UnifiedEntity>(1)?;
285 read_entity_from_reader(
286 &mut NativeReader::with_budget(bytes, position, budget),
287 format_version,
288 )
289}
290
291fn read_entity_from_reader(
292 reader: &mut NativeReader<'_, '_>,
293 format_version: u32,
294) -> RdbFileResult<UnifiedEntity> {
295 let id = EntityId::new(reader.variable_u64()?);
296 let kind = match reader.byte()? {
297 0 => {
298 let table = super::system_collection_names::canonical_owned(reader.string()?);
299 reader.reserve_bytes(table.len().saturating_add(2 * std::mem::size_of::<usize>()))?;
300 EntityKind::TableRow {
301 table: table.into(),
302 row_id: reader.variable_u64()?,
303 }
304 }
305 1 => {
306 reader.reserve_elements::<GraphNodeKind>(1)?;
307 EntityKind::GraphNode(Box::new(GraphNodeKind {
308 label: reader.string()?,
309 node_type: reader.string()?,
310 }))
311 }
312 2 => {
313 reader.reserve_elements::<GraphEdgeKind>(1)?;
314 EntityKind::GraphEdge(Box::new(GraphEdgeKind {
315 label: reader.string()?,
316 from_node: reader.string()?,
317 to_node: reader.string()?,
318 weight: u32::from_le_bytes(reader.fixed()?),
319 }))
320 }
321 3 => EntityKind::Vector {
322 collection: super::system_collection_names::canonical_owned(reader.string()?),
323 },
324 4 => {
325 reader.reserve_elements::<TimeSeriesPointKind>(1)?;
326 EntityKind::TimeSeriesPoint(Box::new(TimeSeriesPointKind {
327 series: super::system_collection_names::canonical_owned(reader.string()?),
328 metric: reader.string()?,
329 }))
330 }
331 5 => EntityKind::QueueMessage {
332 queue: super::system_collection_names::canonical_owned(reader.string()?),
333 position: reader.variable_u64()?,
334 },
335 tag => return Err(invalid(format!("Unknown EntityKind type: {tag}"))),
336 };
337 let mut data = match reader.byte()? {
338 0 => {
339 let count = reader.count(1)?;
340 reader.reserve_elements::<reddb_types::Value>(count)?;
341 let mut columns = Vec::with_capacity(count);
342 for _ in 0..count {
343 columns.push(reader.value()?);
344 }
345 EntityData::Row(RowData::new(columns))
346 }
347 1 => EntityData::Node(NodeData::with_properties(read_properties(reader)?)),
348 2 => {
349 let mut edge = EdgeData::new(f32::from_le_bytes(reader.fixed()?));
350 edge.properties = read_properties(reader)?;
351 EntityData::Edge(edge)
352 }
353 3 => {
354 let mut vector = VectorData::new(read_vector(reader)?);
355 if format_version >= STORE_VERSION_V6 && reader.byte()? != 0 {
356 vector.content = Some(reader.string()?);
357 }
358 EntityData::Vector(vector)
359 }
360 4 => {
361 let metric = reader.string()?;
362 let timestamp_ns = reader.variable_u64()?;
363 let value = f64::from_le_bytes(reader.fixed()?);
364 let mut tags = HashMap::new();
365 let series_id = if format_version >= STORE_VERSION_V11 && reader.variable_u32()? == 1 {
366 Some(reader.variable_u64()?)
367 } else {
368 if format_version >= STORE_VERSION_V5 {
369 let count = reader.count(2)?;
370 reader.reserve_map::<String, String>(count)?;
371 tags = HashMap::with_capacity(count);
372 for _ in 0..count {
373 tags.insert(reader.string()?, reader.string()?);
374 }
375 }
376 None
377 };
378 let fields = if format_version >= STORE_VERSION_V12 {
379 read_properties(reader)?
380 } else {
381 HashMap::new()
382 };
383 EntityData::TimeSeries(TimeSeriesData {
384 metric,
385 timestamp_ns,
386 value,
387 series_id,
388 tags,
389 fields,
390 })
391 }
392 5 => EntityData::QueueMessage(QueueMessageData {
393 payload: reader.value()?,
394 priority: None,
395 enqueued_at_ns: reader.variable_u64()?,
396 attempts: reader.variable_u32()?,
397 max_attempts: 3,
398 acked: false,
399 }),
400 6 => EntityData::Row(RowData {
401 columns: Vec::new(),
402 named: Some(read_properties(reader)?),
403 schema: None,
404 }),
405 tag => return Err(invalid(format!("Unknown EntityData type: {tag}"))),
406 };
407 let created_at = reader.variable_u64()?;
408 let updated_at = reader.variable_u64()?;
409 let count = reader.count(3)?;
410 reader.reserve_elements::<EmbeddingSlot>(count)?;
411 reader.reserve_elements::<EmbeddingSlot>(count)?;
412 let mut embeddings = Vec::with_capacity(count);
413 for _ in 0..count {
414 let name = reader.string()?;
415 let vector = read_vector(reader)?;
416 let model = reader.string()?;
417 embeddings.push(EmbeddingSlot::new(name, vector, model));
418 }
419 let count = reader.count(3)?;
420 reader.reserve_elements::<CrossRef>(count)?;
421 reader.reserve_elements::<CrossRef>(count)?;
422 let mut cross_refs = Vec::with_capacity(count);
423 for _ in 0..count {
424 let source = EntityId::new(reader.variable_u64()?);
425 let target = EntityId::new(reader.variable_u64()?);
426 let ref_type = RefType::from_byte(reader.byte()?);
427 let (collection, weight, created_at) = if format_version >= STORE_VERSION_V2 {
428 (
429 super::system_collection_names::canonical_owned(reader.string()?),
430 f32::from_le_bytes(reader.fixed()?),
431 reader.variable_u64()?,
432 )
433 } else {
434 (String::new(), 1.0, 0)
435 };
436 let mut cross_ref = CrossRef::new(source, target, collection, ref_type);
437 cross_ref.weight = weight;
438 cross_ref.created_at = created_at;
439 cross_refs.push(cross_ref);
440 }
441 let sequence_id = reader.variable_u64()?;
442 if format_version >= STORE_VERSION_V4 {
443 if let EntityData::QueueMessage(message) = &mut data {
444 if reader.remaining() != 0 {
445 message.priority = if reader.byte()? != 0 {
446 Some(i32::from_le_bytes(reader.fixed()?))
447 } else {
448 None
449 };
450 message.max_attempts = reader.variable_u32()?;
451 message.acked = reader.byte()? != 0;
452 }
453 }
454 }
455 let mut entity = UnifiedEntity::new(id, kind, data);
456 entity.created_at = created_at;
457 entity.updated_at = updated_at;
458 entity.sequence_id = sequence_id;
459 if format_version >= STORE_VERSION_V8 && reader.remaining() != 0 && reader.byte()? != 0 {
460 entity.set_logical_id(EntityId::new(reader.variable_u64()?));
461 }
462 if format_version >= STORE_VERSION_V9 && reader.remaining() != 0 {
463 entity.set_xmin(reader.variable_u64()?);
464 entity.set_xmax(reader.variable_u64()?);
465 }
466 if !embeddings.is_empty() || !cross_refs.is_empty() {
467 reader.reserve_elements::<EntityAux>(1)?;
468 entity.embeddings_mut().extend(embeddings);
469 entity.cross_refs_mut().extend(cross_refs);
470 }
471 Ok(entity)
472}
473
474fn read_properties(
475 reader: &mut NativeReader<'_, '_>,
476) -> RdbFileResult<HashMap<String, reddb_types::Value>> {
477 let count = reader.count(2)?;
478 reader.reserve_map::<String, reddb_types::Value>(count)?;
479 let mut properties = HashMap::with_capacity(count);
480 for _ in 0..count {
481 properties.insert(reader.string()?, reader.value()?);
482 }
483 Ok(properties)
484}
485
486fn read_vector(reader: &mut NativeReader<'_, '_>) -> RdbFileResult<Vec<f32>> {
487 let count = reader.count(4)?;
488 reader.reserve_elements::<f32>(count)?;
489 let mut vector = Vec::with_capacity(count);
490 for _ in 0..count {
491 vector.push(f32::from_le_bytes(reader.fixed()?));
492 }
493 Ok(vector)
494}
495
496fn invalid(error: impl std::fmt::Display) -> RdbFileError {
497 RdbFileError::InvalidOperation(error.to_string())
498}