Skip to main content

lsm_tree/io/
mod.rs

1// Local I/O trait surface, mirroring `std::io::{Read, Write, Seek}`
2// so that THE BOUNDS on `fs::Fs` / `fs::FsFile` no longer carry
3// `std::io::*` directly. The signatures match `std::io` exactly, so
4// backends gated behind `#[cfg(feature = "std")]` (such as `std_fs`
5// and `io_uring_fs`) keep using `std::io::*` internally — they just
6// satisfy this crate's traits via the supertrait alias + blanket
7// impls below.
8//
9// Scope of this module's contribution to the no-std epic (see #311):
10// it removes `std::io::{Read, Write, Seek}` from the trait BOUNDS.
11// The `fs` module still uses `std::io::Result` for return types and
12// `&std::path::Path` for path arguments in `Fs` / `FsFile` method
13// signatures — those migrate to `crate::io::Result<T>` and a
14// `crate::path` equivalent in follow-up commits. Until both follow-
15// ups land, `fs::*` does NOT yet compile under
16// `--no-default-features --features alloc`.
17//
18// Why not pull in an external `core_io` / `core2` / `core3` /
19// `embedded-io` crate: those add a maintainer dependency for what
20// is ultimately three stable trait signatures plus an error type.
21// The signatures here have not meaningfully changed since Rust 1.0,
22// so maintenance is near zero, and we keep the no-std contract
23// under our own control.
24
25use alloc::boxed::Box;
26use alloc::string::String;
27use core::fmt;
28
29/// Specialised `Result` for I/O operations on this crate's traits.
30pub type Result<T> = core::result::Result<T, Error>;
31
32/// Mirrors [`std::io::ErrorKind`] for the variants this crate actually
33/// constructs. Kept in alphabetical order so additions stay easy to spot.
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35#[non_exhaustive]
36pub enum ErrorKind {
37    /// Entity (file, directory, key, etc.) already exists.
38    AlreadyExists,
39    /// Broken pipe — the other end of a stream is closed.
40    BrokenPipe,
41    /// Operation attempted across distinct devices/filesystems.
42    CrossesDevices,
43    /// Operation was interrupted (`EINTR`-equivalent); usually retriable.
44    Interrupted,
45    /// Data being read does not match the expected format/schema.
46    InvalidData,
47    /// A function argument had an invalid value.
48    InvalidInput,
49    /// Entity was not found.
50    NotFound,
51    /// Catch-all for errors that don't fit any other variant.
52    Other,
53    /// Operation denied due to lack of permissions.
54    PermissionDenied,
55    /// Reader hit end-of-file before satisfying the request.
56    UnexpectedEof,
57    /// Operation is not supported on this platform / backend / build.
58    Unsupported,
59    /// Operation would block (`EAGAIN` / `EWOULDBLOCK`-equivalent); the caller
60    /// can retry. Mirrors [`std::io::ErrorKind::WouldBlock`].
61    WouldBlock,
62    /// `write` returned `Ok(0)` while bytes still needed to be
63    /// written. Mirrors [`std::io::ErrorKind::WriteZero`] so callers
64    /// can distinguish a stuck-writer short write from a generic
65    /// [`Other`](Self::Other) failure.
66    WriteZero,
67}
68
69impl ErrorKind {
70    fn as_str(self) -> &'static str {
71        match self {
72            Self::AlreadyExists => "entity already exists",
73            Self::BrokenPipe => "broken pipe",
74            Self::CrossesDevices => "cross-device link or rename",
75            Self::Interrupted => "operation interrupted",
76            Self::InvalidData => "invalid data",
77            Self::InvalidInput => "invalid input parameter",
78            Self::NotFound => "entity not found",
79            Self::Other => "other error",
80            Self::PermissionDenied => "permission denied",
81            Self::UnexpectedEof => "unexpected end of file",
82            Self::Unsupported => "unsupported",
83            Self::WouldBlock => "operation would block",
84            Self::WriteZero => "write returned 0 bytes",
85        }
86    }
87}
88
89impl fmt::Display for ErrorKind {
90    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91        f.write_str(self.as_str())
92    }
93}
94
95/// I/O error mirroring [`std::io::Error`].
96///
97/// Carries an [`ErrorKind`] plus an optional message string for
98/// context. The rendered `Display` form is `"<kind>"` when no
99/// message is attached, or `"<kind>: <message>"` when one is.
100///
101/// Under `feature = "std"`, the `From<std::io::Error>` bridge
102/// below applies a tri-state message-attachment policy so the
103/// rendered text matches the information density of the input
104/// without paying a heap allocation for plain kind-only inputs:
105///
106/// 1. **std error carries context** (`raw_os_error.is_some()` OR
107///    `get_ref().is_some()` — the canonical std discriminator
108///    for "more than just a kind") — the std `Display` output is
109///    captured as the message. The original OS / errno / path
110///    text survives the conversion and appears after the kind
111///    tag.
112/// 2. **std error is plain kind-only AND we mapped the kind**
113///    (`std::io::Error::from(ErrorKind::NotFound)` etc.) — no
114///    message is attached. The kind tag already conveys the
115///    information; capturing the std `Display` output would just
116///    repeat it (`"entity not found: entity not found"`) and burn
117///    a heap allocation on the hot path.
118/// 3. **std error is plain kind-only but we did NOT map the
119///    kind** (the `#[non_exhaustive]` `std::io::ErrorKind`
120///    catch-all branch — e.g. `OutOfMemory` mapping to our
121///    `ErrorKind::Other`) — the std `Display` output IS captured
122///    so the user-visible discriminant isn't lost in the
123///    `Other` bucket. Renders as `"other error: out of memory"`
124///    rather than just `"other error"`.
125pub struct Error {
126    kind: ErrorKind,
127    message: Option<Box<str>>,
128}
129
130impl Error {
131    /// Construct an error with the given kind and a context message.
132    ///
133    /// Analogous to [`std::io::Error::new`] but intentionally
134    /// narrower: this constructor takes a `String`-coercible
135    /// message and stores it verbatim, where std's `new()`
136    /// accepts `E: Into<Box<dyn std::error::Error + Send + Sync>>`
137    /// and carries a chained source via `Error::source()`. This
138    /// crate's error type has no source-chaining surface (and
139    /// can't have one under `no_std + alloc` without an alloc
140    /// dyn-trait shim), so an analogous "wrap an inner error"
141    /// helper would be misleading; callers wanting the source
142    /// payload of a std error use the `From<std::io::Error>`
143    /// bridge below, which renders the std Display into the
144    /// message field.
145    pub fn new<M: Into<String>>(kind: ErrorKind, message: M) -> Self {
146        Self {
147            kind,
148            message: Some(message.into().into_boxed_str()),
149        }
150    }
151
152    /// Construct an error with only an [`ErrorKind`] (no message).
153    /// Matches [`std::io::Error::from`] for `ErrorKind`.
154    #[must_use]
155    pub const fn from_kind(kind: ErrorKind) -> Self {
156        Self {
157            kind,
158            message: None,
159        }
160    }
161
162    /// Return the [`ErrorKind`] this error carries.
163    #[must_use]
164    pub const fn kind(&self) -> ErrorKind {
165        self.kind
166    }
167
168    /// Construct an [`ErrorKind::Other`] error carrying `message`.
169    /// Mirrors [`std::io::Error::other`].
170    pub fn other<M: Into<String>>(message: M) -> Self {
171        Self::new(ErrorKind::Other, message)
172    }
173}
174
175impl From<ErrorKind> for Error {
176    fn from(kind: ErrorKind) -> Self {
177        Self::from_kind(kind)
178    }
179}
180
181impl fmt::Debug for Error {
182    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
183        let mut dbg = f.debug_struct("Error");
184        dbg.field("kind", &self.kind);
185        if let Some(msg) = &self.message {
186            dbg.field("message", msg);
187        }
188        dbg.finish()
189    }
190}
191
192impl fmt::Display for Error {
193    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
194        match &self.message {
195            Some(msg) => write!(f, "{}: {msg}", self.kind.as_str()),
196            None => f.write_str(self.kind.as_str()),
197        }
198    }
199}
200
201// `core::error::Error` (stable since 1.81, MSRV here is 1.92) so this error
202// is usable as a trait-object source under both `std` and `no_std`. Under
203// `std`, `std::error::Error` re-exports `core::error::Error`, so this is the
204// same impl std callers have always seen.
205impl core::error::Error for Error {}
206
207/// Bridge from `std::io::Error`. Maps the std `ErrorKind` to
208/// this crate's [`ErrorKind`] when a variant exists for it, and
209/// falls back to [`ErrorKind::Other`] for any std variant we
210/// don't track (the std type is `#[non_exhaustive]`, so the
211/// catch-all is required). The `Display` message is captured via
212/// the tri-state policy documented on [`Error`]: kept for
213/// contextful std errors and for unmapped kinds (so the
214/// discriminant is preserved even in the `Other` bucket),
215/// dropped for plain kind-only errors whose kind we DO map (to
216/// avoid the redundant `"<kind>: <kind>"` render and the heap
217/// allocation). Net effect: `?` from std-backed backends
218/// propagates the original context to operators, but callers
219/// inspecting `err.kind()` after the conversion should be aware
220/// that an unknown std kind now reads as
221/// `ErrorKind::Other` rather than the originating variant.
222#[cfg(feature = "std")]
223impl From<std::io::Error> for Error {
224    fn from(err: std::io::Error) -> Self {
225        let std_kind = err.kind();
226        let (kind, kind_is_mapped) = match std_kind {
227            std::io::ErrorKind::AlreadyExists => (ErrorKind::AlreadyExists, true),
228            std::io::ErrorKind::BrokenPipe => (ErrorKind::BrokenPipe, true),
229            std::io::ErrorKind::CrossesDevices => (ErrorKind::CrossesDevices, true),
230            std::io::ErrorKind::Interrupted => (ErrorKind::Interrupted, true),
231            std::io::ErrorKind::InvalidData => (ErrorKind::InvalidData, true),
232            std::io::ErrorKind::InvalidInput => (ErrorKind::InvalidInput, true),
233            std::io::ErrorKind::NotFound => (ErrorKind::NotFound, true),
234            std::io::ErrorKind::PermissionDenied => (ErrorKind::PermissionDenied, true),
235            std::io::ErrorKind::UnexpectedEof => (ErrorKind::UnexpectedEof, true),
236            std::io::ErrorKind::Unsupported => (ErrorKind::Unsupported, true),
237            std::io::ErrorKind::WouldBlock => (ErrorKind::WouldBlock, true),
238            std::io::ErrorKind::WriteZero => (ErrorKind::WriteZero, true),
239            // `Other` is a mapped kind: a kind-only `Other` std error
240            // would otherwise fall through to the unmapped branch and
241            // attach Display ("other error"), producing
242            // "other error: other error" on render plus a heap alloc.
243            std::io::ErrorKind::Other => (ErrorKind::Other, true),
244            _ => (ErrorKind::Other, false),
245        };
246        // Message-attachment policy:
247        //
248        // - If the std error carries actual context (an `errno`, a
249        //   path / OS message, or a custom payload, detected by
250        //   `raw_os_error.is_some() || get_ref().is_some()` — the
251        //   canonical std-side discriminator for "this error
252        //   carries more than just a kind"), preserve its Display
253        //   output as our message so the OS-level detail survives.
254        //
255        // - If the std error is a plain kind-only one
256        //   (`std::io::Error::from(ErrorKind::X)`) AND we mapped
257        //   the kind, skip the message — our `Display` already
258        //   prefixes the kind tag, so storing "entity not found"
259        //   as the message would produce
260        //   "entity not found: entity not found" on render AND
261        //   burn an unnecessary heap allocation.
262        //
263        // - If the std error is kind-only but we DIDN'T map the
264        //   kind (`std::io::ErrorKind` is `#[non_exhaustive]`, e.g.
265        //   `OutOfMemory` / `ResourceBusy`), the user-visible
266        //   discriminant is otherwise lost in our `Other` bucket.
267        //   Preserve the std `Display` text in that case so an
268        //   unmapped kind still renders something useful (e.g.
269        //   "other error: out of memory") instead of just
270        //   "other error".
271        if err.raw_os_error().is_some() || err.get_ref().is_some() {
272            Self::new(kind, alloc::format!("{err}"))
273        } else if kind_is_mapped {
274            Self::from_kind(kind)
275        } else {
276            Self::new(kind, alloc::format!("{err}"))
277        }
278    }
279}
280
281/// Bridge back to `std::io::Error` so existing call sites that consume
282/// `std::io::Result<_>` (and where `From` is invoked via `?`) keep
283/// compiling once their input switches to this module.
284#[cfg(feature = "std")]
285impl From<Error> for std::io::Error {
286    fn from(err: Error) -> Self {
287        let kind = match err.kind {
288            ErrorKind::AlreadyExists => std::io::ErrorKind::AlreadyExists,
289            ErrorKind::BrokenPipe => std::io::ErrorKind::BrokenPipe,
290            ErrorKind::CrossesDevices => std::io::ErrorKind::CrossesDevices,
291            ErrorKind::Interrupted => std::io::ErrorKind::Interrupted,
292            ErrorKind::InvalidData => std::io::ErrorKind::InvalidData,
293            ErrorKind::InvalidInput => std::io::ErrorKind::InvalidInput,
294            ErrorKind::NotFound => std::io::ErrorKind::NotFound,
295            ErrorKind::Other => std::io::ErrorKind::Other,
296            ErrorKind::PermissionDenied => std::io::ErrorKind::PermissionDenied,
297            ErrorKind::UnexpectedEof => std::io::ErrorKind::UnexpectedEof,
298            ErrorKind::Unsupported => std::io::ErrorKind::Unsupported,
299            ErrorKind::WouldBlock => std::io::ErrorKind::WouldBlock,
300            ErrorKind::WriteZero => std::io::ErrorKind::WriteZero,
301        };
302        match err.message {
303            Some(msg) => Self::new(kind, msg.into_string()),
304            None => Self::from(kind),
305        }
306    }
307}
308
309/// Seek target, mirroring [`std::io::SeekFrom`].
310#[derive(Debug, Clone, Copy, PartialEq, Eq)]
311pub enum SeekFrom {
312    /// Seek to an absolute offset from the start of the stream.
313    Start(u64),
314    /// Seek to an offset relative to the end of the stream.
315    End(i64),
316    /// Seek to an offset relative to the current cursor position.
317    Current(i64),
318}
319
320#[cfg(feature = "std")]
321impl From<SeekFrom> for std::io::SeekFrom {
322    fn from(s: SeekFrom) -> Self {
323        match s {
324            SeekFrom::Start(n) => Self::Start(n),
325            SeekFrom::End(n) => Self::End(n),
326            SeekFrom::Current(n) => Self::Current(n),
327        }
328    }
329}
330
331#[cfg(feature = "std")]
332impl From<std::io::SeekFrom> for SeekFrom {
333    fn from(s: std::io::SeekFrom) -> Self {
334        match s {
335            std::io::SeekFrom::Start(n) => Self::Start(n),
336            std::io::SeekFrom::End(n) => Self::End(n),
337            std::io::SeekFrom::Current(n) => Self::Current(n),
338        }
339    }
340}
341
342// Under `feature = "std"`, the `Read` / `Write` / `Seek` traits in
343// this module are supertrait aliases over `std::io::{Read,Write,
344// Seek}`. Any `T: std::io::Read` automatically implements
345// `crate::io::Read` via the blanket below — AND `T: crate::io::Read`
346// implies `T: std::io::Read` (because crate::io::Read is a supertrait).
347// This second direction is what lets `dyn FsFile` (bounded on
348// `crate::io::Read`) flow into `std::io::BufReader`, `byteorder`,
349// and the rest of the std ecosystem without any explicit adapter.
350//
351// Under `--no-default-features --features alloc`, std::io does not
352// exist, so the traits are standalone with their own method bodies
353// returning `crate::io::Result<T>` (the local Error type above).
354// Signatures stay identical between modes so call sites compile in
355// both builds.
356//
357// This dual-shape is the whole reason we own this module instead of
358// depending on `core2` / `core3` / `embedded-io` — none of those let
359// the std-mode trait *be* `std::io::Read` (they're meant to replace
360// std::io, not bridge to it), and bridging the other way needs an
361// adapter shim at every backend boundary.
362
363/// Read trait. Under `std` it's a supertrait alias for
364/// [`std::io::Read`]; under `no_std + alloc` it carries its own
365/// signature mirroring the std contract.
366#[cfg(feature = "std")]
367pub trait Read: std::io::Read {}
368#[cfg(feature = "std")]
369impl<R: std::io::Read + ?Sized> Read for R {}
370
371/// Write trait. Under `std` it's a supertrait alias for
372/// [`std::io::Write`]; under `no_std + alloc` it carries its own
373/// signature mirroring the std contract.
374#[cfg(feature = "std")]
375pub trait Write: std::io::Write {}
376#[cfg(feature = "std")]
377impl<W: std::io::Write + ?Sized> Write for W {}
378
379/// Seek trait. Under `std` it's a supertrait alias for
380/// [`std::io::Seek`]; under `no_std + alloc` it carries its own
381/// signature mirroring the std contract.
382#[cfg(feature = "std")]
383pub trait Seek: std::io::Seek {}
384#[cfg(feature = "std")]
385impl<S: std::io::Seek + ?Sized> Seek for S {}
386
387/// Buffered-read trait. Under `std` this is a supertrait alias of
388/// [`std::io::BufRead`] (blanket-implemented), so std readers satisfy it
389/// directly; under `no_std` it is the native trait defined below.
390#[cfg(feature = "std")]
391pub trait BufRead: std::io::BufRead {}
392#[cfg(feature = "std")]
393impl<B: std::io::BufRead + ?Sized> BufRead for B {}
394
395/// Read trait mirroring [`std::io::Read`]. Only the methods this
396/// crate depends on are surfaced; default implementations follow the
397/// std contract verbatim so behaviour matches.
398#[cfg(not(feature = "std"))]
399pub trait Read {
400    /// Read bytes into `buf`. Returns the number of bytes read. A
401    /// return value of `0` indicates the source has reached EOF.
402    ///
403    /// # Errors
404    ///
405    /// Returns any I/O error encountered by the backing reader.
406    fn read(&mut self, buf: &mut [u8]) -> Result<usize>;
407
408    /// Read exactly `buf.len()` bytes. Returns
409    /// [`ErrorKind::UnexpectedEof`] if EOF is reached before the
410    /// buffer is full.
411    ///
412    /// # Errors
413    ///
414    /// Returns any I/O error from the underlying reader, or
415    /// [`ErrorKind::UnexpectedEof`] on short read.
416    fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<()> {
417        while !buf.is_empty() {
418            match self.read(buf) {
419                Ok(0) => break,
420                Ok(n) => {
421                    // `split_at_mut(n)` keeps the crate-level
422                    // `#![deny(clippy::indexing_slicing)]` happy
423                    // under `--no-default-features --features
424                    // alloc` clippy: indexing form `buf = &mut
425                    // buf[n..]` would lint-fail on the no-std
426                    // build even though `n` is bounded by the
427                    // `Ok(n)` return contract of `read()`.
428                    let (_, rest) = buf.split_at_mut(n);
429                    buf = rest;
430                }
431                Err(e) if e.kind() == ErrorKind::Interrupted => {}
432                Err(e) => return Err(e),
433            }
434        }
435        if buf.is_empty() {
436            Ok(())
437        } else {
438            // Match the stable message text `std::io::Read::read_exact`
439            // emits on the same short-read condition — callers that
440            // grep diagnostics for "failed to fill whole buffer" keep
441            // working without a feature-conditional branch.
442            Err(Error::new(
443                ErrorKind::UnexpectedEof,
444                "failed to fill whole buffer",
445            ))
446        }
447    }
448
449    /// Adapter that reads at most `limit` bytes from this reader, mirroring
450    /// [`std::io::Read::take`]. Used to bound a parser against a forged length
451    /// prefix.
452    fn take(self, limit: u64) -> Take<Self>
453    where
454        Self: Sized,
455    {
456        Take { inner: self, limit }
457    }
458}
459
460/// Limit-bounded reader returned by [`Read::take`], mirroring
461/// [`std::io::Take`].
462#[cfg(not(feature = "std"))]
463pub struct Take<R> {
464    inner: R,
465    limit: u64,
466}
467
468#[cfg(not(feature = "std"))]
469impl<R: Read> Read for Take<R> {
470    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
471        if self.limit == 0 {
472            return Ok(0);
473        }
474        // Cap this read at the remaining limit; `min` keeps `max <= buf.len()`.
475        let max = (buf.len() as u64).min(self.limit) as usize;
476        // `split_at_mut` instead of `&mut buf[..max]` to satisfy the crate's
477        // `deny(clippy::indexing_slicing)` on the no_std build.
478        let (head, _) = buf.split_at_mut(max);
479        let n = self.inner.read(head)?;
480        self.limit -= n as u64;
481        Ok(n)
482    }
483}
484
485/// Buffered-read trait mirroring the [`std::io::BufRead`] subset the storage
486/// layer uses (`fill_buf` + `consume`). Lets a record reader peek at the next
487/// bytes — and detect a clean EOF via an empty fill — without consuming them.
488#[cfg(not(feature = "std"))]
489pub trait BufRead: Read {
490    /// Return the buffer's current contents, refilling from the underlying
491    /// reader when empty. An empty return means EOF.
492    ///
493    /// # Errors
494    ///
495    /// Returns any I/O error from the underlying reader.
496    fn fill_buf(&mut self) -> Result<&[u8]>;
497
498    /// Mark `amt` bytes from the start of the buffer as consumed so they are
499    /// not returned by the next [`fill_buf`](Self::fill_buf)/[`read`](Read::read).
500    fn consume(&mut self, amt: usize);
501}
502
503/// Write trait mirroring [`std::io::Write`].
504#[cfg(not(feature = "std"))]
505pub trait Write {
506    /// Write `buf` into the sink. Returns the number of bytes
507    /// accepted by the writer in this call.
508    ///
509    /// # Errors
510    ///
511    /// Returns any I/O error from the underlying writer.
512    fn write(&mut self, buf: &[u8]) -> Result<usize>;
513
514    /// Flush buffered output to the underlying medium.
515    ///
516    /// # Errors
517    ///
518    /// Returns any I/O error from the underlying writer.
519    fn flush(&mut self) -> Result<()>;
520
521    /// Write the entire `buf`, retrying on `Interrupted` and
522    /// returning [`ErrorKind::WriteZero`] if the writer stops
523    /// accepting bytes early. Matches the semantics of
524    /// [`std::io::Write::write_all`].
525    ///
526    /// # Errors
527    ///
528    /// Returns the underlying writer's error, or
529    /// [`ErrorKind::WriteZero`] on short write.
530    fn write_all(&mut self, mut buf: &[u8]) -> Result<()> {
531        while !buf.is_empty() {
532            match self.write(buf) {
533                Ok(0) => {
534                    return Err(Error::new(
535                        ErrorKind::WriteZero,
536                        "failed to write whole buffer",
537                    ));
538                }
539                Ok(n) => {
540                    // `split_at(n)` keeps the crate-level
541                    // `#![deny(clippy::indexing_slicing)]` happy
542                    // under `--no-default-features --features
543                    // alloc` clippy: same reasoning as the
544                    // matching `read_exact` default impl above.
545                    let (_, rest) = buf.split_at(n);
546                    buf = rest;
547                }
548                Err(e) if e.kind() == ErrorKind::Interrupted => {}
549                Err(e) => return Err(e),
550            }
551        }
552        Ok(())
553    }
554}
555
556/// Seek trait mirroring [`std::io::Seek`].
557#[cfg(not(feature = "std"))]
558pub trait Seek {
559    /// Seek the stream cursor to the offset described by `pos`.
560    /// Returns the resulting absolute byte offset from the start of
561    /// the stream.
562    ///
563    /// # Errors
564    ///
565    /// Returns any I/O error from the underlying seeker.
566    fn seek(&mut self, pos: SeekFrom) -> Result<u64>;
567
568    /// Current stream position (offset from the start). Mirrors
569    /// [`std::io::Seek::stream_position`].
570    ///
571    /// # Errors
572    ///
573    /// Returns any I/O error from the underlying seeker.
574    fn stream_position(&mut self) -> Result<u64> {
575        self.seek(SeekFrom::Current(0))
576    }
577
578    /// Seek relative to the current position. Mirrors
579    /// [`std::io::Seek::seek_relative`].
580    ///
581    /// # Errors
582    ///
583    /// Returns any I/O error from the underlying seeker.
584    fn seek_relative(&mut self, offset: i64) -> Result<()> {
585        self.seek(SeekFrom::Current(offset))?;
586        Ok(())
587    }
588}
589
590// Blanket forwarding impls so readers/writers behind a reference or a box
591// (e.g. `&mut R`, `Box<dyn FsFile>`) satisfy the io traits — std provides the
592// equivalent blankets for its own io traits, so this only fills the no_std gap.
593#[cfg(not(feature = "std"))]
594impl<R: Read + ?Sized> Read for &mut R {
595    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
596        (**self).read(buf)
597    }
598    fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> {
599        (**self).read_exact(buf)
600    }
601}
602
603#[cfg(not(feature = "std"))]
604impl<R: Read + ?Sized> Read for alloc::boxed::Box<R> {
605    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
606        (**self).read(buf)
607    }
608    fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> {
609        (**self).read_exact(buf)
610    }
611}
612
613#[cfg(not(feature = "std"))]
614impl<W: Write + ?Sized> Write for &mut W {
615    fn write(&mut self, buf: &[u8]) -> Result<usize> {
616        (**self).write(buf)
617    }
618    fn flush(&mut self) -> Result<()> {
619        (**self).flush()
620    }
621    fn write_all(&mut self, buf: &[u8]) -> Result<()> {
622        (**self).write_all(buf)
623    }
624}
625
626#[cfg(not(feature = "std"))]
627impl<W: Write + ?Sized> Write for alloc::boxed::Box<W> {
628    fn write(&mut self, buf: &[u8]) -> Result<usize> {
629        (**self).write(buf)
630    }
631    fn flush(&mut self) -> Result<()> {
632        (**self).flush()
633    }
634    fn write_all(&mut self, buf: &[u8]) -> Result<()> {
635        (**self).write_all(buf)
636    }
637}
638
639#[cfg(not(feature = "std"))]
640impl<S: Seek + ?Sized> Seek for &mut S {
641    fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
642        (**self).seek(pos)
643    }
644    fn stream_position(&mut self) -> Result<u64> {
645        (**self).stream_position()
646    }
647    fn seek_relative(&mut self, offset: i64) -> Result<()> {
648        (**self).seek_relative(offset)
649    }
650}
651
652#[cfg(not(feature = "std"))]
653impl<S: Seek + ?Sized> Seek for alloc::boxed::Box<S> {
654    fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
655        (**self).seek(pos)
656    }
657    fn stream_position(&mut self) -> Result<u64> {
658        (**self).stream_position()
659    }
660    fn seek_relative(&mut self, offset: i64) -> Result<()> {
661        (**self).seek_relative(offset)
662    }
663}
664
665#[cfg(not(feature = "std"))]
666impl<B: BufRead + ?Sized> BufRead for &mut B {
667    fn fill_buf(&mut self) -> Result<&[u8]> {
668        (**self).fill_buf()
669    }
670    fn consume(&mut self, amt: usize) {
671        (**self).consume(amt);
672    }
673}
674
675#[cfg(not(feature = "std"))]
676impl<B: BufRead + ?Sized> BufRead for alloc::boxed::Box<B> {
677    fn fill_buf(&mut self) -> Result<&[u8]> {
678        (**self).fill_buf()
679    }
680    fn consume(&mut self, amt: usize) {
681        (**self).consume(amt);
682    }
683}
684
685// `varint-rs` only blankets its traits over `std::io` (under its `std`
686// feature), and a foreign trait cannot be blanket-impl'd here (orphan rule).
687// So `no_std` builds get crate-local varint extension traits with the same
688// method surface, blanketed over this module's `Read`/`Write`. The encoding is
689// canonical unsigned LEB128 — byte-identical to `varint-rs` — so frames written
690// under `std` and `no_std` interoperate. Call sites swap only the import path.
691/// Writes unsigned integers as canonical LEB128 varints over a [`Write`].
692///
693/// The `no_std` counterpart of `varint-rs`'s `VarintWriter`; the encoding is
694/// identical so frames interoperate across `std` / `no_std` builds.
695#[cfg(not(feature = "std"))]
696pub trait VarintWriter: Write {
697    /// Writes `value` as canonical unsigned LEB128.
698    ///
699    /// # Errors
700    /// Propagates the underlying writer's error.
701    fn write_u64_varint(&mut self, mut value: u64) -> Result<()> {
702        loop {
703            let mut byte = (value & 0x7f) as u8;
704            value >>= 7;
705            if value != 0 {
706                byte |= 0x80;
707            }
708            self.write_all(&[byte])?;
709            if value == 0 {
710                return Ok(());
711            }
712        }
713    }
714
715    /// Writes `value` as canonical unsigned LEB128.
716    ///
717    /// # Errors
718    /// Propagates the underlying writer's error.
719    fn write_u32_varint(&mut self, value: u32) -> Result<()> {
720        self.write_u64_varint(u64::from(value))
721    }
722
723    /// Writes `value` as canonical unsigned LEB128.
724    ///
725    /// # Errors
726    /// Propagates the underlying writer's error.
727    fn write_u16_varint(&mut self, value: u16) -> Result<()> {
728        self.write_u64_varint(u64::from(value))
729    }
730}
731
732#[cfg(not(feature = "std"))]
733impl<W: Write + ?Sized> VarintWriter for W {}
734
735/// Reads canonical LEB128 varints over a [`Read`].
736///
737/// The `no_std` counterpart of `varint-rs`'s `VarintReader`; the encoding is
738/// identical so frames interoperate across `std` / `no_std` builds.
739#[cfg(not(feature = "std"))]
740pub trait VarintReader: Read {
741    /// Reads a canonical unsigned LEB128 value.
742    ///
743    /// # Errors
744    /// [`ErrorKind::UnexpectedEof`] on a truncated value, or
745    /// [`ErrorKind::InvalidData`] on an overlong encoding; plus any underlying
746    /// reader error.
747    fn read_u64_varint(&mut self) -> Result<u64> {
748        let mut result: u64 = 0;
749        let mut shift: u32 = 0;
750        loop {
751            let mut byte = [0u8; 1];
752            self.read_exact(&mut byte)?;
753            // 10 groups of 7 bits cover a full u64; reject overlong encodings.
754            if shift >= 64 {
755                return Err(Error::new(ErrorKind::InvalidData, "varint overflows u64"));
756            }
757            // 10th byte (shift == 63): only bit 0 fits in u64. Any higher bit
758            // set is a non-canonical / overflowing encoding — reject it rather
759            // than silently shifting those bits away.
760            if shift == 63 && byte[0] & 0xFE != 0 {
761                return Err(Error::new(
762                    ErrorKind::InvalidData,
763                    "non-canonical varint overflow",
764                ));
765            }
766            result |= (u64::from(byte[0] & 0x7f)) << shift;
767            if byte[0] & 0x80 == 0 {
768                return Ok(result);
769            }
770            shift += 7;
771        }
772    }
773
774    /// Reads a canonical unsigned LEB128 value, narrowed to `u32`.
775    ///
776    /// # Errors
777    /// As [`Self::read_u64_varint`], plus [`ErrorKind::InvalidData`] if the
778    /// value does not fit `u32`.
779    fn read_u32_varint(&mut self) -> Result<u32> {
780        let v = self.read_u64_varint()?;
781        u32::try_from(v).map_err(|_| Error::new(ErrorKind::InvalidData, "varint exceeds u32"))
782    }
783
784    /// Reads a canonical unsigned LEB128 value, narrowed to `u16`.
785    ///
786    /// # Errors
787    /// As [`Self::read_u64_varint`], plus [`ErrorKind::InvalidData`] if the
788    /// value does not fit `u16`.
789    fn read_u16_varint(&mut self) -> Result<u16> {
790        let v = self.read_u64_varint()?;
791        u16::try_from(v).map_err(|_| Error::new(ErrorKind::InvalidData, "varint exceeds u16"))
792    }
793}
794
795#[cfg(not(feature = "std"))]
796impl<R: Read + ?Sized> VarintReader for R {}
797
798// ---------------------------------------------------------------------------
799// Fixed-width integer I/O — a `no_std`-capable, drop-in replacement for
800// `byteorder`'s `WriteBytesExt` / `ReadBytesExt` / `ByteOrder` over this
801// module's `Read` / `Write` (which is `std::io` under `std` and the native
802// traits under `no_std`). The API mirrors `byteorder` exactly — same
803// `w.write_u32::<LittleEndian>(x)` / `r.read_u32::<LittleEndian>()` call
804// shape — so migrating a wire-format module off `byteorder` is just an
805// import swap, and the encoding is byte-identical (`to_le_bytes` /
806// `to_be_bytes`). High-level round-trip tests cover correctness; no
807// low-level duplication here.
808// ---------------------------------------------------------------------------
809
810/// Byte-order marker for fixed-width integer encoding.
811///
812/// Mirrors `byteorder::ByteOrder`. Implemented by [`LittleEndian`] /
813/// [`BigEndian`]; methods convert between fixed-size byte arrays and integers
814/// so call sites stay free of slice indexing.
815pub trait ByteOrder {
816    /// Decode a `u16` from its 2-byte representation.
817    fn u16_from(b: [u8; 2]) -> u16;
818    /// Decode a `u32` from its 4-byte representation.
819    fn u32_from(b: [u8; 4]) -> u32;
820    /// Decode a `u64` from its 8-byte representation.
821    fn u64_from(b: [u8; 8]) -> u64;
822    /// Decode a `u128` from its 16-byte representation.
823    fn u128_from(b: [u8; 16]) -> u128;
824    /// Encode a `u16` to its 2-byte representation.
825    fn u16_to(n: u16) -> [u8; 2];
826    /// Encode a `u32` to its 4-byte representation.
827    fn u32_to(n: u32) -> [u8; 4];
828    /// Encode a `u64` to its 8-byte representation.
829    fn u64_to(n: u64) -> [u8; 8];
830    /// Encode a `u128` to its 16-byte representation.
831    fn u128_to(n: u128) -> [u8; 16];
832
833    // Static slice helpers matching `byteorder::ByteOrder`'s API, so call
834    // sites of the form `LittleEndian::write_u32(buf, n)` / `read_u32(buf)`
835    // migrate unchanged. `split_at[_mut]` (not indexing) keeps the crate-level
836    // `deny(indexing_slicing)` happy; like byteorder, they panic if `buf` is
837    // shorter than the integer width (a caller bug, not a data condition).
838    /// Read a `u16` from the first 2 bytes of `buf`.
839    #[must_use]
840    fn read_u16(buf: &[u8]) -> u16 {
841        let (head, _) = buf.split_at(2);
842        let mut a = [0u8; 2];
843        a.copy_from_slice(head);
844        Self::u16_from(a)
845    }
846    /// Read a `u32` from the first 4 bytes of `buf`.
847    #[must_use]
848    fn read_u32(buf: &[u8]) -> u32 {
849        let (head, _) = buf.split_at(4);
850        let mut a = [0u8; 4];
851        a.copy_from_slice(head);
852        Self::u32_from(a)
853    }
854    /// Read a `u64` from the first 8 bytes of `buf`.
855    #[must_use]
856    fn read_u64(buf: &[u8]) -> u64 {
857        let (head, _) = buf.split_at(8);
858        let mut a = [0u8; 8];
859        a.copy_from_slice(head);
860        Self::u64_from(a)
861    }
862    /// Write `n` into the first 2 bytes of `buf`.
863    fn write_u16(buf: &mut [u8], n: u16) {
864        let (head, _) = buf.split_at_mut(2);
865        head.copy_from_slice(&Self::u16_to(n));
866    }
867    /// Write `n` into the first 4 bytes of `buf`.
868    fn write_u32(buf: &mut [u8], n: u32) {
869        let (head, _) = buf.split_at_mut(4);
870        head.copy_from_slice(&Self::u32_to(n));
871    }
872    /// Write `n` into the first 8 bytes of `buf`.
873    fn write_u64(buf: &mut [u8], n: u64) {
874        let (head, _) = buf.split_at_mut(8);
875        head.copy_from_slice(&Self::u64_to(n));
876    }
877}
878
879/// Little-endian [`ByteOrder`] (matches `byteorder::LittleEndian`).
880#[derive(Clone, Copy, Debug)]
881pub enum LittleEndian {}
882/// Big-endian [`ByteOrder`] (matches `byteorder::BigEndian`).
883#[derive(Clone, Copy, Debug)]
884pub enum BigEndian {}
885
886/// Short alias for [`LittleEndian`] (matches `byteorder::LE`).
887pub type LE = LittleEndian;
888/// Short alias for [`BigEndian`] (matches `byteorder::BE`).
889pub type BE = BigEndian;
890
891impl ByteOrder for LittleEndian {
892    fn u16_from(b: [u8; 2]) -> u16 {
893        u16::from_le_bytes(b)
894    }
895    fn u32_from(b: [u8; 4]) -> u32 {
896        u32::from_le_bytes(b)
897    }
898    fn u64_from(b: [u8; 8]) -> u64 {
899        u64::from_le_bytes(b)
900    }
901    fn u128_from(b: [u8; 16]) -> u128 {
902        u128::from_le_bytes(b)
903    }
904    fn u16_to(n: u16) -> [u8; 2] {
905        n.to_le_bytes()
906    }
907    fn u32_to(n: u32) -> [u8; 4] {
908        n.to_le_bytes()
909    }
910    fn u64_to(n: u64) -> [u8; 8] {
911        n.to_le_bytes()
912    }
913    fn u128_to(n: u128) -> [u8; 16] {
914        n.to_le_bytes()
915    }
916}
917
918impl ByteOrder for BigEndian {
919    fn u16_from(b: [u8; 2]) -> u16 {
920        u16::from_be_bytes(b)
921    }
922    fn u32_from(b: [u8; 4]) -> u32 {
923        u32::from_be_bytes(b)
924    }
925    fn u64_from(b: [u8; 8]) -> u64 {
926        u64::from_be_bytes(b)
927    }
928    fn u128_from(b: [u8; 16]) -> u128 {
929        u128::from_be_bytes(b)
930    }
931    fn u16_to(n: u16) -> [u8; 2] {
932        n.to_be_bytes()
933    }
934    fn u32_to(n: u32) -> [u8; 4] {
935        n.to_be_bytes()
936    }
937    fn u64_to(n: u64) -> [u8; 8] {
938        n.to_be_bytes()
939    }
940    fn u128_to(n: u128) -> [u8; 16] {
941        n.to_be_bytes()
942    }
943}
944
945/// Fixed-width integer writes over [`Write`], mirroring
946/// `byteorder::WriteBytesExt`. Blanket-implemented for every [`Write`].
947pub trait WriteBytesExt: Write {
948    /// Write a single byte.
949    ///
950    /// # Errors
951    /// Propagates the underlying writer's error.
952    fn write_u8(&mut self, n: u8) -> Result<()> {
953        self.write_all(&[n])?;
954        Ok(())
955    }
956    /// Write a signed byte.
957    ///
958    /// # Errors
959    /// Propagates the underlying writer's error.
960    fn write_i8(&mut self, n: i8) -> Result<()> {
961        self.write_all(&n.to_le_bytes())?;
962        Ok(())
963    }
964    /// Write a `u16` in the byte order `T`.
965    ///
966    /// # Errors
967    /// Propagates the underlying writer's error.
968    fn write_u16<T: ByteOrder>(&mut self, n: u16) -> Result<()> {
969        self.write_all(&T::u16_to(n))?;
970        Ok(())
971    }
972    /// Write a `u32` in the byte order `T`.
973    ///
974    /// # Errors
975    /// Propagates the underlying writer's error.
976    fn write_u32<T: ByteOrder>(&mut self, n: u32) -> Result<()> {
977        self.write_all(&T::u32_to(n))?;
978        Ok(())
979    }
980    /// Write a `u64` in the byte order `T`.
981    ///
982    /// # Errors
983    /// Propagates the underlying writer's error.
984    fn write_u64<T: ByteOrder>(&mut self, n: u64) -> Result<()> {
985        self.write_all(&T::u64_to(n))?;
986        Ok(())
987    }
988    /// Write a `u128` in the byte order `T`.
989    ///
990    /// # Errors
991    /// Propagates the underlying writer's error.
992    fn write_u128<T: ByteOrder>(&mut self, n: u128) -> Result<()> {
993        self.write_all(&T::u128_to(n))?;
994        Ok(())
995    }
996    /// Write an `f32` (IEEE-754 bit pattern) in the byte order `T`.
997    ///
998    /// # Errors
999    /// Propagates the underlying writer's error.
1000    fn write_f32<T: ByteOrder>(&mut self, n: f32) -> Result<()> {
1001        self.write_u32::<T>(n.to_bits())
1002    }
1003    /// Write an `f64` (IEEE-754 bit pattern) in the byte order `T`.
1004    ///
1005    /// # Errors
1006    /// Propagates the underlying writer's error.
1007    fn write_f64<T: ByteOrder>(&mut self, n: f64) -> Result<()> {
1008        self.write_u64::<T>(n.to_bits())
1009    }
1010}
1011impl<W: Write + ?Sized> WriteBytesExt for W {}
1012
1013/// Fixed-width integer reads over [`Read`], mirroring
1014/// `byteorder::ReadBytesExt`. Blanket-implemented for every [`Read`].
1015pub trait ReadBytesExt: Read {
1016    /// Read a single byte.
1017    ///
1018    /// # Errors
1019    /// [`ErrorKind::UnexpectedEof`] on short read, or the reader's error.
1020    fn read_u8(&mut self) -> Result<u8> {
1021        let mut b = [0u8; 1];
1022        self.read_exact(&mut b)?;
1023        Ok(u8::from_le_bytes(b))
1024    }
1025    /// Read a signed byte.
1026    ///
1027    /// # Errors
1028    /// [`ErrorKind::UnexpectedEof`] on short read, or the reader's error.
1029    fn read_i8(&mut self) -> Result<i8> {
1030        let mut b = [0u8; 1];
1031        self.read_exact(&mut b)?;
1032        Ok(i8::from_le_bytes(b))
1033    }
1034    /// Read a `u16` in the byte order `T`.
1035    ///
1036    /// # Errors
1037    /// [`ErrorKind::UnexpectedEof`] on short read, or the reader's error.
1038    fn read_u16<T: ByteOrder>(&mut self) -> Result<u16> {
1039        let mut b = [0u8; 2];
1040        self.read_exact(&mut b)?;
1041        Ok(T::u16_from(b))
1042    }
1043    /// Read a `u32` in the byte order `T`.
1044    ///
1045    /// # Errors
1046    /// [`ErrorKind::UnexpectedEof`] on short read, or the reader's error.
1047    fn read_u32<T: ByteOrder>(&mut self) -> Result<u32> {
1048        let mut b = [0u8; 4];
1049        self.read_exact(&mut b)?;
1050        Ok(T::u32_from(b))
1051    }
1052    /// Read a `u64` in the byte order `T`.
1053    ///
1054    /// # Errors
1055    /// [`ErrorKind::UnexpectedEof`] on short read, or the reader's error.
1056    fn read_u64<T: ByteOrder>(&mut self) -> Result<u64> {
1057        let mut b = [0u8; 8];
1058        self.read_exact(&mut b)?;
1059        Ok(T::u64_from(b))
1060    }
1061    /// Read a `u128` in the byte order `T`.
1062    ///
1063    /// # Errors
1064    /// [`ErrorKind::UnexpectedEof`] on short read, or the reader's error.
1065    fn read_u128<T: ByteOrder>(&mut self) -> Result<u128> {
1066        let mut b = [0u8; 16];
1067        self.read_exact(&mut b)?;
1068        Ok(T::u128_from(b))
1069    }
1070    /// Read an `f32` (IEEE-754 bit pattern) in the byte order `T`.
1071    ///
1072    /// # Errors
1073    /// [`ErrorKind::UnexpectedEof`] on short read, or the reader's error.
1074    fn read_f32<T: ByteOrder>(&mut self) -> Result<f32> {
1075        Ok(f32::from_bits(self.read_u32::<T>()?))
1076    }
1077    /// Read an `f64` (IEEE-754 bit pattern) in the byte order `T`.
1078    ///
1079    /// # Errors
1080    /// [`ErrorKind::UnexpectedEof`] on short read, or the reader's error.
1081    fn read_f64<T: ByteOrder>(&mut self) -> Result<f64> {
1082        Ok(f64::from_bits(self.read_u64::<T>()?))
1083    }
1084}
1085impl<R: Read + ?Sized> ReadBytesExt for R {}
1086
1087// `no_std` concrete impls so wire-format code that writes into a `Vec<u8>` or
1088// reads from a `&[u8]` keeps compiling once it moves off `byteorder` (under
1089// `std` these come from `std::io`'s own impls via the supertrait aliases).
1090#[cfg(not(feature = "std"))]
1091impl Write for alloc::vec::Vec<u8> {
1092    fn write(&mut self, buf: &[u8]) -> Result<usize> {
1093        self.extend_from_slice(buf);
1094        Ok(buf.len())
1095    }
1096    fn flush(&mut self) -> Result<()> {
1097        Ok(())
1098    }
1099    fn write_all(&mut self, buf: &[u8]) -> Result<()> {
1100        self.extend_from_slice(buf);
1101        Ok(())
1102    }
1103}
1104
1105#[cfg(not(feature = "std"))]
1106impl Read for &[u8] {
1107    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
1108        let n = buf.len().min(self.len());
1109        let (head, rest) = self.split_at(n);
1110        let (dst, _) = buf.split_at_mut(n);
1111        dst.copy_from_slice(head);
1112        *self = rest;
1113        Ok(n)
1114    }
1115}
1116
1117// In-memory cursor, mirroring `crate::io::Cursor`. Under `std` it IS
1118// `crate::io::Cursor` (re-export); under `no_std` it's a local equivalent so
1119// wire-format code reading/seeking over a `&[u8]` or writing into a `Vec<u8>`
1120// keeps compiling. Same API surface (`new` / `position` / `set_position` /
1121// `into_inner` / `get_ref`) so call sites are identical across both builds.
1122#[cfg(feature = "std")]
1123pub use std::io::Cursor;
1124
1125/// In-memory `Read` + `Seek` (and `Write` for `Vec` inner) cursor over a
1126/// byte buffer, mirroring [`crate::io::Cursor`] for `no_std` builds.
1127#[cfg(not(feature = "std"))]
1128pub struct Cursor<T> {
1129    inner: T,
1130    pos: u64,
1131}
1132
1133#[cfg(not(feature = "std"))]
1134impl<T> Cursor<T> {
1135    /// Wraps `inner`, starting the cursor at position 0.
1136    pub const fn new(inner: T) -> Self {
1137        Self { inner, pos: 0 }
1138    }
1139    /// Current byte position.
1140    #[must_use]
1141    pub const fn position(&self) -> u64 {
1142        self.pos
1143    }
1144    /// Sets the byte position.
1145    pub const fn set_position(&mut self, pos: u64) {
1146        self.pos = pos;
1147    }
1148    /// Consumes the cursor, returning the wrapped buffer.
1149    pub fn into_inner(self) -> T {
1150        self.inner
1151    }
1152    /// Borrows the wrapped buffer.
1153    pub const fn get_ref(&self) -> &T {
1154        &self.inner
1155    }
1156    /// Mutably borrows the wrapped buffer.
1157    pub const fn get_mut(&mut self) -> &mut T {
1158        &mut self.inner
1159    }
1160}
1161
1162#[cfg(not(feature = "std"))]
1163impl<T: AsRef<[u8]>> Read for Cursor<T> {
1164    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
1165        let inner = self.inner.as_ref();
1166        // Position past end → 0 bytes (matches std).
1167        let start = (self.pos as usize).min(inner.len());
1168        let (_, remaining) = inner.split_at(start);
1169        let n = buf.len().min(remaining.len());
1170        let (src, _) = remaining.split_at(n);
1171        let (dst, _) = buf.split_at_mut(n);
1172        dst.copy_from_slice(src);
1173        self.pos += n as u64;
1174        Ok(n)
1175    }
1176}
1177
1178#[cfg(not(feature = "std"))]
1179impl<T: AsRef<[u8]>> Seek for Cursor<T> {
1180    fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
1181        let len = self.inner.as_ref().len() as u64;
1182        // Clamp a caller-supplied relative seek into `[0, u64::MAX]` rather than
1183        // underflowing on a negative offset that would point before the start
1184        // (the offset is internal, not on-disk-decoded input).
1185        let new = match pos {
1186            SeekFrom::Start(n) => n,
1187            SeekFrom::End(off) => len.saturating_add_signed(off),
1188            SeekFrom::Current(off) => self.pos.saturating_add_signed(off),
1189        };
1190        self.pos = new;
1191        Ok(new)
1192    }
1193}
1194
1195#[cfg(not(feature = "std"))]
1196impl Write for Cursor<alloc::vec::Vec<u8>> {
1197    fn write(&mut self, buf: &[u8]) -> Result<usize> {
1198        // Like std: writing at `pos` overwrites in place and extends the Vec
1199        // when the write runs past the current end.
1200        let pos = self.pos as usize;
1201        if pos > self.inner.len() {
1202            self.inner.resize(pos, 0);
1203        }
1204        let end = pos + buf.len();
1205        if end > self.inner.len() {
1206            self.inner.resize(end, 0);
1207        }
1208        let (_, tail) = self.inner.split_at_mut(pos);
1209        let (dst, _) = tail.split_at_mut(buf.len());
1210        dst.copy_from_slice(buf);
1211        self.pos = end as u64;
1212        Ok(buf.len())
1213    }
1214    fn flush(&mut self) -> Result<()> {
1215        Ok(())
1216    }
1217}
1218
1219#[cfg(feature = "std")]
1220pub use std::io::BufReader;
1221
1222/// Buffering reader, mirroring the subset of [`std::io::BufReader`] the storage
1223/// layer uses (record framing reads), for `no_std` builds. Coalesces small
1224/// reads and exposes [`BufRead`] so a record parser can peek for a clean EOF.
1225#[cfg(not(feature = "std"))]
1226pub struct BufReader<R: Read> {
1227    inner: R,
1228    // `capacity`-sized; the valid (filled, not-yet-consumed) window is
1229    // `buf[pos..cap]`.
1230    buf: alloc::vec::Vec<u8>,
1231    pos: usize,
1232    cap: usize,
1233}
1234
1235#[cfg(not(feature = "std"))]
1236impl<R: Read> BufReader<R> {
1237    /// Wraps `inner` with the default 8 KiB buffer.
1238    pub fn new(inner: R) -> Self {
1239        Self::with_capacity(8 * 1024, inner)
1240    }
1241
1242    /// Wraps `inner` with a `capacity`-byte buffer.
1243    pub fn with_capacity(capacity: usize, inner: R) -> Self {
1244        Self {
1245            inner,
1246            buf: alloc::vec![0u8; capacity],
1247            pos: 0,
1248            cap: 0,
1249        }
1250    }
1251
1252    /// Mutable access to the inner reader. Bypasses the buffer — mixing direct
1253    /// inner reads with buffered ones loses buffered bytes, like std.
1254    pub fn get_mut(&mut self) -> &mut R {
1255        &mut self.inner
1256    }
1257
1258    /// Shared access to the inner reader.
1259    pub fn get_ref(&self) -> &R {
1260        &self.inner
1261    }
1262}
1263
1264#[cfg(not(feature = "std"))]
1265impl<R: Read> Read for BufReader<R> {
1266    fn read(&mut self, dest: &mut [u8]) -> Result<usize> {
1267        // A read at least as large as the buffer, with nothing buffered, goes
1268        // straight to the inner reader (buffering would just add a copy).
1269        if self.pos >= self.cap && dest.len() >= self.buf.len() {
1270            return self.inner.read(dest);
1271        }
1272        let n = {
1273            let available = self.fill_buf()?;
1274            let n = available.len().min(dest.len());
1275            let (src, _) = available.split_at(n);
1276            let (dst, _) = dest.split_at_mut(n);
1277            dst.copy_from_slice(src);
1278            n
1279        };
1280        self.consume(n);
1281        Ok(n)
1282    }
1283}
1284
1285#[cfg(not(feature = "std"))]
1286impl<R: Read> BufRead for BufReader<R> {
1287    fn fill_buf(&mut self) -> Result<&[u8]> {
1288        if self.pos >= self.cap {
1289            self.cap = self.inner.read(&mut self.buf)?;
1290            self.pos = 0;
1291        }
1292        // Return `buf[pos..cap]` via split_at to keep `deny(indexing_slicing)`.
1293        let (_, rest) = self.buf.split_at(self.pos);
1294        let (out, _) = rest.split_at(self.cap - self.pos);
1295        Ok(out)
1296    }
1297
1298    fn consume(&mut self, amt: usize) {
1299        self.pos = (self.pos + amt).min(self.cap);
1300    }
1301}
1302
1303#[cfg(not(feature = "std"))]
1304impl<R: Read + Seek> Seek for BufReader<R> {
1305    fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
1306        // The buffered bytes belong to the old position; drop them.
1307        self.pos = 0;
1308        self.cap = 0;
1309        self.inner.seek(pos)
1310    }
1311}
1312
1313#[cfg(feature = "std")]
1314pub use std::io::BufWriter;
1315
1316/// Buffering writer, mirroring the subset of [`std::io::BufWriter`] the
1317/// storage layer uses, for `no_std` builds. Coalesces small writes into one
1318/// `capacity`-sized buffer and flushes it to the inner writer on overflow,
1319/// explicit [`flush`](Write::flush), [`seek`](Seek::seek), or drop.
1320#[cfg(not(feature = "std"))]
1321pub struct BufWriter<W: Write> {
1322    inner: W,
1323    buf: alloc::vec::Vec<u8>,
1324}
1325
1326#[cfg(not(feature = "std"))]
1327impl<W: Write> BufWriter<W> {
1328    /// Wraps `inner` with the default 8 KiB buffer.
1329    pub fn new(inner: W) -> Self {
1330        Self::with_capacity(8 * 1024, inner)
1331    }
1332
1333    /// Wraps `inner` with a `capacity`-byte buffer.
1334    pub fn with_capacity(capacity: usize, inner: W) -> Self {
1335        Self {
1336            inner,
1337            buf: alloc::vec::Vec::with_capacity(capacity),
1338        }
1339    }
1340
1341    /// Mutable access to the inner writer. Does NOT flush the buffer first —
1342    /// matches [`std::io::BufWriter::get_mut`].
1343    pub fn get_mut(&mut self) -> &mut W {
1344        &mut self.inner
1345    }
1346
1347    /// Shared access to the inner writer.
1348    pub fn get_ref(&self) -> &W {
1349        &self.inner
1350    }
1351
1352    fn flush_buf(&mut self) -> Result<()> {
1353        if !self.buf.is_empty() {
1354            self.inner.write_all(&self.buf)?;
1355            self.buf.clear();
1356        }
1357        Ok(())
1358    }
1359}
1360
1361#[cfg(not(feature = "std"))]
1362impl<W: Write> Write for BufWriter<W> {
1363    fn write(&mut self, data: &[u8]) -> Result<usize> {
1364        // Flush first if this write wouldn't fit alongside what's buffered.
1365        if self.buf.len() + data.len() > self.buf.capacity() {
1366            self.flush_buf()?;
1367        }
1368        // A write at least as large as the whole buffer bypasses it entirely
1369        // (buffering it would just add a copy), matching std::io::BufWriter.
1370        if data.len() >= self.buf.capacity() {
1371            self.inner.write(data)
1372        } else {
1373            self.buf.extend_from_slice(data);
1374            Ok(data.len())
1375        }
1376    }
1377
1378    fn flush(&mut self) -> Result<()> {
1379        self.flush_buf()?;
1380        self.inner.flush()
1381    }
1382}
1383
1384#[cfg(not(feature = "std"))]
1385impl<W: Write + Seek> Seek for BufWriter<W> {
1386    fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
1387        // Buffered bytes belong before the seek target, so flush them out
1388        // before moving the inner cursor (matches std::io::BufWriter).
1389        self.flush_buf()?;
1390        self.inner.seek(pos)
1391    }
1392}
1393
1394#[cfg(not(feature = "std"))]
1395impl<W: Write> Drop for BufWriter<W> {
1396    fn drop(&mut self) {
1397        // Best-effort flush, like std: a drop can't surface an error, and the
1398        // storage writer always flushes explicitly before teardown anyway.
1399        let _ = self.flush_buf();
1400    }
1401}
1402
1403#[cfg(test)]
1404mod tests;