1#[path = "metadata_codec/budget.rs"]
3mod budget;
4use super::entity::{EntityId, UnifiedEntity};
5use super::metadata::{Metadata, MetadataValue};
6
7fn read_entity(data: &[u8], version: u32) -> Result<UnifiedEntity, String> {
8 super::entity_codec::read_entity_binary(data, &mut 0, version).map_err(invalid)
9}
10
11fn invalid(error: impl std::fmt::Display) -> String {
12 error.to_string()
13}
14
15fn read_count(data: &[u8], position: &mut usize, bytes_min: usize) -> Result<usize, String> {
16 let count = usize::try_from(read_u32(data, position)?).map_err(invalid)?;
17 if count > data.len().saturating_sub(*position) / bytes_min {
18 return Err(invalid("metadata count exceeds the remaining payload"));
19 }
20 Ok(count)
21}
22
23pub fn serialize_entity_record(
25 entity: &UnifiedEntity,
26 metadata: Option<&Metadata>,
27 version: u32,
28) -> Vec<u8> {
29 let mut bytes = Vec::with_capacity(256);
30 super::entity_codec::write_entity_binary(&mut bytes, entity, version);
31 let entity = bytes;
32 if metadata.is_some_and(|metadata| !metadata.fields.is_empty()) {
33 reddb_file::encode_native_entity_record_frame(&entity, Some(&serialize_metadata(metadata)))
34 } else {
35 reddb_file::encode_native_entity_record_frame(&entity, None)
36 }
37}
38
39pub fn deserialize_entity_record(
41 data: &[u8],
42 version: u32,
43) -> Result<(UnifiedEntity, Option<Metadata>), String> {
44 let Some(frame) = reddb_file::decode_native_entity_record_frame(data).map_err(invalid)? else {
45 return read_entity(data, version).map(|entity| (entity, None));
46 };
47 let entity = read_entity(frame.entity, version)?;
48 let metadata = if frame.metadata.is_empty() {
49 None
50 } else {
51 let metadata = deserialize_metadata(frame.metadata)?;
52 (!metadata.is_empty()).then_some(metadata)
53 };
54 Ok((entity, metadata))
55}
56
57pub fn serialize_metadata(metadata: Option<&Metadata>) -> Vec<u8> {
58 let Some(metadata) = metadata else {
59 return Vec::new();
60 };
61 if metadata.is_empty() {
62 return Vec::new();
63 }
64
65 let mut entries: Vec<_> = metadata.iter().collect();
66 entries.sort_by_key(|(a, _)| *a);
67
68 let mut buf = Vec::new();
69 buf.extend_from_slice(&(entries.len() as u32).to_le_bytes());
70 for (key, value) in entries {
71 write_string(&mut buf, key);
72 write_metadata_value(&mut buf, value);
73 }
74 buf
75}
76
77pub fn deserialize_metadata(data: &[u8]) -> Result<Metadata, String> {
78 let mut pos = 0usize;
79 let count = read_count(data, &mut pos, 5)?;
80 let mut metadata = Metadata::new();
81 for _ in 0..count {
82 let key = read_string(data, &mut pos)?;
83 let value = read_metadata_value(data, &mut pos, 0)?;
84 metadata.set(key, value);
85 }
86 Ok(metadata)
87}
88
89fn write_string(buf: &mut Vec<u8>, value: &str) {
90 reddb_file::encode_native_len_prefixed_str(buf, value);
91}
92
93fn write_collection_name(buf: &mut Vec<u8>, value: &str) {
94 write_string(
95 buf,
96 super::system_collection_names::canonical_or_self(value),
97 );
98}
99
100fn write_bytes(buf: &mut Vec<u8>, value: &[u8]) {
101 reddb_file::encode_native_len_prefixed_bytes(buf, value);
102}
103
104fn write_ref_target(buf: &mut Vec<u8>, target: &super::metadata::RefTarget) {
105 use super::metadata::RefTarget;
106
107 match target {
108 RefTarget::TableRow { table, row_id } => {
109 buf.push(0);
110 write_collection_name(buf, table);
111 buf.extend_from_slice(&row_id.to_le_bytes());
112 }
113 RefTarget::Node {
114 collection,
115 node_id,
116 } => {
117 buf.push(1);
118 write_collection_name(buf, collection);
119 buf.extend_from_slice(&node_id.raw().to_le_bytes());
120 }
121 RefTarget::Edge {
122 collection,
123 edge_id,
124 } => {
125 buf.push(2);
126 write_collection_name(buf, collection);
127 buf.extend_from_slice(&edge_id.raw().to_le_bytes());
128 }
129 RefTarget::Vector {
130 collection,
131 vector_id,
132 } => {
133 buf.push(3);
134 write_collection_name(buf, collection);
135 buf.extend_from_slice(&vector_id.raw().to_le_bytes());
136 }
137 RefTarget::Entity {
138 collection,
139 entity_id,
140 } => {
141 buf.push(4);
142 write_collection_name(buf, collection);
143 buf.extend_from_slice(&entity_id.raw().to_le_bytes());
144 }
145 }
146}
147
148fn write_metadata_value(buf: &mut Vec<u8>, value: &MetadataValue) {
149 match value {
150 MetadataValue::Null => buf.push(0),
151 MetadataValue::Bool(v) => {
152 buf.push(1);
153 buf.push(u8::from(*v));
154 }
155 MetadataValue::Int(v) => {
156 buf.push(2);
157 buf.extend_from_slice(&v.to_le_bytes());
158 }
159 MetadataValue::Float(v) => {
160 buf.push(3);
161 buf.extend_from_slice(&v.to_le_bytes());
162 }
163 MetadataValue::String(v) => {
164 buf.push(4);
165 write_string(buf, v);
166 }
167 MetadataValue::Bytes(v) => {
168 buf.push(5);
169 write_bytes(buf, v);
170 }
171 MetadataValue::Array(values) => {
172 buf.push(6);
173 buf.extend_from_slice(&(values.len() as u32).to_le_bytes());
174 for value in values {
175 write_metadata_value(buf, value);
176 }
177 }
178 MetadataValue::Object(values) => {
179 buf.push(7);
180 let mut entries: Vec<_> = values.iter().collect();
181 entries.sort_by_key(|(a, _)| *a);
182 buf.extend_from_slice(&(entries.len() as u32).to_le_bytes());
183 for (key, value) in entries {
184 write_string(buf, key);
185 write_metadata_value(buf, value);
186 }
187 }
188 MetadataValue::Timestamp(v) => {
189 buf.push(8);
190 buf.extend_from_slice(&v.to_le_bytes());
191 }
192 MetadataValue::Geo { lat, lon } => {
193 buf.push(9);
194 buf.extend_from_slice(&lat.to_le_bytes());
195 buf.extend_from_slice(&lon.to_le_bytes());
196 }
197 MetadataValue::Reference(target) => {
198 buf.push(10);
199 write_ref_target(buf, target);
200 }
201 MetadataValue::References(targets) => {
202 buf.push(11);
203 buf.extend_from_slice(&(targets.len() as u32).to_le_bytes());
204 for target in targets {
205 write_ref_target(buf, target);
206 }
207 }
208 }
209}
210
211fn read_exact_slice<'a>(data: &'a [u8], pos: &mut usize, len: usize) -> Result<&'a [u8], String> {
212 if *pos > data.len() || len > data.len() - *pos {
213 return Err(invalid("truncated metadata payload".to_string()));
214 }
215 let slice = &data[*pos..*pos + len];
216 *pos += len;
217 Ok(slice)
218}
219
220fn read_u32(data: &[u8], pos: &mut usize) -> Result<u32, String> {
221 let bytes = read_exact_slice(data, pos, 4)?;
222 let mut raw = [0u8; 4];
223 raw.copy_from_slice(bytes);
224 Ok(u32::from_le_bytes(raw))
225}
226
227fn read_u64(data: &[u8], pos: &mut usize) -> Result<u64, String> {
228 let bytes = read_exact_slice(data, pos, 8)?;
229 let mut raw = [0u8; 8];
230 raw.copy_from_slice(bytes);
231 Ok(u64::from_le_bytes(raw))
232}
233
234fn read_i64(data: &[u8], pos: &mut usize) -> Result<i64, String> {
235 let bytes = read_exact_slice(data, pos, 8)?;
236 let mut raw = [0u8; 8];
237 raw.copy_from_slice(bytes);
238 Ok(i64::from_le_bytes(raw))
239}
240
241fn read_f64(data: &[u8], pos: &mut usize) -> Result<f64, String> {
242 let bytes = read_exact_slice(data, pos, 8)?;
243 let mut raw = [0u8; 8];
244 raw.copy_from_slice(bytes);
245 Ok(f64::from_le_bytes(raw))
246}
247
248fn read_u8(data: &[u8], pos: &mut usize) -> Result<u8, String> {
249 let bytes = read_exact_slice(data, pos, 1)?;
250 Ok(bytes[0])
251}
252
253fn read_string(data: &[u8], pos: &mut usize) -> Result<String, String> {
254 reddb_file::decode_native_len_prefixed_string(data, pos).map_err(|err| invalid(err.to_string()))
255}
256
257fn read_bytes(data: &[u8], pos: &mut usize) -> Result<Vec<u8>, String> {
258 reddb_file::decode_native_len_prefixed_bytes(data, pos)
259 .map(|bytes| bytes.to_vec())
260 .map_err(|err| invalid(err.to_string()))
261}
262
263fn read_ref_target(data: &[u8], pos: &mut usize) -> Result<super::metadata::RefTarget, String> {
264 use super::metadata::RefTarget;
265
266 match read_u8(data, pos)? {
267 0 => Ok(RefTarget::TableRow {
268 table: super::system_collection_names::canonical_owned(read_string(data, pos)?),
269 row_id: read_u64(data, pos)?,
270 }),
271 1 => Ok(RefTarget::Node {
272 collection: super::system_collection_names::canonical_owned(read_string(data, pos)?),
273 node_id: EntityId::new(read_u64(data, pos)?),
274 }),
275 2 => Ok(RefTarget::Edge {
276 collection: super::system_collection_names::canonical_owned(read_string(data, pos)?),
277 edge_id: EntityId::new(read_u64(data, pos)?),
278 }),
279 3 => Ok(RefTarget::Vector {
280 collection: super::system_collection_names::canonical_owned(read_string(data, pos)?),
281 vector_id: EntityId::new(read_u64(data, pos)?),
282 }),
283 4 => Ok(RefTarget::Entity {
284 collection: super::system_collection_names::canonical_owned(read_string(data, pos)?),
285 entity_id: EntityId::new(read_u64(data, pos)?),
286 }),
287 tag => Err(invalid(format!("unknown metadata ref target tag {tag}"))),
288 }
289}
290
291fn read_metadata_value(data: &[u8], pos: &mut usize, depth: u32) -> Result<MetadataValue, String> {
292 if depth > reddb_rql::limits::JSON_LITERAL_MAX_DEPTH {
293 return Err(invalid("metadata nesting exceeds the maximum depth"));
294 }
295 match read_u8(data, pos)? {
296 0 => Ok(MetadataValue::Null),
297 1 => Ok(MetadataValue::Bool(read_u8(data, pos)? != 0)),
298 2 => Ok(MetadataValue::Int(read_i64(data, pos)?)),
299 3 => Ok(MetadataValue::Float(read_f64(data, pos)?)),
300 4 => Ok(MetadataValue::String(read_string(data, pos)?)),
301 5 => Ok(MetadataValue::Bytes(read_bytes(data, pos)?)),
302 6 => {
303 let count = read_count(data, pos, 1)?;
304 let mut values = Vec::with_capacity(count);
305 for _ in 0..count {
306 values.push(read_metadata_value(data, pos, depth + 1)?);
307 }
308 Ok(MetadataValue::Array(values))
309 }
310 7 => {
311 let count = read_count(data, pos, 5)?;
312 let mut values = std::collections::HashMap::with_capacity(count);
313 for _ in 0..count {
314 let key = read_string(data, pos)?;
315 let value = read_metadata_value(data, pos, depth + 1)?;
316 values.insert(key, value);
317 }
318 Ok(MetadataValue::Object(values))
319 }
320 8 => Ok(MetadataValue::Timestamp(read_u64(data, pos)?)),
321 9 => Ok(MetadataValue::Geo {
322 lat: read_f64(data, pos)?,
323 lon: read_f64(data, pos)?,
324 }),
325 10 => Ok(MetadataValue::Reference(read_ref_target(data, pos)?)),
326 11 => {
327 let count = read_count(data, pos, 13)?;
328 let mut targets = Vec::with_capacity(count);
329 for _ in 0..count {
330 targets.push(read_ref_target(data, pos)?);
331 }
332 Ok(MetadataValue::References(targets))
333 }
334 tag => Err(invalid(format!("unknown metadata value tag {tag}"))),
335 }
336}
337
338pub fn deserialize_entity_record_with_budget(
340 data: &[u8],
341 version: u32,
342 budget: &mut reddb_file::native_value::DecodeBudget,
343) -> Result<(UnifiedEntity, Option<Metadata>), super::error::StoreError> {
344 use super::error::StoreError;
345 let frame = reddb_file::decode_native_entity_record_frame(data).map_err(decode_error)?;
346 let (entity_bytes, metadata_bytes) =
347 frame.map_or((data, &[][..]), |frame| (frame.entity, frame.metadata));
348 let entity =
349 super::entity_codec::read_entity_binary_with_budget(entity_bytes, &mut 0, version, budget)
350 .map_err(decode_error)?;
351 let metadata = if metadata_bytes.is_empty() {
352 None
353 } else {
354 self::budget::admit(metadata_bytes, budget).map_err(decode_error)?;
355 Some(deserialize_metadata(metadata_bytes).map_err(StoreError::Serialization)?)
356 };
357 Ok((entity, metadata))
358}
359
360pub(super) fn decode_error(error: reddb_file::RdbFileError) -> super::error::StoreError {
361 match error {
362 reddb_file::RdbFileError::ReadLimitExceeded {
363 required_bytes,
364 limit_bytes,
365 } => super::error::StoreError::ReadLimitExceeded {
366 required_bytes,
367 limit_bytes,
368 },
369 other => super::error::StoreError::Serialization(other.to_string()),
370 }
371}