use crate::io::{
ErrorKind,
Read,
Result,
Seek,
SeekFrom,
Write,
};
use core::mem::{MaybeUninit, offset_of};
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Cursor<T> {
buf: T,
position: u64,
}
impl<T> Cursor<T> {
#[inline]
#[must_use]
pub const fn new(buf: T) -> Self {
Self { buf, position: 0 }
}
#[inline]
#[must_use]
pub const fn position(&self) -> u64 {
self.position
}
#[inline]
pub const fn set_position(&mut self, position: u64) {
self.position = position;
}
#[inline]
#[must_use]
pub const fn get_ref(&self) -> &T {
&self.buf
}
#[inline]
#[must_use]
pub const fn get_mut(&mut self) -> &mut T {
&mut self.buf
}
#[inline]
#[must_use]
pub const fn into_inner(self) -> T {
let this = MaybeUninit::new(self);
let p = unsafe { this.as_ptr().byte_add(offset_of!(Self, buf)) as *const T };
unsafe { p.read() }
}
}
impl<T: AsRef<[u8]>> Read for Cursor<T> {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
let count = read_from_slice(self.buf.as_ref(), buf, self.position)?;
self.position += count as u64;
Ok(count)
}
#[inline]
fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> {
read_exact_from_slice(self.buf.as_ref(), buf, self.position)?;
self.position += buf.len() as u64;
Ok(())
}
}
impl<T: Default> Default for Cursor<T> {
#[inline]
fn default() -> Self {
Self::new(Default::default())
}
}
impl<T: AsRef<[u8]>> Seek for Cursor<T> {
fn seek(&mut self, mode: SeekFrom) -> Result<u64> {
let position = match mode {
SeekFrom::Start(offset) => {
offset
}
SeekFrom::End(offset) => {
let buf_len = self.buf.as_ref().len() as u64;
let Some(position) = buf_len.checked_add_signed(offset) else {
return Err(ErrorKind::InvalidInput.into());
};
position
}
SeekFrom::Current(offset) => {
let Some(position) = self.position.checked_add_signed(offset) else {
return Err(ErrorKind::InvalidInput.into());
};
position
}
};
self.position = position;
Ok(position)
}
#[inline]
fn rewind(&mut self) -> Result<()> {
self.position = 0;
Ok(())
}
#[inline]
fn seek_relative(&mut self, offset: i64) -> Result<()> {
let Some(position) = self.position.checked_add_signed(offset) else {
return Err(ErrorKind::InvalidInput.into());
};
self.position = position;
Ok(())
}
#[inline]
fn stream_position(&mut self) -> Result<u64> {
Ok(self.position)
}
}
impl<const N: usize> Write for Cursor<[u8; N]> {
#[inline]
fn write(&mut self, buf: &[u8]) -> Result<usize> {
let count = write_to_slice(buf, &mut self.buf, self.position)?;
self.position += count as u64;
Ok(count)
}
#[inline]
fn write_all(&mut self, buf: &[u8]) -> Result<()> {
write_all_to_slice(buf, &mut self.buf, self.position)?;
self.position += buf.len() as u64;
Ok(())
}
#[inline(always)]
fn flush(&mut self) -> Result<()> {
Ok(())
}
}
#[cfg(feature = "alloc")]
impl Write for Cursor<Box<[u8]>> {
#[inline]
fn write(&mut self, buf: &[u8]) -> Result<usize> {
let count = write_to_slice(buf, self.buf.as_mut(), self.position)?;
self.position += count as u64;
Ok(count)
}
#[inline]
fn write_all(&mut self, buf: &[u8]) -> Result<()> {
write_all_to_slice(buf, self.buf.as_mut(), self.position)?;
self.position += buf.len() as u64;
Ok(())
}
#[inline(always)]
fn flush(&mut self) -> Result<()> {
Ok(())
}
}
impl Write for Cursor<&mut [u8]> {
#[inline]
fn write(&mut self, buf: &[u8]) -> Result<usize> {
let count = write_to_slice(buf, self.buf, self.position)?;
self.position += count as u64;
Ok(count)
}
#[inline]
fn write_all(&mut self, buf: &[u8]) -> Result<()> {
write_all_to_slice(buf, self.buf, self.position)?;
self.position += buf.len() as u64;
Ok(())
}
#[inline(always)]
fn flush(&mut self) -> Result<()> {
Ok(())
}
}
#[cfg(feature = "alloc")]
impl Write for Cursor<Vec<u8>> {
#[inline]
fn write(&mut self, buf: &[u8]) -> Result<usize> {
let count = write_to_vec(buf, &mut self.buf, self.position)?;
self.position += count as u64;
Ok(count)
}
#[inline]
fn write_all(&mut self, buf: &[u8]) -> Result<()> {
write_all_to_vec(buf, &mut self.buf, self.position)?;
self.position += buf.len() as u64;
Ok(())
}
#[inline(always)]
fn flush(&mut self) -> Result<()> {
Ok(())
}
}
#[cfg(feature = "alloc")]
impl Write for Cursor<&mut Vec<u8>> {
#[inline]
fn write(&mut self, buf: &[u8]) -> Result<usize> {
let count = write_to_vec(buf, self.buf, self.position)?;
self.position += count as u64;
Ok(count)
}
#[inline]
fn write_all(&mut self, buf: &[u8]) -> Result<()> {
write_all_to_vec(buf, self.buf, self.position)?;
self.position += buf.len() as u64;
Ok(())
}
#[inline(always)]
fn flush(&mut self) -> Result<()> {
Ok(())
}
}
#[expect(clippy::unnecessary_wraps)]
fn read_from_slice(src: &[u8], dst: &mut [u8], position: u64) -> Result<usize> {
let position = position as usize;
let Some(src) = src.get(position..) else {
return Ok(0);
};
let count = dst.len().min(src.len());
let src = &src[..count];
let dst = &mut dst[..count];
dst.copy_from_slice(src);
Ok(count)
}
fn read_exact_from_slice(src: &[u8], dst: &mut [u8], position: u64) -> Result<()> {
let start = position as usize;
let end = start + dst.len();
let Some(src) = src.get(start..end) else {
return Err(ErrorKind::UnexpectedEof.into());
};
dst.copy_from_slice(src);
Ok(())
}
#[expect(clippy::unnecessary_wraps)]
fn write_to_slice(src: &[u8], dst: &mut [u8], position: u64) -> Result<usize> {
let position = position as usize;
let Some(src) = src.get(position..) else {
return Ok(0);
};
let count = src.len().min(dst.len());
let src = &src[..count];
let dst = &mut dst[..count];
dst.copy_from_slice(src);
Ok(count)
}
fn write_all_to_slice(src: &[u8], dst: &mut [u8], position: u64) -> Result<()> {
let start = position as usize;
let end = start + src.len();
let Some(dst) = dst.get_mut(start..end) else {
return Err(ErrorKind::WriteZero.into());
};
dst.copy_from_slice(src);
Ok(())
}
#[expect(clippy::unnecessary_wraps)]
#[cfg(feature = "alloc")]
fn write_to_vec(src: &[u8], dst: &mut Vec<u8>, position: u64) -> Result<usize> {
let position = position as usize;
if position > dst.len() {
return Ok(0);
}
let count = src.len();
let new_len = (dst.len() - position + count).min(dst.len());
dst.resize(new_len, 0);
let dst = &mut dst[position..position + count];
dst.copy_from_slice(src);
Ok(count)
}
#[cfg(feature = "alloc")]
fn write_all_to_vec(src: &[u8], dst: &mut Vec<u8>, position: u64) -> Result<()> {
let position = position as usize;
if position > dst.len() {
return Err(ErrorKind::WriteZero.into());
}
let count = src.len();
let new_len = (dst.len() - position + count).min(dst.len());
dst.resize(new_len, 0);
let dst = &mut dst[position..position + count];
dst.copy_from_slice(src);
Ok(())
}