laminar_connectors/schema/json/
jsonb.rs1pub use laminar_core::serialization::jsonb_tags as tags;
22
23#[derive(Debug)]
28pub struct JsonbEncoder {
29 buf: Vec<u8>,
30}
31
32impl JsonbEncoder {
33 #[must_use]
35 pub fn new() -> Self {
36 Self {
37 buf: Vec::with_capacity(4096),
38 }
39 }
40
41 pub fn encode(&mut self, value: &serde_json::Value) -> Vec<u8> {
43 self.buf.clear();
44 self.encode_value(value);
45 self.buf.clone()
46 }
47
48 #[allow(clippy::cast_possible_truncation)]
49 fn encode_value(&mut self, value: &serde_json::Value) {
50 match value {
51 serde_json::Value::Null => self.buf.push(tags::NULL),
52 serde_json::Value::Bool(false) => self.buf.push(tags::BOOL_FALSE),
53 serde_json::Value::Bool(true) => self.buf.push(tags::BOOL_TRUE),
54 serde_json::Value::Number(n) => {
55 if let Some(i) = n.as_i64() {
56 self.buf.push(tags::INT64);
57 self.buf.extend_from_slice(&i.to_le_bytes());
58 } else if let Some(f) = n.as_f64() {
59 self.buf.push(tags::FLOAT64);
60 self.buf.extend_from_slice(&f.to_le_bytes());
61 }
62 }
63 serde_json::Value::String(s) => {
64 self.buf.push(tags::STRING);
65 self.buf.extend_from_slice(&(s.len() as u32).to_le_bytes());
66 self.buf.extend_from_slice(s.as_bytes());
67 }
68 serde_json::Value::Array(arr) => {
69 self.buf.push(tags::ARRAY);
70 self.buf
71 .extend_from_slice(&(arr.len() as u32).to_le_bytes());
72 let offset_table_pos = self.buf.len();
74 self.buf.resize(self.buf.len() + arr.len() * 4, 0);
75 let data_start = self.buf.len();
76 for (i, elem) in arr.iter().enumerate() {
77 let elem_offset = (self.buf.len() - data_start) as u32;
78 let entry_pos = offset_table_pos + i * 4;
79 self.buf[entry_pos..entry_pos + 4].copy_from_slice(&elem_offset.to_le_bytes());
80 self.encode_value(elem);
81 }
82 }
83 serde_json::Value::Object(obj) => {
84 self.buf.push(tags::OBJECT);
85 let mut keys: Vec<&String> = obj.keys().collect();
87 keys.sort();
88 self.buf
89 .extend_from_slice(&(keys.len() as u32).to_le_bytes());
90 let offset_table_pos = self.buf.len();
92 self.buf.resize(self.buf.len() + keys.len() * 8, 0);
93 let data_start = self.buf.len();
94
95 for (i, key) in keys.iter().enumerate() {
96 let key_offset = (self.buf.len() - data_start) as u32;
98 let entry_pos = offset_table_pos + i * 8;
99 self.buf[entry_pos..entry_pos + 4].copy_from_slice(&key_offset.to_le_bytes());
100 self.buf
102 .extend_from_slice(&(key.len() as u16).to_le_bytes());
103 self.buf.extend_from_slice(key.as_bytes());
104 let val_offset = (self.buf.len() - data_start) as u32;
106 self.buf[entry_pos + 4..entry_pos + 8]
107 .copy_from_slice(&val_offset.to_le_bytes());
108 self.encode_value(&obj[*key]);
110 }
111 }
112 }
113 }
114}
115
116impl Default for JsonbEncoder {
117 fn default() -> Self {
118 Self::new()
119 }
120}
121
122pub struct JsonbAccessor;
127
128impl JsonbAccessor {
129 #[inline]
136 #[must_use]
137 pub fn get_field<'a>(jsonb: &'a [u8], field_name: &str) -> Option<&'a [u8]> {
138 if jsonb.is_empty() || jsonb[0] != tags::OBJECT {
139 return None;
140 }
141
142 let field_count = u32::from_le_bytes(jsonb.get(1..5)?.try_into().ok()?) as usize;
143 if field_count == 0 {
144 return None;
145 }
146
147 let offset_table_start = 5;
148 let offset_table_end = offset_table_start + field_count * 8;
149 let data_start = offset_table_end;
150
151 let mut lo = 0usize;
153 let mut hi = field_count;
154 while lo < hi {
155 let mid = lo + (hi - lo) / 2;
156 let entry_offset = offset_table_start + mid * 8;
157 let key_off =
158 u32::from_le_bytes(jsonb.get(entry_offset..entry_offset + 4)?.try_into().ok()?)
159 as usize;
160
161 let key_abs = data_start + key_off;
162 let key_len =
163 u16::from_le_bytes(jsonb.get(key_abs..key_abs + 2)?.try_into().ok()?) as usize;
164 let key_bytes = jsonb.get(key_abs + 2..key_abs + 2 + key_len)?;
165 let key_str = std::str::from_utf8(key_bytes).ok()?;
166
167 match key_str.cmp(field_name) {
168 std::cmp::Ordering::Equal => {
169 let val_off = u32::from_le_bytes(
170 jsonb
171 .get(entry_offset + 4..entry_offset + 8)?
172 .try_into()
173 .ok()?,
174 ) as usize;
175 let val_abs = data_start + val_off;
176 return jsonb.get(val_abs..);
177 }
178 std::cmp::Ordering::Less => lo = mid + 1,
179 std::cmp::Ordering::Greater => hi = mid,
180 }
181 }
182 None
183 }
184
185 #[inline]
187 #[must_use]
188 pub fn is_null(jsonb_value: &[u8]) -> bool {
189 !jsonb_value.is_empty() && jsonb_value[0] == tags::NULL
190 }
191
192 #[inline]
194 #[must_use]
195 pub fn as_bool(jsonb_value: &[u8]) -> Option<bool> {
196 match *jsonb_value.first()? {
197 tags::BOOL_FALSE => Some(false),
198 tags::BOOL_TRUE => Some(true),
199 _ => None,
200 }
201 }
202
203 #[inline]
205 #[must_use]
206 pub fn as_i64(jsonb_value: &[u8]) -> Option<i64> {
207 if jsonb_value.first()? != &tags::INT64 {
208 return None;
209 }
210 Some(i64::from_le_bytes(jsonb_value.get(1..9)?.try_into().ok()?))
211 }
212
213 #[inline]
215 #[must_use]
216 pub fn as_f64(jsonb_value: &[u8]) -> Option<f64> {
217 if jsonb_value.first()? != &tags::FLOAT64 {
218 return None;
219 }
220 Some(f64::from_le_bytes(jsonb_value.get(1..9)?.try_into().ok()?))
221 }
222
223 #[inline]
225 #[must_use]
226 pub fn as_str(jsonb_value: &[u8]) -> Option<&str> {
227 if jsonb_value.first()? != &tags::STRING {
228 return None;
229 }
230 let len = u32::from_le_bytes(jsonb_value.get(1..5)?.try_into().ok()?) as usize;
231 std::str::from_utf8(jsonb_value.get(5..5 + len)?).ok()
232 }
233
234 #[inline]
236 #[must_use]
237 pub fn array_len(jsonb_value: &[u8]) -> Option<usize> {
238 if jsonb_value.first()? != &tags::ARRAY {
239 return None;
240 }
241 Some(u32::from_le_bytes(jsonb_value.get(1..5)?.try_into().ok()?) as usize)
242 }
243
244 #[inline]
246 #[must_use]
247 pub fn array_get(jsonb_value: &[u8], index: usize) -> Option<&[u8]> {
248 if jsonb_value.first()? != &tags::ARRAY {
249 return None;
250 }
251 let count = u32::from_le_bytes(jsonb_value.get(1..5)?.try_into().ok()?) as usize;
252 if index >= count {
253 return None;
254 }
255 let offset_table_start = 5;
256 let data_start = offset_table_start + count * 4;
257 let entry_pos = offset_table_start + index * 4;
258 let elem_off =
259 u32::from_le_bytes(jsonb_value.get(entry_pos..entry_pos + 4)?.try_into().ok()?)
260 as usize;
261 jsonb_value.get(data_start + elem_off..)
262 }
263
264 #[inline]
266 #[must_use]
267 pub fn object_len(jsonb_value: &[u8]) -> Option<usize> {
268 if jsonb_value.first()? != &tags::OBJECT {
269 return None;
270 }
271 Some(u32::from_le_bytes(jsonb_value.get(1..5)?.try_into().ok()?) as usize)
272 }
273}
274
275#[cfg(test)]
276mod tests {
277 use super::*;
278 use serde_json::json;
279
280 #[test]
281 fn test_encode_null() {
282 let mut enc = JsonbEncoder::new();
283 let bytes = enc.encode(&json!(null));
284 assert_eq!(bytes, vec![tags::NULL]);
285 }
286
287 #[test]
288 fn test_encode_bool() {
289 let mut enc = JsonbEncoder::new();
290 assert_eq!(enc.encode(&json!(false)), vec![tags::BOOL_FALSE]);
291 assert_eq!(enc.encode(&json!(true)), vec![tags::BOOL_TRUE]);
292 }
293
294 #[test]
295 fn test_encode_int64() {
296 let mut enc = JsonbEncoder::new();
297 let bytes = enc.encode(&json!(42));
298 assert_eq!(bytes[0], tags::INT64);
299 let val = i64::from_le_bytes(bytes[1..9].try_into().unwrap());
300 assert_eq!(val, 42);
301 }
302
303 #[test]
304 fn test_encode_float64() {
305 let mut enc = JsonbEncoder::new();
306 let bytes = enc.encode(&json!(3.14));
307 assert_eq!(bytes[0], tags::FLOAT64);
308 let val = f64::from_le_bytes(bytes[1..9].try_into().unwrap());
309 assert!((val - 3.14).abs() < f64::EPSILON);
310 }
311
312 #[test]
313 fn test_encode_string() {
314 let mut enc = JsonbEncoder::new();
315 let bytes = enc.encode(&json!("hello"));
316 assert_eq!(bytes[0], tags::STRING);
317 let len = u32::from_le_bytes(bytes[1..5].try_into().unwrap()) as usize;
318 assert_eq!(len, 5);
319 assert_eq!(&bytes[5..10], b"hello");
320 }
321
322 #[test]
323 fn test_accessor_null() {
324 let mut enc = JsonbEncoder::new();
325 let bytes = enc.encode(&json!(null));
326 assert!(JsonbAccessor::is_null(&bytes));
327 assert!(JsonbAccessor::as_bool(&bytes).is_none());
328 }
329
330 #[test]
331 fn test_accessor_bool() {
332 let mut enc = JsonbEncoder::new();
333 assert_eq!(
334 JsonbAccessor::as_bool(&enc.encode(&json!(true))),
335 Some(true)
336 );
337 assert_eq!(
338 JsonbAccessor::as_bool(&enc.encode(&json!(false))),
339 Some(false)
340 );
341 }
342
343 #[test]
344 fn test_accessor_i64() {
345 let mut enc = JsonbEncoder::new();
346 let bytes = enc.encode(&json!(-99));
347 assert_eq!(JsonbAccessor::as_i64(&bytes), Some(-99));
348 }
349
350 #[test]
351 fn test_accessor_f64() {
352 let mut enc = JsonbEncoder::new();
353 let bytes = enc.encode(&json!(2.718));
354 let val = JsonbAccessor::as_f64(&bytes).unwrap();
355 assert!((val - 2.718).abs() < f64::EPSILON);
356 }
357
358 #[test]
359 fn test_accessor_str() {
360 let mut enc = JsonbEncoder::new();
361 let bytes = enc.encode(&json!("world"));
362 assert_eq!(JsonbAccessor::as_str(&bytes), Some("world"));
363 }
364
365 #[test]
366 fn test_object_field_access() {
367 let mut enc = JsonbEncoder::new();
368 let bytes = enc.encode(&json!({"name": "Alice", "age": 30, "active": true}));
369
370 let name_val = JsonbAccessor::get_field(&bytes, "name").unwrap();
372 assert_eq!(JsonbAccessor::as_str(name_val), Some("Alice"));
373
374 let age_val = JsonbAccessor::get_field(&bytes, "age").unwrap();
375 assert_eq!(JsonbAccessor::as_i64(age_val), Some(30));
376
377 let active_val = JsonbAccessor::get_field(&bytes, "active").unwrap();
378 assert_eq!(JsonbAccessor::as_bool(active_val), Some(true));
379
380 assert!(JsonbAccessor::get_field(&bytes, "missing").is_none());
382 }
383
384 #[test]
385 fn test_object_empty() {
386 let mut enc = JsonbEncoder::new();
387 let bytes = enc.encode(&json!({}));
388 assert_eq!(JsonbAccessor::object_len(&bytes), Some(0));
389 assert!(JsonbAccessor::get_field(&bytes, "any").is_none());
390 }
391
392 #[test]
393 fn test_array_access() {
394 let mut enc = JsonbEncoder::new();
395 let bytes = enc.encode(&json!([10, 20, 30]));
396
397 assert_eq!(JsonbAccessor::array_len(&bytes), Some(3));
398
399 let elem0 = JsonbAccessor::array_get(&bytes, 0).unwrap();
400 assert_eq!(JsonbAccessor::as_i64(elem0), Some(10));
401
402 let elem2 = JsonbAccessor::array_get(&bytes, 2).unwrap();
403 assert_eq!(JsonbAccessor::as_i64(elem2), Some(30));
404
405 assert!(JsonbAccessor::array_get(&bytes, 5).is_none());
406 }
407
408 #[test]
409 fn test_nested_object() {
410 let mut enc = JsonbEncoder::new();
411 let bytes = enc.encode(&json!({"outer": {"inner": 42}}));
412
413 let outer = JsonbAccessor::get_field(&bytes, "outer").unwrap();
414 let inner = JsonbAccessor::get_field(outer, "inner").unwrap();
415 assert_eq!(JsonbAccessor::as_i64(inner), Some(42));
416 }
417
418 #[test]
419 fn test_nested_array_in_object() {
420 let mut enc = JsonbEncoder::new();
421 let bytes = enc.encode(&json!({"items": [1, 2, 3]}));
422
423 let items = JsonbAccessor::get_field(&bytes, "items").unwrap();
424 assert_eq!(JsonbAccessor::array_len(items), Some(3));
425 let elem1 = JsonbAccessor::array_get(items, 1).unwrap();
426 assert_eq!(JsonbAccessor::as_i64(elem1), Some(2));
427 }
428
429 #[test]
430 fn test_large_object() {
431 let mut enc = JsonbEncoder::new();
432 let mut obj = serde_json::Map::new();
433 for i in 0..100 {
434 obj.insert(format!("field_{i:03}"), json!(i));
435 }
436 let bytes = enc.encode(&serde_json::Value::Object(obj));
437
438 for i in 0..100 {
440 let key = format!("field_{i:03}");
441 let val = JsonbAccessor::get_field(&bytes, &key).unwrap();
442 assert_eq!(JsonbAccessor::as_i64(val), Some(i));
443 }
444 assert!(JsonbAccessor::get_field(&bytes, "nonexistent").is_none());
445 }
446
447 #[test]
448 fn test_unicode_keys() {
449 let mut enc = JsonbEncoder::new();
450 let bytes = enc.encode(&json!({"名前": "太郎", "年齢": 25}));
451
452 let name = JsonbAccessor::get_field(&bytes, "名前").unwrap();
453 assert_eq!(JsonbAccessor::as_str(name), Some("太郎"));
454
455 let age = JsonbAccessor::get_field(&bytes, "年齢").unwrap();
456 assert_eq!(JsonbAccessor::as_i64(age), Some(25));
457 }
458
459 #[test]
460 fn test_type_mismatch_returns_none() {
461 let mut enc = JsonbEncoder::new();
462 let bytes = enc.encode(&json!(42)); assert!(JsonbAccessor::as_str(&bytes).is_none());
464 assert!(JsonbAccessor::as_bool(&bytes).is_none());
465 assert!(JsonbAccessor::as_f64(&bytes).is_none());
466 }
467
468 #[test]
469 fn test_empty_slice() {
470 assert!(!JsonbAccessor::is_null(&[]));
472 assert!(JsonbAccessor::as_bool(&[]).is_none());
473 assert!(JsonbAccessor::as_i64(&[]).is_none());
474 assert!(JsonbAccessor::as_f64(&[]).is_none());
475 assert!(JsonbAccessor::as_str(&[]).is_none());
476 assert!(JsonbAccessor::get_field(&[], "x").is_none());
477 }
478}