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;