",
+]
description = "`std::io`-like I/O traits for `no_std` environment"
license = "GPL-3.0-or-later OR Apache-2.0 OR MulanPSL-2.0"
homepage = "https://github.com/arceos-org/arceos"
@@ -16,4 +19,4 @@ alloc = []
default = []
[dependencies]
-axerrno = "0.1"
+axerrno = { version = "0.1" }
diff --git a/README.md b/README.md
index 0ee809b..642b090 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,52 @@
-# axio
+axio
+
+
+
+> [`std::io`][1]-like I/O traits for `no_std` environment.
+
+
+
+
+
+---
+
+
[](https://crates.io/crates/axio)
[](https://docs.rs/axio)
[](https://github.com/arceos-org/axio/actions/workflows/ci.yml)
+[](https://deepwiki.org/arceos-org/axio)
+[](https://libraries.io/cargo/axio)
+[](https://crates.io/crates/axio)
+[](https://github.com/arceos-org/axio)
-[`std::io`][1]-like I/O traits for `no_std` environment.
+[](https://github.com/arceos-org/axio/pulse)
+[](https://rust-lang.github.io/rustup/concepts/channels.html)
+[](https://github.com/arceos-org/axio/blob/main/LICENSE)
+
+
+
+---
[1]: https://doc.rust-lang.org/std/io/index.html
+
+
+
+## Example
+
+```rust
+fn main() {
+ use axio::{Read, BufReader};
+
+ let data = b"hello world";
+ let mut reader = BufReader::new(&data[..]);
+ let mut buf = [0u8; 5];
+ reader.read_exact(&mut buf).unwrap();
+}
+```
diff --git a/rust-toolchain b/rust-toolchain
new file mode 100644
index 0000000..517b722
--- /dev/null
+++ b/rust-toolchain
@@ -0,0 +1,2 @@
+[toolchain]
+channel = "nightly-2025-05-12"
diff --git a/src/buffered/bufreader.rs b/src/buffered/bufreader.rs
index b466c1e..c37fb91 100644
--- a/src/buffered/bufreader.rs
+++ b/src/buffered/bufreader.rs
@@ -3,6 +3,7 @@ use crate::{BufRead, Read, Result};
#[cfg(feature = "alloc")]
use alloc::{string::String, vec::Vec};
+/// Default buffer size used by `BufReader` (1 KB)
const DEFAULT_BUF_SIZE: usize = 1024;
/// The `BufReader` struct adds buffering to any reader.
@@ -15,6 +16,19 @@ pub struct BufReader {
impl BufReader {
/// Creates a new `BufReader` with a default buffer capacity (1 KB).
+ ///
+ /// # Examples
+ /// ```
+ /// use axio::BufReader;
+ /// use axio::Read;
+ ///
+ /// fn example() -> impl Read {
+ /// "test".as_bytes()
+ /// }
+ ///
+ /// let reader = example();
+ /// let buf_reader = BufReader::new(reader);
+ /// ```
pub const fn new(inner: R) -> BufReader {
Self {
inner,
@@ -66,6 +80,8 @@ impl BufReader {
}
impl Read for BufReader {
+ /// Reads data into the provided buffer, using the internal buffer to
+ /// minimize direct reads from the underlying reader
fn read(&mut self, buf: &mut [u8]) -> Result {
// If we don't have any buffered data and we're doing a massive read
// (larger than our internal buffer), bypass our internal buffer
@@ -82,10 +98,11 @@ impl Read for BufReader {
Ok(nread)
}
- // Small read_exacts from a BufReader are extremely common when used with a deserializer.
- // The default implementation calls read in a loop, which results in surprisingly poor code
- // generation for the common path where the buffer has enough bytes to fill the passed-in
- // buffer.
+ /// Reads exactly enough bytes to fill the buffer, using buffered data first
+ /// Small read_exacts from a BufReader are extremely common when used with a deserializer.
+ /// The default implementation calls read in a loop, which results in surprisingly poor code
+ /// generation for the common path where the buffer has enough bytes to fill the passed-in
+ /// buffer.
fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> {
let amt = buf.len();
if let Some(claimed) = self.buffer().get(..amt) {
@@ -96,8 +113,9 @@ impl Read for BufReader {
self.inner.read_exact(buf)
}
- // The inner reader might have an optimized `read_to_end`. Drain our buffer and then
- // delegate to the inner implementation.
+ /// Reads all bytes until EOF, appending them to the provided vector
+ /// The inner reader might have an optimized `read_to_end`. Drain our buffer and then
+ /// delegate to the inner implementation.
#[cfg(feature = "alloc")]
fn read_to_end(&mut self, buf: &mut Vec) -> Result {
let inner_buf = self.buffer();
@@ -107,8 +125,9 @@ impl Read for BufReader {
Ok(nread + self.inner.read_to_end(buf)?)
}
- // The inner reader might have an optimized `read_to_end`. Drain our buffer and then
- // delegate to the inner implementation.
+ /// Reads all bytes until EOF as UTF-8, appending them to the string
+ /// The inner reader might have an optimized `read_to_end`. Drain our buffer and then
+ /// delegate to the inner implementation.
#[cfg(feature = "alloc")]
fn read_to_string(&mut self, buf: &mut String) -> Result {
// In the general `else` case below we must read bytes into a side buffer, check
@@ -143,6 +162,7 @@ impl Read for BufReader {
}
impl BufRead for BufReader {
+ /// Fills the internal buffer if empty and returns its contents
fn fill_buf(&mut self) -> Result<&[u8]> {
if self.is_empty() {
let read_len = self.inner.read(&mut self.buf)?;
@@ -152,7 +172,190 @@ impl BufRead for BufReader {
Ok(self.buffer())
}
+ /// Consumes the specified number of bytes from the buffer
fn consume(&mut self, amt: usize) {
self.pos = core::cmp::min(self.pos + amt, self.filled);
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::Result;
+
+ struct TempReader {
+ data: &'static [u8],
+ pos: usize,
+ }
+
+ impl TempReader {
+ fn new(data: &'static [u8]) -> Self {
+ Self { data, pos: 0 }
+ }
+ }
+
+ impl Read for TempReader {
+ fn read(&mut self, buf: &mut [u8]) -> Result {
+ let remaining = self.data.len() - self.pos;
+ if remaining == 0 {
+ return Ok(0);
+ }
+ let to_copy = core::cmp::min(buf.len(), remaining);
+ buf[..to_copy].copy_from_slice(&self.data[self.pos..self.pos + to_copy]);
+ self.pos += to_copy;
+ Ok(to_copy)
+ }
+ }
+
+ #[test]
+ fn test_get_ref() {
+ let reader = TempReader::new(b"test");
+ let buf_reader = BufReader::new(reader);
+ assert_eq!(buf_reader.get_ref().data, b"test");
+ }
+
+ #[test]
+ fn test_get_mut() {
+ let reader = TempReader::new(b"test");
+ let mut buf_reader = BufReader::new(reader);
+ assert_eq!(buf_reader.get_mut().data, b"test");
+ }
+
+ #[test]
+ fn test_buffer_empty() {
+ let reader = TempReader::new(b"");
+ let buf_reader = BufReader::new(reader);
+ assert!(buf_reader.buffer().is_empty());
+ }
+
+ #[test]
+ fn test_into_inner() {
+ let reader = TempReader::new(b"test");
+ let buf_reader = BufReader::new(reader);
+ let inner = buf_reader.into_inner();
+ assert_eq!(inner.data, b"test");
+ }
+
+ #[test]
+ fn test_read_small() {
+ let reader = TempReader::new(b"hello world");
+ let mut buf_reader = BufReader::new(reader);
+
+ let mut buf = [0; 5];
+ assert_eq!(buf_reader.read(&mut buf).unwrap(), 5);
+ assert_eq!(&buf, b"hello");
+
+ assert_eq!(buf_reader.read(&mut buf).unwrap(), 5);
+ assert_eq!(&buf, b" worl");
+
+ assert_eq!(buf_reader.read(&mut buf).unwrap(), 1);
+ assert_eq!(&buf[..1], b"d");
+
+ assert_eq!(buf_reader.read(&mut buf).unwrap(), 0);
+ }
+
+ #[test]
+ fn test_read_large() {
+ const DATA: &'static [u8] = &[1u8; DEFAULT_BUF_SIZE * 2];
+ let reader = TempReader::new(DATA);
+ let mut buf_reader = BufReader::new(reader);
+
+ let mut buf = [0u8; DEFAULT_BUF_SIZE * 2];
+ assert_eq!(buf_reader.read(&mut buf).unwrap(), DEFAULT_BUF_SIZE * 2);
+ assert_eq!(&buf, DATA);
+ }
+
+ #[test]
+ fn test_read_exact() {
+ let reader = TempReader::new(b"hello world");
+ let mut buf_reader = BufReader::new(reader);
+
+ let mut buf = [0; 5];
+ buf_reader.read_exact(&mut buf).unwrap();
+ assert_eq!(&buf, b"hello");
+
+ buf_reader.read_exact(&mut buf).unwrap();
+ assert_eq!(&buf, b" worl");
+
+ let mut buf2 = [0; 1];
+ buf_reader.read_exact(&mut buf2).unwrap();
+ assert_eq!(&buf2, b"d");
+
+ let mut buf3 = [0; 1];
+ assert!(buf_reader.read_exact(&mut buf3).is_err());
+ }
+
+ #[test]
+ #[cfg(feature = "alloc")]
+ fn test_read_to_end() {
+ let reader = TempReader::new(b"hello world");
+ let mut buf_reader = BufReader::new(reader);
+
+ let mut buf = Vec::new();
+ assert_eq!(buf_reader.read_to_end(&mut buf).unwrap(), 11);
+ assert_eq!(buf, b"hello world");
+ }
+
+ #[test]
+ #[cfg(feature = "alloc")]
+ fn test_read_to_string() {
+ let reader = TempReader::new(b"hello world");
+ let mut buf_reader = BufReader::new(reader);
+
+ let mut buf = String::new();
+ assert_eq!(buf_reader.read_to_string(&mut buf).unwrap(), 11);
+ assert_eq!(buf, "hello world");
+ }
+
+ #[test]
+ fn test_fill_buf() {
+ let reader = TempReader::new(b"hello world");
+ let mut buf_reader = BufReader::new(reader);
+
+ let buf = buf_reader.fill_buf().unwrap();
+ assert_eq!(buf, b"hello world");
+
+ buf_reader.consume(5);
+ let buf = buf_reader.fill_buf().unwrap();
+ assert_eq!(buf, b" world");
+ }
+
+ #[test]
+ fn test_consume() {
+ let reader = TempReader::new(b"hello world");
+ let mut buf_reader = BufReader::new(reader);
+
+ buf_reader.fill_buf().unwrap();
+ assert_eq!(buf_reader.buffer(), b"hello world");
+
+ buf_reader.consume(5);
+ assert_eq!(buf_reader.buffer(), b" world");
+
+ buf_reader.consume(6);
+ assert!(buf_reader.buffer().is_empty());
+ }
+
+ #[test]
+ fn test_edge_cases() {
+ // Empty reader
+ let reader = TempReader::new(b"");
+ let mut buf_reader = BufReader::new(reader);
+ let mut buf = [0; 1];
+ assert_eq!(buf_reader.read(&mut buf).unwrap(), 0);
+
+ // Single byte
+ let reader = TempReader::new(b"x");
+ let mut buf_reader = BufReader::new(reader);
+ let mut buf = [0; 1];
+ assert_eq!(buf_reader.read(&mut buf).unwrap(), 1);
+ assert_eq!(buf[0], b'x');
+
+ // Exact buffer size
+ const DATA: &'static [u8] = &[1u8; DEFAULT_BUF_SIZE];
+ let reader = TempReader::new(DATA);
+ let mut buf_reader = BufReader::new(reader);
+ let mut buf = [0u8; DEFAULT_BUF_SIZE];
+ assert_eq!(buf_reader.read(&mut buf).unwrap(), DEFAULT_BUF_SIZE);
+ assert_eq!(&buf, DATA);
+ }
+}
diff --git a/src/buffered/mod.rs b/src/buffered/mod.rs
index c7dfe1e..3e94d86 100644
--- a/src/buffered/mod.rs
+++ b/src/buffered/mod.rs
@@ -1,3 +1,11 @@
+//! Buffered I/O module.
+//!
+//! ```
+//! # #![allow(unused_imports)]
+//! use axio::BufReader;
+//! ```
+
mod bufreader;
+/// Re-export the `BufReader` type from the bufreader module.
pub use self::bufreader::BufReader;
diff --git a/src/error.rs b/src/error.rs
index ee2d716..972abeb 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,2 +1,5 @@
+/// Re-export of `AxError` from axerrno crate as the standard Error type
pub use axerrno::AxError as Error;
+
+/// Re-export of `AxResult` from axerrno crate as the standard Result type
pub use axerrno::AxResult as Result;
diff --git a/src/impls.rs b/src/impls.rs
index 96f83db..98a3cb1 100644
--- a/src/impls.rs
+++ b/src/impls.rs
@@ -1,7 +1,15 @@
+//! Implementation of core I/O traits for basic types
+
use crate::{prelude::*, Result};
use core::cmp;
+/// Implementation of Read trait for byte slices (&[u8])
+///
+/// Provides efficient reading operations directly from byte slices without additional buffering.
impl Read for &[u8] {
+ /// Reads bytes from the slice into the provided buffer
+ ///
+ /// Returns the number of bytes read.
#[inline]
fn read(&mut self, buf: &mut [u8]) -> Result {
let amt = cmp::min(buf.len(), self.len());
@@ -21,6 +29,21 @@ impl Read for &[u8] {
Ok(amt)
}
+ /// Reads all remaining bytes until end of slice
+ ///
+ /// Need enable `alloc` feature to use this function cause its needs heap allocation.
+ #[inline]
+ #[cfg(feature = "alloc")]
+ fn read_to_end(&mut self, buf: &mut alloc::vec::Vec) -> Result {
+ buf.extend_from_slice(self);
+ let len = self.len();
+ *self = &self[len..];
+ Ok(len)
+ }
+
+ /// Reads exactly the requested number of bytes
+ ///
+ /// Returns an error if not enough bytes are available.
#[inline]
fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> {
if buf.len() > self.len() {
@@ -42,13 +65,112 @@ impl Read for &[u8] {
*self = b;
Ok(())
}
+}
- #[inline]
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_read() {
+ let data = b"hello";
+ let mut slice = &data[..];
+ let mut buf = [0u8; 5];
+
+ // test full read
+ let res = slice.read(&mut buf).unwrap();
+ assert_eq!(res, 5);
+ assert_eq!(buf, *b"hello");
+ assert!(slice.is_empty());
+
+ // test partial read
+ let data = b"world";
+ let mut slice = &data[..];
+ let mut buf = [0u8; 3];
+ let res = slice.read(&mut buf).unwrap();
+ assert_eq!(res, 3);
+ assert_eq!(buf, *b"wor");
+ assert_eq!(slice, b"ld");
+
+ // test single-byte read optimization
+ let data = b"x";
+ let mut slice = &data[..];
+ let mut buf = [0u8; 1];
+ let res = slice.read(&mut buf).unwrap();
+ assert_eq!(res, 1);
+ assert_eq!(buf[0], b'x');
+ assert!(slice.is_empty());
+
+ // test empty read
+ let mut empty_reader = &b""[..];
+ assert_eq!(empty_reader.read(&mut buf).unwrap(), 0);
+ }
+
+ #[test]
+ fn test_read_partial() {
+ let data = b"hello world";
+ let mut reader = &data[..];
+ let mut buf = [0u8; 5];
+
+ // test full read
+ assert_eq!(reader.read(&mut buf).unwrap(), 5);
+ assert_eq!(&buf, b"hello");
+
+ // test partial read
+ assert_eq!(reader.read(&mut buf).unwrap(), 5);
+ assert_eq!(&buf, b" worl");
+
+ // test single-byte read
+ let mut small_buf = [0u8; 1];
+ assert_eq!(reader.read(&mut small_buf).unwrap(), 1);
+ assert_eq!(&small_buf, b"d");
+ }
+
+ #[test]
+ fn test_read_exact() {
+ let data = b"heke";
+ let mut slice = &data[..];
+ let mut buf = [0u8; 4];
+
+ // test exact read
+ slice.read_exact(&mut buf).unwrap();
+ assert_eq!(buf, *b"heke");
+ assert!(slice.is_empty());
+
+ // test insufficient data
+ let mut slice = &data[..];
+ let mut buf = [0u8; 5];
+ let res = slice.read_exact(&mut buf);
+ assert!(res.is_err());
+ assert_eq!(slice, b"heke");
+
+ // test single-byte exact read
+ let data = b"x";
+ let mut slice = &data[..];
+ let mut buf = [0u8; 1];
+ assert!(slice.read_exact(&mut buf).is_ok());
+ assert_eq!(buf[0], b'x');
+ }
+
+ #[test]
#[cfg(feature = "alloc")]
- fn read_to_end(&mut self, buf: &mut alloc::vec::Vec) -> Result {
- buf.extend_from_slice(self);
- let len = self.len();
- *self = &self[len..];
- Ok(len)
+ fn test_read_to_end() {
+ use alloc::vec::Vec;
+
+ let data = b"test";
+ let mut slice = &data[..];
+ let mut buf = Vec::new();
+
+ // test read all data
+ let res = slice.read_to_end(&mut buf).unwrap();
+ assert_eq!(res, 4);
+ assert_eq!(buf, b"test");
+ assert!(slice.is_empty());
+
+ // test empty read
+ let mut empty_reader = &b""[..];
+ let mut empty_buf = Vec::new();
+ assert_eq!(empty_reader.read_to_end(&mut empty_buf).unwrap(), 0);
+ assert!(empty_buf.is_empty());
}
}
diff --git a/src/lib.rs b/src/lib.rs
index 4a8a9a5..a819dfc 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,4 +1,16 @@
//! [`std::io`]-like I/O traits for `no_std` environment.
+//!
+//! # Examples
+//!
+//! Basic usage with byte slices:
+//! ```
+//! use axio::{Read, BufReader};
+//!
+//! let data = b"hello world";
+//! let mut reader = BufReader::new(&data[..]);
+//! let mut buf = [0u8; 5];
+//! reader.read_exact(&mut buf).unwrap();
+//! ```
#![cfg_attr(not(doc), no_std)]
#![feature(doc_auto_cfg)]
@@ -13,9 +25,13 @@ mod buffered;
mod error;
mod impls;
+/// Re-export of commonly used I/O traits and types
pub mod prelude;
+/// A buffered reader that reads from another reader
pub use self::buffered::BufReader;
+
+/// Standard error and result types for I/O operations
pub use self::error::{Error, Result};
#[cfg(feature = "alloc")]
@@ -377,3 +393,89 @@ pub struct PollState {
/// Object can be writen now.
pub writable: bool,
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::Result;
+
+ // test Read trait
+ #[test]
+ fn test_read_trait() {
+ let data = b"hello";
+ let mut slice = &data[..];
+ let mut buf = [0u8; 5];
+
+ // test full read
+ assert_eq!(slice.read(&mut buf).unwrap(), 5);
+ assert_eq!(buf, *b"hello");
+ assert!(slice.is_empty());
+
+ // test partial read
+ let data = b"world";
+ let mut slice = &data[..];
+ let mut buf = [0u8; 3];
+ assert_eq!(slice.read(&mut buf).unwrap(), 3);
+ assert_eq!(buf, *b"wor");
+ assert_eq!(slice, b"ld");
+ }
+
+ #[test]
+ fn test_read_exact() {
+ let data = b"test";
+ let mut slice = &data[..];
+ let mut buf = [0u8; 4];
+
+ // test exact read
+ slice.read_exact(&mut buf).unwrap();
+ assert_eq!(buf, *b"test");
+ assert!(slice.is_empty());
+
+ // test insufficient data
+ let mut slice = &data[..];
+ let mut buf = [0u8; 5];
+ assert!(slice.read_exact(&mut buf).is_err());
+ }
+
+ #[test]
+ fn test_write_trait() {
+ struct TestWriter {
+ buf: [u8; 10],
+ pos: usize,
+ }
+
+ impl Write for TestWriter {
+ fn write(&mut self, buf: &[u8]) -> Result {
+ let remaining = self.buf.len() - self.pos;
+ let to_write = buf.len().min(remaining);
+ self.buf[self.pos..self.pos + to_write].copy_from_slice(&buf[..to_write]);
+ self.pos += to_write;
+ Ok(to_write)
+ }
+
+ fn flush(&mut self) -> Result {
+ Ok(())
+ }
+ }
+
+ let mut writer = TestWriter {
+ buf: [0; 10],
+ pos: 0,
+ };
+
+ // test single write
+ assert_eq!(writer.write(b"hello").unwrap(), 5);
+ assert_eq!(&writer.buf[..5], b"hello");
+ }
+
+ #[test]
+ fn test_poll_state() {
+ let state = PollState {
+ readable: true,
+ writable: false,
+ };
+
+ assert!(state.readable);
+ assert!(!state.writable);
+ }
+}
diff --git a/src/prelude.rs b/src/prelude.rs
index 9f8020c..5ce5434 100644
--- a/src/prelude.rs
+++ b/src/prelude.rs
@@ -5,7 +5,8 @@
//!
//! ```
//! # #![allow(unused_imports)]
-//! use std::io::prelude::*;
+//! use axio::prelude::*;
//! ```
+/// Re-exports commonly used I/O traits.
pub use super::{BufRead, Read, Seek, Write};