wolfram_serialize/reader.rs
1//! Minimal byte-level reader.
2//!
3//! [`Reader`] is the raw input abstraction: one required method, `read_bytes`,
4//! which hands back a **zero-copy view** into the source — no allocation, no
5//! copy. The view is tied to the buffer lifetime `'de`, so it outlives the
6//! `&mut self` borrow; that is what enables zero-copy *borrowed* deserialization
7//! (a `&'de str` / `&'de [u8]` field can point straight into the input buffer).
8//!
9//! The default [`SliceReader`] is backed by a `&[u8]`, which is exactly the
10//! LibraryLink case (a fully-materialized buffer): build one over
11//! `numeric_array.as_slice()` and WXF values are read straight out of kernel
12//! memory.
13//!
14//! We keep our own trait rather than `std::io::Read` because `io::Read` copies
15//! into a caller buffer and cannot lend a buffer-lifetime view. (And a *copying*
16//! reader would be useless anyway: [`FromWXF`][crate::FromWXF] needs `&'de`
17//! views, which an `io::Read` source can't produce — zero-copy and streaming are
18//! incompatible.)
19
20use crate::Error;
21
22/// Raw byte source that lends **buffer-lifetime** views. `'de` is the lifetime
23/// of the underlying buffer. Reads consume forward; there is no rewind, no peek.
24///
25/// Only slice-backed readers (where all the data is already present) can
26/// implement this, since `read_bytes` must return a view that outlives the
27/// `&mut self` borrow.
28pub trait Reader<'de> {
29 /// Consume `n` bytes, returning a zero-copy view tied to the underlying
30 /// buffer (lifetime `'de`). The copy path treats it as a transient slice;
31 /// the borrow path (`&'de str` / `&'de [u8]`) retains it.
32 fn read_bytes(&mut self, n: usize) -> Result<&'de [u8], Error>;
33
34 /// Consume and return the next byte.
35 fn read_byte(&mut self) -> Result<u8, Error> {
36 Ok(self.read_bytes(1)?[0])
37 }
38}
39
40/// Slice-backed [`Reader`]: holds `&[u8]` plus a position. Every read is a
41/// bounds-checked sub-slice — no allocation, no copy.
42pub struct SliceReader<'a> {
43 bytes: &'a [u8],
44 pos: usize,
45}
46
47impl<'a> SliceReader<'a> {
48 /// Construct over an in-memory byte buffer.
49 pub fn new(bytes: &'a [u8]) -> Self {
50 SliceReader { bytes, pos: 0 }
51 }
52}
53
54impl<'de> Reader<'de> for SliceReader<'de> {
55 fn read_bytes(&mut self, n: usize) -> Result<&'de [u8], Error> {
56 let end = self
57 .pos
58 .checked_add(n)
59 .ok_or_else(|| Error::invalid("byte count overflow".into()))?;
60 // Copy out the `&'de [u8]` reference first so the returned slice is tied
61 // to the buffer lifetime `'de`, not to this `&mut self` borrow.
62 let buf: &'de [u8] = self.bytes;
63 let slice = buf.get(self.pos..end).ok_or_else(|| {
64 Error::invalid(format!("unexpected EOF reading {} bytes", n))
65 })?;
66 self.pos = end;
67 Ok(slice)
68 }
69}