use std::marker::PhantomData;
use bytes::Bytes;
use serde::Serialize;
use serde_json::Value;
use crate::{Diff, Result, diff};
pub(super) const MAX_DELTA_FRAMES: usize = 256;
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct ProducerConfig {
pub delta_ratio: u32,
pub compression: bool,
}
impl ProducerConfig {
pub fn with_delta_ratio(mut self, delta_ratio: u32) -> Self {
self.delta_ratio = delta_ratio;
self
}
pub fn with_compression(mut self, compression: bool) -> Self {
self.compression = compression;
self
}
}
impl Default for ProducerConfig {
fn default() -> Self {
Self {
delta_ratio: 8,
compression: false,
}
}
}
#[derive(Clone, Debug)]
pub struct Encoded {
pub payload: Bytes,
pub keyframe: bool,
}
#[must_use = "the frame must be written and committed, or dropped to resynchronize the encoder"]
pub struct Pending<'a, T> {
encoder: &'a mut Encoder<T>,
encoded: Encoded,
committed: bool,
}
impl<T> Pending<'_, T> {
pub fn commit(mut self) {
self.committed = true;
}
}
impl<T> std::ops::Deref for Pending<'_, T> {
type Target = Encoded;
fn deref(&self) -> &Encoded {
&self.encoded
}
}
impl<T> Drop for Pending<'_, T> {
fn drop(&mut self) {
if !self.committed {
self.encoder.reset();
}
}
}
pub struct Encoder<T> {
config: ProducerConfig,
last: Option<Value>,
flate: Option<moq_flate::Encoder>,
delta_bytes: u64,
snapshot_len: u64,
group_frames: usize,
resync: bool,
_marker: PhantomData<fn(T)>,
}
impl<T> Encoder<T> {
pub fn new(config: ProducerConfig) -> Self {
Self {
config,
last: None,
flate: None,
delta_bytes: 0,
snapshot_len: 0,
group_frames: 0,
resync: false,
_marker: PhantomData,
}
}
pub fn value(&self) -> Option<&Value> {
self.last.as_ref()
}
pub fn reset(&mut self) {
self.flate = None;
self.delta_bytes = 0;
self.snapshot_len = 0;
self.group_frames = 0;
self.resync = true;
}
}
impl<T: Serialize> Encoder<T> {
pub fn update(&mut self, value: &T) -> Result<Option<Pending<'_, T>>> {
Ok(self.encode(value)?.map(|encoded| Pending {
encoder: self,
encoded,
committed: false,
}))
}
fn encode(&mut self, value: &T) -> Result<Option<Encoded>> {
if self.resync {
return self.snapshot(value).map(Some);
}
let Some(last) = self.last.as_ref() else {
return self.snapshot(value).map(Some);
};
let Diff { patch, forced_snapshot } = diff(last, value);
if !forced_snapshot && patch.as_object().is_some_and(serde_json::Map::is_empty) {
return Ok(None);
}
if forced_snapshot || !self.delta_allowed() {
return self.snapshot(value).map(Some);
}
let bytes = serde_json::to_vec(&patch)?;
let payload = match self.flate.as_mut() {
Some(flate) => flate.frame(&bytes),
None => Bytes::from(bytes),
};
self.delta_bytes += payload.len() as u64;
self.group_frames += 1;
json_patch::merge(self.last.as_mut().expect("a snapshot precedes any delta"), &patch);
Ok(Some(Encoded {
payload,
keyframe: false,
}))
}
fn delta_allowed(&self) -> bool {
let ratio = u64::from(self.config.delta_ratio);
ratio != 0
&& self.group_frames > 0
&& self.group_frames < MAX_DELTA_FRAMES
&& self.delta_bytes <= ratio * self.snapshot_len
}
fn snapshot(&mut self, value: &T) -> Result<Encoded> {
let snapshot = serde_json::to_vec(value)?;
let last = serde_json::from_slice(&snapshot)?;
let (payload, flate) = match self.config.compression {
true => {
let mut flate = moq_flate::Encoder::new();
let payload = flate.frame(&snapshot);
(payload, Some(flate))
}
false => (Bytes::from(snapshot), None),
};
self.snapshot_len = payload.len() as u64;
self.delta_bytes = 0;
self.group_frames = 1;
self.flate = flate;
self.last = Some(last);
self.resync = false;
Ok(Encoded {
payload,
keyframe: true,
})
}
}
#[cfg(test)]
mod test {
use super::*;
use serde_json::json;
fn encode(config: ProducerConfig, values: &[Value]) -> Vec<(bool, usize)> {
let mut encoder = Encoder::<Value>::new(config);
let mut out = Vec::new();
for value in values {
if let Some(frame) = encoder.update(value).unwrap() {
out.push((frame.keyframe, frame.payload.len()));
frame.commit();
}
}
out
}
fn commit(encoder: &mut Encoder<Value>, value: &Value) -> Option<Encoded> {
let frame = encoder.update(value).unwrap()?;
let encoded = Encoded {
payload: frame.payload.clone(),
keyframe: frame.keyframe,
};
frame.commit();
Some(encoded)
}
#[test]
fn first_update_is_a_keyframe() {
let frames = encode(ProducerConfig::default(), &[json!({ "a": 1 })]);
assert_eq!(frames.len(), 1);
assert!(frames[0].0);
}
#[test]
fn unchanged_value_encodes_nothing() {
let frames = encode(ProducerConfig::default(), &[json!({ "a": 1 }), json!({ "a": 1 })]);
assert_eq!(frames.len(), 1);
}
#[test]
fn changes_ride_as_deltas() {
let frames = encode(
ProducerConfig::default().with_delta_ratio(100),
&[
json!({ "a": 1, "b": 1 }),
json!({ "a": 1, "b": 2 }),
json!({ "a": 1, "b": 3 }),
],
);
assert_eq!(frames.iter().map(|f| f.0).collect::<Vec<_>>(), vec![true, false, false]);
}
#[test]
fn deltas_off_forces_a_keyframe_per_change() {
let frames = encode(
ProducerConfig::default().with_delta_ratio(0),
&[json!({ "a": 1 }), json!({ "a": 2 })],
);
assert_eq!(frames.iter().map(|f| f.0).collect::<Vec<_>>(), vec![true, true]);
}
#[test]
fn a_null_field_forces_a_keyframe() {
let frames = encode(
ProducerConfig::default().with_delta_ratio(100),
&[json!({ "a": 1, "b": 1 }), json!({ "a": 1, "b": null })],
);
assert_eq!(frames.iter().map(|f| f.0).collect::<Vec<_>>(), vec![true, true]);
}
#[test]
fn a_non_object_root_forces_a_keyframe() {
let frames = encode(
ProducerConfig::default().with_delta_ratio(100),
&[json!({ "a": 1 }), json!([1, 2, 3])],
);
assert_eq!(frames.iter().map(|f| f.0).collect::<Vec<_>>(), vec![true, true]);
}
#[test]
fn frame_cap_forces_a_keyframe() {
let values: Vec<Value> = (0..=MAX_DELTA_FRAMES).map(|n| json!({ "n": n })).collect();
let frames = encode(ProducerConfig::default().with_delta_ratio(1_000_000), &values);
assert_eq!(frames.len(), MAX_DELTA_FRAMES + 1);
assert_eq!(frames.iter().filter(|f| f.0).count(), 2);
assert!(frames[MAX_DELTA_FRAMES].0);
}
#[test]
fn reset_forces_the_next_update_to_be_a_keyframe() {
let mut encoder = Encoder::<Value>::new(ProducerConfig::default().with_delta_ratio(100));
assert!(commit(&mut encoder, &json!({ "a": 1 })).unwrap().keyframe);
assert!(!commit(&mut encoder, &json!({ "a": 2 })).unwrap().keyframe);
encoder.reset();
assert!(commit(&mut encoder, &json!({ "a": 3 })).unwrap().keyframe);
}
#[test]
fn an_uncommitted_frame_resynchronizes_the_encoder() {
let mut encoder = Encoder::<Value>::new(ProducerConfig::default().with_delta_ratio(100));
commit(&mut encoder, &json!({ "a": 1 })).unwrap();
commit(&mut encoder, &json!({ "a": 2 })).unwrap();
drop(encoder.update(&json!({ "a": 3 })).unwrap().expect("a delta"));
let recovered = commit(&mut encoder, &json!({ "a": 4 })).expect("a resynchronizing snapshot");
assert!(recovered.keyframe);
assert_eq!(
serde_json::from_slice::<Value>(&recovered.payload).unwrap(),
json!({ "a": 4 }),
"the snapshot carries the whole value, not a patch"
);
}
#[test]
fn an_uncommitted_first_frame_is_reencoded() {
let mut encoder = Encoder::<Value>::new(ProducerConfig::default());
drop(encoder.update(&json!({ "a": 1 })).unwrap().expect("a snapshot"));
let retried = commit(&mut encoder, &json!({ "a": 1 })).expect("the same value, re-encoded");
assert!(retried.keyframe);
}
#[test]
fn reset_republishes_an_unchanged_value() {
let mut encoder = Encoder::<Value>::new(ProducerConfig::default());
commit(&mut encoder, &json!({ "a": 1 })).unwrap();
encoder.reset();
assert!(
commit(&mut encoder, &json!({ "a": 1 }))
.expect("a fresh snapshot")
.keyframe
);
}
#[test]
fn compressed_deltas_reuse_the_group_window() {
let phrase = "Media over QUIC delivers real-time latency at massive scale";
let frames = encode(
ProducerConfig::default().with_delta_ratio(100).with_compression(true),
&[json!({ "note": phrase }), json!({ "note": phrase, "echo": phrase })],
);
let raw = serde_json::to_vec(&json!({ "echo": phrase })).unwrap().len();
assert_eq!(frames.len(), 2);
assert!(
frames[1].1 < raw / 2,
"windowed delta {} vs raw patch {raw}",
frames[1].1
);
}
struct Ticking(std::cell::Cell<u32>);
impl serde::Serialize for Ticking {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
use serde::ser::SerializeMap;
let n = self.0.get();
self.0.set(n + 1);
let mut map = serializer.serialize_map(Some(1))?;
map.serialize_entry("n", &n)?;
map.end()
}
}
#[test]
fn a_snapshot_serializes_its_value_once() {
let value = Ticking(std::cell::Cell::new(0));
let mut encoder = Encoder::<Ticking>::new(ProducerConfig::default());
let payload = {
let frame = encoder.update(&value).unwrap().expect("a snapshot");
let payload = frame.payload.clone();
frame.commit();
payload
};
assert_eq!(value.0.get(), 1, "the value should be serialized exactly once");
let emitted: Value = serde_json::from_slice(&payload).unwrap();
assert_eq!(emitted, json!({ "n": 0 }));
assert_eq!(encoder.value(), Some(&emitted), "the baseline must be what was emitted");
}
#[test]
fn value_tracks_the_baseline() {
let mut encoder = Encoder::<Value>::new(ProducerConfig::default().with_delta_ratio(100));
assert_eq!(encoder.value(), None);
commit(&mut encoder, &json!({ "a": 1, "b": 1 }));
commit(&mut encoder, &json!({ "a": 1, "b": 2 }));
assert_eq!(encoder.value(), Some(&json!({ "a": 1, "b": 2 })));
}
}