#![no_std]
#![allow(async_fn_in_trait)]
#[cfg(feature = "std")]
extern crate std;
#[cfg(not(feature = "std"))]
mod error;
#[cfg(not(feature = "std"))]
pub use error::{Error, ErrorKind, Result};
#[cfg(feature = "std")]
pub use std::io::{Error, ErrorKind, Result};
#[cfg(feature = "std")]
pub use std::path::{Path, PathBuf};
#[cfg(feature = "std")]
pub use std::io::SeekFrom;
#[cfg(not(feature = "std"))]
mod traits;
#[cfg(not(feature = "std"))]
pub use traits::SeekFrom;
#[macro_export]
macro_rules! try_io_result_option {
($expr:expr) => {
match $expr {
Ok(val) => val,
Err(err) => return Some(Err(err)),
}
};
}
#[derive(Debug, Clone)]
pub struct Cursor<'a> {
data: &'a [u8],
cursor: usize,
}
impl<'a> Cursor<'a> {
pub fn new(data: &'a [u8]) -> Self {
Self { data, cursor: 0 }
}
pub fn position(&self) -> usize {
self.cursor
}
pub fn set_position(&mut self, pos: usize) {
self.cursor = pos;
}
pub fn get_ref(&self) -> &'a [u8] {
self.data
}
#[allow(dead_code)]
fn read_impl(&mut self, buf: &mut [u8]) -> Result<usize> {
let remaining = self.data.len().saturating_sub(self.cursor);
let to_read = buf.len().min(remaining);
if to_read > 0 {
buf[..to_read].copy_from_slice(&self.data[self.cursor..self.cursor + to_read]);
self.cursor += to_read;
}
Ok(to_read)
}
#[allow(dead_code)]
fn seek_impl(&mut self, pos: SeekFrom) -> Result<u64> {
let new_pos = match pos {
SeekFrom::Start(offset) => offset as i64,
SeekFrom::End(offset) => self.data.len() as i64 + offset,
SeekFrom::Current(offset) => self.cursor as i64 + offset,
};
if new_pos < 0 {
#[cfg(feature = "std")]
return Err(Error::new(
ErrorKind::InvalidInput,
"invalid seek to negative position",
));
#[cfg(not(feature = "std"))]
return Err(Error::new(
ErrorKind::InvalidInput,
"invalid seek to negative position",
));
}
self.cursor = new_pos as usize;
Ok(self.cursor as u64)
}
}
#[cfg(feature = "sync")]
mod sync_api;
#[cfg(feature = "sync")]
pub mod sync {
pub use super::sync_api::*;
}
#[cfg(feature = "sync")]
impl sync::Read for Cursor<'_> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
self.read_impl(buf)
}
}
#[cfg(all(feature = "sync", not(feature = "std")))]
impl sync::Seek for Cursor<'_> {
fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
self.seek_impl(pos)
}
}
#[cfg(all(feature = "sync", feature = "std"))]
impl std::io::Seek for Cursor<'_> {
fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
let our_pos = match pos {
std::io::SeekFrom::Start(n) => SeekFrom::Start(n),
std::io::SeekFrom::End(n) => SeekFrom::End(n),
std::io::SeekFrom::Current(n) => SeekFrom::Current(n),
};
self.seek_impl(our_pos)
}
}
#[cfg(feature = "sync")]
pub use sync::*;
#[cfg(feature = "async")]
mod async_api;
#[cfg(feature = "async")]
pub mod r#async {
pub use super::async_api::*;
}
#[cfg(feature = "async")]
impl r#async::Read for Cursor<'_> {
async fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
self.read_impl(buf)
}
}
#[cfg(feature = "async")]
impl r#async::Seek for Cursor<'_> {
async fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
self.seek_impl(pos)
}
}
#[cfg(all(test, feature = "sync"))]
mod tests {
extern crate std;
use super::*;
use std::format;
#[test]
fn cursor_new_starts_at_zero() {
let data = [1, 2, 3, 4, 5];
let cursor = Cursor::new(&data);
assert_eq!(cursor.position(), 0);
assert_eq!(cursor.get_ref(), &data);
}
#[test]
fn cursor_set_position() {
let data = [0u8; 10];
let mut cursor = Cursor::new(&data);
cursor.set_position(5);
assert_eq!(cursor.position(), 5);
cursor.set_position(0);
assert_eq!(cursor.position(), 0);
}
#[test]
fn cursor_read_basic() {
let data = [10, 20, 30, 40, 50];
let mut cursor = Cursor::new(&data);
let mut buf = [0u8; 3];
let n = cursor.read_impl(&mut buf).unwrap();
assert_eq!(n, 3);
assert_eq!(buf, [10, 20, 30]);
assert_eq!(cursor.position(), 3);
}
#[test]
fn cursor_read_past_end() {
let data = [1, 2];
let mut cursor = Cursor::new(&data);
let mut buf = [0u8; 5];
let n = cursor.read_impl(&mut buf).unwrap();
assert_eq!(n, 2);
assert_eq!(&buf[..2], &[1, 2]);
assert_eq!(cursor.position(), 2);
let n = cursor.read_impl(&mut buf).unwrap();
assert_eq!(n, 0);
}
#[test]
fn cursor_read_empty_buffer() {
let data = [1, 2, 3];
let mut cursor = Cursor::new(&data);
let mut buf = [0u8; 0];
let n = cursor.read_impl(&mut buf).unwrap();
assert_eq!(n, 0);
assert_eq!(cursor.position(), 0);
}
#[test]
fn cursor_seek_start() {
let data = [0u8; 20];
let mut cursor = Cursor::new(&data);
let pos = cursor.seek_impl(SeekFrom::Start(10)).unwrap();
assert_eq!(pos, 10);
assert_eq!(cursor.position(), 10);
}
#[test]
fn cursor_seek_end() {
let data = [0u8; 20];
let mut cursor = Cursor::new(&data);
let pos = cursor.seek_impl(SeekFrom::End(-5)).unwrap();
assert_eq!(pos, 15);
assert_eq!(cursor.position(), 15);
}
#[test]
fn cursor_seek_current() {
let data = [0u8; 20];
let mut cursor = Cursor::new(&data);
cursor.set_position(10);
let pos = cursor.seek_impl(SeekFrom::Current(3)).unwrap();
assert_eq!(pos, 13);
let pos = cursor.seek_impl(SeekFrom::Current(-5)).unwrap();
assert_eq!(pos, 8);
}
#[test]
fn cursor_seek_negative_position_errors() {
let data = [0u8; 10];
let mut cursor = Cursor::new(&data);
let result = cursor.seek_impl(SeekFrom::End(-20));
assert!(result.is_err());
let err = result.unwrap_err();
assert_eq!(err.kind(), ErrorKind::InvalidInput);
}
#[test]
fn cursor_seek_to_start_of_stream() {
let data = [0u8; 10];
let mut cursor = Cursor::new(&data);
cursor.set_position(5);
let pos = cursor.seek_impl(SeekFrom::Start(0)).unwrap();
assert_eq!(pos, 0);
}
#[test]
fn cursor_clone() {
let data = [1, 2, 3, 4, 5];
let mut cursor = Cursor::new(&data);
cursor.set_position(3);
let clone = cursor.clone();
assert_eq!(clone.position(), 3);
assert_eq!(clone.get_ref(), cursor.get_ref());
}
#[test]
fn cursor_debug_format() {
let data = [1, 2, 3];
let cursor = Cursor::new(&data);
let debug = format!("{:?}", cursor);
assert!(debug.contains("Cursor"));
}
#[test]
fn sync_read_trait() {
use sync::Read;
let data = [10, 20, 30, 40, 50];
let mut cursor = Cursor::new(&data);
let mut buf = [0u8; 3];
let n = cursor.read(&mut buf).unwrap();
assert_eq!(n, 3);
assert_eq!(buf, [10, 20, 30]);
}
#[test]
fn sync_read_exact_success() {
use sync::Read;
let data = [1, 2, 3, 4, 5];
let mut cursor = Cursor::new(&data);
let mut buf = [0u8; 5];
cursor.read_exact(&mut buf).unwrap();
assert_eq!(buf, [1, 2, 3, 4, 5]);
}
#[test]
fn sync_read_exact_eof() {
use sync::Read;
let data = [1, 2];
let mut cursor = Cursor::new(&data);
let mut buf = [0u8; 5];
let result = cursor.read_exact(&mut buf);
assert!(result.is_err());
}
#[test]
fn sync_seek_trait() {
use sync::Seek;
let data = [0u8; 20];
let mut cursor = Cursor::new(&data);
let pos = cursor.seek(SeekFrom::Start(10)).unwrap();
assert_eq!(pos, 10);
let pos = cursor.stream_position().unwrap();
assert_eq!(pos, 10);
}
#[test]
fn sync_seek_relative() {
use sync::Seek;
let data = [0u8; 20];
let mut cursor = Cursor::new(&data);
cursor.seek(SeekFrom::Start(5)).unwrap();
cursor.seek_relative(3).unwrap();
assert_eq!(cursor.stream_position().unwrap(), 8);
cursor.seek_relative(-2).unwrap();
assert_eq!(cursor.stream_position().unwrap(), 6);
}
#[test]
fn read_ext_read_struct() {
use sync::ReadExt;
let data = [0x78, 0x56, 0x34, 0x12]; let mut cursor = Cursor::new(&data);
let val: u32 = cursor.read_struct().unwrap();
assert_eq!(val, u32::from_ne_bytes([0x78, 0x56, 0x34, 0x12]));
}
#[test]
fn read_ext_read_struct_eof() {
use sync::ReadExt;
let data = [0x78, 0x56]; let mut cursor = Cursor::new(&data);
let result: Result<u32> = cursor.read_struct();
assert!(result.is_err());
}
#[test]
fn try_io_result_option_ok() {
fn test_fn() -> Option<Result<u32>> {
let val: Result<u32> = Ok(42);
let v = try_io_result_option!(val);
Some(Ok(v))
}
let result = test_fn();
assert!(matches!(result, Some(Ok(42))));
}
#[test]
fn try_io_result_option_err() {
fn test_fn() -> Option<Result<u32>> {
let val: Result<u32> = Err(Error::new(ErrorKind::NotFound, "not found"));
let _v = try_io_result_option!(val);
Some(Ok(0)) }
let result = test_fn();
assert!(matches!(result, Some(Err(_))));
}
}