Skip to main content

moq_json/
diff.rs

1//! Generate an [RFC 7396](https://www.rfc-editor.org/rfc/rfc7396.html) JSON Merge Patch directly
2//! from a value, diffing it against the previously published value as it is serialized.
3//!
4//! A serde [`Serializer`] walks the new value and compares each field against the corresponding
5//! node of the old [`Value`], so unchanged scalars and subtrees cost only a comparison (no
6//! allocation) and only changed fields are written into reusable patch buffers. This avoids
7//! materializing a `Value` tree for either the new value or the patch on the encoding path.
8//!
9//! A long-lived encoder also memoizes the root's entries as bytes (see [`Memo`]), so an entry that
10//! did not change is skipped without walking its part of the old `Value` at all.
11
12use std::cell::{Cell, RefCell};
13
14use serde::Serialize;
15use serde::ser::{Impossible, SerializeMap, SerializeSeq, SerializeStruct, Serializer};
16use serde_json::{Map, Value};
17
18/// The result of diffing a value into an RFC 7396 merge patch.
19pub struct Diff {
20	/// A merge patch that transforms the old value into the new one.
21	pub patch: Value,
22
23	/// Set when the change can't be faithfully expressed as a merge patch, so the caller should
24	/// publish a full snapshot instead. This happens when a value is set to JSON null, which merge
25	/// patch reads as a key deletion, or when the root is not an object. Arrays are fine: merge patch
26	/// replaces them wholesale, which is still typically smaller than a full snapshot.
27	pub forced_snapshot: bool,
28}
29
30/// Generate an RFC 7396 merge patch transforming `old` into `new`.
31///
32/// Only object roots produce a recursive patch; any other root forces a snapshot. A merge patch that
33/// would delete a key it shouldn't (a value genuinely set to null) also forces a snapshot.
34pub fn diff<T: Serialize>(old: &Value, new: &T) -> Diff {
35	let result = bytes(old, new, &RefCell::new(Scratch::default())).unwrap_or(PatchBytes {
36		patch: Vec::new(),
37		forced_snapshot: true,
38	});
39	Diff {
40		patch: if result.patch.is_empty() {
41			Value::Object(Map::new())
42		} else {
43			serde_json::from_slice(&result.patch).expect("the diff serializer emits JSON")
44		},
45		forced_snapshot: result.forced_snapshot,
46	}
47}
48
49#[derive(Default)]
50pub(crate) struct Scratch {
51	seen: Vec<Vec<String>>,
52	pending: Vec<String>,
53	bytes: Vec<Vec<u8>>,
54	last_capacity: usize,
55	/// `None` for a one-off diff, where nothing would ever read it back.
56	memo: Option<Memo>,
57}
58
59impl Scratch {
60	/// Scratch for a long-lived encoder, which memoizes the root's entries across diffs.
61	pub(crate) fn memoized() -> Self {
62		Self {
63			memo: Some(Memo::default()),
64			..Self::default()
65		}
66	}
67
68	fn buffer(&mut self, depth: usize) -> &mut Vec<u8> {
69		if self.bytes.len() <= depth {
70			self.bytes.resize_with(depth + 1, Vec::new);
71		}
72		&mut self.bytes[depth]
73	}
74
75	/// The baseline now matches the value last diffed, so its root entries are what the next diff
76	/// compares against.
77	pub(crate) fn commit_memo(&mut self) {
78		if let Some(memo) = self.memo.as_mut() {
79			std::mem::swap(&mut memo.current, &mut memo.next);
80		}
81	}
82
83	/// The baseline was reseeded from somewhere else, so the memoized entries no longer describe it.
84	pub(crate) fn clear_memo(&mut self) {
85		if let Some(memo) = self.memo.as_mut() {
86			memo.current.clear();
87			memo.next.clear();
88		}
89	}
90}
91
92/// The root object's entries as serialized by the last committed diff.
93///
94/// A diff walks every field of the new value against the baseline `Value`, and on a large table
95/// that walk is a pointer chase through a tree far bigger than the cache, paid for every row
96/// whether it changed or not. Serializing an entry is sequential and cheap by comparison, so an
97/// entry whose bytes match the last diff's is known unchanged without touching the baseline.
98///
99/// Only the root is memoized: that is where a large keyed table (a stats frame) keeps its rows.
100///
101/// Bytes and `Value` equality agree except where `serde_json`'s text loses information: an `f32`
102/// never equals its parsed widening, so the value diff resends it every time and the memo does not,
103/// and `-0.0` equals `0.0`, so the memo resends that change and the value diff does not. Both are
104/// no-ops to a consumer.
105#[derive(Default)]
106///
107/// No memoized entry repeats a key at any depth, which the value diff would refuse. [`lockstep`]
108/// only walks keys that match an entry's last bytes position by position, so it inherits that, and
109/// everything else it writes is checked.
110pub(crate) struct Memo {
111	/// Matches the baseline: each entry is that key's value in the baseline, serialized.
112	current: Entries,
113	/// Filled by the diff in progress, and swapped in once its frame is committed.
114	next: Entries,
115	/// Reused by the repeated-key check.
116	check: RefCell<crate::merge::CheckScratch>,
117}
118
119#[derive(Default)]
120struct Entries {
121	/// Each entry's raw key followed by its serialized value, back to back.
122	bytes: Vec<u8>,
123	/// Each entry's key end and value end in `bytes`, in the order the entries were serialized.
124	ends: Vec<(usize, usize)>,
125	/// Set once a key fails to ascend. Until then a lookup can skip past entries the new value dropped.
126	unsorted: bool,
127}
128
129impl Entries {
130	fn clear(&mut self) {
131		self.bytes.clear();
132		self.ends.clear();
133		self.unsorted = false;
134	}
135
136	fn key(&self, index: usize) -> &[u8] {
137		let start = index.checked_sub(1).map_or(0, |prev| self.ends[prev].1);
138		&self.bytes[start..self.ends[index].0]
139	}
140
141	fn value(&self, index: usize) -> &[u8] {
142		&self.bytes[self.ends[index].0..self.ends[index].1]
143	}
144
145	/// Find `key`, starting at `cursor` since entries usually arrive in the same order as last time.
146	/// A miss only costs the full diff, so this never searches the whole table.
147	fn find(&self, key: &[u8], cursor: &mut usize) -> Option<usize> {
148		// The same position, or one past it when the entry before this one was dropped.
149		for index in [*cursor, *cursor + 1] {
150			if index < self.ends.len() && self.key(index) == key {
151				*cursor = index + 1;
152				return Some(index);
153			}
154		}
155		if self.unsorted {
156			return None;
157		}
158		// Ascending keys: skip the dropped ones. A key that is new stops short, so the entry after
159		// it still lines up with the cursor.
160		while *cursor < self.ends.len() && self.key(*cursor) < key {
161			*cursor += 1;
162		}
163		if *cursor < self.ends.len() && self.key(*cursor) == key {
164			*cursor += 1;
165			return Some(*cursor - 1);
166		}
167		None
168	}
169}
170
171pub(crate) struct PatchBytes {
172	pub patch: Vec<u8>,
173	pub forced_snapshot: bool,
174}
175
176/// Diff directly into JSON bytes, reusing child buffers across updates.
177///
178/// With a memoized scratch this also records the new value's root entries, which the caller commits
179/// with [`Scratch::commit_memo`] once `old` has been brought up to `new`.
180pub(crate) fn bytes<T: Serialize>(old: &Value, new: &T, scratch: &RefCell<Scratch>) -> Result<PatchBytes, String> {
181	if let Some(memo) = scratch.borrow_mut().memo.as_mut() {
182		memo.next.clear();
183	}
184	let forced = Cell::new(false);
185	let node = new.serialize(Differ {
186		baseline: old,
187		present: true,
188		forced: &forced,
189		scratch,
190		depth: 0,
191	});
192	match node {
193		Ok(Node::Same) => Ok(PatchBytes {
194			patch: Vec::new(),
195			forced_snapshot: forced.get(),
196		}),
197		Ok(Node::Diff) => {
198			let mut scratch = scratch.borrow_mut();
199			let patch = std::mem::take(scratch.buffer(0));
200			scratch.last_capacity = patch.capacity();
201			let forced_snapshot = forced.get() || !patch.starts_with(b"{") || !old.is_object();
202			Ok(PatchBytes { patch, forced_snapshot })
203		}
204		Err(err) => Err(err.0),
205	}
206}
207
208/// One node's verdict from the diffing serializer.
209enum Node {
210	/// Equal to the baseline; nothing to emit.
211	Same,
212	/// Differs; the patch bytes are in this node's scratch buffer.
213	Diff,
214}
215
216const NULL: Value = Value::Null;
217
218/// Serializer that diffs `T` against `baseline` and yields a merge patch. `forced` is set if a
219/// genuine null is emitted (merge patch can't represent it, so the caller must snapshot).
220#[derive(Copy, Clone)]
221struct Differ<'a> {
222	baseline: &'a Value,
223	present: bool,
224	forced: &'a Cell<bool>,
225	scratch: &'a RefCell<Scratch>,
226	depth: usize,
227}
228
229impl<'a> Differ<'a> {
230	/// The baseline child for `key` and whether the baseline actually had that key (a missing key
231	/// means the field is an addition, which `MapDiff` uses to keep deletion detection cheap).
232	fn child(&self, key: &str) -> (Differ<'a>, bool) {
233		let (baseline, existed) = match self.baseline {
234			Value::Object(m) => match m.get(key) {
235				Some(value) => (value, true),
236				None => (&NULL, false),
237			},
238			_ => (&NULL, false),
239		};
240		(
241			Differ {
242				baseline,
243				present: existed,
244				forced: self.forced,
245				scratch: self.scratch,
246				depth: self.depth + 1,
247			},
248			existed,
249		)
250	}
251
252	/// Serialize a changed scalar into its reusable buffer.
253	fn scalar<T: Serialize + ?Sized>(self, value: &T, equal: bool, null: bool) -> Result<Node, Error> {
254		if equal {
255			return Ok(Node::Same);
256		}
257		if null {
258			self.forced.set(true);
259		}
260		let mut scratch = self.scratch.borrow_mut();
261		let capacity = scratch.last_capacity;
262		let bytes = scratch.buffer(self.depth);
263		bytes.clear();
264		if self.depth == 0 {
265			bytes.reserve(capacity);
266		}
267		serde_json::to_writer(bytes, value).map_err(|err| Error(err.to_string()))?;
268		Ok(Node::Diff)
269	}
270}
271
272/// Diff two serializations of one root entry into its merge patch, when they share a shape.
273///
274/// Both sides come from `serde_json`'s compact writer, so equal bytes mean equal values and a key's
275/// bytes are canonical. Objects with the same keys in the same order recurse, and any other value
276/// that differs is replaced by its new bytes, which is what the value diff emits for it. `None`
277/// when the keys differ, or a replacement object holds a null the value diff would have to judge,
278/// leaving the entry to the value diff.
279fn lockstep(
280	old: &[u8],
281	new: &[u8],
282	patch: &mut Vec<u8>,
283	forced: &mut bool,
284	check: &RefCell<crate::merge::CheckScratch>,
285) -> Option<()> {
286	if old.first() != Some(&b'{') || new.first() != Some(&b'{') {
287		return replace(new, patch, forced, check);
288	}
289	// An empty object on either side means the keys changed.
290	if old.get(1) == Some(&b'}') || new.get(1) == Some(&b'}') {
291		return None;
292	}
293	let (mut i, mut j) = (1, 1);
294	let mut first = true;
295	loop {
296		let (old_key, new_key) = (skip(old, i)?, skip(new, j)?);
297		if old[i..old_key] != new[j..new_key] {
298			return None;
299		}
300		// Past the key's colon to its value.
301		let (old_end, new_end) = (skip(old, old_key + 1)?, skip(new, new_key + 1)?);
302		let (old_value, new_value) = (&old[old_key + 1..old_end], &new[new_key + 1..new_end]);
303		if old_value != new_value {
304			patch.push(if first { b'{' } else { b',' });
305			first = false;
306			patch.extend_from_slice(&new[j..=new_key]);
307			lockstep(old_value, new_value, patch, forced, check)?;
308		}
309		match (old.get(old_end), new.get(new_end)) {
310			(Some(b','), Some(b',')) => (i, j) = (old_end + 1, new_end + 1),
311			(Some(b'}'), Some(b'}')) => break,
312			_ => return None,
313		}
314	}
315	debug_assert!(!first, "differing bytes under the same keys leave a changed value");
316	patch.push(b'}');
317	Some(())
318}
319
320/// Write `new` wholesale, as the value diff does for a value that changed type or is not an object.
321///
322/// A null is a deletion in a merge patch, so the value diff forces a snapshot for one written as an
323/// object value. A top-level null is caught here, and a replacement object holding one anywhere is
324/// left to the value diff rather than parsed (inside an array, or a string, it would be data). So is
325/// a replacement that repeats a key, for the value diff to refuse.
326fn replace(
327	new: &[u8],
328	patch: &mut Vec<u8>,
329	forced: &mut bool,
330	check: &RefCell<crate::merge::CheckScratch>,
331) -> Option<()> {
332	match new {
333		b"null" => *forced = true,
334		[b'{', ..] if new.windows(4).any(|window| window == b"null") => return None,
335		[b'{' | b'[', ..] if crate::merge::check(new, check).is_err() => return None,
336		_ => {}
337	}
338	patch.extend_from_slice(new);
339	Some(())
340}
341
342/// The end of the JSON value (or key) starting at `start`, in trusted compact output.
343fn skip(bytes: &[u8], start: usize) -> Option<usize> {
344	let mut depth = 0usize;
345	let mut index = start;
346	loop {
347		match *bytes.get(index)? {
348			b'"' => {
349				index += 1;
350				loop {
351					match *bytes.get(index)? {
352						b'\\' => index += 2,
353						b'"' => break,
354						_ => index += 1,
355					}
356				}
357				index += 1;
358			}
359			b'{' | b'[' => {
360				depth += 1;
361				index += 1;
362			}
363			b'}' | b']' => {
364				depth = depth.checked_sub(1)?;
365				index += 1;
366			}
367			_ => index += 1,
368		}
369		if depth == 0 && matches!(bytes.get(index), None | Some(b',' | b'}' | b']' | b':')) {
370			return Some(index);
371		}
372	}
373}
374
375/// Minimal serde error for the diffing serializer. JSON-shaped data never produces one in practice.
376#[derive(Debug)]
377struct Error(String);
378
379impl std::fmt::Display for Error {
380	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
381		f.write_str(&self.0)
382	}
383}
384
385impl std::error::Error for Error {}
386
387impl serde::ser::Error for Error {
388	fn custom<M: std::fmt::Display>(msg: M) -> Self {
389		Error(msg.to_string())
390	}
391}
392
393/// Build a Value with no diffing (used for array elements, which merge patch replaces wholesale).
394fn to_plain<T: Serialize + ?Sized>(value: &T) -> Result<Value, Error> {
395	serde_json::to_value(value).map_err(|e| Error(e.to_string()))
396}
397
398impl<'a> Serializer for Differ<'a> {
399	type Ok = Node;
400	type Error = Error;
401	type SerializeSeq = SeqDiff<'a>;
402	type SerializeTuple = SeqDiff<'a>;
403	type SerializeTupleStruct = SeqDiff<'a>;
404	type SerializeTupleVariant = VariantSeq<'a>;
405	type SerializeMap = MapDiff<'a>;
406	type SerializeStruct = MapDiff<'a>;
407	type SerializeStructVariant = VariantMap<'a>;
408
409	fn serialize_bool(self, v: bool) -> Result<Node, Error> {
410		self.scalar(&v, self.baseline == &Value::Bool(v), false)
411	}
412	fn serialize_i8(self, v: i8) -> Result<Node, Error> {
413		self.scalar(&v, self.baseline == &Value::from(v), false)
414	}
415	fn serialize_i16(self, v: i16) -> Result<Node, Error> {
416		self.scalar(&v, self.baseline == &Value::from(v), false)
417	}
418	fn serialize_i32(self, v: i32) -> Result<Node, Error> {
419		self.scalar(&v, self.baseline == &Value::from(v), false)
420	}
421	fn serialize_i64(self, v: i64) -> Result<Node, Error> {
422		self.scalar(&v, self.baseline == &Value::from(v), false)
423	}
424	fn serialize_i128(self, v: i128) -> Result<Node, Error> {
425		{
426			let plain = to_plain(&v)?;
427			self.scalar(&plain, self.baseline == &plain, false)
428		}
429	}
430	fn serialize_u8(self, v: u8) -> Result<Node, Error> {
431		self.scalar(&v, self.baseline == &Value::from(v), false)
432	}
433	fn serialize_u16(self, v: u16) -> Result<Node, Error> {
434		self.scalar(&v, self.baseline == &Value::from(v), false)
435	}
436	fn serialize_u32(self, v: u32) -> Result<Node, Error> {
437		self.scalar(&v, self.baseline == &Value::from(v), false)
438	}
439	fn serialize_u64(self, v: u64) -> Result<Node, Error> {
440		self.scalar(&v, self.baseline == &Value::from(v), false)
441	}
442	fn serialize_u128(self, v: u128) -> Result<Node, Error> {
443		{
444			let plain = to_plain(&v)?;
445			self.scalar(&plain, self.baseline == &plain, false)
446		}
447	}
448	fn serialize_f32(self, v: f32) -> Result<Node, Error> {
449		self.scalar(&v, self.baseline == &Value::from(v), false)
450	}
451	fn serialize_f64(self, v: f64) -> Result<Node, Error> {
452		self.scalar(&v, self.baseline == &Value::from(v), false)
453	}
454	fn serialize_char(self, v: char) -> Result<Node, Error> {
455		let mut utf8 = [0; 4];
456		self.scalar(&v, self.baseline.as_str() == Some(v.encode_utf8(&mut utf8)), false)
457	}
458	fn serialize_str(self, v: &str) -> Result<Node, Error> {
459		// Strings are the common churn-free field, so compare against the baseline without allocating a
460		// `Value::String` on the unchanged path.
461		if matches!(self.baseline, Value::String(b) if b == v) {
462			Ok(Node::Same)
463		} else {
464			self.scalar(&v, false, false)
465		}
466	}
467	fn serialize_bytes(self, v: &[u8]) -> Result<Node, Error> {
468		let mut seq = self.serialize_seq(Some(v.len()))?;
469		for byte in v {
470			SerializeSeq::serialize_element(&mut seq, byte)?;
471		}
472		SerializeSeq::end(seq)
473	}
474	fn serialize_none(self) -> Result<Node, Error> {
475		self.scalar(&Value::Null, self.present && self.baseline.is_null(), true)
476	}
477	fn serialize_some<T: Serialize + ?Sized>(self, value: &T) -> Result<Node, Error> {
478		value.serialize(self)
479	}
480	fn serialize_unit(self) -> Result<Node, Error> {
481		self.scalar(&Value::Null, self.present && self.baseline.is_null(), true)
482	}
483	fn serialize_unit_struct(self, _name: &'static str) -> Result<Node, Error> {
484		self.scalar(&Value::Null, self.present && self.baseline.is_null(), true)
485	}
486	fn serialize_unit_variant(self, _name: &'static str, _idx: u32, variant: &'static str) -> Result<Node, Error> {
487		self.scalar(&variant, self.baseline.as_str() == Some(variant), false)
488	}
489	fn serialize_newtype_struct<T: Serialize + ?Sized>(self, _name: &'static str, value: &T) -> Result<Node, Error> {
490		value.serialize(self)
491	}
492	fn serialize_newtype_variant<T: Serialize + ?Sized>(
493		self,
494		_name: &'static str,
495		_idx: u32,
496		variant: &'static str,
497		value: &T,
498	) -> Result<Node, Error> {
499		// An externally-tagged newtype variant serializes as `{ "Variant": value }`. Diff that object
500		// against the baseline like any other object, so the tag is preserved and the payload diffs
501		// minimally (a variant switch deletes the old tag and adds the new one).
502		Variant { name: variant, value }.serialize(self)
503	}
504	fn serialize_seq(self, len: Option<usize>) -> Result<SeqDiff<'a>, Error> {
505		let _ = len;
506		self.scratch.borrow_mut().buffer(self.depth).clear();
507		Ok(SeqDiff {
508			differ: self,
509			changed: false,
510			len: 0,
511		})
512	}
513	fn serialize_tuple(self, len: usize) -> Result<SeqDiff<'a>, Error> {
514		self.serialize_seq(Some(len))
515	}
516	fn serialize_tuple_struct(self, _name: &'static str, len: usize) -> Result<SeqDiff<'a>, Error> {
517		self.serialize_seq(Some(len))
518	}
519	fn serialize_tuple_variant(
520		self,
521		_name: &'static str,
522		_idx: u32,
523		variant: &'static str,
524		len: usize,
525	) -> Result<VariantSeq<'a>, Error> {
526		// A tuple variant serializes as `{ "Variant": [..] }`, replaced wholesale.
527		Ok(VariantSeq {
528			differ: self,
529			variant,
530			items: self.child(variant).0.serialize_seq(Some(len))?,
531		})
532	}
533	fn serialize_map(self, _len: Option<usize>) -> Result<MapDiff<'a>, Error> {
534		self.scratch.borrow_mut().buffer(self.depth).clear();
535		Ok(MapDiff {
536			differ: self,
537			entries: 0,
538			seen_len: 0,
539			ordered: true,
540			added_key: false,
541			memo_cursor: 0,
542		})
543	}
544	fn serialize_struct(self, _name: &'static str, len: usize) -> Result<MapDiff<'a>, Error> {
545		self.serialize_map(Some(len))
546	}
547	fn serialize_struct_variant(
548		self,
549		_name: &'static str,
550		_idx: u32,
551		variant: &'static str,
552		_len: usize,
553	) -> Result<VariantMap<'a>, Error> {
554		// A struct variant serializes as `{ "Variant": { .. } }`, replaced wholesale.
555		Ok(VariantMap {
556			differ: self,
557			variant,
558			fields: self.child(variant).0.serialize_map(Some(_len))?,
559		})
560	}
561}
562
563/// Serialize an externally tagged newtype without materializing its payload.
564struct Variant<'a, T: ?Sized> {
565	name: &'static str,
566	value: &'a T,
567}
568
569impl<T: Serialize + ?Sized> Serialize for Variant<'_, T> {
570	fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
571		let mut map = serializer.serialize_map(Some(1))?;
572		map.serialize_entry(self.name, self.value)?;
573		map.end()
574	}
575}
576
577/// Finish an externally tagged variant from the already serialized child patch.
578fn finish_variant(differ: Differ<'_>, variant: &'static str, node: Node) -> Result<Node, Error> {
579	let (_, existed) = differ.child(variant);
580	let mut outer = differ.serialize_map(Some(1))?;
581	outer.entry(variant, existed, node)?;
582	SerializeMap::end(outer)
583}
584
585struct VariantSeq<'a> {
586	differ: Differ<'a>,
587	variant: &'static str,
588	items: SeqDiff<'a>,
589}
590
591impl serde::ser::SerializeTupleVariant for VariantSeq<'_> {
592	type Ok = Node;
593	type Error = Error;
594	fn serialize_field<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), Error> {
595		SerializeSeq::serialize_element(&mut self.items, value)
596	}
597	fn end(self) -> Result<Node, Error> {
598		finish_variant(self.differ, self.variant, SerializeSeq::end(self.items)?)
599	}
600}
601
602struct VariantMap<'a> {
603	differ: Differ<'a>,
604	variant: &'static str,
605	fields: MapDiff<'a>,
606}
607
608impl serde::ser::SerializeStructVariant for VariantMap<'_> {
609	type Ok = Node;
610	type Error = Error;
611	fn serialize_field<T: Serialize + ?Sized>(&mut self, key: &'static str, value: &T) -> Result<(), Error> {
612		SerializeStruct::serialize_field(&mut self.fields, key, value)
613	}
614	fn end(self) -> Result<Node, Error> {
615		finish_variant(self.differ, self.variant, SerializeStruct::end(self.fields)?)
616	}
617}
618
619/// Arrays are replaced wholesale by merge patch, but an unchanged array needs no copy.
620struct SeqDiff<'a> {
621	differ: Differ<'a>,
622	changed: bool,
623	len: usize,
624}
625
626impl SeqDiff<'_> {
627	fn begin(&mut self, old: &[Value]) -> Result<(), Error> {
628		let mut scratch = self.differ.scratch.borrow_mut();
629		let capacity = scratch.last_capacity;
630		let bytes = scratch.buffer(self.differ.depth);
631		bytes.clear();
632		if self.differ.depth == 0 {
633			bytes.reserve(capacity);
634		}
635		bytes.push(b'[');
636		for item in &old[..self.len] {
637			if bytes.len() > 1 {
638				bytes.push(b',');
639			}
640			serde_json::to_writer(&mut *bytes, item).map_err(|err| Error(err.to_string()))?;
641		}
642		self.changed = true;
643		Ok(())
644	}
645}
646
647impl SerializeSeq for SeqDiff<'_> {
648	type Ok = Node;
649	type Error = Error;
650	fn serialize_element<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), Error> {
651		let old = self.differ.baseline.as_array();
652		if !self.changed {
653			let baseline = old.and_then(|old| old.get(self.len)).unwrap_or(&NULL);
654			// Null inside an array is data, not a merge-patch deletion.
655			let forced = Cell::new(false);
656			let equal = matches!(
657				value.serialize(Differ {
658					baseline,
659					present: true,
660					forced: &forced,
661					scratch: self.differ.scratch,
662					depth: self.differ.depth + 1,
663				})?,
664				Node::Same
665			) && old.is_some_and(|old| self.len < old.len());
666			if !equal {
667				self.begin(old.map(Vec::as_slice).unwrap_or(&[]))?;
668			}
669		}
670		if self.changed {
671			let mut scratch = self.differ.scratch.borrow_mut();
672			let bytes = scratch.buffer(self.differ.depth);
673			if bytes.len() > 1 {
674				bytes.push(b',');
675			}
676			serde_json::to_writer(bytes, value).map_err(|err| Error(err.to_string()))?;
677		}
678		self.len += 1;
679		Ok(())
680	}
681	fn end(mut self) -> Result<Node, Error> {
682		if !self.changed {
683			if let Some(old) = self.differ.baseline.as_array() {
684				if self.len == old.len() {
685					return Ok(Node::Same);
686				}
687				self.begin(old)?;
688			} else {
689				self.begin(&[])?;
690			}
691		}
692		self.differ.scratch.borrow_mut().buffer(self.differ.depth).push(b']');
693		Ok(Node::Diff)
694	}
695}
696
697impl serde::ser::SerializeTuple for SeqDiff<'_> {
698	type Ok = Node;
699	type Error = Error;
700	fn serialize_element<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), Error> {
701		SerializeSeq::serialize_element(self, value)
702	}
703	fn end(self) -> Result<Node, Error> {
704		SerializeSeq::end(self)
705	}
706}
707
708impl serde::ser::SerializeTupleStruct for SeqDiff<'_> {
709	type Ok = Node;
710	type Error = Error;
711	fn serialize_field<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), Error> {
712		SerializeSeq::serialize_element(self, value)
713	}
714	fn end(self) -> Result<Node, Error> {
715		SerializeSeq::end(self)
716	}
717}
718
719/// How the [`Memo`] handled a root entry.
720enum Memoized {
721	/// Not at the root, or no memo: diff the value.
722	Off,
723	/// Settled against the last diff's bytes.
724	Hit(Node),
725	/// New, or changed shape: diff the bytes just recorded.
726	Miss,
727}
728
729/// Objects recurse; only changed entries are written into the reusable patch buffer.
730struct MapDiff<'a> {
731	differ: Differ<'a>,
732	entries: usize,
733	seen_len: usize,
734	ordered: bool,
735	added_key: bool,
736	/// Where the next root entry is expected in the [`Memo`].
737	memo_cursor: usize,
738}
739
740impl MapDiff<'_> {
741	/// Diff one entry, through the [`Memo`] when there is one.
742	fn value<T: Serialize + ?Sized>(&mut self, key: &str, value: &T) -> Result<(), Error> {
743		let (node, existed) = match self.memoized(key, value)? {
744			Memoized::Hit(node) => (node, true),
745			Memoized::Off => {
746				let (child, existed) = self.differ.child(key);
747				(value.serialize(child)?, existed)
748			}
749			Memoized::Miss => {
750				// Diff the bytes the memo just recorded rather than serializing `value` again, so the
751				// memo, the patch, and the baseline all come from one serialization. Checked first,
752				// since a parse keeps the last of a repeated key where the value diff refuses it.
753				let entry: Value = {
754					let scratch = self.differ.scratch.borrow();
755					let memo = scratch.memo.as_ref().expect("a miss implies a memo");
756					let (start, end) = *memo.next.ends.last().expect("a miss records its entry");
757					let bytes = &memo.next.bytes[start..end];
758					crate::merge::check(bytes, &memo.check)
759						.and_then(|()| serde_json::from_slice(bytes))
760						.map_err(|err| Error(err.to_string()))?
761				};
762				let (child, existed) = self.differ.child(key);
763				(entry.serialize(child)?, existed)
764			}
765		};
766		self.entry(key, existed, node)
767	}
768
769	/// Record a root entry in the [`Memo`] and diff it against the bytes it had in the last diff.
770	///
771	/// Equal bytes mean the baseline holds exactly the value they parse to, so the entry is unchanged
772	/// without walking its subtree. Unequal bytes of the same shape are diffed by [`lockstep`], with
773	/// the patch left in the child buffer like any other [`Node::Diff`].
774	fn memoized<T: Serialize + ?Sized>(&mut self, key: &str, value: &T) -> Result<Memoized, Error> {
775		if self.differ.depth != 0 {
776			return Ok(Memoized::Off);
777		}
778		let mut scratch = self.differ.scratch.borrow_mut();
779		let Scratch { memo, bytes, .. } = &mut *scratch;
780		let Some(Memo { current, next, check }) = memo.as_mut() else {
781			return Ok(Memoized::Off);
782		};
783		if let Some(last) = next.ends.len().checked_sub(1)
784			&& next.key(last) >= key.as_bytes()
785		{
786			next.unsorted = true;
787		}
788		next.bytes.extend_from_slice(key.as_bytes());
789		let key_end = next.bytes.len();
790		serde_json::to_writer(&mut next.bytes, value).map_err(|err| Error(err.to_string()))?;
791		next.ends.push((key_end, next.bytes.len()));
792
793		let Some(index) = current.find(key.as_bytes(), &mut self.memo_cursor) else {
794			return Ok(Memoized::Miss);
795		};
796		let (old, new) = (current.value(index), &next.bytes[key_end..]);
797		if old == new {
798			return Ok(Memoized::Hit(Node::Same));
799		}
800		if bytes.len() < 2 {
801			bytes.resize_with(2, Vec::new);
802		}
803		let patch = &mut bytes[1];
804		patch.clear();
805		let mut forced = false;
806		if lockstep(old, new, patch, &mut forced, check).is_none() {
807			return Ok(Memoized::Miss);
808		}
809		if forced {
810			self.differ.forced.set(true);
811		}
812		Ok(Memoized::Hit(Node::Diff))
813	}
814
815	fn write_entry(&mut self, key: &str, child: Option<usize>) -> Result<(), Error> {
816		let depth = self.differ.depth;
817		let mut scratch = self.differ.scratch.borrow_mut();
818		let capacity = scratch.last_capacity;
819		let bytes = scratch.buffer(depth);
820		if self.entries == 0 {
821			if depth == 0 {
822				bytes.reserve(capacity);
823			}
824			bytes.push(b'{');
825		} else {
826			bytes.push(b',');
827		}
828		serde_json::to_writer(&mut *bytes, key).map_err(|err| Error(err.to_string()))?;
829		bytes.push(b':');
830		if let Some(child) = child {
831			let (parents, children) = scratch.bytes.split_at_mut(child);
832			parents[depth].extend_from_slice(&children[0]);
833		} else {
834			scratch.bytes[depth].extend_from_slice(b"null");
835		}
836		self.entries += 1;
837		Ok(())
838	}
839
840	fn entry(&mut self, key: &str, existed: bool, node: Node) -> Result<(), Error> {
841		self.added_key |= !existed;
842		if let Node::Diff = node {
843			self.write_entry(key, Some(self.differ.depth + 1))?;
844		}
845		let mut scratch = self.differ.scratch.borrow_mut();
846		if scratch.seen.len() <= self.differ.depth {
847			scratch.seen.resize_with(self.differ.depth + 1, Vec::new);
848		}
849		let seen = &mut scratch.seen[self.differ.depth];
850		if self.seen_len > 0 {
851			match seen[self.seen_len - 1].as_str().cmp(key) {
852				std::cmp::Ordering::Equal => return Err(Error("duplicate JSON object key".into())),
853				std::cmp::Ordering::Greater => self.ordered = false,
854				std::cmp::Ordering::Less => {}
855			}
856		}
857		if self.seen_len == seen.len() {
858			seen.push(key.to_owned());
859		} else {
860			seen[self.seen_len].clear();
861			seen[self.seen_len].push_str(key);
862		}
863		self.seen_len += 1;
864		Ok(())
865	}
866
867	fn finish(mut self) -> Result<Node, Error> {
868		if !self.ordered {
869			let mut scratch = self.differ.scratch.borrow_mut();
870			let seen = &mut scratch.seen[self.differ.depth][..self.seen_len];
871			seen.sort_unstable();
872			if seen.windows(2).any(|pair| pair[0] == pair[1]) {
873				return Err(Error("duplicate JSON object key".into()));
874			}
875		}
876		if let Value::Object(base) = self.differ.baseline
877			&& (self.added_key || self.seen_len != base.len())
878		{
879			let depth = self.differ.depth;
880			let mut scratch = self.differ.scratch.borrow_mut();
881			let Scratch {
882				seen,
883				bytes,
884				last_capacity,
885				..
886			} = &mut *scratch;
887			// A map emptied of every key offered none, so nothing sized `seen` for this depth.
888			let seen: &mut [String] = match seen.get_mut(depth) {
889				Some(seen) => &mut seen[..self.seen_len],
890				None => &mut [],
891			};
892			let out = &mut bytes[depth];
893			if self.ordered {
894				seen.sort_unstable();
895			}
896			for key in base.keys() {
897				if seen.binary_search_by(|seen| seen.as_str().cmp(key)).is_err() {
898					if self.entries == 0 {
899						if depth == 0 {
900							out.reserve(*last_capacity);
901						}
902						out.push(b'{');
903					} else {
904						out.push(b',');
905					}
906					serde_json::to_writer(&mut *out, key).map_err(|err| Error(err.to_string()))?;
907					out.extend_from_slice(b":null");
908					self.entries += 1;
909				}
910			}
911		}
912		if self.entries == 0 {
913			if self.differ.baseline.is_object() {
914				return Ok(Node::Same);
915			}
916			self.differ
917				.scratch
918				.borrow_mut()
919				.buffer(self.differ.depth)
920				.extend_from_slice(b"{}");
921			return Ok(Node::Diff);
922		}
923		self.differ.scratch.borrow_mut().buffer(self.differ.depth).push(b'}');
924		Ok(Node::Diff)
925	}
926}
927
928impl SerializeMap for MapDiff<'_> {
929	type Ok = Node;
930	type Error = Error;
931	fn serialize_key<T: Serialize + ?Sized>(&mut self, key: &T) -> Result<(), Error> {
932		let mut scratch = self.differ.scratch.borrow_mut();
933		if scratch.pending.len() <= self.differ.depth {
934			scratch.pending.resize_with(self.differ.depth + 1, String::new);
935		}
936		let pending = &mut scratch.pending[self.differ.depth];
937		pending.clear();
938		key.serialize(KeySer(pending))
939	}
940	fn serialize_value<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), Error> {
941		let depth = self.differ.depth;
942		let key = std::mem::take(&mut self.differ.scratch.borrow_mut().pending[depth]);
943		let result = self.value(&key, value);
944		self.differ.scratch.borrow_mut().pending[depth] = key;
945		result
946	}
947	fn end(self) -> Result<Node, Error> {
948		self.finish()
949	}
950}
951
952impl SerializeStruct for MapDiff<'_> {
953	type Ok = Node;
954	type Error = Error;
955	fn serialize_field<T: Serialize + ?Sized>(&mut self, key: &'static str, value: &T) -> Result<(), Error> {
956		self.value(key, value)
957	}
958	// A field skipped via `skip_serializing_if` is simply never offered here, so it stays out of `seen`
959	// and `finish` emits it as a null deletion if the baseline had it (the default `skip_field` suffices).
960	fn end(self) -> Result<Node, Error> {
961		self.finish()
962	}
963}
964
965/// Serializes a map key to its `String`, the only form JSON object keys take. Anything else is an
966/// error, mirroring `serde_json`'s own key handling.
967struct KeySer<'a>(&'a mut String);
968
969impl Serializer for KeySer<'_> {
970	type Ok = ();
971	type Error = Error;
972	type SerializeSeq = Impossible<(), Error>;
973	type SerializeTuple = Impossible<(), Error>;
974	type SerializeTupleStruct = Impossible<(), Error>;
975	type SerializeTupleVariant = Impossible<(), Error>;
976	type SerializeMap = Impossible<(), Error>;
977	type SerializeStruct = Impossible<(), Error>;
978	type SerializeStructVariant = Impossible<(), Error>;
979
980	fn serialize_str(self, v: &str) -> Result<(), Error> {
981		{
982			self.0.push_str(v);
983			Ok(())
984		}
985	}
986	fn serialize_char(self, v: char) -> Result<(), Error> {
987		{
988			use std::fmt::Write;
989			write!(self.0, "{v}").expect("writing to String cannot fail");
990			Ok(())
991		}
992	}
993	fn serialize_bool(self, v: bool) -> Result<(), Error> {
994		{
995			use std::fmt::Write;
996			write!(self.0, "{v}").expect("writing to String cannot fail");
997			Ok(())
998		}
999	}
1000	fn serialize_i8(self, v: i8) -> Result<(), Error> {
1001		{
1002			use std::fmt::Write;
1003			write!(self.0, "{v}").expect("writing to String cannot fail");
1004			Ok(())
1005		}
1006	}
1007	fn serialize_i16(self, v: i16) -> Result<(), Error> {
1008		{
1009			use std::fmt::Write;
1010			write!(self.0, "{v}").expect("writing to String cannot fail");
1011			Ok(())
1012		}
1013	}
1014	fn serialize_i32(self, v: i32) -> Result<(), Error> {
1015		{
1016			use std::fmt::Write;
1017			write!(self.0, "{v}").expect("writing to String cannot fail");
1018			Ok(())
1019		}
1020	}
1021	fn serialize_i64(self, v: i64) -> Result<(), Error> {
1022		{
1023			use std::fmt::Write;
1024			write!(self.0, "{v}").expect("writing to String cannot fail");
1025			Ok(())
1026		}
1027	}
1028	fn serialize_u8(self, v: u8) -> Result<(), Error> {
1029		{
1030			use std::fmt::Write;
1031			write!(self.0, "{v}").expect("writing to String cannot fail");
1032			Ok(())
1033		}
1034	}
1035	fn serialize_u16(self, v: u16) -> Result<(), Error> {
1036		{
1037			use std::fmt::Write;
1038			write!(self.0, "{v}").expect("writing to String cannot fail");
1039			Ok(())
1040		}
1041	}
1042	fn serialize_u32(self, v: u32) -> Result<(), Error> {
1043		{
1044			use std::fmt::Write;
1045			write!(self.0, "{v}").expect("writing to String cannot fail");
1046			Ok(())
1047		}
1048	}
1049	fn serialize_u64(self, v: u64) -> Result<(), Error> {
1050		{
1051			use std::fmt::Write;
1052			write!(self.0, "{v}").expect("writing to String cannot fail");
1053			Ok(())
1054		}
1055	}
1056	fn serialize_unit_variant(self, _name: &'static str, _idx: u32, variant: &'static str) -> Result<(), Error> {
1057		{
1058			self.0.push_str(variant);
1059			Ok(())
1060		}
1061	}
1062	fn serialize_newtype_struct<T: Serialize + ?Sized>(self, _name: &'static str, value: &T) -> Result<(), Error> {
1063		value.serialize(self)
1064	}
1065	fn serialize_some<T: Serialize + ?Sized>(self, value: &T) -> Result<(), Error> {
1066		value.serialize(self)
1067	}
1068	fn serialize_f32(self, _v: f32) -> Result<(), Error> {
1069		Err(Error("float map key".into()))
1070	}
1071	fn serialize_f64(self, _v: f64) -> Result<(), Error> {
1072		Err(Error("float map key".into()))
1073	}
1074	fn serialize_bytes(self, _v: &[u8]) -> Result<(), Error> {
1075		Err(Error("bytes map key".into()))
1076	}
1077	fn serialize_none(self) -> Result<(), Error> {
1078		Err(Error("null map key".into()))
1079	}
1080	fn serialize_unit(self) -> Result<(), Error> {
1081		Err(Error("unit map key".into()))
1082	}
1083	fn serialize_unit_struct(self, _name: &'static str) -> Result<(), Error> {
1084		Err(Error("unit struct map key".into()))
1085	}
1086	fn serialize_newtype_variant<T: Serialize + ?Sized>(
1087		self,
1088		_name: &'static str,
1089		_idx: u32,
1090		_variant: &'static str,
1091		_value: &T,
1092	) -> Result<(), Error> {
1093		Err(Error("newtype variant map key".into()))
1094	}
1095	fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Error> {
1096		Err(Error("seq map key".into()))
1097	}
1098	fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Error> {
1099		Err(Error("tuple map key".into()))
1100	}
1101	fn serialize_tuple_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeTupleStruct, Error> {
1102		Err(Error("tuple struct map key".into()))
1103	}
1104	fn serialize_tuple_variant(
1105		self,
1106		_name: &'static str,
1107		_idx: u32,
1108		_variant: &'static str,
1109		_len: usize,
1110	) -> Result<Self::SerializeTupleVariant, Error> {
1111		Err(Error("tuple variant map key".into()))
1112	}
1113	fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Error> {
1114		Err(Error("map map key".into()))
1115	}
1116	fn serialize_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeStruct, Error> {
1117		Err(Error("struct map key".into()))
1118	}
1119	fn serialize_struct_variant(
1120		self,
1121		_name: &'static str,
1122		_idx: u32,
1123		_variant: &'static str,
1124		_len: usize,
1125	) -> Result<Self::SerializeStructVariant, Error> {
1126		Err(Error("struct variant map key".into()))
1127	}
1128}
1129
1130#[cfg(test)]
1131mod test {
1132	use super::*;
1133	use serde_json::json;
1134
1135	/// A straightforward Value-vs-Value merge-patch diff, used only as a test oracle: the production
1136	/// `diff` (the serializer) must agree with it on every case.
1137	fn reference(old: &Value, new: &Value) -> Diff {
1138		fn objects(
1139			old: &Map<String, Value>,
1140			new: &Map<String, Value>,
1141			patch: &mut Map<String, Value>,
1142			forced: &mut bool,
1143		) {
1144			for key in old.keys() {
1145				if !new.contains_key(key) {
1146					patch.insert(key.clone(), Value::Null);
1147				}
1148			}
1149			for (key, new_val) in new {
1150				let old_val = old.get(key);
1151				if old_val == Some(new_val) {
1152					continue;
1153				}
1154				if let (Some(Value::Object(old_obj)), Value::Object(new_obj)) = (old_val, new_val) {
1155					let mut sub = Map::new();
1156					objects(old_obj, new_obj, &mut sub, forced);
1157					if !sub.is_empty() {
1158						patch.insert(key.clone(), Value::Object(sub));
1159					}
1160					continue;
1161				}
1162				if new_val.is_null() {
1163					*forced = true;
1164				}
1165				patch.insert(key.clone(), new_val.clone());
1166			}
1167		}
1168
1169		if let (Value::Object(old_obj), Value::Object(new_obj)) = (old, new) {
1170			let mut patch = Map::new();
1171			let mut forced = false;
1172			objects(old_obj, new_obj, &mut patch, &mut forced);
1173			Diff {
1174				patch: Value::Object(patch),
1175				forced_snapshot: forced,
1176			}
1177		} else {
1178			Diff {
1179				patch: new.clone(),
1180				forced_snapshot: true,
1181			}
1182		}
1183	}
1184
1185	/// The serializer must produce the same patch and forced flag as the reference oracle, and (when
1186	/// not forced) applying the patch to `old` must reproduce `new`.
1187	fn check(old: Value, new: Value) {
1188		let want = reference(&old, &new);
1189		let got = diff(&old, &new);
1190		assert_eq!(got.patch, want.patch, "patch mismatch for {old} -> {new}");
1191		assert_eq!(
1192			got.forced_snapshot, want.forced_snapshot,
1193			"forced mismatch for {old} -> {new}"
1194		);
1195		if !got.forced_snapshot {
1196			let mut applied = old.clone();
1197			json_patch::merge(&mut applied, &got.patch);
1198			assert_eq!(applied, new, "patch did not roundtrip for {old} -> {new}");
1199		}
1200	}
1201
1202	#[test]
1203	fn replacing_scalar_with_empty_object_is_a_change() {
1204		check(json!({ "value": 1 }), json!({ "value": {} }));
1205		check(json!({ "value": null }), json!({ "value": {} }));
1206		let result = diff(&json!(1), &json!({}));
1207		assert!(result.forced_snapshot);
1208		assert_eq!(result.patch, json!({}));
1209	}
1210
1211	#[test]
1212	fn duplicate_serialized_map_keys_force_a_snapshot() {
1213		struct Duplicate;
1214		impl Serialize for Duplicate {
1215			fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
1216				let mut map = serializer.serialize_map(Some(3))?;
1217				map.serialize_entry("key", &1)?;
1218				map.serialize_entry("other", &2)?;
1219				map.serialize_entry("key", &3)?;
1220				map.end()
1221			}
1222		}
1223		assert!(diff(&json!({ "key": 1, "other": 2 }), &Duplicate).forced_snapshot);
1224	}
1225
1226	#[test]
1227	fn emptying_a_map_removes_every_key() {
1228		// No key is serialized at the emptied map's depth, so nothing has sized the
1229		// scratch for it yet.
1230		check(json!({ "a": 1 }), json!({}));
1231		check(json!({ "a": 1, "b": 2 }), json!({}));
1232		check(json!({ "x": { "a": 1 } }), json!({ "x": {} }));
1233	}
1234
1235	#[test]
1236	fn changed_scalar() {
1237		check(json!({ "a": 1, "b": 2 }), json!({ "a": 1, "b": 3 }));
1238	}
1239
1240	#[test]
1241	fn added_key() {
1242		let result = diff(&json!({ "a": 1 }), &json!({ "a": 1, "b": 2 }));
1243		assert!(!result.forced_snapshot);
1244		assert_eq!(result.patch, json!({ "b": 2 }));
1245		check(json!({ "a": 1 }), json!({ "a": 1, "b": 2 }));
1246	}
1247
1248	#[test]
1249	fn added_null_key_forces_snapshot() {
1250		check(json!({ "a": 1 }), json!({ "a": 1, "x": null }));
1251		check(
1252			json!({ "items": [{ "a": 1 }] }),
1253			json!({ "items": [{ "a": 1, "x": null }] }),
1254		);
1255	}
1256
1257	#[test]
1258	fn removed_key_is_null() {
1259		let result = diff(&json!({ "a": 1, "b": 2 }), &json!({ "a": 1 }));
1260		assert!(!result.forced_snapshot, "removing a key is a clean delete");
1261		assert_eq!(result.patch, json!({ "b": null }));
1262		check(json!({ "a": 1, "b": 2 }), json!({ "a": 1 }));
1263	}
1264
1265	#[test]
1266	fn nested_object_only_includes_changed_keys() {
1267		let result = diff(&json!({ "o": { "x": 1, "y": 2 } }), &json!({ "o": { "x": 1, "y": 9 } }));
1268		assert!(!result.forced_snapshot);
1269		assert_eq!(result.patch, json!({ "o": { "y": 9 } }));
1270		check(json!({ "o": { "x": 1, "y": 2 } }), json!({ "o": { "x": 1, "y": 9 } }));
1271	}
1272
1273	#[test]
1274	fn unchanged_object_is_empty_patch() {
1275		let result = diff(&json!({ "a": 1, "o": { "x": 1 } }), &json!({ "a": 1, "o": { "x": 1 } }));
1276		assert!(!result.forced_snapshot);
1277		assert_eq!(result.patch, json!({}));
1278	}
1279
1280	#[test]
1281	fn changed_array_is_wholesale_delta() {
1282		let result = diff(&json!({ "a": [1, 2] }), &json!({ "a": [1, 2, 3] }));
1283		assert!(!result.forced_snapshot);
1284		assert_eq!(result.patch, json!({ "a": [1, 2, 3] }));
1285		check(json!({ "a": [1, 2] }), json!({ "a": [1, 2, 3] }));
1286	}
1287
1288	#[test]
1289	fn unchanged_array_is_pruned() {
1290		let result = diff(&json!({ "a": [1, 2, 3], "b": 1 }), &json!({ "a": [1, 2, 3], "b": 2 }));
1291		assert_eq!(
1292			result.patch,
1293			json!({ "b": 2 }),
1294			"an unchanged array stays out of the patch"
1295		);
1296	}
1297
1298	#[test]
1299	fn added_array_is_delta() {
1300		check(json!({ "a": 1 }), json!({ "a": 1, "b": [1] }));
1301	}
1302
1303	#[test]
1304	fn nested_array_is_delta() {
1305		check(json!({ "o": { "x": 1 } }), json!({ "o": { "x": 1, "list": [1] } }));
1306	}
1307
1308	#[test]
1309	fn array_of_objects_replaces_wholesale() {
1310		check(
1311			json!({ "items": [{ "id": 1, "v": 1 }, { "id": 2, "v": 2 }] }),
1312			json!({ "items": [{ "id": 1, "v": 9 }, { "id": 2, "v": 2 }] }),
1313		);
1314	}
1315
1316	#[test]
1317	fn set_to_null_forces_snapshot() {
1318		// A genuine null value can't be represented: merge patch would delete the key.
1319		let result = diff(&json!({ "a": 1 }), &json!({ "a": null }));
1320		assert!(result.forced_snapshot);
1321		assert!(reference(&json!({ "a": 1 }), &json!({ "a": null })).forced_snapshot);
1322	}
1323
1324	#[test]
1325	fn nested_null_forces_snapshot() {
1326		let old = json!({ "o": { "x": 1 } });
1327		let new = json!({ "o": { "x": null } });
1328		assert!(diff(&old, &new).forced_snapshot);
1329		assert_eq!(diff(&old, &new).forced_snapshot, reference(&old, &new).forced_snapshot);
1330	}
1331
1332	#[test]
1333	fn replacing_object_with_scalar() {
1334		check(json!({ "a": { "x": 1 } }), json!({ "a": 5 }));
1335	}
1336
1337	#[test]
1338	fn replacing_scalar_with_object() {
1339		check(json!({ "a": 5 }), json!({ "a": { "x": 1 } }));
1340	}
1341
1342	#[test]
1343	fn non_object_root_forces_snapshot() {
1344		let result = diff(&json!(1), &json!(2));
1345		assert!(result.forced_snapshot);
1346		assert_eq!(result.patch, json!(2));
1347	}
1348
1349	#[test]
1350	fn array_root_forces_snapshot() {
1351		let result = diff(&json!([1, 2]), &json!([1, 2, 3]));
1352		assert!(result.forced_snapshot);
1353		assert_eq!(result.patch, json!([1, 2, 3]));
1354	}
1355
1356	#[test]
1357	fn unchanged_scalar_root_is_not_forced() {
1358		// An equal non-object root is a no-op (empty patch), matching the producer's dedup.
1359		let result = diff(&json!(7), &json!(7));
1360		assert!(!result.forced_snapshot);
1361		assert_eq!(result.patch, json!({}));
1362	}
1363
1364	#[test]
1365	fn floats_and_bools_and_strings() {
1366		check(
1367			json!({ "f": 1.5, "b": true, "s": "hi" }),
1368			json!({ "f": 2.5, "b": false, "s": "bye" }),
1369		);
1370	}
1371
1372	// ---- Typed structs (the serializer's whole point: diff `T` without building its Value) ----
1373
1374	#[derive(serde::Serialize, serde::Deserialize, Default, PartialEq, Debug)]
1375	struct Doc {
1376		#[serde(skip_serializing_if = "Option::is_none")]
1377		video: Option<String>,
1378		#[serde(skip_serializing_if = "Option::is_none")]
1379		scte35: Option<u32>,
1380		count: u64,
1381		tags: Vec<String>,
1382	}
1383
1384	/// Diff a typed struct against the Value of a prior typed struct; the patch must roundtrip.
1385	fn check_struct(old: &Doc, new: &Doc) {
1386		let old_value = serde_json::to_value(old).unwrap();
1387		let result = diff(&old_value, new);
1388
1389		// Cross-check against the oracle fed the equivalent Values.
1390		let new_value = serde_json::to_value(new).unwrap();
1391		let want = reference(&old_value, &new_value);
1392		assert_eq!(result.patch, want.patch, "struct patch differs from oracle");
1393		assert_eq!(result.forced_snapshot, want.forced_snapshot);
1394
1395		if !result.forced_snapshot {
1396			let mut applied = old_value;
1397			json_patch::merge(&mut applied, &result.patch);
1398			assert_eq!(applied, new_value, "struct patch did not roundtrip");
1399		}
1400	}
1401
1402	#[test]
1403	fn struct_field_change() {
1404		check_struct(
1405			&Doc {
1406				count: 1,
1407				tags: vec!["a".into()],
1408				..Default::default()
1409			},
1410			&Doc {
1411				count: 2,
1412				tags: vec!["a".into()],
1413				..Default::default()
1414			},
1415		);
1416	}
1417
1418	#[test]
1419	fn struct_option_some_to_none_is_deletion() {
1420		// A skipped field (None) must become a null deletion of the previously-present key.
1421		let result = diff(
1422			&serde_json::to_value(Doc {
1423				video: Some("v1".into()),
1424				count: 1,
1425				..Default::default()
1426			})
1427			.unwrap(),
1428			&Doc {
1429				video: None,
1430				count: 1,
1431				..Default::default()
1432			},
1433		);
1434		assert!(!result.forced_snapshot, "deleting a skipped key is clean");
1435		assert_eq!(result.patch, json!({ "video": null }));
1436		check_struct(
1437			&Doc {
1438				video: Some("v1".into()),
1439				count: 1,
1440				..Default::default()
1441			},
1442			&Doc {
1443				video: None,
1444				count: 1,
1445				..Default::default()
1446			},
1447		);
1448	}
1449
1450	#[test]
1451	fn struct_option_none_to_some_is_addition() {
1452		check_struct(
1453			&Doc {
1454				count: 1,
1455				..Default::default()
1456			},
1457			&Doc {
1458				scte35: Some(42),
1459				count: 1,
1460				..Default::default()
1461			},
1462		);
1463	}
1464
1465	#[test]
1466	fn struct_unchanged_is_empty_patch() {
1467		let doc = Doc {
1468			video: Some("v".into()),
1469			scte35: Some(1),
1470			count: 7,
1471			tags: vec!["x".into(), "y".into()],
1472		};
1473		let result = diff(&serde_json::to_value(&doc).unwrap(), &doc);
1474		assert!(!result.forced_snapshot);
1475		assert_eq!(result.patch, json!({}));
1476	}
1477
1478	#[test]
1479	fn struct_vec_changes_wholesale() {
1480		check_struct(
1481			&Doc {
1482				count: 1,
1483				tags: vec!["a".into(), "b".into()],
1484				..Default::default()
1485			},
1486			&Doc {
1487				count: 1,
1488				tags: vec!["a".into(), "c".into()],
1489				..Default::default()
1490			},
1491		);
1492	}
1493
1494	#[derive(serde::Serialize)]
1495	struct Nested {
1496		inner: Inner,
1497		name: String,
1498	}
1499	#[derive(serde::Serialize)]
1500	struct Inner {
1501		a: u32,
1502		b: u32,
1503	}
1504
1505	#[test]
1506	fn nested_struct_only_changed_field() {
1507		let old = serde_json::to_value(Nested {
1508			inner: Inner { a: 1, b: 2 },
1509			name: "n".into(),
1510		})
1511		.unwrap();
1512		let new = Nested {
1513			inner: Inner { a: 1, b: 9 },
1514			name: "n".into(),
1515		};
1516		let result = diff(&old, &new);
1517		assert_eq!(result.patch, json!({ "inner": { "b": 9 } }));
1518		assert!(!result.forced_snapshot);
1519	}
1520
1521	#[derive(serde::Serialize)]
1522	enum Tag {
1523		Active,
1524		Idle,
1525	}
1526
1527	#[derive(serde::Serialize)]
1528	struct Stated {
1529		state: Tag,
1530		seq: u32,
1531	}
1532
1533	#[test]
1534	fn unit_enum_variant_is_string() {
1535		// Externally-tagged unit variants serialize as the variant name string.
1536		let old = serde_json::to_value(Stated {
1537			state: Tag::Active,
1538			seq: 1,
1539		})
1540		.unwrap();
1541		assert_eq!(old, json!({ "state": "Active", "seq": 1 }));
1542		let result = diff(
1543			&old,
1544			&Stated {
1545				state: Tag::Idle,
1546				seq: 1,
1547			},
1548		);
1549		assert!(!result.forced_snapshot);
1550		assert_eq!(result.patch, json!({ "state": "Idle" }));
1551	}
1552
1553	/// Diff a typed value against the Value of a prior value: the patch must match the oracle fed the
1554	/// equivalent Values, and roundtrip.
1555	fn check_typed<T: Serialize>(old: &Value, new: &T) {
1556		let new_value = serde_json::to_value(new).unwrap();
1557		let want = reference(old, &new_value);
1558		let got = diff(old, new);
1559		assert_eq!(got.patch, want.patch, "patch differs from oracle");
1560		assert_eq!(got.forced_snapshot, want.forced_snapshot, "forced differs from oracle");
1561		if !got.forced_snapshot {
1562			let mut applied = old.clone();
1563			json_patch::merge(&mut applied, &got.patch);
1564			assert_eq!(applied, new_value, "patch did not roundtrip");
1565		}
1566	}
1567
1568	#[derive(serde::Serialize)]
1569	enum Payload {
1570		Newtype(u32),
1571		Tuple(u32, String),
1572		Struct { x: u32, y: u32 },
1573	}
1574
1575	#[derive(serde::Serialize)]
1576	struct Holder {
1577		payload: Payload,
1578		seq: u32,
1579	}
1580
1581	#[test]
1582	fn newtype_variant_keeps_its_tag() {
1583		// Regression: a newtype variant must serialize as `{ "Newtype": v }`, not collapse to `v`.
1584		let old = serde_json::to_value(Holder {
1585			payload: Payload::Newtype(1),
1586			seq: 0,
1587		})
1588		.unwrap();
1589		assert_eq!(old, json!({ "payload": { "Newtype": 1 }, "seq": 0 }));
1590		let result = diff(
1591			&old,
1592			&Holder {
1593				payload: Payload::Newtype(2),
1594				seq: 0,
1595			},
1596		);
1597		assert_eq!(result.patch, json!({ "payload": { "Newtype": 2 } }));
1598		check_typed(
1599			&old,
1600			&Holder {
1601				payload: Payload::Newtype(2),
1602				seq: 0,
1603			},
1604		);
1605	}
1606
1607	#[test]
1608	fn tuple_variant_keeps_its_tag() {
1609		let old = serde_json::to_value(Holder {
1610			payload: Payload::Tuple(1, "a".into()),
1611			seq: 0,
1612		})
1613		.unwrap();
1614		assert_eq!(old, json!({ "payload": { "Tuple": [1, "a"] }, "seq": 0 }));
1615		check_typed(
1616			&old,
1617			&Holder {
1618				payload: Payload::Tuple(2, "a".into()),
1619				seq: 0,
1620			},
1621		);
1622	}
1623
1624	#[test]
1625	fn struct_variant_keeps_its_tag() {
1626		let old = serde_json::to_value(Holder {
1627			payload: Payload::Struct { x: 1, y: 2 },
1628			seq: 0,
1629		})
1630		.unwrap();
1631		assert_eq!(old, json!({ "payload": { "Struct": { "x": 1, "y": 2 } }, "seq": 0 }));
1632		check_typed(
1633			&old,
1634			&Holder {
1635				payload: Payload::Struct { x: 1, y: 9 },
1636				seq: 0,
1637			},
1638		);
1639	}
1640
1641	#[derive(serde::Deserialize)]
1642	struct Vector {
1643		name: String,
1644		old: Value,
1645		new: Value,
1646		forced: bool,
1647		patch: Option<Value>,
1648	}
1649
1650	/// Shared cross-impl fixture: the TS suite (js/json) asserts the same vectors so both
1651	/// implementations agree on every snapshot/delta decision and patch shape.
1652	#[test]
1653	fn golden_vectors() {
1654		let vectors: Vec<Vector> = serde_json::from_str(include_str!("../tests/vectors.json")).unwrap();
1655		for case in vectors {
1656			let result = diff(&case.old, &case.new);
1657			assert_eq!(result.forced_snapshot, case.forced, "{}: forced_snapshot", case.name);
1658
1659			if let Some(expected) = case.patch {
1660				assert_eq!(result.patch, expected, "{}: patch", case.name);
1661				let mut applied = case.old.clone();
1662				json_patch::merge(&mut applied, &result.patch);
1663				assert_eq!(applied, case.new, "{}: roundtrip", case.name);
1664			}
1665		}
1666	}
1667
1668	/// Exercise the diff over a sequence of evolving documents, asserting agreement with the oracle
1669	/// and full roundtrip at every step (the way the producer applies deltas).
1670	#[test]
1671	fn evolving_document_matches_oracle() {
1672		let mut docs = Vec::new();
1673		for tick in 0u64..40 {
1674			docs.push(json!({
1675				"id": "device-1",
1676				"static": { "model": "x", "tags": ["a", "b", "c"] },
1677				"counters": { "n": tick, "errors": tick / 10 },
1678				"reading": (tick as f64 * 0.5),
1679				"flags": { "online": tick % 2 == 0, "charging": tick % 3 == 0 },
1680				"list": [tick, tick + 1],
1681			}));
1682		}
1683		for pair in docs.windows(2) {
1684			check(pair[0].clone(), pair[1].clone());
1685		}
1686	}
1687}