1use std::cell::RefCell;
4use std::marker::PhantomData;
5use std::sync::OnceLock;
6
7use bytes::Bytes;
8use serde::Serialize;
9use serde_json::Value;
10
11use crate::{Compression, Result};
12
13pub(super) const MAX_DELTA_FRAMES: usize = 256;
18
19enum Baseline {
27 Parsed(Value),
29
30 Encoded {
33 bytes: Bytes,
34 parsed: OnceLock<Option<Value>>,
35 },
36}
37
38impl Baseline {
39 fn value(&self) -> Option<&Value> {
41 match self {
42 Self::Parsed(value) => Some(value),
43 Self::Encoded { bytes, parsed } => parsed.get_or_init(|| serde_json::from_slice(bytes).ok()).as_ref(),
46 }
47 }
48}
49
50#[derive(Debug, Clone)]
55#[non_exhaustive]
56pub struct Config {
57 pub delta_ratio: u32,
72
73 pub compression: Compression,
80}
81
82impl Config {
83 pub fn with_delta_ratio(mut self, delta_ratio: u32) -> Self {
85 self.delta_ratio = delta_ratio;
86 self
87 }
88}
89
90impl Default for Config {
91 fn default() -> Self {
92 Self {
93 delta_ratio: 8,
94 compression: Compression::None,
95 }
96 }
97}
98
99#[derive(Clone, Debug)]
101pub struct Encoded {
102 pub payload: Bytes,
104
105 pub keyframe: bool,
117}
118
119#[must_use = "the frame must be written and committed, or dropped to resynchronize the encoder"]
132pub struct Pending<'a, T> {
133 encoder: &'a mut Encoder<T>,
134 encoded: Encoded,
135 committed: bool,
136}
137
138impl<T> Pending<'_, T> {
139 pub fn commit(mut self) {
144 self.committed = true;
145 }
146}
147
148impl<T> std::ops::Deref for Pending<'_, T> {
149 type Target = Encoded;
150
151 fn deref(&self) -> &Encoded {
152 &self.encoded
153 }
154}
155
156impl<T> Drop for Pending<'_, T> {
157 fn drop(&mut self) {
158 if !self.committed {
159 self.encoder.reset();
160 }
161 }
162}
163
164pub struct Encoder<T> {
194 config: Config,
195
196 last: Option<Baseline>,
199
200 scratch: RefCell<crate::diff::Scratch>,
203
204 flate: Option<moq_flate::Encoder>,
206
207 delta_bytes: u64,
210
211 snapshot_len: u64,
214
215 group_frames: usize,
217
218 resync: bool,
223
224 _marker: PhantomData<fn(T)>,
225}
226
227impl<T> Encoder<T> {
228 pub fn new(config: Config) -> Self {
230 Self {
231 config,
232 last: None,
233 scratch: RefCell::new(crate::diff::Scratch::memoized()),
234 flate: None,
235 delta_bytes: 0,
236 snapshot_len: 0,
237 group_frames: 0,
238 resync: false,
239 _marker: PhantomData,
240 }
241 }
242
243 pub fn value(&self) -> Option<&Value> {
251 self.last.as_ref()?.value()
252 }
253
254 pub fn reset(&mut self) {
263 self.flate = None;
264 self.delta_bytes = 0;
265 self.snapshot_len = 0;
266 self.group_frames = 0;
267 self.resync = true;
268 }
269}
270
271impl<T: Serialize> Encoder<T> {
272 pub fn update(&mut self, value: &T) -> Result<Option<Pending<'_, T>>> {
278 Ok(self.encode(value)?.map(|encoded| Pending {
279 encoder: self,
280 encoded,
281 committed: false,
282 }))
283 }
284
285 fn encode(&mut self, value: &T) -> Result<Option<Encoded>> {
290 if self.resync {
294 return self.snapshot(value).map(Some);
295 }
296
297 if let Some(Baseline::Encoded { bytes, .. }) = self.last.as_ref() {
302 let bytes = bytes.clone();
303 let next = serde_json::to_vec(value)?;
304 if next.as_slice() == bytes.as_ref() {
305 return Ok(None);
306 }
307 return self.snapshot_encoded(next).map(Some);
308 }
309
310 let Some(Baseline::Parsed(last)) = self.last.as_ref() else {
312 return self.snapshot(value).map(Some);
313 };
314
315 let crate::diff::PatchBytes { patch, forced_snapshot } =
317 crate::diff::bytes(last, value, &self.scratch).map_err(crate::Error::Json)?;
318
319 if !forced_snapshot && patch.is_empty() {
321 self.scratch.get_mut().commit_memo();
322 return Ok(None);
323 }
324
325 if forced_snapshot || !self.delta_allowed() {
328 return self.snapshot(value).map(Some);
329 }
330
331 let bytes = Bytes::from(patch);
333
334 if self.config.compression.is_deflate() && bytes.len() as u64 > moq_flate::DEFAULT_MAX_FRAME_SIZE {
338 return Err(moq_flate::Error::TooLarge(moq_flate::DEFAULT_MAX_FRAME_SIZE).into());
339 }
340 let payload = match self.flate.as_mut() {
341 Some(flate) => flate.frame(&bytes),
342 None => bytes.clone(),
343 };
344
345 if self.snapshot_len + self.delta_bytes + payload.len() as u64 > moq_net::group::MAX_CACHE_BYTES {
355 return self.snapshot(value).map(Some);
356 }
357
358 self.delta_bytes += payload.len() as u64;
359 self.group_frames += 1;
360
361 let Some(Baseline::Parsed(last)) = self.last.as_mut() else {
365 unreachable!("a parsed snapshot precedes any delta")
366 };
367 crate::merge::apply_generated_bytes(last, &bytes)?;
368 self.scratch.get_mut().commit_memo();
369
370 Ok(Some(Encoded {
371 payload,
372 keyframe: false,
373 }))
374 }
375
376 fn delta_allowed(&self) -> bool {
384 let ratio = u64::from(self.config.delta_ratio);
385 ratio != 0
386 && self.group_frames > 0
387 && self.group_frames < MAX_DELTA_FRAMES
388 && self.delta_bytes <= ratio * self.snapshot_len
389 }
390
391 fn snapshot(&mut self, value: &T) -> Result<Encoded> {
393 let snapshot = serde_json::to_vec(value)?;
396 self.snapshot_encoded(snapshot)
397 }
398
399 fn snapshot_encoded(&mut self, snapshot: Vec<u8>) -> Result<Encoded> {
402 if self.config.compression.is_deflate() && snapshot.len() as u64 > moq_flate::DEFAULT_MAX_FRAME_SIZE {
406 return Err(moq_flate::Error::TooLarge(moq_flate::DEFAULT_MAX_FRAME_SIZE).into());
407 }
408
409 let snapshot = Bytes::from(snapshot);
423 let last = if self.config.delta_ratio == 0 {
424 Baseline::Encoded {
427 bytes: snapshot.clone(),
428 parsed: OnceLock::new(),
429 }
430 } else {
431 Baseline::Parsed(serde_json::from_slice(&snapshot)?)
432 };
433
434 let (payload, flate) = match self.config.compression {
437 Compression::Deflate => {
438 let mut flate = moq_flate::Encoder::new();
439 let payload = flate.frame(&snapshot);
440 (payload, Some(flate))
441 }
442 Compression::None => (snapshot, None),
443 };
444
445 self.snapshot_len = payload.len() as u64;
446 self.delta_bytes = 0;
447 self.group_frames = 1;
448 self.flate = flate;
449 self.last = Some(last);
450 self.resync = false;
451 self.scratch.get_mut().clear_memo();
453
454 Ok(Encoded {
455 payload,
456 keyframe: true,
457 })
458 }
459}
460
461#[cfg(test)]
462mod test {
463 use super::*;
464 use serde_json::json;
465
466 #[test]
467 fn duplicate_serialized_keys_are_refused() {
468 use serde::ser::SerializeMap;
469 struct Duplicate {
470 duplicate: bool,
471 }
472 impl Serialize for Duplicate {
473 fn serialize<S: serde::Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
474 let mut map = serializer.serialize_map(Some(2 + usize::from(self.duplicate)))?;
475 map.serialize_entry("a", &1)?;
476 map.serialize_entry("b", &2)?;
477 if self.duplicate {
478 map.serialize_entry("a", &3)?;
479 }
480 map.end()
481 }
482 }
483 let mut encoder = Encoder::<Duplicate>::new(Config::default());
484 encoder
485 .update(&Duplicate { duplicate: false })
486 .unwrap()
487 .unwrap()
488 .commit();
489 let err = encoder.encode(&Duplicate { duplicate: true }).unwrap_err();
490 assert!(err.to_string().contains("duplicate JSON object key"));
491 }
492
493 fn encode(config: Config, values: &[Value]) -> Vec<(bool, usize)> {
496 let mut encoder = Encoder::<Value>::new(config);
497 let mut out = Vec::new();
498 for value in values {
499 if let Some(frame) = encoder.update(value).unwrap() {
500 out.push((frame.keyframe, frame.payload.len()));
501 frame.commit();
502 }
503 }
504 out
505 }
506
507 fn commit(encoder: &mut Encoder<Value>, value: &Value) -> Option<Encoded> {
509 let frame = encoder.update(value).unwrap()?;
510 let encoded = Encoded {
511 payload: frame.payload.clone(),
512 keyframe: frame.keyframe,
513 };
514 frame.commit();
515 Some(encoded)
516 }
517
518 #[test]
519 fn first_update_is_a_keyframe() {
520 let frames = encode(Config::default(), &[json!({ "a": 1 })]);
521 assert_eq!(frames.len(), 1);
522 assert!(frames[0].0);
523 }
524
525 #[test]
526 fn unchanged_value_encodes_nothing() {
527 let frames = encode(Config::default(), &[json!({ "a": 1 }), json!({ "a": 1 })]);
528 assert_eq!(frames.len(), 1);
529 }
530
531 #[test]
532 fn changes_ride_as_deltas() {
533 let frames = encode(
534 Config::default().with_delta_ratio(100),
535 &[
536 json!({ "a": 1, "b": 1 }),
537 json!({ "a": 1, "b": 2 }),
538 json!({ "a": 1, "b": 3 }),
539 ],
540 );
541 assert_eq!(frames.iter().map(|f| f.0).collect::<Vec<_>>(), vec![true, false, false]);
542 }
543
544 #[test]
545 fn deltas_off_forces_a_keyframe_per_change() {
546 let frames = encode(
547 Config::default().with_delta_ratio(0),
548 &[json!({ "a": 1 }), json!({ "a": 2 })],
549 );
550 assert_eq!(frames.iter().map(|f| f.0).collect::<Vec<_>>(), vec![true, true]);
551 }
552
553 #[test]
557 fn deltas_off_still_skips_an_unchanged_value() {
558 let frames = encode(
559 Config::default().with_delta_ratio(0),
560 &[json!({ "a": 1 }), json!({ "a": 1 }), json!({ "a": 1 })],
561 );
562 assert_eq!(frames.len(), 1);
563 }
564
565 #[test]
568 fn deltas_off_detects_a_change_under_the_same_keys() {
569 let frames = encode(
570 Config::default().with_delta_ratio(0),
571 &[json!({ "a": 1, "b": 2 }), json!({ "a": 1, "b": 3 })],
572 );
573 assert_eq!(frames.len(), 2);
574 }
575
576 #[test]
580 fn deltas_off_still_exposes_the_value() {
581 let mut encoder = Encoder::<Value>::new(Config::default().with_delta_ratio(0));
582 assert_eq!(encoder.value(), None);
583
584 commit(&mut encoder, &json!({ "a": 1, "b": 2 })).unwrap();
585 assert_eq!(encoder.value(), Some(&json!({ "a": 1, "b": 2 })));
586
587 commit(&mut encoder, &json!({ "a": 1, "b": 3 })).unwrap();
588 assert_eq!(encoder.value(), Some(&json!({ "a": 1, "b": 3 })));
589 }
590
591 #[test]
594 fn deltas_off_while_compressing_keeps_the_plaintext_baseline() {
595 let mut config = Config::default().with_delta_ratio(0);
596 config.compression = Compression::Deflate;
597
598 let mut encoder = Encoder::<Value>::new(config);
599 commit(&mut encoder, &json!({ "a": 1 })).unwrap();
600 assert_eq!(encoder.value(), Some(&json!({ "a": 1 })));
601 assert!(commit(&mut encoder, &json!({ "a": 1 })).is_none());
602 }
603
604 #[test]
608 fn a_null_field_forces_a_keyframe() {
609 let frames = encode(
610 Config::default().with_delta_ratio(100),
611 &[json!({ "a": 1, "b": 1 }), json!({ "a": 1, "b": null })],
612 );
613 assert_eq!(frames.iter().map(|f| f.0).collect::<Vec<_>>(), vec![true, true]);
614 }
615
616 #[test]
618 fn a_non_object_root_forces_a_keyframe() {
619 let frames = encode(
620 Config::default().with_delta_ratio(100),
621 &[json!({ "a": 1 }), json!([1, 2, 3])],
622 );
623 assert_eq!(frames.iter().map(|f| f.0).collect::<Vec<_>>(), vec![true, true]);
624 }
625
626 #[test]
627 fn frame_cap_forces_a_keyframe() {
628 let values: Vec<Value> = (0..=MAX_DELTA_FRAMES).map(|n| json!({ "n": n })).collect();
629 let frames = encode(Config::default().with_delta_ratio(1_000_000), &values);
630
631 assert_eq!(frames.len(), MAX_DELTA_FRAMES + 1);
633 assert_eq!(frames.iter().filter(|f| f.0).count(), 2);
634 assert!(frames[MAX_DELTA_FRAMES].0);
635 }
636
637 #[test]
640 fn reset_forces_the_next_update_to_be_a_keyframe() {
641 let mut encoder = Encoder::<Value>::new(Config::default().with_delta_ratio(100));
642 assert!(commit(&mut encoder, &json!({ "a": 1 })).unwrap().keyframe);
643 assert!(!commit(&mut encoder, &json!({ "a": 2 })).unwrap().keyframe);
644
645 encoder.reset();
646 assert!(commit(&mut encoder, &json!({ "a": 3 })).unwrap().keyframe);
647 }
648
649 #[test]
653 fn an_uncommitted_frame_resynchronizes_the_encoder() {
654 let mut encoder = Encoder::<Value>::new(Config::default().with_delta_ratio(100));
655 commit(&mut encoder, &json!({ "a": 1 })).unwrap();
656
657 commit(&mut encoder, &json!({ "a": 2 })).unwrap();
659
660 drop(encoder.update(&json!({ "a": 3 })).unwrap().expect("a delta"));
662
663 let recovered = commit(&mut encoder, &json!({ "a": 4 })).expect("a resynchronizing snapshot");
666 assert!(recovered.keyframe);
667 assert_eq!(
668 serde_json::from_slice::<Value>(&recovered.payload).unwrap(),
669 json!({ "a": 4 }),
670 "the snapshot carries the whole value, not a patch"
671 );
672 }
673
674 #[test]
677 fn an_uncommitted_first_frame_is_reencoded() {
678 let mut encoder = Encoder::<Value>::new(Config::default());
679 drop(encoder.update(&json!({ "a": 1 })).unwrap().expect("a snapshot"));
680
681 let retried = commit(&mut encoder, &json!({ "a": 1 })).expect("the same value, re-encoded");
682 assert!(retried.keyframe);
683 }
684
685 #[test]
688 fn reset_republishes_an_unchanged_value() {
689 let mut encoder = Encoder::<Value>::new(Config::default());
690 commit(&mut encoder, &json!({ "a": 1 })).unwrap();
691
692 encoder.reset();
693 assert!(
694 commit(&mut encoder, &json!({ "a": 1 }))
695 .expect("a fresh snapshot")
696 .keyframe
697 );
698 }
699
700 #[test]
701 fn compressed_deltas_reuse_the_group_window() {
702 let phrase = "Media over QUIC delivers real-time latency at massive scale";
703 let frames = encode(
704 Config {
705 delta_ratio: 100,
706 compression: Compression::Deflate,
707 },
708 &[json!({ "note": phrase }), json!({ "note": phrase, "echo": phrase })],
709 );
710
711 let raw = serde_json::to_vec(&json!({ "echo": phrase })).unwrap().len();
713 assert_eq!(frames.len(), 2);
714 assert!(
715 frames[1].1 < raw / 2,
716 "windowed delta {} vs raw patch {raw}",
717 frames[1].1
718 );
719 }
720
721 struct Ticking(std::cell::Cell<u32>);
724
725 impl serde::Serialize for Ticking {
726 fn serialize<S: serde::Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
727 use serde::ser::SerializeMap;
728
729 let n = self.0.get();
730 self.0.set(n + 1);
731
732 let mut map = serializer.serialize_map(Some(1))?;
733 map.serialize_entry("n", &n)?;
734 map.end()
735 }
736 }
737
738 #[test]
742 fn a_snapshot_serializes_its_value_once() {
743 let value = Ticking(std::cell::Cell::new(0));
744 let mut encoder = Encoder::<Ticking>::new(Config::default());
745 let payload = {
746 let frame = encoder.update(&value).unwrap().expect("a snapshot");
747 let payload = frame.payload.clone();
748 frame.commit();
749 payload
750 };
751
752 assert_eq!(value.0.get(), 1, "the value should be serialized exactly once");
753
754 let emitted: Value = serde_json::from_slice(&payload).unwrap();
755 assert_eq!(emitted, json!({ "n": 0 }));
756 assert_eq!(encoder.value(), Some(&emitted), "the baseline must be what was emitted");
757 }
758
759 #[test]
763 fn a_delta_serializes_each_entry_once() {
764 let value = std::collections::BTreeMap::from([("row", Ticking(std::cell::Cell::new(0)))]);
765 let mut encoder = Encoder::new(Config::default().with_delta_ratio(100));
766 encoder.update(&value).unwrap().expect("a snapshot").commit();
767
768 let frame = encoder.update(&value).unwrap().expect("a delta");
769 assert!(!frame.keyframe);
770 let emitted: Value = serde_json::from_slice(&frame.payload).unwrap();
771 frame.commit();
772
773 assert_eq!(
774 value["row"].0.get(),
775 2,
776 "each update should serialize the entry exactly once"
777 );
778 assert_eq!(emitted, json!({ "row": { "n": 1 } }));
779 assert_eq!(encoder.value(), Some(&emitted), "the baseline must be what was emitted");
780 }
781
782 #[test]
786 fn a_repeated_nested_key_is_refused_through_the_memo() {
787 use serde::ser::SerializeMap;
788
789 struct Doc {
791 repeat: bool,
792 }
793 struct Row<'a>(&'a Doc);
794 struct Repeat;
795
796 impl Serialize for Repeat {
797 fn serialize<S: serde::Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
798 let mut map = serializer.serialize_map(Some(2))?;
799 map.serialize_entry("x", &1)?;
800 map.serialize_entry("x", &2)?;
801 map.end()
802 }
803 }
804 impl Serialize for Row<'_> {
805 fn serialize<S: serde::Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
806 let mut map = serializer.serialize_map(Some(1))?;
807 match self.0.repeat {
808 true => map.serialize_entry("o", &Repeat)?,
809 false => map.serialize_entry("o", &1)?,
810 }
811 map.end()
812 }
813 }
814 impl Serialize for Doc {
815 fn serialize<S: serde::Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
816 let mut map = serializer.serialize_map(Some(1))?;
817 map.serialize_entry("row", &Row(self))?;
818 map.end()
819 }
820 }
821
822 let config = Config::default().with_delta_ratio(100);
823 let (plain, repeat) = (Doc { repeat: false }, Doc { repeat: true });
824
825 let mut encoder = Encoder::<Doc>::new(config.clone());
827 encoder.update(&plain).unwrap().expect("a snapshot").commit();
828 let err = encoder.encode(&repeat).unwrap_err();
829 assert!(err.to_string().contains("duplicate JSON object key"), "{err}");
830
831 let mut encoder = Encoder::<Doc>::new(config);
833 encoder.update(&plain).unwrap().expect("a snapshot").commit();
834 assert!(encoder.update(&plain).unwrap().is_none(), "unchanged, now memoized");
835 let err = encoder.encode(&repeat).unwrap_err();
836 assert!(err.to_string().contains("duplicate JSON object key"), "{err}");
837 }
838
839 struct Rows(Vec<(String, Value)>);
841
842 impl Serialize for Rows {
843 fn serialize<S: serde::Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
844 serializer.collect_map(self.0.iter().map(|(key, value)| (key, value)))
845 }
846 }
847
848 struct Rng(u64);
850
851 impl Rng {
852 fn below(&mut self, n: u64) -> u64 {
853 self.0 ^= self.0 << 13;
854 self.0 ^= self.0 >> 7;
855 self.0 ^= self.0 << 17;
856 self.0 % n
857 }
858
859 fn row(&mut self) -> Value {
862 let strings = ["plain", "q\"uote", "back\\slash", "},{\"x\":1", "null", "a:b,c"];
863 let mut row = serde_json::Map::new();
864 row.insert(
865 "n".into(),
866 match self.below(30) {
867 0 => Value::Null,
868 n => json!(n % 4),
869 },
870 );
871 if self.below(4) > 0 {
872 row.insert("s".into(), json!(strings[self.below(strings.len() as u64) as usize]));
873 }
874 let mut nested = serde_json::Map::new();
875 nested.insert("a".into(), json!(self.below(3)));
876 if self.below(3) == 0 {
877 nested.insert("b".into(), json!([self.below(2), null]));
878 }
879 if self.below(40) == 0 {
880 nested.insert("c".into(), Value::Null);
881 }
882 row.insert("o".into(), Value::Object(nested));
883 row.insert(
884 "t".into(),
885 match self.below(5) {
886 0 => json!({ "k": self.below(2) }),
887 1 => json!({}),
888 2 => json!([{ "k": null }]),
889 3 => json!(1.5 + self.below(2) as f64),
890 _ => json!("t"),
891 },
892 );
893 if self.below(60) == 0 {
894 row.insert("z".into(), Value::Null);
895 }
896 Value::Object(row)
897 }
898 }
899
900 #[test]
904 fn memo_matches_the_value_diff() {
905 for (seed, compression) in [
906 (1, Compression::None),
907 (2, Compression::Deflate),
908 (3, Compression::None),
909 ] {
910 let mut config = Config::default().with_delta_ratio(2);
911 config.compression = compression;
912 let mut memoized = Encoder::<Rows>::new(config.clone());
913 let mut plain = Encoder::<Rows>::new(config);
914 plain.scratch = RefCell::new(crate::diff::Scratch::default());
915
916 let mut rng = Rng(0x9E37_79B9_7F4A_7C15 ^ seed);
917 let mut rows: Vec<(String, Value)> = (0..40).map(|i| (format!("row-{i:03}"), rng.row())).collect();
918 let mut emitted = 0;
919 for tick in 0..400 {
920 for row in rows.iter_mut() {
921 if rng.below(4) == 0 {
922 row.1 = rng.row();
923 }
924 }
925 if rng.below(3) == 0 {
926 let index = rng.below(rows.len() as u64) as usize;
927 rows.remove(index);
928 }
929 if rng.below(3) == 0 {
930 rows.push((format!("row-{:03}", 40 + rng.below(40)), rng.row()));
931 }
932 rows.sort_by(|a, b| a.0.cmp(&b.0));
933 rows.dedup_by(|a, b| a.0 == b.0);
934 if seed == 3 && rng.below(10) == 0 {
936 let (a, b) = (
937 rng.below(rows.len() as u64) as usize,
938 rng.below(rows.len() as u64) as usize,
939 );
940 rows.swap(a, b);
941 }
942
943 let value = Rows(rows.clone());
944 let want = plain.update(&value).unwrap().map(|frame| {
945 let encoded = (*frame).clone();
946 frame.commit();
947 encoded
948 });
949 let got = memoized.update(&value).unwrap().map(|frame| {
950 let encoded = (*frame).clone();
951 frame.commit();
952 encoded
953 });
954 match (want, got) {
955 (None, None) => {}
956 (Some(want), Some(got)) => {
957 assert_eq!(got.keyframe, want.keyframe, "seed {seed} tick {tick}: keyframe");
958 assert_eq!(got.payload, want.payload, "seed {seed} tick {tick}: payload");
959 emitted += usize::from(!got.keyframe);
960 }
961 (want, got) => panic!("seed {seed} tick {tick}: {want:?} vs {got:?}"),
962 }
963 assert_eq!(memoized.value(), plain.value(), "seed {seed} tick {tick}: baseline");
964 }
965 assert!(emitted > 100, "seed {seed}: only {emitted} deltas exercised the memo");
966 }
967 }
968
969 #[test]
970 fn value_tracks_the_baseline() {
971 let mut encoder = Encoder::<Value>::new(Config::default().with_delta_ratio(100));
972 assert_eq!(encoder.value(), None);
973
974 commit(&mut encoder, &json!({ "a": 1, "b": 1 }));
975 commit(&mut encoder, &json!({ "a": 1, "b": 2 }));
976
977 assert_eq!(encoder.value(), Some(&json!({ "a": 1, "b": 2 })));
979 }
980}