1use crate::json::{Map, Value as JsonValue};
28use crate::key_dictionary::KeyDictionary;
29use crate::types::Value;
30use crate::value_codec;
31
32pub fn decode_container_to_json(bytes: &[u8]) -> Option<JsonValue> {
35 if !bytes.starts_with(MAGIC) {
36 return None;
37 }
38 let fields = decode(bytes).ok()?;
39 let mut map = Map::new();
40 for (key, value) in fields {
41 let json = match &value {
42 Value::Integer(n) => JsonValue::Integer(*n),
43 other => other.to_json(),
44 };
45 map.insert(key, json);
46 }
47 Some(JsonValue::Object(map))
48}
49
50pub const MAGIC: &[u8; 4] = b"RDOC";
52
53pub const VERSION: u8 = 0x03;
55
56pub const VERSION_DICT: u8 = 0x04;
62
63const SUPERSEDED_VERSION: u8 = 0x01;
64const SUPERSEDED_VERSION_DICT: u8 = 0x02;
65
66const ENTRY_SIZE: usize = 6;
68
69const DICT_ENTRY_SIZE: usize = 9;
75
76const KEY_KIND_DICT: u8 = 0x00;
78
79const KEY_KIND_INLINE: u8 = 0x01;
81
82pub type DictEntry = (u8, u32, u32);
87
88#[derive(Debug, PartialEq)]
90pub enum DocBodyError {
91 TruncatedData,
93 BadMagic,
95 UnsupportedVersion(u8),
97 SupersededVersion(u8),
99 OffsetOutOfBounds,
101 InvalidFieldName,
103 FieldLimitExceeded,
105 UnknownKeyId(u32),
107 BadKeyKind(u8),
109 ValueCodecError(crate::types::ValueError),
111}
112
113impl From<crate::types::ValueError> for DocBodyError {
114 fn from(e: crate::types::ValueError) -> Self {
115 DocBodyError::ValueCodecError(e)
116 }
117}
118
119impl std::fmt::Display for DocBodyError {
120 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121 match self {
122 Self::TruncatedData => write!(f, "document body: truncated data"),
123 Self::BadMagic => write!(f, "document body: bad magic bytes (expected RDOC)"),
124 Self::UnsupportedVersion(v) => write!(f, "document body: unsupported version {v}"),
125 Self::SupersededVersion(v) => write!(
126 f,
127 "document body: format version {v} is superseded by version {VERSION} for the \
128 lossless-integer clean break (#1768). Pre-bump bodies stored numbers as f64, \
129 so they are refused rather than silently reinterpreted; re-insert the document \
130 to rewrite it in the current format."
131 ),
132 Self::OffsetOutOfBounds => write!(f, "document body: offset points outside buffer"),
133 Self::InvalidFieldName => write!(f, "document body: field name is not valid UTF-8"),
134 Self::FieldLimitExceeded => {
135 write!(f, "document body: field or key-length limit exceeded")
136 }
137 Self::UnknownKeyId(id) => {
138 write!(f, "document body: key-id {id} not found in dictionary")
139 }
140 Self::BadKeyKind(k) => write!(f, "document body: unrecognised key kind {k:#04x}"),
141 Self::ValueCodecError(e) => write!(f, "document body: value codec error: {e}"),
142 }
143 }
144}
145
146impl std::error::Error for DocBodyError {}
147
148pub fn encode(fields: &[(&str, &Value)], out: &mut Vec<u8>) -> Result<(), DocBodyError> {
156 let n = fields.len();
157 if n > u16::MAX as usize {
158 return Err(DocBodyError::FieldLimitExceeded);
159 }
160
161 let mut key_buf: Vec<u8> = Vec::new();
164 let mut val_buf: Vec<u8> = Vec::new();
165 let mut key_lens: Vec<u16> = Vec::with_capacity(n);
166 let mut val_starts: Vec<u32> = Vec::with_capacity(n);
167
168 for (key, value) in fields {
169 let kb = key.as_bytes();
170 if kb.len() > u16::MAX as usize {
171 return Err(DocBodyError::FieldLimitExceeded);
172 }
173 key_lens.push(kb.len() as u16);
174 key_buf.extend_from_slice(kb);
175
176 val_starts.push(val_buf.len() as u32); value_codec::encode(value, &mut val_buf);
178 }
179
180 let vals_abs_start = 4 + 1 + 2 + n * ENTRY_SIZE + key_buf.len();
183
184 out.extend_from_slice(MAGIC);
186 out.push(VERSION);
187 out.extend_from_slice(&(n as u16).to_le_bytes());
188
189 for i in 0..n {
191 let abs_val_offset = vals_abs_start + val_starts[i] as usize;
192 out.extend_from_slice(&key_lens[i].to_le_bytes());
193 out.extend_from_slice(&(abs_val_offset as u32).to_le_bytes());
194 }
195
196 out.extend_from_slice(&key_buf);
198 out.extend_from_slice(&val_buf);
199
200 Ok(())
201}
202
203pub fn decode(data: &[u8]) -> Result<Vec<(String, Value)>, DocBodyError> {
207 let (n, table) = parse_header(data)?;
208 let header_end = 7 + n * ENTRY_SIZE; let mut key_cursor = header_end;
210 let mut result = Vec::with_capacity(n);
211
212 for &(key_len, val_offset) in &table {
213 let key_end = key_cursor + key_len as usize;
215 if key_end > data.len() {
216 return Err(DocBodyError::OffsetOutOfBounds);
217 }
218 let key = std::str::from_utf8(&data[key_cursor..key_end])
219 .map_err(|_| DocBodyError::InvalidFieldName)?
220 .to_string();
221 key_cursor = key_end;
222
223 let value = decode_value_at_offset(data, val_offset)?;
225 result.push((key, value));
226 }
227
228 Ok(result)
229}
230
231pub fn field_names(data: &[u8]) -> Result<Vec<String>, DocBodyError> {
238 let (n, table) = parse_header(data)?;
239 let header_end = 7 + n * ENTRY_SIZE; let mut key_cursor = header_end;
241 let mut names = Vec::with_capacity(n);
242
243 for &(key_len, _val_offset) in &table {
244 let key_end = key_cursor + key_len as usize;
245 if key_end > data.len() {
246 return Err(DocBodyError::OffsetOutOfBounds);
247 }
248 let key = std::str::from_utf8(&data[key_cursor..key_end])
249 .map_err(|_| DocBodyError::InvalidFieldName)?
250 .to_string();
251 key_cursor = key_end;
252 names.push(key);
253 }
254
255 Ok(names)
256}
257
258pub fn read_field_by_name(data: &[u8], name: &str) -> Result<Option<Value>, DocBodyError> {
264 let (n, table) = parse_header(data)?;
265 let header_end = 7 + n * ENTRY_SIZE;
266 let mut key_cursor = header_end;
267
268 for &(key_len, val_offset) in &table {
269 let key_end = key_cursor + key_len as usize;
270 if key_end > data.len() {
271 return Err(DocBodyError::OffsetOutOfBounds);
272 }
273 let key_bytes = &data[key_cursor..key_end];
274 key_cursor = key_end;
275
276 if key_bytes == name.as_bytes() {
277 let value = decode_value_at_offset(data, val_offset)?;
278 return Ok(Some(value));
279 }
280 }
281
282 Ok(None)
283}
284
285pub fn decode_value_at_offset(data: &[u8], val_offset: u32) -> Result<Value, DocBodyError> {
291 let off = val_offset as usize;
292 if off >= data.len() {
293 return Err(DocBodyError::OffsetOutOfBounds);
294 }
295 let (value, _) = value_codec::decode(&data[off..])?;
296 Ok(value)
297}
298
299fn version_error(version: u8) -> DocBodyError {
300 if version == SUPERSEDED_VERSION || version == SUPERSEDED_VERSION_DICT {
301 DocBodyError::SupersededVersion(version)
302 } else {
303 DocBodyError::UnsupportedVersion(version)
304 }
305}
306
307pub fn parse_header(data: &[u8]) -> Result<(usize, Vec<(u16, u32)>), DocBodyError> {
315 if data.len() < 7 {
316 return Err(DocBodyError::TruncatedData);
317 }
318 if &data[0..4] != MAGIC.as_slice() {
319 return Err(DocBodyError::BadMagic);
320 }
321 if data[4] != VERSION {
322 return Err(version_error(data[4]));
323 }
324
325 let n = u16::from_le_bytes([data[5], data[6]]) as usize;
326 let table_end = 7 + n * ENTRY_SIZE;
327 if data.len() < table_end {
328 return Err(DocBodyError::TruncatedData);
329 }
330
331 let mut table = Vec::with_capacity(n);
332 for i in 0..n {
333 let base = 7 + i * ENTRY_SIZE;
334 let key_len = u16::from_le_bytes([data[base], data[base + 1]]);
335 let val_offset = u32::from_le_bytes([
336 data[base + 2],
337 data[base + 3],
338 data[base + 4],
339 data[base + 5],
340 ]);
341 table.push((key_len, val_offset));
342 }
343
344 Ok((n, table))
345}
346
347pub fn encode_with_dictionary(
360 fields: &[(&str, &Value)],
361 dict: &mut KeyDictionary,
362 classify: impl Fn(&str) -> bool,
363 out: &mut Vec<u8>,
364) -> Result<(), DocBodyError> {
365 let n = fields.len();
366 if n > u16::MAX as usize {
367 return Err(DocBodyError::FieldLimitExceeded);
368 }
369
370 let mut key_buf: Vec<u8> = Vec::new();
373 let mut val_buf: Vec<u8> = Vec::new();
374 let mut kinds: Vec<u8> = Vec::with_capacity(n);
375 let mut refs: Vec<u32> = Vec::with_capacity(n);
376 let mut val_starts: Vec<u32> = Vec::with_capacity(n);
377
378 for (key, value) in fields {
379 if classify(key) {
380 let id = dict.intern(key);
381 kinds.push(KEY_KIND_DICT);
382 refs.push(id);
383 } else {
384 let kb = key.as_bytes();
385 if kb.len() > u16::MAX as usize {
386 return Err(DocBodyError::FieldLimitExceeded);
387 }
388 kinds.push(KEY_KIND_INLINE);
389 refs.push(kb.len() as u32);
390 key_buf.extend_from_slice(kb);
391 }
392 val_starts.push(val_buf.len() as u32); value_codec::encode(value, &mut val_buf);
394 }
395
396 let vals_abs_start = 4 + 1 + 2 + n * DICT_ENTRY_SIZE + key_buf.len();
399
400 out.extend_from_slice(MAGIC);
401 out.push(VERSION_DICT);
402 out.extend_from_slice(&(n as u16).to_le_bytes());
403
404 for i in 0..n {
405 let abs_val_offset = vals_abs_start + val_starts[i] as usize;
406 out.push(kinds[i]);
407 out.extend_from_slice(&refs[i].to_le_bytes());
408 out.extend_from_slice(&(abs_val_offset as u32).to_le_bytes());
409 }
410
411 out.extend_from_slice(&key_buf);
412 out.extend_from_slice(&val_buf);
413
414 Ok(())
415}
416
417pub fn decode_with_dictionary(
424 data: &[u8],
425 dict: &KeyDictionary,
426) -> Result<Vec<(String, Value)>, DocBodyError> {
427 let (n, table) = parse_dict_header(data)?;
428 let header_end = 7 + n * DICT_ENTRY_SIZE; let mut key_cursor = header_end;
430 let mut result = Vec::with_capacity(n);
431
432 for &(kind, key_ref, val_offset) in &table {
433 let key = match kind {
434 KEY_KIND_DICT => dict
435 .name_of(key_ref)
436 .ok_or(DocBodyError::UnknownKeyId(key_ref))?
437 .to_string(),
438 KEY_KIND_INLINE => {
439 let key_end = key_cursor + key_ref as usize;
440 if key_end > data.len() {
441 return Err(DocBodyError::OffsetOutOfBounds);
442 }
443 let name = std::str::from_utf8(&data[key_cursor..key_end])
444 .map_err(|_| DocBodyError::InvalidFieldName)?
445 .to_string();
446 key_cursor = key_end;
447 name
448 }
449 other => return Err(DocBodyError::BadKeyKind(other)),
450 };
451
452 let value = decode_value_at_offset(data, val_offset)?;
453 result.push((key, value));
454 }
455
456 Ok(result)
457}
458
459pub fn parse_dict_header(data: &[u8]) -> Result<(usize, Vec<DictEntry>), DocBodyError> {
465 if data.len() < 7 {
466 return Err(DocBodyError::TruncatedData);
467 }
468 if &data[0..4] != MAGIC.as_slice() {
469 return Err(DocBodyError::BadMagic);
470 }
471 if data[4] != VERSION_DICT {
472 return Err(version_error(data[4]));
473 }
474
475 let n = u16::from_le_bytes([data[5], data[6]]) as usize;
476 let table_end = 7 + n * DICT_ENTRY_SIZE;
477 if data.len() < table_end {
478 return Err(DocBodyError::TruncatedData);
479 }
480
481 let mut table = Vec::with_capacity(n);
482 for i in 0..n {
483 let base = 7 + i * DICT_ENTRY_SIZE;
484 let kind = data[base];
485 let key_ref = u32::from_le_bytes([
486 data[base + 1],
487 data[base + 2],
488 data[base + 3],
489 data[base + 4],
490 ]);
491 let val_offset = u32::from_le_bytes([
492 data[base + 5],
493 data[base + 6],
494 data[base + 7],
495 data[base + 8],
496 ]);
497 table.push((kind, key_ref, val_offset));
498 }
499
500 Ok((n, table))
501}
502
503#[cfg(test)]
504mod tests {
505 use super::*;
506 use crate::types::Value;
507 use std::net::{IpAddr, Ipv4Addr};
508
509 fn round_trip(fields: &[(&str, Value)]) -> Vec<(String, Value)> {
510 let refs: Vec<(&str, &Value)> = fields.iter().map(|(k, v)| (*k, v)).collect();
511 let mut buf = Vec::new();
512 encode(&refs, &mut buf).expect("encode");
513 decode(&buf).expect("decode")
514 }
515
516 #[test]
517 fn empty_document_round_trips() {
518 let got = round_trip(&[]);
519 assert!(got.is_empty());
520 }
521
522 #[test]
523 fn single_field_round_trip() {
524 let fields = [("name", Value::text("Alice"))];
525 let got = round_trip(&fields);
526 assert_eq!(got.len(), 1);
527 assert_eq!(got[0].0, "name");
528 assert_eq!(got[0].1, Value::text("Alice"));
529 }
530
531 #[test]
532 fn multi_field_round_trip() {
533 let fields = [
534 ("id", Value::Integer(42)),
535 ("email", Value::Email("a@example.com".to_string())),
536 ("active", Value::Boolean(true)),
537 ("score", Value::Float(9.5)),
538 ];
539 let got = round_trip(&fields);
540 assert_eq!(got.len(), 4);
541 for (i, (k, v)) in fields.iter().enumerate() {
542 assert_eq!(got[i].0, *k);
543 assert_eq!(got[i].1, *v);
544 }
545 }
546
547 #[test]
549 fn rich_semantic_types_round_trip() {
550 let fields = [
551 ("email", Value::Email("user@example.com".to_string())),
552 ("ipv4", Value::Ipv4(0x7f000001)),
553 ("subnet", Value::Subnet(0x0a000000, 0xff000000)),
554 ("color", Value::Color([0xDE, 0xAD, 0xBE])),
555 ("phone", Value::Phone(5511999000000)),
556 ("semver", Value::Semver(1_002_003)),
557 ("uuid", Value::Uuid([0xAB; 16])),
558 (
559 "money",
560 Value::Money {
561 asset_code: "USD".to_string(),
562 minor_units: 9999,
563 scale: 2,
564 },
565 ),
566 ("geo", Value::GeoPoint(-23_550_520, -46_633_308)),
567 (
568 "ip_mixed",
569 Value::IpAddr(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1))),
570 ),
571 ("url", Value::Url("https://reddb.io".to_string())),
572 ("color_alpha", Value::ColorAlpha([1, 2, 3, 255])),
573 ("lang", Value::Lang2(*b"en")),
574 ("country", Value::Country3(*b"USA")),
575 ];
576 let got = round_trip(&fields);
577 assert_eq!(got.len(), fields.len());
578 for (i, (k, v)) in fields.iter().enumerate() {
579 assert_eq!(got[i].0, *k, "key mismatch at {i}");
580 assert_eq!(got[i].1, *v, "value mismatch for field {k}");
581 }
582 }
583
584 #[test]
586 fn read_field_by_name_skips_other_fields() {
587 let fields = [
588 ("a", Value::Integer(1)),
589 ("b", Value::text("hello")),
590 ("c", Value::Boolean(false)),
591 ];
592 let refs: Vec<(&str, &Value)> = fields.iter().map(|(k, v)| (*k, v)).collect();
593 let mut buf = Vec::new();
594 encode(&refs, &mut buf).expect("encode");
595
596 let v = read_field_by_name(&buf, "b")
597 .expect("lookup")
598 .expect("found");
599 assert_eq!(v, Value::text("hello"));
600
601 let missing = read_field_by_name(&buf, "z").expect("lookup");
602 assert!(missing.is_none());
603 }
604
605 #[test]
607 fn field_names_lists_keys_in_encode_order() {
608 let fields = [
609 ("name", Value::text("Alice")),
610 ("score", Value::Integer(30)),
611 ("active", Value::Boolean(true)),
612 ];
613 let refs: Vec<(&str, &Value)> = fields.iter().map(|(k, v)| (*k, v)).collect();
614 let mut buf = Vec::new();
615 encode(&refs, &mut buf).expect("encode");
616
617 let names = field_names(&buf).expect("field_names");
618 assert_eq!(names, vec!["name", "score", "active"]);
619
620 assert!(field_names(&[]).is_err());
621 }
622
623 #[test]
625 fn decode_value_at_offset_matches_full_decode() {
626 let fields = [("x", Value::Integer(100)), ("y", Value::Float(1.5))];
627 let refs: Vec<(&str, &Value)> = fields.iter().map(|(k, v)| (*k, v)).collect();
628 let mut buf = Vec::new();
629 encode(&refs, &mut buf).expect("encode");
630
631 let (n, table) = parse_header(&buf).expect("parse_header");
632 assert_eq!(n, 2);
633
634 for (i, (k, v)) in fields.iter().enumerate() {
635 let (_key_len, val_offset) = table[i];
636 let got = decode_value_at_offset(&buf, val_offset).expect("decode_at_offset");
637 assert_eq!(got, *v, "field {k} mismatch");
638 }
639 }
640
641 #[test]
642 fn rejects_bad_magic() {
643 let mut buf = vec![b'X', b'X', b'X', b'X', VERSION, 0, 0];
644 assert_eq!(decode(&buf), Err(DocBodyError::BadMagic));
645 buf[0..4].copy_from_slice(MAGIC);
646 buf[4] = 0x99;
647 assert_eq!(decode(&buf), Err(DocBodyError::UnsupportedVersion(0x99)));
648 }
649
650 #[test]
651 fn superseded_plain_version_is_rejected_didactically() {
652 let fields = [("id", Value::Integer(1))];
653 let refs: Vec<(&str, &Value)> = fields.iter().map(|(k, v)| (*k, v)).collect();
654 let mut buf = Vec::new();
655 encode(&refs, &mut buf).expect("encode");
656 assert_eq!(buf[4], VERSION);
657
658 buf[4] = SUPERSEDED_VERSION;
659 let err = decode(&buf).expect_err("pre-bump body must be refused");
660 assert_eq!(err, DocBodyError::SupersededVersion(SUPERSEDED_VERSION));
661
662 let msg = err.to_string();
663 assert!(msg.contains("superseded"), "{msg}");
664 assert!(msg.contains("#1768"), "{msg}");
665 assert!(msg.contains("re-insert"), "{msg}");
666 }
667
668 #[test]
669 fn superseded_dict_version_is_rejected_didactically() {
670 let fields = [("id", Value::Integer(1))];
671 let refs: Vec<(&str, &Value)> = fields.iter().map(|(k, v)| (*k, v)).collect();
672 let mut dict = KeyDictionary::new();
673 let mut buf = Vec::new();
674 encode_with_dictionary(&refs, &mut dict, |_| true, &mut buf).expect("encode");
675 assert_eq!(buf[4], VERSION_DICT);
676
677 buf[4] = SUPERSEDED_VERSION_DICT;
678 let err = decode_with_dictionary(&buf, &dict).expect_err("pre-bump body must be refused");
679 assert_eq!(
680 err,
681 DocBodyError::SupersededVersion(SUPERSEDED_VERSION_DICT)
682 );
683 }
684
685 #[test]
686 fn rejects_truncated_buffer() {
687 assert_eq!(decode(&[]), Err(DocBodyError::TruncatedData));
688 assert_eq!(
689 decode(&[b'R', b'D', b'O', b'C', 1, 0]),
690 Err(DocBodyError::TruncatedData)
691 );
692 }
693
694 #[test]
695 fn null_values_round_trip() {
696 let fields = [("nothing", Value::Null), ("something", Value::Integer(7))];
697 let got = round_trip(&fields);
698 assert_eq!(got[0].1, Value::Null);
699 assert_eq!(got[1].1, Value::Integer(7));
700 }
701
702 #[test]
703 fn array_value_round_trip() {
704 let fields = [(
705 "tags",
706 Value::Array(vec![Value::text("a"), Value::text("b"), Value::Integer(3)]),
707 )];
708 let got = round_trip(&fields);
709 assert_eq!(
710 got[0].1,
711 Value::Array(vec![Value::text("a"), Value::text("b"), Value::Integer(3)])
712 );
713 }
714
715 #[test]
717 fn offset_table_offsets_are_valid() {
718 let fields = [("k", Value::Boolean(true))];
719 let refs: Vec<(&str, &Value)> = fields.iter().map(|(k, v)| (*k, v)).collect();
720 let mut buf = Vec::new();
721 encode(&refs, &mut buf).expect("encode");
722
723 let (n, table) = parse_header(&buf).expect("header");
724 assert_eq!(n, 1);
725 let (_klen, val_offset) = table[0];
726 let (v, _) = value_codec::decode(&buf[val_offset as usize..]).expect("decode at offset");
728 assert_eq!(v, Value::Boolean(true));
729 }
730
731 fn all_common(_: &str) -> bool {
735 true
736 }
737
738 #[test]
741 fn common_keys_are_interned_as_key_ids() {
742 let fields = [
743 ("id", Value::Integer(1)),
744 ("name", Value::text("Alice")),
745 ("active", Value::Boolean(true)),
746 ];
747 let refs: Vec<(&str, &Value)> = fields.iter().map(|(k, v)| (*k, v)).collect();
748 let mut dict = KeyDictionary::new();
749 let mut buf = Vec::new();
750 encode_with_dictionary(&refs, &mut dict, all_common, &mut buf).expect("encode");
751
752 assert_eq!(dict.len(), 3);
754 assert_eq!(dict.id_of("id"), Some(0));
755 assert_eq!(dict.id_of("name"), Some(1));
756 assert_eq!(dict.id_of("active"), Some(2));
757
758 let (n, table) = parse_dict_header(&buf).expect("header");
760 assert_eq!(n, 3);
761 for (kind, _key_ref, _off) in &table {
762 assert_eq!(*kind, KEY_KIND_DICT);
763 }
764 assert!(!buf.windows(4).any(|w| w == b"name"));
766 }
767
768 #[test]
771 fn new_common_key_appends_to_dictionary() {
772 let mut dict = KeyDictionary::new();
773
774 let doc1 = [("id", Value::Integer(1)), ("name", Value::text("Alice"))];
775 let r1: Vec<(&str, &Value)> = doc1.iter().map(|(k, v)| (*k, v)).collect();
776 let mut b1 = Vec::new();
777 encode_with_dictionary(&r1, &mut dict, all_common, &mut b1).expect("encode 1");
778 assert_eq!(dict.len(), 2);
779
780 let doc2 = [
782 ("id", Value::Integer(2)),
783 ("name", Value::text("Bob")),
784 ("email", Value::Email("bob@example.com".to_string())),
785 ];
786 let r2: Vec<(&str, &Value)> = doc2.iter().map(|(k, v)| (*k, v)).collect();
787 let mut b2 = Vec::new();
788 encode_with_dictionary(&r2, &mut dict, all_common, &mut b2).expect("encode 2");
789
790 assert_eq!(dict.len(), 3);
792 assert_eq!(dict.id_of("id"), Some(0));
793 assert_eq!(dict.id_of("name"), Some(1));
794 assert_eq!(dict.id_of("email"), Some(2));
795
796 let g1 = decode_with_dictionary(&b1, &dict).expect("decode 1");
798 assert_eq!(g1[1], ("name".to_string(), Value::text("Alice")));
799 let g2 = decode_with_dictionary(&b2, &dict).expect("decode 2");
800 assert_eq!(
801 g2[2],
802 (
803 "email".to_string(),
804 Value::Email("bob@example.com".to_string())
805 )
806 );
807 }
808
809 #[test]
811 fn rare_key_stays_inline_and_out_of_dictionary() {
812 let common = |k: &str| k == "id" || k == "name";
814 let fields = [
815 ("id", Value::Integer(7)),
816 ("name", Value::text("Carol")),
817 ("x9f3_one_off_attr", Value::text("rare-value")),
818 ];
819 let refs: Vec<(&str, &Value)> = fields.iter().map(|(k, v)| (*k, v)).collect();
820 let mut dict = KeyDictionary::new();
821 let mut buf = Vec::new();
822 encode_with_dictionary(&refs, &mut dict, common, &mut buf).expect("encode");
823
824 assert_eq!(dict.len(), 2);
826 assert_eq!(dict.id_of("x9f3_one_off_attr"), None);
827
828 let (_n, table) = parse_dict_header(&buf).expect("header");
830 assert_eq!(table[0].0, KEY_KIND_DICT);
831 assert_eq!(table[1].0, KEY_KIND_DICT);
832 assert_eq!(table[2].0, KEY_KIND_INLINE);
833 assert!(buf
834 .windows("x9f3_one_off_attr".len())
835 .any(|w| w == b"x9f3_one_off_attr"));
836
837 let got = decode_with_dictionary(&buf, &dict).expect("decode");
839 assert_eq!(
840 got[2],
841 ("x9f3_one_off_attr".to_string(), Value::text("rare-value"))
842 );
843 }
844
845 #[test]
848 fn mixed_dictionary_and_inline_round_trip() {
849 let common = |k: &str| matches!(k, "id" | "email" | "active");
850 let fields = [
851 ("id", Value::Integer(42)),
852 ("email", Value::Email("a@example.com".to_string())),
853 ("active", Value::Boolean(true)),
854 ("one_off_color", Value::Color([0xDE, 0xAD, 0xBE])),
855 ("rare_geo", Value::GeoPoint(-23_550_520, -46_633_308)),
856 ];
857 let refs: Vec<(&str, &Value)> = fields.iter().map(|(k, v)| (*k, v)).collect();
858 let mut dict = KeyDictionary::new();
859 let mut buf = Vec::new();
860 encode_with_dictionary(&refs, &mut dict, common, &mut buf).expect("encode");
861
862 let mut dict_bytes = Vec::new();
864 dict.encode(&mut dict_bytes);
865 let reloaded = KeyDictionary::decode(&dict_bytes).expect("reload dict");
866
867 let got = decode_with_dictionary(&buf, &reloaded).expect("decode");
868 assert_eq!(got.len(), fields.len());
869 for (i, (k, v)) in fields.iter().enumerate() {
870 assert_eq!(got[i].0, *k, "key mismatch at {i}");
871 assert_eq!(got[i].1, *v, "value mismatch for field {k}");
872 }
873 }
874
875 #[test]
876 fn empty_dictionary_document_round_trips() {
877 let mut dict = KeyDictionary::new();
878 let mut buf = Vec::new();
879 encode_with_dictionary(&[], &mut dict, all_common, &mut buf).expect("encode");
880 let got = decode_with_dictionary(&buf, &dict).expect("decode");
881 assert!(got.is_empty());
882 assert!(dict.is_empty());
883 }
884
885 #[test]
888 fn unknown_key_id_is_rejected() {
889 let fields = [("only_key", Value::Integer(1))];
890 let refs: Vec<(&str, &Value)> = fields.iter().map(|(k, v)| (*k, v)).collect();
891 let mut dict = KeyDictionary::new();
892 let mut buf = Vec::new();
893 encode_with_dictionary(&refs, &mut dict, all_common, &mut buf).expect("encode");
894
895 let empty = KeyDictionary::new();
896 assert_eq!(
897 decode_with_dictionary(&buf, &empty),
898 Err(DocBodyError::UnknownKeyId(0))
899 );
900 }
901
902 #[test]
905 fn v1_and_v2_versions_do_not_alias() {
906 let fields = [("k", Value::Integer(1))];
907 let refs: Vec<(&str, &Value)> = fields.iter().map(|(k, v)| (*k, v)).collect();
908
909 let mut v1 = Vec::new();
910 encode(&refs, &mut v1).expect("v1");
911 assert_eq!(v1[4], VERSION);
912
913 let mut dict = KeyDictionary::new();
914 let mut v2 = Vec::new();
915 encode_with_dictionary(&refs, &mut dict, all_common, &mut v2).expect("v2");
916 assert_eq!(v2[4], VERSION_DICT);
917
918 assert_eq!(
920 decode(&v2),
921 Err(DocBodyError::UnsupportedVersion(VERSION_DICT))
922 );
923 assert_eq!(
924 parse_dict_header(&v1),
925 Err(DocBodyError::UnsupportedVersion(VERSION))
926 );
927 }
928}