Skip to main content

revision/
walk.rs

1//! Structured, selective decoding of revisioned encodings.
2//!
3//! The [`WalkRevisioned`] trait lets callers progress element-by-element through
4//! revisioned bytes, deciding per-element whether to **decode**, **skip**, or
5//! **walk into** further structure. This sits between [`DeserializeRevisioned`]
6//! (decode everything into a value) and [`SkipRevisioned`] (consume bytes
7//! without producing a value).
8//!
9//! [`DeserializeRevisioned`]: crate::DeserializeRevisioned
10//! [`SkipRevisioned`]: crate::SkipRevisioned
11//!
12//! # Shapes
13//!
14//! Walkers are shape-specific:
15//!
16//! - [`LeafWalker`] — primitives that have no internal structure (e.g.
17//!   `u32`, `String`); only `decode` or `skip` are meaningful.
18//! - [`OptionWalker`] — `Option<T>`; peek the tag, then optionally walk into
19//!   the payload.
20//! - [`ResultWalker`] — `Result<T, E>`; peek the variant, then walk into
21//!   either side.
22//! - [`SeqWalker`] — homogeneous sequences (`Vec`, `HashSet`, …); iterate
23//!   items, skip/decode/walk per item.
24//! - [`MapWalker`] — keyed sequences (`HashMap`, `BTreeMap`, …); iterate
25//!   entries, skip/decode key + value individually.
26//! - Per-type walkers generated by `#[revisioned(...)]` for structs/enums.
27//!
28//! # Wire format invariants
29//!
30//! - `#[revisioned(...)]` types prefix their body with a `u16` revision header.
31//! - Enums then encode a `u32` variant discriminant.
32//! - Sequences encode `usize len` then elements.
33//! - Maps encode `usize len` then `(key, value)` pairs.
34//!
35//! Walkers honour these invariants and read the relevant prefix in
36//! `walk_revisioned` before returning so the caller can step over the body.
37
38use std::cmp::Ordering;
39use std::io::Read;
40use std::marker::PhantomData;
41
42use crate::slice_reader::BorrowedReader;
43use crate::{DeserializeRevisioned, Error, Revisioned, SkipRevisioned};
44
45// -----------------------------------------------------------------------------
46// Trait
47// -----------------------------------------------------------------------------
48
49/// Structured, element-by-element traversal of revisioned bytes.
50///
51/// `walk_revisioned` consumes the revision-level prefix (e.g. the `u16`
52/// revision tag emitted by the derive macro for `#[revisioned(...)]` types)
53/// and returns a shape-specific walker positioned at the start of the body.
54///
55/// The associated walker type carries `&'r mut R` and any decoding state. Once
56/// dropped (or `finish`-ed), the reader is positioned past the encoded value.
57///
58/// # Example
59///
60/// ```ignore
61/// use revision::{DeserializeRevisioned, WalkRevisioned, prelude::*};
62///
63/// let mut reader = bytes.as_slice();
64/// let mut entries = <std::collections::BTreeMap<String, u32>>::walk_revisioned(&mut reader)?;
65/// while let Some(mut entry) = entries.next_entry()? {
66///     let key: String = entry.decode_key()?;
67///     if key == "score" {
68///         let value: u32 = entry.decode_value()?;
69///         println!("score = {}", value);
70///     } else {
71///         entry.skip_value()?;
72///     }
73/// }
74/// ```
75pub trait WalkRevisioned: Revisioned {
76	/// Walker produced by [`walk_revisioned`](Self::walk_revisioned).
77	///
78	/// The walker borrows the reader for `'r` and exposes shape-specific
79	/// progression methods.
80	type Walker<'r, R: BorrowedReader + 'r>;
81
82	/// Read the type's revision-level prefix and return a walker positioned at
83	/// the start of the body.
84	///
85	/// The reader must be slice-backed ([`BorrowedReader`]). Callers with a
86	/// `File` or `TcpStream` should buffer the payload into a `Vec<u8>` first
87	/// — revisioned compounds carry their byte-length up front, so the full
88	/// payload has to be present before a walk can begin anyway. Walking on a
89	/// slice-backed source lets the optimised wire format borrow variant
90	/// bodies and indexed payloads directly, avoiding per-walk allocations.
91	fn walk_revisioned<'r, R: BorrowedReader>(
92		reader: &'r mut R,
93	) -> Result<Self::Walker<'r, R>, Error>;
94}
95
96// -----------------------------------------------------------------------------
97// Zero-copy peek primitives
98// -----------------------------------------------------------------------------
99
100/// Marker for revisioned types whose wire format is `usize len || raw bytes`.
101///
102/// Types implementing this trait can have their wire payload **peeked as
103/// `&[u8]`** from a [`BorrowedReader`] without allocating. Walker methods
104/// such as [`LeafWalker::with_bytes`], [`MapWalker::find_bytes`],
105/// [`MapEntry::with_key_bytes`] / [`MapEntry::with_value_bytes`], and
106/// [`SeqItem::with_bytes`] are gated on this trait so callers can compare,
107/// hash, or copy the wire bytes without paying for an intermediate
108/// `String` / `Vec<u8>` / `Bytes`.
109///
110/// The trait does **not** apply to derived `#[revisioned(...)]` types,
111/// which prepend a `u16` revision header to their body. Only types whose
112/// `SerializeRevisioned` impl writes `usize` then raw bytes qualify; this
113/// includes the standard string- and bytes-shaped types
114/// (`String`, `&str`, `Box<str>`, `Arc<str>`, `Cow<'_, str>`, `Vec<u8>`,
115/// `Vec<i8>`, `PathBuf`) and feature-gated types like `bytes::Bytes`.
116///
117/// Downstream crates can opt their newtypes in by adding a one-line impl
118/// (e.g. `impl LengthPrefixedBytes for MyId {}`) when the newtype's
119/// `SerializeRevisioned` ultimately writes `usize len || raw bytes`.
120///
121/// Marking a type incorrectly — when its encoding is **not** exactly that
122/// layout — will mis-parse under [`LeafWalker::with_bytes`],
123/// [`MapWalker::find_bytes`], and related helpers; the trait is a **trusted**
124/// wire-shape contract, not validated at runtime beyond normal deserialization.
125/// That is the same class of hazard as an incorrect manual [`Revisioned`]
126/// implementation: the library cannot prove the marker matches the bytes.
127pub trait LengthPrefixedBytes: Revisioned {}
128
129impl LengthPrefixedBytes for String {}
130// `str` is `?Sized`, so a `LeafWalker<'_, str, R>` cannot exist in
131// practice — this impl is here so the `Cow<'_, T>` blanket impl below
132// can apply to `Cow<'_, str>` (whose `T::Owned` is `String`).
133impl LengthPrefixedBytes for str {}
134impl LengthPrefixedBytes for std::sync::Arc<str> {}
135impl LengthPrefixedBytes for Box<str> {}
136impl LengthPrefixedBytes for std::path::PathBuf {}
137impl LengthPrefixedBytes for Vec<u8> {}
138impl LengthPrefixedBytes for Vec<i8> {}
139
140impl<T> LengthPrefixedBytes for std::borrow::Cow<'_, T>
141where
142	T: ToOwned + Revisioned,
143	T::Owned: LengthPrefixedBytes,
144{
145}
146
147#[cfg(feature = "bytes")]
148impl LengthPrefixedBytes for bytes::Bytes {}
149
150/// Peek a length-prefixed byte payload at the reader's current position
151/// and pass it to `f` as a borrowed slice; advance the reader past the
152/// bytes once `f` returns.
153///
154/// Used by [`LeafWalker::with_bytes`], [`MapEntry::with_key_bytes`] /
155/// [`with_value_bytes`](MapEntry::with_value_bytes), [`SeqItem::with_bytes`],
156/// and [`MapWalker::find_bytes`]. Each of those methods is just a thin
157/// wrapper around this helper plus the appropriate cursor / counter
158/// bookkeeping.
159///
160/// **Panic safety:** if `f` panics, the reader is left positioned between
161/// the consumed length prefix and the un-advanced body bytes. The reader is
162/// in a corrupt state and should not be reused after the unwind. In the
163/// usual case where a panic ends use of the reader this does not matter.
164#[inline]
165pub(crate) fn with_length_prefixed_bytes<R, F, T>(reader: &mut R, f: F) -> Result<T, Error>
166where
167	R: BorrowedReader,
168	F: FnOnce(&[u8]) -> T,
169{
170	let len = usize::deserialize_revisioned(reader)?;
171	let result = {
172		let bytes = reader.peek_bytes(len)?;
173		f(bytes)
174	};
175	reader.advance(len)?;
176	Ok(result)
177}
178
179// -----------------------------------------------------------------------------
180// LeafWalker — primitives and length-prefixed scalars
181// -----------------------------------------------------------------------------
182
183/// Walker for a primitive (or otherwise structurally opaque) revisioned type.
184///
185/// Only `decode` and `skip` are meaningful; there is no internal structure to
186/// step through.
187pub struct LeafWalker<'r, T, R: BorrowedReader + 'r> {
188	reader: &'r mut R,
189	_marker: PhantomData<fn() -> T>,
190}
191
192impl<'r, T, R: BorrowedReader + 'r> LeafWalker<'r, T, R> {
193	/// Construct a leaf walker; intended for use by [`WalkRevisioned`] impls.
194	#[inline]
195	pub fn new(reader: &'r mut R) -> Self {
196		Self {
197			reader,
198			_marker: PhantomData,
199		}
200	}
201
202	/// Decode the value, advancing the reader past it.
203	#[inline]
204	pub fn decode(self) -> Result<T, Error>
205	where
206		T: DeserializeRevisioned,
207	{
208		T::deserialize_revisioned(self.reader)
209	}
210
211	/// Skip the value, advancing the reader past it.
212	#[inline]
213	pub fn skip(self) -> Result<(), Error>
214	where
215		T: SkipRevisioned,
216	{
217		T::skip_revisioned(self.reader)
218	}
219
220	/// Walk into the value as a structured walker. Equivalent to calling
221	/// `T::walk_revisioned` directly on the underlying reader; provided so
222	/// `LeafWalker` doubles as a "pre-walk handle" that lets the caller
223	/// choose between decoding, skipping, or descending without committing
224	/// up front.
225	#[inline]
226	pub fn walk(self) -> Result<T::Walker<'r, R>, Error>
227	where
228		T: WalkRevisioned,
229	{
230		T::walk_revisioned(self.reader)
231	}
232
233	/// Borrow the underlying reader. Useful for hand-rolled walkers that need
234	/// to read auxiliary state before continuing.
235	#[inline]
236	pub fn reader(&mut self) -> &mut R {
237		self.reader
238	}
239
240	/// Surrender the underlying reader without consuming the value.
241	#[inline]
242	pub fn into_reader(self) -> &'r mut R {
243		self.reader
244	}
245}
246
247impl<'r, T, R> LeafWalker<'r, T, R>
248where
249	T: LengthPrefixedBytes,
250	R: BorrowedReader + 'r,
251{
252	/// Inspect the leaf's wire bytes without allocating, calling `f` with
253	/// a slice borrowed from the underlying buffer; the reader is
254	/// advanced past the bytes after `f` returns.
255	///
256	/// Only available when the underlying reader is slice-backed
257	/// ([`BorrowedReader`]) and the value's wire format is exactly
258	/// `usize len || raw bytes` ([`LengthPrefixedBytes`]). For decoded
259	/// access use [`decode`](Self::decode); for fall-through advancement
260	/// use [`skip`](Self::skip).
261	#[inline]
262	pub fn with_bytes<F, U>(self, f: F) -> Result<U, Error>
263	where
264		F: FnOnce(&[u8]) -> U,
265	{
266		with_length_prefixed_bytes(self.reader, f)
267	}
268}
269
270// -----------------------------------------------------------------------------
271// OptionWalker — Option<T>
272// -----------------------------------------------------------------------------
273
274/// Walker for `Option<T>`.
275///
276/// The wire format is a `u8` tag (`0` = `None`, `1` = `Some`) followed by an
277/// inner `T` payload when present.
278pub struct OptionWalker<'r, T, R: BorrowedReader + 'r> {
279	reader: &'r mut R,
280	tag: u8,
281	_marker: PhantomData<fn() -> T>,
282}
283
284impl<'r, T, R: BorrowedReader + 'r> OptionWalker<'r, T, R> {
285	/// Construct an option walker. Reads the `u8` tag from `reader`.
286	#[inline]
287	pub fn new(reader: &'r mut R) -> Result<Self, Error> {
288		let tag = u8::deserialize_revisioned(reader)?;
289		match tag {
290			0 | 1 => Ok(Self {
291				reader,
292				tag,
293				_marker: PhantomData,
294			}),
295			v => Err(Error::Deserialize(format!("Invalid option value {v}"))),
296		}
297	}
298
299	/// `true` when the value is `Some(_)` and the inner walker can be entered.
300	#[inline]
301	pub fn is_some(&self) -> bool {
302		self.tag == 1
303	}
304
305	/// `true` when the value is `None`.
306	#[inline]
307	pub fn is_none(&self) -> bool {
308		self.tag == 0
309	}
310
311	/// Decode the value, advancing the reader past it.
312	#[inline]
313	pub fn decode(self) -> Result<Option<T>, Error>
314	where
315		T: DeserializeRevisioned,
316	{
317		match self.tag {
318			0 => Ok(None),
319			1 => Ok(Some(T::deserialize_revisioned(self.reader)?)),
320			v => Err(Error::Deserialize(format!("Invalid option value {v}"))),
321		}
322	}
323
324	/// Skip the value, advancing the reader past it.
325	#[inline]
326	pub fn skip(self) -> Result<(), Error>
327	where
328		T: SkipRevisioned,
329	{
330		if self.tag == 1 {
331			T::skip_revisioned(self.reader)?;
332		}
333		Ok(())
334	}
335
336	/// Walk into the `Some(_)` payload. Returns an error if the value is `None`.
337	#[inline]
338	pub fn into_some(self) -> Result<T::Walker<'r, R>, Error>
339	where
340		T: WalkRevisioned,
341	{
342		match self.tag {
343			0 => Err(Error::Deserialize("Cannot walk into None payload".into())),
344			1 => T::walk_revisioned(self.reader),
345			v => Err(Error::Deserialize(format!("Invalid option value {v}"))),
346		}
347	}
348}
349
350// -----------------------------------------------------------------------------
351// ResultWalker — Result<T, E>
352// -----------------------------------------------------------------------------
353
354/// Walker for `Result<T, E>`.
355///
356/// The wire format is a `u32` tag (`0` = `Ok(T)`, `1` = `Err(E)`) followed by
357/// the corresponding payload.
358pub struct ResultWalker<'r, T, E, R: BorrowedReader + 'r> {
359	reader: &'r mut R,
360	tag: u32,
361	_marker: PhantomData<fn() -> Result<T, E>>,
362}
363
364impl<'r, T, E, R: BorrowedReader + 'r> ResultWalker<'r, T, E, R> {
365	/// Construct a result walker. Reads the `u32` variant tag from `reader`.
366	#[inline]
367	pub fn new(reader: &'r mut R) -> Result<Self, Error> {
368		let tag = u32::deserialize_revisioned(reader)?;
369		match tag {
370			0 | 1 => Ok(Self {
371				reader,
372				tag,
373				_marker: PhantomData,
374			}),
375			v => Err(Error::Deserialize(format!("Unknown Result variant index {v}"))),
376		}
377	}
378
379	/// `true` when the value is `Ok(_)`.
380	#[inline]
381	pub fn is_ok(&self) -> bool {
382		self.tag == 0
383	}
384
385	/// `true` when the value is `Err(_)`.
386	#[inline]
387	pub fn is_err(&self) -> bool {
388		self.tag == 1
389	}
390
391	/// Decode the value, advancing the reader past it.
392	#[inline]
393	pub fn decode(self) -> Result<Result<T, E>, Error>
394	where
395		T: DeserializeRevisioned,
396		E: DeserializeRevisioned,
397	{
398		match self.tag {
399			0 => Ok(Ok(T::deserialize_revisioned(self.reader)?)),
400			1 => Ok(Err(E::deserialize_revisioned(self.reader)?)),
401			v => Err(Error::Deserialize(format!("Unknown Result variant index {v}"))),
402		}
403	}
404
405	/// Skip the value, advancing the reader past it.
406	#[inline]
407	pub fn skip(self) -> Result<(), Error>
408	where
409		T: SkipRevisioned,
410		E: SkipRevisioned,
411	{
412		match self.tag {
413			0 => T::skip_revisioned(self.reader),
414			1 => E::skip_revisioned(self.reader),
415			v => Err(Error::Deserialize(format!("Unknown Result variant index {v}"))),
416		}
417	}
418
419	/// Walk into the `Ok(_)` payload. Errors if the value is `Err`.
420	#[inline]
421	pub fn into_ok(self) -> Result<T::Walker<'r, R>, Error>
422	where
423		T: WalkRevisioned,
424	{
425		match self.tag {
426			0 => T::walk_revisioned(self.reader),
427			1 => Err(Error::Deserialize("Cannot walk into Err as Ok".into())),
428			v => Err(Error::Deserialize(format!("Unknown Result variant index {v}"))),
429		}
430	}
431
432	/// Walk into the `Err(_)` payload. Errors if the value is `Ok`.
433	#[inline]
434	pub fn into_err(self) -> Result<E::Walker<'r, R>, Error>
435	where
436		E: WalkRevisioned,
437	{
438		match self.tag {
439			0 => Err(Error::Deserialize("Cannot walk into Ok as Err".into())),
440			1 => E::walk_revisioned(self.reader),
441			v => Err(Error::Deserialize(format!("Unknown Result variant index {v}"))),
442		}
443	}
444}
445
446// -----------------------------------------------------------------------------
447// SeqWalker — Vec<T>, HashSet<T>, BTreeSet<T>, BinaryHeap<T>, …
448// -----------------------------------------------------------------------------
449
450/// Walker for a homogeneous sequence (length-prefix followed by `T` elements).
451///
452/// All collections whose [`SerializeRevisioned`] writes `usize len || T per
453/// element` use this walker — `HashSet<T>`, `BTreeSet<T>`, `BinaryHeap<T>`,
454/// `imbl::{Vector, OrdSet, HashSet}`, and `Vec<T>` for non-bulk element
455/// types.
456///
457/// **Caveat for `Vec<T>` only:** with the `specialised-vectors` feature
458/// (enabled by default), [`Vec<T>`](crate::implementations::vecs) uses a
459/// compact bulk layout for primitive numeric types, `bool`, and (when the
460/// optional `uuid` / `rust_decimal` features are enabled) `uuid::Uuid` /
461/// `rust_decimal::Decimal`. [`Vec<T>::walk_revisioned`] rejects those
462/// element types up front; sets and heaps are unaffected because they
463/// always use per-element framing.
464///
465/// [`SerializeRevisioned`]: crate::SerializeRevisioned
466pub struct SeqWalker<'r, T, R: BorrowedReader + 'r> {
467	reader: &'r mut R,
468	remaining: usize,
469	_marker: PhantomData<fn() -> T>,
470}
471
472impl<'r, T, R: BorrowedReader + 'r> SeqWalker<'r, T, R> {
473	/// Construct a sequence walker. Reads the `usize` length prefix.
474	#[inline]
475	pub fn new(reader: &'r mut R) -> Result<Self, Error> {
476		let len = usize::deserialize_revisioned(reader)?;
477		Ok(Self {
478			reader,
479			remaining: len,
480			_marker: PhantomData,
481		})
482	}
483
484	/// Number of items still to be visited.
485	#[inline]
486	pub fn remaining(&self) -> usize {
487		self.remaining
488	}
489
490	/// Yield the next item handle, if any.
491	///
492	/// The returned [`SeqItem`] borrows the walker until it is consumed
493	/// (`decode`, `skip`, or `walk`).
494	#[inline]
495	pub fn next_item(&mut self) -> Option<SeqItem<'_, 'r, T, R>> {
496		if self.remaining == 0 {
497			None
498		} else {
499			Some(SeqItem {
500				walker: self,
501			})
502		}
503	}
504
505	/// Skip every remaining item.
506	#[inline]
507	pub fn skip_remaining(mut self) -> Result<(), Error>
508	where
509		T: SkipRevisioned,
510	{
511		while self.remaining > 0 {
512			T::skip_revisioned(self.reader)?;
513			self.remaining -= 1;
514		}
515		Ok(())
516	}
517}
518
519/// One item position inside a [`SeqWalker`].
520pub struct SeqItem<'a, 'r, T, R: BorrowedReader + 'r> {
521	walker: &'a mut SeqWalker<'r, T, R>,
522}
523
524impl<'a, 'r, T, R: BorrowedReader + 'r> SeqItem<'a, 'r, T, R> {
525	/// Decode this item.
526	#[inline]
527	pub fn decode(self) -> Result<T, Error>
528	where
529		T: DeserializeRevisioned,
530	{
531		let v = T::deserialize_revisioned(self.walker.reader)?;
532		self.walker.remaining -= 1;
533		Ok(v)
534	}
535
536	/// Skip this item.
537	#[inline]
538	pub fn skip(self) -> Result<(), Error>
539	where
540		T: SkipRevisioned,
541	{
542		T::skip_revisioned(self.walker.reader)?;
543		self.walker.remaining -= 1;
544		Ok(())
545	}
546
547	/// Walk into this item, returning a sub-walker borrowing the parent.
548	///
549	/// The parent walker cannot advance until the returned walker is dropped
550	/// or consumed. After it is dropped, the next call to
551	/// [`SeqWalker::next_item`] yields the following item.
552	#[inline]
553	pub fn walk(self) -> Result<T::Walker<'a, R>, Error>
554	where
555		T: WalkRevisioned,
556	{
557		let walker = T::walk_revisioned(self.walker.reader)?;
558		self.walker.remaining -= 1;
559		Ok(walker)
560	}
561}
562
563impl<'a, 'r, T, R> SeqItem<'a, 'r, T, R>
564where
565	T: LengthPrefixedBytes,
566	R: BorrowedReader + 'r,
567{
568	/// Inspect the item's wire bytes without allocating, calling `f` with
569	/// a slice borrowed from the underlying buffer; the reader is
570	/// advanced past the item once `f` returns and the seq walker's
571	/// remaining counter is decremented.
572	///
573	/// Useful for searching sorted sequences (`Vec<String>`,
574	/// `BTreeSet<Bytes>`, …) by byte compare without paying for a
575	/// per-item decode allocation.
576	#[inline]
577	pub fn with_bytes<F, U>(self, f: F) -> Result<U, Error>
578	where
579		F: FnOnce(&[u8]) -> U,
580	{
581		let result = with_length_prefixed_bytes(self.walker.reader, f)?;
582		self.walker.remaining -= 1;
583		Ok(result)
584	}
585}
586
587// -----------------------------------------------------------------------------
588// MapWalker — HashMap<K, V>, BTreeMap<K, V>, …
589// -----------------------------------------------------------------------------
590
591/// Walker for a `(K, V)` map (length-prefix followed by alternating
592/// key/value encodings).
593pub struct MapWalker<'r, K, V, R: BorrowedReader + 'r> {
594	reader: &'r mut R,
595	remaining: usize,
596	_marker: PhantomData<fn() -> (K, V)>,
597}
598
599/// Where the cursor sits within a single [`MapEntry`] iteration.
600#[derive(Clone, Copy, Debug, Eq, PartialEq)]
601enum MapCursor {
602	/// Reader is positioned at the start of the key.
603	BeforeKey,
604	/// Reader is positioned at the start of the value.
605	BeforeValue,
606}
607
608impl<'r, K, V, R: BorrowedReader + 'r> MapWalker<'r, K, V, R> {
609	/// Construct a map walker. Reads the `usize` length prefix.
610	#[inline]
611	pub fn new(reader: &'r mut R) -> Result<Self, Error> {
612		let len = usize::deserialize_revisioned(reader)?;
613		Ok(Self {
614			reader,
615			remaining: len,
616			_marker: PhantomData,
617		})
618	}
619
620	/// Number of entries still to be visited.
621	#[inline]
622	pub fn remaining(&self) -> usize {
623		self.remaining
624	}
625
626	/// Borrow the next entry, if any.
627	///
628	/// The caller must consume the returned [`MapEntry`] (call one of
629	/// `decode_value`, `skip_value`, or `walk_value`) before calling
630	/// `next_entry` again. Forgetting to consume the entry's value is a
631	/// programming error: the next entry will start mid-payload.
632	#[inline]
633	pub fn next_entry(&mut self) -> Option<MapEntry<'_, 'r, K, V, R>> {
634		if self.remaining == 0 {
635			None
636		} else {
637			Some(MapEntry {
638				walker: self,
639				cursor: MapCursor::BeforeKey,
640			})
641		}
642	}
643
644	/// Skip every remaining entry.
645	#[inline]
646	pub fn skip_remaining(mut self) -> Result<(), Error>
647	where
648		K: SkipRevisioned,
649		V: SkipRevisioned,
650	{
651		while self.remaining > 0 {
652			K::skip_revisioned(self.reader)?;
653			V::skip_revisioned(self.reader)?;
654			self.remaining -= 1;
655		}
656		Ok(())
657	}
658
659	/// Linear search by **decoded** key, returning a value handle for the
660	/// matching entry or `None` if no entry passes `predicate`.
661	///
662	/// **Sorted maps only:** The predicate is compared with **encoded key order**
663	/// as visited on the wire (the order [`BTreeMap`](std::collections::BTreeMap)
664	/// serialises). Using this on bytes produced from a [`HashMap`](std::collections::HashMap)
665	/// while assuming lexicographic or sorted-key ordering is invalid: you may match
666	/// the wrong entry or drop the tail under `Ordering::Greater` while the true key
667	/// still appears later in the stream.
668	///
669	/// The returned [`LeafWalker`] is positioned at the start of the value's
670	/// encoding (i.e. the type-level prefix has not been read yet); the
671	/// caller can then call `decode`, `skip`, or `walk` on it.
672	///
673	/// Every strictly preceding entry (full key and value) is skipped.
674	/// For the matching entry, only the key has been read off the wire;
675	/// the value remains for the leaf walker. Entries that sort **after**
676	/// the matched key stay on the reader, but this method consumes `self`,
677	/// so you cannot call [`next_entry`](Self::next_entry) afterward — there
678	/// is no way to resume the same [`MapWalker`]. Prefer streaming iteration
679	/// if you must handle one hit then walk the tail.
680	///
681	/// On `None` (no match) the entire remainder of the map is consumed.
682	#[inline]
683	pub fn find<F>(mut self, mut predicate: F) -> Result<Option<LeafWalker<'r, V, R>>, Error>
684	where
685		K: DeserializeRevisioned + SkipRevisioned,
686		V: SkipRevisioned,
687		F: FnMut(&K) -> Ordering,
688	{
689		while self.remaining > 0 {
690			let key = K::deserialize_revisioned(self.reader)?;
691			match predicate(&key) {
692				Ordering::Less => {
693					V::skip_revisioned(self.reader)?;
694					self.remaining -= 1;
695				}
696				Ordering::Equal => {
697					return Ok(Some(LeafWalker::new(self.reader)));
698				}
699				Ordering::Greater => {
700					V::skip_revisioned(self.reader)?;
701					self.remaining -= 1;
702					while self.remaining > 0 {
703						K::skip_revisioned(self.reader)?;
704						V::skip_revisioned(self.reader)?;
705						self.remaining -= 1;
706					}
707					return Ok(None);
708				}
709			}
710		}
711		Ok(None)
712	}
713}
714
715impl<'r, K, V, R> MapWalker<'r, K, V, R>
716where
717	K: LengthPrefixedBytes + SkipRevisioned,
718	V: SkipRevisioned,
719	R: BorrowedReader + 'r,
720{
721	/// Linear search by **byte-borrowed** key, returning a value handle for
722	/// the matching entry or `None` if no entry passes `predicate`.
723	///
724	/// Inherits the **sorted-map** requirement from [`find`](Self::find): wire
725	/// visit order must match the predicate's assumed ordering (see
726	/// [`find`](Self::find)).
727	///
728	/// Sibling of [`find`](Self::find) for keys whose wire format is
729	/// exactly `usize len || raw bytes` ([`LengthPrefixedBytes`]). Each
730	/// visited key is presented to `predicate` as a borrowed `&[u8]`
731	/// without being decoded into `K`, so a 128-key scan over a
732	/// `BTreeMap<Strand, _>` does zero allocations regardless of key
733	/// length.
734	///
735	/// Behaviour identical to [`find`](Self::find) otherwise: the
736	/// `Less / Equal / Greater` control flow assumes the underlying map
737	/// is sorted by key; `Equal` returns a leaf at the matched value and
738	/// consumes `self` (see [`find`](Self::find)); on no-match the entire
739	/// remainder is consumed.
740	#[inline]
741	pub fn find_bytes<F>(mut self, mut predicate: F) -> Result<Option<LeafWalker<'r, V, R>>, Error>
742	where
743		F: FnMut(&[u8]) -> Ordering,
744	{
745		while self.remaining > 0 {
746			let key_len = usize::deserialize_revisioned(self.reader)?;
747			let cmp = {
748				let key_bytes = self.reader.peek_bytes(key_len)?;
749				predicate(key_bytes)
750			};
751			self.reader.advance(key_len)?;
752			match cmp {
753				Ordering::Less => {
754					V::skip_revisioned(self.reader)?;
755					self.remaining -= 1;
756				}
757				Ordering::Equal => {
758					return Ok(Some(LeafWalker::new(self.reader)));
759				}
760				Ordering::Greater => {
761					V::skip_revisioned(self.reader)?;
762					self.remaining -= 1;
763					while self.remaining > 0 {
764						K::skip_revisioned(self.reader)?;
765						V::skip_revisioned(self.reader)?;
766						self.remaining -= 1;
767					}
768					return Ok(None);
769				}
770			}
771		}
772		Ok(None)
773	}
774}
775
776/// One key+value pair inside a [`MapWalker`].
777///
778/// The cursor is positioned at the start of the key. The caller must first
779/// handle the key (`decode_key` or `skip_key`) and then the value
780/// (`decode_value`, `skip_value`, or `walk_value`). `decode_pair` and
781/// `skip_pair` shortcut both at once.
782///
783/// Calling methods out of order returns [`Error::Deserialize`] in all builds
784/// (not only debug builds), without advancing the reader when the check fails
785/// before I/O.
786pub struct MapEntry<'a, 'r, K, V, R: BorrowedReader + 'r> {
787	walker: &'a mut MapWalker<'r, K, V, R>,
788	cursor: MapCursor,
789}
790
791impl<'a, 'r, K, V, R: BorrowedReader + 'r> MapEntry<'a, 'r, K, V, R> {
792	fn expect_cursor(&self, expected: MapCursor, operation: &'static str) -> Result<(), Error> {
793		if self.cursor != expected {
794			return Err(Error::Deserialize(format!(
795				"MapEntry: invalid cursor for {operation} (expected {expected:?}, found {:?})",
796				self.cursor,
797			)));
798		}
799		Ok(())
800	}
801
802	/// Decode the key. The cursor advances to the value.
803	#[inline]
804	pub fn decode_key(&mut self) -> Result<K, Error>
805	where
806		K: DeserializeRevisioned,
807	{
808		self.expect_cursor(MapCursor::BeforeKey, "decode_key")?;
809		let k = K::deserialize_revisioned(self.walker.reader)?;
810		self.cursor = MapCursor::BeforeValue;
811		Ok(k)
812	}
813
814	/// Skip the key. The cursor advances to the value.
815	#[inline]
816	pub fn skip_key(&mut self) -> Result<(), Error>
817	where
818		K: SkipRevisioned,
819	{
820		self.expect_cursor(MapCursor::BeforeKey, "skip_key")?;
821		K::skip_revisioned(self.walker.reader)?;
822		self.cursor = MapCursor::BeforeValue;
823		Ok(())
824	}
825
826	/// Decode the value (key must already be consumed).
827	#[inline]
828	pub fn decode_value(self) -> Result<V, Error>
829	where
830		V: DeserializeRevisioned,
831	{
832		self.expect_cursor(MapCursor::BeforeValue, "decode_value")?;
833		let v = V::deserialize_revisioned(self.walker.reader)?;
834		self.walker.remaining -= 1;
835		Ok(v)
836	}
837
838	/// Skip the value (key must already be consumed).
839	#[inline]
840	pub fn skip_value(self) -> Result<(), Error>
841	where
842		V: SkipRevisioned,
843	{
844		self.expect_cursor(MapCursor::BeforeValue, "skip_value")?;
845		V::skip_revisioned(self.walker.reader)?;
846		self.walker.remaining -= 1;
847		Ok(())
848	}
849
850	/// Walk into the value. The parent map walker is borrowed for the
851	/// returned walker's lifetime.
852	#[inline]
853	pub fn walk_value(self) -> Result<V::Walker<'a, R>, Error>
854	where
855		V: WalkRevisioned,
856	{
857		self.expect_cursor(MapCursor::BeforeValue, "walk_value")?;
858		let w = V::walk_revisioned(self.walker.reader)?;
859		self.walker.remaining -= 1;
860		Ok(w)
861	}
862
863	/// Decode key and value together, advancing past the entry.
864	#[inline]
865	pub fn decode_pair(mut self) -> Result<(K, V), Error>
866	where
867		K: DeserializeRevisioned,
868		V: DeserializeRevisioned,
869	{
870		let key = self.decode_key()?;
871		let value = self.decode_value()?;
872		Ok((key, value))
873	}
874
875	/// Skip key and value together, advancing past the entry.
876	#[inline]
877	pub fn skip_pair(mut self) -> Result<(), Error>
878	where
879		K: SkipRevisioned,
880		V: SkipRevisioned,
881	{
882		self.skip_key()?;
883		self.skip_value()
884	}
885}
886
887impl<'a, 'r, K, V, R> MapEntry<'a, 'r, K, V, R>
888where
889	R: BorrowedReader + 'r,
890{
891	/// Inspect the entry's key as raw wire bytes without allocating.
892	///
893	/// Available when the key type's wire format is `usize len || raw
894	/// bytes` ([`LengthPrefixedBytes`]) and the reader is slice-backed
895	/// ([`BorrowedReader`]). The cursor advances past the key once `f`
896	/// returns; the caller must then handle the value
897	/// (`decode_value` / `skip_value` / `walk_value`).
898	///
899	/// Cheaper sibling of [`decode_key`](Self::decode_key) for the
900	/// common case "compare key bytes against a needle and decide what
901	/// to do with the value".
902	#[inline]
903	pub fn with_key_bytes<F, U>(&mut self, f: F) -> Result<U, Error>
904	where
905		K: LengthPrefixedBytes,
906		F: FnOnce(&[u8]) -> U,
907	{
908		self.expect_cursor(MapCursor::BeforeKey, "with_key_bytes")?;
909		let result = with_length_prefixed_bytes(self.walker.reader, f)?;
910		self.cursor = MapCursor::BeforeValue;
911		Ok(result)
912	}
913
914	/// Inspect the entry's value as raw wire bytes without allocating
915	/// (key must already be consumed).
916	///
917	/// Available when the value type's wire format is `usize len || raw
918	/// bytes` ([`LengthPrefixedBytes`]) and the reader is slice-backed
919	/// ([`BorrowedReader`]). Consumes the entry; the entry's
920	/// `remaining` counter is decremented.
921	///
922	/// Cheaper sibling of [`decode_value`](Self::decode_value) for the
923	/// "filter map by value bytes" pattern.
924	#[inline]
925	pub fn with_value_bytes<F, U>(self, f: F) -> Result<U, Error>
926	where
927		V: LengthPrefixedBytes,
928		F: FnOnce(&[u8]) -> U,
929	{
930		self.expect_cursor(MapCursor::BeforeValue, "with_value_bytes")?;
931		let result = with_length_prefixed_bytes(self.walker.reader, f)?;
932		self.walker.remaining -= 1;
933		Ok(result)
934	}
935}
936
937// -----------------------------------------------------------------------------
938// StructWalker — generic positional walker for `#[revisioned(...)]` structs.
939//
940// The walker tracks the wire revision and a logical field index (`position`).
941// After [`StructWalker::walk`], that counter advances even though the nested
942// walker still owns the remainder of that field on the wire until dropped.
943// -----------------------------------------------------------------------------
944
945/// Generic walker for revisioned structs.
946///
947/// Field-type information is supplied by the caller per call rather than
948/// stored in the walker. The caller is responsible for invoking field methods
949/// in the wire order and matching the schema at the wire revision.
950///
951/// [`position`](Self::position) counts fields **started** (`decode`, `skip`, or
952/// `walk`), not necessarily bytes fully consumed when the last operation was
953/// [`walk`](Self::walk).
954pub struct StructWalker<'r, R: BorrowedReader + 'r> {
955	reader: &'r mut R,
956	revision: u16,
957	position: u32,
958}
959
960impl<'r, R: BorrowedReader + 'r> StructWalker<'r, R> {
961	/// Construct a struct walker. The caller must already have read the
962	/// type-level revision header.
963	#[inline]
964	pub fn new(reader: &'r mut R, revision: u16) -> Self {
965		Self {
966			reader,
967			revision,
968			position: 0,
969		}
970	}
971
972	/// Wire revision of the encoded value being walked.
973	#[inline]
974	pub fn revision(&self) -> u16 {
975		self.revision
976	}
977
978	/// Count of fields for which [`decode`](Self::decode), [`skip`](Self::skip),
979	/// or [`walk`](Self::walk) has succeeded in wire order (starts at `0`;
980	/// increments by one per call).
981	///
982	/// This tracks which schema slot you will touch next; it does **not** imply
983	/// the previous field's bytes are fully past on the reader when that field was
984	/// opened with [`walk`](Self::walk). In that case [`position`](Self::position)
985	/// bumps as soon as the nested walker is constructed, while the child still
986	/// borrows the reader for the rest of that field until it is dropped.
987	#[inline]
988	pub fn position(&self) -> u32 {
989		self.position
990	}
991
992	/// Decode the next field as type `T`.
993	#[inline]
994	pub fn decode<T: DeserializeRevisioned>(&mut self) -> Result<T, Error> {
995		let v = T::deserialize_revisioned(self.reader)?;
996		self.position += 1;
997		Ok(v)
998	}
999
1000	/// Skip the next field as type `T`.
1001	#[inline]
1002	pub fn skip<T: SkipRevisioned>(&mut self) -> Result<(), Error> {
1003		T::skip_revisioned(self.reader)?;
1004		self.position += 1;
1005		Ok(())
1006	}
1007
1008	/// Walk into the next field as type `T`. The returned walker borrows
1009	/// from `&mut self`; the parent walker can resume after it is dropped.
1010	///
1011	/// [`position`](Self::position) increments when this returns `Ok`, before the
1012	/// nested walker consumes the field payload — do not treat a higher position
1013	/// as “the reader has left that field” until the child walker finishes.
1014	#[inline]
1015	pub fn walk<T: WalkRevisioned>(&mut self) -> Result<T::Walker<'_, R>, Error> {
1016		let w = T::walk_revisioned(self.reader)?;
1017		self.position += 1;
1018		Ok(w)
1019	}
1020
1021	/// Consume the struct walker and walk into the next field as type `T`,
1022	/// transferring ownership of the underlying reader to the returned
1023	/// walker. Subsequent fields cannot be walked.
1024	#[inline]
1025	pub fn into_walk<T: WalkRevisioned>(self) -> Result<T::Walker<'r, R>, Error> {
1026		T::walk_revisioned(self.reader)
1027	}
1028
1029	/// Borrow the underlying reader. Useful for hand-rolled walkers needing
1030	/// auxiliary reads.
1031	#[inline]
1032	pub fn reader(&mut self) -> &mut R {
1033		self.reader
1034	}
1035
1036	/// Surrender the underlying reader after the caller has manually
1037	/// consumed every remaining field.
1038	#[inline]
1039	pub fn into_reader(self) -> &'r mut R {
1040		self.reader
1041	}
1042}
1043
1044// -----------------------------------------------------------------------------
1045// EnumWalker — generic walker for `#[revisioned(...)]` enums.
1046//
1047// Reads the `u32` variant discriminant on construction; the caller dispatches
1048// on it and supplies the payload type to `decode`, `skip`, or `into_walk`.
1049// -----------------------------------------------------------------------------
1050
1051/// Generic walker for revisioned enums.
1052///
1053/// On construction the walker reads the `u32` variant discriminant. The
1054/// caller dispatches on `discriminant()` (or via a derive-generated table)
1055/// and supplies the appropriate payload type to one of the consume methods.
1056pub struct EnumWalker<'r, R: BorrowedReader + 'r> {
1057	reader: &'r mut R,
1058	revision: u16,
1059	discriminant: u32,
1060}
1061
1062impl<'r, R: BorrowedReader + 'r> EnumWalker<'r, R> {
1063	/// Construct an enum walker. Reads the `u32` variant discriminant from
1064	/// `reader`. The caller must already have read the type-level revision
1065	/// header.
1066	#[inline]
1067	pub fn new(reader: &'r mut R, revision: u16) -> Result<Self, Error> {
1068		let discriminant = u32::deserialize_revisioned(reader)?;
1069		Ok(Self {
1070			reader,
1071			revision,
1072			discriminant,
1073		})
1074	}
1075
1076	/// Wire revision of the encoded value being walked.
1077	#[inline]
1078	pub fn revision(&self) -> u16 {
1079		self.revision
1080	}
1081
1082	/// Variant discriminant on the wire.
1083	#[inline]
1084	pub fn discriminant(&self) -> u32 {
1085		self.discriminant
1086	}
1087
1088	/// Decode the variant payload as type `T`. Caller must supply the type
1089	/// matching the variant at this discriminant.
1090	#[inline]
1091	pub fn decode<T: DeserializeRevisioned>(self) -> Result<T, Error> {
1092		T::deserialize_revisioned(self.reader)
1093	}
1094
1095	/// Skip the variant payload as type `T`.
1096	#[inline]
1097	pub fn skip<T: SkipRevisioned>(self) -> Result<(), Error> {
1098		T::skip_revisioned(self.reader)
1099	}
1100
1101	/// Walk into the variant payload as type `T`, transferring ownership
1102	/// of the underlying reader to the returned walker.
1103	#[inline]
1104	pub fn into_walk<T: WalkRevisioned>(self) -> Result<T::Walker<'r, R>, Error> {
1105		T::walk_revisioned(self.reader)
1106	}
1107
1108	/// Borrow the underlying reader.
1109	#[inline]
1110	pub fn reader(&mut self) -> &mut R {
1111		self.reader
1112	}
1113
1114	/// Surrender the underlying reader without consuming the payload.
1115	#[inline]
1116	pub fn into_reader(self) -> &'r mut R {
1117		self.reader
1118	}
1119}
1120
1121// -----------------------------------------------------------------------------
1122// Convenience walkers used by enums (Option/Result already specialised above)
1123// -----------------------------------------------------------------------------
1124
1125/// Internal helper that reads the `u32` discriminant of an enum body and
1126/// hands the reader back. Intended for hand-rolled walkers; the derive macro
1127/// emits the same prelude inline.
1128#[inline]
1129pub fn read_enum_discriminant<R: Read>(reader: &mut R) -> Result<u32, Error> {
1130	u32::deserialize_revisioned(reader)
1131}