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_bytes(buf: &mut Vec<u8>, value: &[u8]) {
94 reddb_file::encode_native_len_prefixed_bytes(buf, value);
95}
96
97fn write_ref_target(buf: &mut Vec<u8>, target: &super::metadata::RefTarget) {
98 use super::metadata::RefTarget;
99
100 match target {
101 RefTarget::TableRow { table, row_id } => {
102 buf.push(0);
103 write_string(buf, table);
104 buf.extend_from_slice(&row_id.to_le_bytes());
105 }
106 RefTarget::Node {
107 collection,
108 node_id,
109 } => {
110 buf.push(1);
111 write_string(buf, collection);
112 buf.extend_from_slice(&node_id.raw().to_le_bytes());
113 }
114 RefTarget::Edge {
115 collection,
116 edge_id,
117 } => {
118 buf.push(2);
119 write_string(buf, collection);
120 buf.extend_from_slice(&edge_id.raw().to_le_bytes());
121 }
122 RefTarget::Vector {
123 collection,
124 vector_id,
125 } => {
126 buf.push(3);
127 write_string(buf, collection);
128 buf.extend_from_slice(&vector_id.raw().to_le_bytes());
129 }
130 RefTarget::Entity {
131 collection,
132 entity_id,
133 } => {
134 buf.push(4);
135 write_string(buf, collection);
136 buf.extend_from_slice(&entity_id.raw().to_le_bytes());
137 }
138 }
139}
140
141fn write_metadata_value(buf: &mut Vec<u8>, value: &MetadataValue) {
142 match value {
143 MetadataValue::Null => buf.push(0),
144 MetadataValue::Bool(v) => {
145 buf.push(1);
146 buf.push(u8::from(*v));
147 }
148 MetadataValue::Int(v) => {
149 buf.push(2);
150 buf.extend_from_slice(&v.to_le_bytes());
151 }
152 MetadataValue::Float(v) => {
153 buf.push(3);
154 buf.extend_from_slice(&v.to_le_bytes());
155 }
156 MetadataValue::String(v) => {
157 buf.push(4);
158 write_string(buf, v);
159 }
160 MetadataValue::Bytes(v) => {
161 buf.push(5);
162 write_bytes(buf, v);
163 }
164 MetadataValue::Array(values) => {
165 buf.push(6);
166 buf.extend_from_slice(&(values.len() as u32).to_le_bytes());
167 for value in values {
168 write_metadata_value(buf, value);
169 }
170 }
171 MetadataValue::Object(values) => {
172 buf.push(7);
173 let mut entries: Vec<_> = values.iter().collect();
174 entries.sort_by_key(|(a, _)| *a);
175 buf.extend_from_slice(&(entries.len() as u32).to_le_bytes());
176 for (key, value) in entries {
177 write_string(buf, key);
178 write_metadata_value(buf, value);
179 }
180 }
181 MetadataValue::Timestamp(v) => {
182 buf.push(8);
183 buf.extend_from_slice(&v.to_le_bytes());
184 }
185 MetadataValue::Geo { lat, lon } => {
186 buf.push(9);
187 buf.extend_from_slice(&lat.to_le_bytes());
188 buf.extend_from_slice(&lon.to_le_bytes());
189 }
190 MetadataValue::Reference(target) => {
191 buf.push(10);
192 write_ref_target(buf, target);
193 }
194 MetadataValue::References(targets) => {
195 buf.push(11);
196 buf.extend_from_slice(&(targets.len() as u32).to_le_bytes());
197 for target in targets {
198 write_ref_target(buf, target);
199 }
200 }
201 }
202}
203
204fn read_exact_slice<'a>(data: &'a [u8], pos: &mut usize, len: usize) -> Result<&'a [u8], String> {
205 if *pos > data.len() || len > data.len() - *pos {
206 return Err(invalid("truncated metadata payload".to_string()));
207 }
208 let slice = &data[*pos..*pos + len];
209 *pos += len;
210 Ok(slice)
211}
212
213fn read_u32(data: &[u8], pos: &mut usize) -> Result<u32, String> {
214 let bytes = read_exact_slice(data, pos, 4)?;
215 let mut raw = [0u8; 4];
216 raw.copy_from_slice(bytes);
217 Ok(u32::from_le_bytes(raw))
218}
219
220fn read_u64(data: &[u8], pos: &mut usize) -> Result<u64, String> {
221 let bytes = read_exact_slice(data, pos, 8)?;
222 let mut raw = [0u8; 8];
223 raw.copy_from_slice(bytes);
224 Ok(u64::from_le_bytes(raw))
225}
226
227fn read_i64(data: &[u8], pos: &mut usize) -> Result<i64, String> {
228 let bytes = read_exact_slice(data, pos, 8)?;
229 let mut raw = [0u8; 8];
230 raw.copy_from_slice(bytes);
231 Ok(i64::from_le_bytes(raw))
232}
233
234fn read_f64(data: &[u8], pos: &mut usize) -> Result<f64, String> {
235 let bytes = read_exact_slice(data, pos, 8)?;
236 let mut raw = [0u8; 8];
237 raw.copy_from_slice(bytes);
238 Ok(f64::from_le_bytes(raw))
239}
240
241fn read_u8(data: &[u8], pos: &mut usize) -> Result<u8, String> {
242 let bytes = read_exact_slice(data, pos, 1)?;
243 Ok(bytes[0])
244}
245
246fn read_string(data: &[u8], pos: &mut usize) -> Result<String, String> {
247 reddb_file::decode_native_len_prefixed_string(data, pos).map_err(|err| invalid(err.to_string()))
248}
249
250fn read_bytes(data: &[u8], pos: &mut usize) -> Result<Vec<u8>, String> {
251 reddb_file::decode_native_len_prefixed_bytes(data, pos)
252 .map(|bytes| bytes.to_vec())
253 .map_err(|err| invalid(err.to_string()))
254}
255
256fn read_ref_target(data: &[u8], pos: &mut usize) -> Result<super::metadata::RefTarget, String> {
257 use super::metadata::RefTarget;
258
259 match read_u8(data, pos)? {
260 0 => Ok(RefTarget::TableRow {
261 table: read_string(data, pos)?,
262 row_id: read_u64(data, pos)?,
263 }),
264 1 => Ok(RefTarget::Node {
265 collection: read_string(data, pos)?,
266 node_id: EntityId::new(read_u64(data, pos)?),
267 }),
268 2 => Ok(RefTarget::Edge {
269 collection: read_string(data, pos)?,
270 edge_id: EntityId::new(read_u64(data, pos)?),
271 }),
272 3 => Ok(RefTarget::Vector {
273 collection: read_string(data, pos)?,
274 vector_id: EntityId::new(read_u64(data, pos)?),
275 }),
276 4 => Ok(RefTarget::Entity {
277 collection: read_string(data, pos)?,
278 entity_id: EntityId::new(read_u64(data, pos)?),
279 }),
280 tag => Err(invalid(format!("unknown metadata ref target tag {tag}"))),
281 }
282}
283
284fn read_metadata_value(data: &[u8], pos: &mut usize, depth: u32) -> Result<MetadataValue, String> {
285 if depth > reddb_rql::limits::JSON_LITERAL_MAX_DEPTH {
286 return Err(invalid("metadata nesting exceeds the maximum depth"));
287 }
288 match read_u8(data, pos)? {
289 0 => Ok(MetadataValue::Null),
290 1 => Ok(MetadataValue::Bool(read_u8(data, pos)? != 0)),
291 2 => Ok(MetadataValue::Int(read_i64(data, pos)?)),
292 3 => Ok(MetadataValue::Float(read_f64(data, pos)?)),
293 4 => Ok(MetadataValue::String(read_string(data, pos)?)),
294 5 => Ok(MetadataValue::Bytes(read_bytes(data, pos)?)),
295 6 => {
296 let count = read_count(data, pos, 1)?;
297 let mut values = Vec::with_capacity(count);
298 for _ in 0..count {
299 values.push(read_metadata_value(data, pos, depth + 1)?);
300 }
301 Ok(MetadataValue::Array(values))
302 }
303 7 => {
304 let count = read_count(data, pos, 5)?;
305 let mut values = std::collections::HashMap::with_capacity(count);
306 for _ in 0..count {
307 let key = read_string(data, pos)?;
308 let value = read_metadata_value(data, pos, depth + 1)?;
309 values.insert(key, value);
310 }
311 Ok(MetadataValue::Object(values))
312 }
313 8 => Ok(MetadataValue::Timestamp(read_u64(data, pos)?)),
314 9 => Ok(MetadataValue::Geo {
315 lat: read_f64(data, pos)?,
316 lon: read_f64(data, pos)?,
317 }),
318 10 => Ok(MetadataValue::Reference(read_ref_target(data, pos)?)),
319 11 => {
320 let count = read_count(data, pos, 13)?;
321 let mut targets = Vec::with_capacity(count);
322 for _ in 0..count {
323 targets.push(read_ref_target(data, pos)?);
324 }
325 Ok(MetadataValue::References(targets))
326 }
327 tag => Err(invalid(format!("unknown metadata value tag {tag}"))),
328 }
329}
330
331pub fn deserialize_entity_record_with_budget(
333 data: &[u8],
334 version: u32,
335 budget: &mut reddb_file::native_value::DecodeBudget,
336) -> Result<(UnifiedEntity, Option<Metadata>), super::error::StoreError> {
337 use super::error::StoreError;
338 let frame = reddb_file::decode_native_entity_record_frame(data).map_err(decode_error)?;
339 let (entity_bytes, metadata_bytes) =
340 frame.map_or((data, &[][..]), |frame| (frame.entity, frame.metadata));
341 let entity =
342 super::entity_codec::read_entity_binary_with_budget(entity_bytes, &mut 0, version, budget)
343 .map_err(decode_error)?;
344 let metadata = if metadata_bytes.is_empty() {
345 None
346 } else {
347 self::budget::admit(metadata_bytes, budget).map_err(decode_error)?;
348 Some(deserialize_metadata(metadata_bytes).map_err(StoreError::Serialization)?)
349 };
350 Ok((entity, metadata))
351}
352
353pub(super) fn decode_error(error: reddb_file::RdbFileError) -> super::error::StoreError {
354 match error {
355 reddb_file::RdbFileError::ReadLimitExceeded {
356 required_bytes,
357 limit_bytes,
358 } => super::error::StoreError::ReadLimitExceeded {
359 required_bytes,
360 limit_bytes,
361 },
362 other => super::error::StoreError::Serialization(other.to_string()),
363 }
364}