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}