From 14a447846759d6df8270e500e64e9097b3a7499e Mon Sep 17 00:00:00 2001 From: Anekoique Date: Mon, 1 Dec 2025 15:57:20 +0800 Subject: [PATCH 1/3] feat: add trait impls for mutable references --- src/impls.rs | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 1 deletion(-) diff --git a/src/impls.rs b/src/impls.rs index 3a2a1c3..5b771cf 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -1,10 +1,11 @@ +use alloc::{string::String, vec::Vec}; use core::{cmp, mem}; use axerrno::ax_bail; use crate::{ + BufRead, Read, Result, Seek, SeekFrom, Write, buf::{Buf, BufMut}, - Read, Result, Write, }; impl Read for &[u8] { @@ -105,3 +106,87 @@ impl BufMut for &mut [u8] { Ok(filled) } } + +impl Read for &mut R { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> Result { + (**self).read(buf) + } + + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> { + (**self).read_exact(buf) + } + + #[cfg(feature = "alloc")] + #[inline] + fn read_to_end(&mut self, buf: &mut alloc::vec::Vec) -> Result { + (**self).read_to_end(buf) + } + + #[cfg(feature = "alloc")] + #[inline] + fn read_to_string(&mut self, buf: &mut alloc::string::String) -> Result { + (**self).read_to_string(buf) + } +} + +impl Write for &mut W { + #[inline] + fn write(&mut self, buf: &[u8]) -> Result { + (**self).write(buf) + } + + #[inline] + fn flush(&mut self) -> Result<()> { + (**self).flush() + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> Result<()> { + (**self).write_all(buf) + } + + #[inline] + fn write_fmt(&mut self, fmt: core::fmt::Arguments<'_>) -> Result<()> { + (**self).write_fmt(fmt) + } +} + +impl Seek for &mut S { + fn seek(&mut self, pos: SeekFrom) -> Result { + (**self).seek(pos) + } + + fn rewind(&mut self) -> Result<()> { + (**self).rewind() + } + + fn stream_position(&mut self) -> Result { + (**self).stream_position() + } +} + +impl BufRead for &mut B { + fn fill_buf(&mut self) -> Result<&[u8]> { + (**self).fill_buf() + } + + fn consume(&mut self, amt: usize) { + (**self).consume(amt) + } + + fn has_data_left(&mut self) -> Result { + (**self).has_data_left() + } + + #[cfg(feature = "alloc")] + fn read_until(&mut self, byte: u8, buf: &mut Vec) -> Result { + (**self).read_until(byte, buf) + } + + #[cfg(feature = "alloc")] + fn read_line(&mut self, buf: &mut String) -> Result { + (**self).read_line(buf) + } +} From c52c21f5859b736a6968d4bd5dfab4211269de39 Mon Sep 17 00:00:00 2001 From: Anekoique Date: Mon, 1 Dec 2025 19:14:43 +0800 Subject: [PATCH 2/3] feat: add read_val & read_cstring --- Cargo.toml | 1 + src/lib.rs | 35 ++++++++++++++++++++++++++++++++--- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index adfb70f..812947c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,3 +17,4 @@ default = ["alloc"] [dependencies] axerrno = "0.2" +bytemuck = { version = "1.23", features = ["derive"] } diff --git a/src/lib.rs b/src/lib.rs index bfea319..6943bce 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,13 +18,13 @@ pub mod prelude; pub use self::{ buf::{Buf, BufMut}, - buffered::BufReader, + buffered::{BufReader, BufWriter}, error::{Error, Result}, }; #[cfg(feature = "alloc")] -use alloc::{string::String, vec::Vec}; -use axerrno::ax_bail; +use alloc::{ffi::CString, string::String, vec::Vec}; +use axerrno::{ax_bail, AxError}; const DEFAULT_BUF_SIZE: usize = 1024; @@ -349,6 +349,35 @@ pub trait BufRead: Read { } } + /// Attempts to decode `T` directly from the currently buffered bytes. + /// + /// Returns `Ok(None)` if fewer than `size_of::()` bytes are available. + #[cfg(feature = "alloc")] + fn read_val(&mut self) -> Result> { + let size = core::mem::size_of::(); + let buf = self.fill_buf()?; + if buf.len() < size { + return Ok(None); + } + let val = bytemuck::try_pod_read_unaligned::(&buf[..size]) + .map_err(|_| AxError::InvalidData)?; + self.consume(size); + Ok(Some(val)) + } + + /// Reads a C-style string, stopping at the first NUL or EOF. + #[cfg(feature = "alloc")] + fn read_cstring(&mut self) -> Result<(CString, usize)> { + let mut buf = Vec::new(); + let n = self.read_until(0, &mut buf)?; + let cstring = if buf.ends_with(&[0]) { + CString::from_vec_with_nul(buf).map_err(|_| AxError::InvalidData)? + } else { + CString::new(buf).map_err(|_| AxError::InvalidData)? + }; + Ok((cstring, n)) + } + /// Read all bytes until a newline (the `0xA` byte) is reached, and append /// them to the provided `String` buffer. #[cfg(feature = "alloc")] From d11fad989a4920d93b9a49376d04cfc1549e5550 Mon Sep 17 00:00:00 2001 From: Anekoique Date: Tue, 2 Dec 2025 02:30:32 +0800 Subject: [PATCH 3/3] feat: add bufwrite & bufwriter --- src/buffered/mod.rs | 5 +- src/buffered/reader.rs | 3 +- src/buffered/writer.rs | 113 +++++++++++++++++++++++++++++++++++++++++ src/impls.rs | 17 ++++++- src/lib.rs | 15 ++++++ src/prelude.rs | 2 +- 6 files changed, 150 insertions(+), 5 deletions(-) create mode 100644 src/buffered/writer.rs diff --git a/src/buffered/mod.rs b/src/buffered/mod.rs index 272e718..f257f12 100644 --- a/src/buffered/mod.rs +++ b/src/buffered/mod.rs @@ -1,3 +1,6 @@ mod reader; +mod writer; -pub use self::reader::BufReader; +pub use self::{reader::BufReader, writer::BufWriter}; + +const DEFAULT_BUF_SIZE: usize = 1024; diff --git a/src/buffered/reader.rs b/src/buffered/reader.rs index a1d65c0..f3773fc 100644 --- a/src/buffered/reader.rs +++ b/src/buffered/reader.rs @@ -1,12 +1,11 @@ use core::mem::MaybeUninit; use crate::{BufRead, Read, Result}; +use super::DEFAULT_BUF_SIZE; #[cfg(feature = "alloc")] use alloc::{string::String, vec::Vec}; -const DEFAULT_BUF_SIZE: usize = 1024; - /// The `BufReader` struct adds buffering to any reader. pub struct BufReader { inner: R, diff --git a/src/buffered/writer.rs b/src/buffered/writer.rs new file mode 100644 index 0000000..d297163 --- /dev/null +++ b/src/buffered/writer.rs @@ -0,0 +1,113 @@ +use core::{ + mem::{ManuallyDrop, MaybeUninit}, + ptr, +}; + +use super::DEFAULT_BUF_SIZE; +use crate::{BufWrite, Result, Write}; + +/// The `BufWriter` struct adds buffering to any writer. +pub struct BufWriter { + inner: W, + pos: usize, + buf: [MaybeUninit; DEFAULT_BUF_SIZE], +} + +impl BufWriter { + /// Creates a new `BufWriter` with a default buffer capacity (1 KB). + pub const fn new(inner: W) -> BufWriter { + Self { + inner, + pos: 0, + buf: [const { MaybeUninit::uninit() }; DEFAULT_BUF_SIZE], + } + } + + /// Gets a reference to the underlying writer. + pub const fn get_ref(&self) -> &W { + &self.inner + } + + /// Gets a mutable reference to the underlying writer. + pub fn get_mut(&mut self) -> &mut W { + &mut self.inner + } + + /// Returns a reference to the internally buffered data. + pub fn buffer(&self) -> &[u8] { + unsafe { self.buf[..self.pos].assume_init_ref() } + } + + /// Returns the number of bytes the internal buffer can hold at once. + pub const fn capacity(&self) -> usize { + DEFAULT_BUF_SIZE + } + + /// Returns the remaining spare capacity in the internal buffer. + pub const fn spare_capacity(&self) -> usize { + self.capacity() - self.pos + } + + /// Unwraps this `BufWriter`, returning the underlying writer. + /// + /// Any buffered data will be flushed before returning. + pub fn into_inner(self) -> Result { + // Prevent Drop from running while we manually extract the inner writer. + let mut this = ManuallyDrop::new(self); + this.flush_buf()?; + Ok(unsafe { ptr::read(&this.inner) }) + } +} + +impl Write for BufWriter { + /// Writes a buffer into this writer, returning how many bytes were written. + fn write(&mut self, buf: &[u8]) -> Result { + if self.spare_capacity() < buf.len() { + self.flush_buf()?; + } + let written = buf.len().min(self.spare_capacity()); + unsafe { + self.buf[self.pos..self.pos + written] + .assume_init_mut() + .copy_from_slice(&buf[..written]); + } + self.pos += written; + Ok(written) + } + + /// Flushes this writer, ensuring that all intermediately buffered contents reach their destination. + fn flush(&mut self) -> Result<()> { + self.flush_buf()?; + self.inner.flush() + } +} + +impl BufWrite for BufWriter { + /// Flushes the internal buffer to the underlying writer. + fn flush_buf(&mut self) -> Result<()> { + if self.pos > 0 { + self.inner + .write_all(unsafe { self.buf[..self.pos].assume_init_ref() })?; + self.pos = 0; + } + Ok(()) + } + + /// Skips a number of bytes in the internal buffer, flushing if necessary. + fn skip_some(&mut self, len: usize) -> Result<()> { + let mut sparce = self.spare_capacity(); + if sparce < len { + self.flush_buf()?; + sparce = self.spare_capacity(); + } + self.pos += len.min(sparce); + Ok(()) + } +} + +/// Drops the `BufWriter`, flushing the internal buffer. +impl Drop for BufWriter { + fn drop(&mut self) { + let _ = self.flush_buf(); + } +} diff --git a/src/impls.rs b/src/impls.rs index 5b771cf..0e87b0f 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -1,10 +1,11 @@ +#[cfg(feature = "alloc")] use alloc::{string::String, vec::Vec}; use core::{cmp, mem}; use axerrno::ax_bail; use crate::{ - BufRead, Read, Result, Seek, SeekFrom, Write, + BufRead, BufWrite, Read, Result, Seek, SeekFrom, Write, buf::{Buf, BufMut}, }; @@ -190,3 +191,17 @@ impl BufRead for &mut B { (**self).read_line(buf) } } + +impl BufWrite for &mut B { + fn flush_buf(&mut self) -> Result<()> { + (**self).flush_buf() + } + + fn skip_some(&mut self, len: usize) -> Result<()> { + (**self).skip_some(len) + } + + fn write_val(&mut self, val: &T) -> Result<()> { + (**self).write_val(val) + } +} diff --git a/src/lib.rs b/src/lib.rs index 6943bce..54c6b25 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -401,6 +401,21 @@ where } } +// A `BufWrite` is a type of `Write`r which has an internal buffer, allowing it +// to perform extra ways of writing. +pub trait BufWrite: Write { + /// Flushes the internal buffer to the underlying writer. + fn flush_buf(&mut self) -> Result<()>; + + /// Skips writing `len` bytes by advancing the internal buffer. + fn skip_some(&mut self, len: usize) -> Result<()>; + + /// Writes the bytes of a plain-old-data value into this writer. + fn write_val(&mut self, val: &T) -> Result<()> { + self.write_all(bytemuck::bytes_of(val)) + } +} + // FIXME: This is reserved for smooth migration of ArceOS. /// I/O poll results. #[derive(Debug, Default, Clone, Copy)] diff --git a/src/prelude.rs b/src/prelude.rs index 55235be..a658bf4 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -8,4 +8,4 @@ //! use std::io::prelude::*; //! ``` -pub use super::{BufRead, Read, Seek, Write}; +pub use super::{BufRead, BufWrite, Read, Seek, Write};