#![cfg_attr(doc, doc = include_str!("../README.md"))]
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use core::marker::PhantomData;
use core::mem::MaybeUninit;
use core::slice;
pub trait Buffer<T>
where
Self: Sized,
{
type Output;
fn buffer_ptr(&mut self) -> *mut T;
fn buffer_len(&self) -> usize;
unsafe fn assume_init(self, len: usize) -> Self::Output;
fn cursor(self) -> Cursor<T, Self> {
Cursor::new(self)
}
}
impl<T> Buffer<T> for &mut [T] {
type Output = usize;
#[inline]
fn buffer_ptr(&mut self) -> *mut T {
self.as_mut_ptr()
}
#[inline]
fn buffer_len(&self) -> usize {
self.len()
}
#[inline]
unsafe fn assume_init(self, len: usize) -> Self::Output {
len
}
}
impl<T, const N: usize> Buffer<T> for &mut [T; N] {
type Output = usize;
#[inline]
fn buffer_ptr(&mut self) -> *mut T {
self.as_mut_ptr()
}
#[inline]
fn buffer_len(&self) -> usize {
N
}
#[inline]
unsafe fn assume_init(self, len: usize) -> Self::Output {
len
}
}
#[cfg(feature = "alloc")]
impl<T> Buffer<T> for &mut Vec<T> {
type Output = usize;
#[inline]
fn buffer_ptr(&mut self) -> *mut T {
self.as_mut_ptr()
}
#[inline]
fn buffer_len(&self) -> usize {
self.len()
}
#[inline]
unsafe fn assume_init(self, len: usize) -> Self::Output {
len
}
}
#[cfg(feature = "alloc")]
impl<T> Buffer<T> for &mut Box<[T]> {
type Output = usize;
#[inline]
fn buffer_ptr(&mut self) -> *mut T {
self.as_mut_ptr()
}
#[inline]
fn buffer_len(&self) -> usize {
self.len()
}
#[inline]
unsafe fn assume_init(self, len: usize) -> Self::Output {
len
}
}
impl<'a, T> Buffer<T> for &'a mut [MaybeUninit<T>] {
type Output = (&'a mut [T], &'a mut [MaybeUninit<T>]);
#[inline]
fn buffer_ptr(&mut self) -> *mut T {
self.as_mut_ptr().cast::<T>()
}
#[inline]
fn buffer_len(&self) -> usize {
self.len()
}
#[inline]
unsafe fn assume_init(self, len: usize) -> Self::Output {
let (init, uninit) = self.split_at_mut(len);
let init = unsafe { slice::from_raw_parts_mut(init.as_mut_ptr().cast::<T>(), init.len()) };
(init, uninit)
}
}
impl<'a, T, const N: usize> Buffer<T> for &'a mut [MaybeUninit<T>; N] {
type Output = (&'a mut [T], &'a mut [MaybeUninit<T>]);
#[inline]
fn buffer_ptr(&mut self) -> *mut T {
self.as_mut_ptr().cast::<T>()
}
#[inline]
fn buffer_len(&self) -> usize {
N
}
#[inline]
unsafe fn assume_init(self, len: usize) -> Self::Output {
let (init, uninit) = self.split_at_mut(len);
let init = unsafe { slice::from_raw_parts_mut(init.as_mut_ptr().cast::<T>(), init.len()) };
(init, uninit)
}
}
#[cfg(feature = "alloc")]
impl<'a, T> Buffer<T> for &'a mut Vec<MaybeUninit<T>> {
type Output = (&'a mut [T], &'a mut [MaybeUninit<T>]);
#[inline]
fn buffer_ptr(&mut self) -> *mut T {
self.as_mut_ptr().cast::<T>()
}
#[inline]
fn buffer_len(&self) -> usize {
self.len()
}
#[inline]
unsafe fn assume_init(self, len: usize) -> Self::Output {
let (init, uninit) = self.split_at_mut(len);
let init = unsafe { slice::from_raw_parts_mut(init.as_mut_ptr().cast::<T>(), init.len()) };
(init, uninit)
}
}
#[cfg(feature = "alloc")]
impl<'a, T> Buffer<T> for &'a mut Box<[MaybeUninit<T>]> {
type Output = (&'a mut [T], &'a mut [MaybeUninit<T>]);
#[inline]
fn buffer_ptr(&mut self) -> *mut T {
self.as_mut_ptr().cast::<T>()
}
#[inline]
fn buffer_len(&self) -> usize {
self.len()
}
#[inline]
unsafe fn assume_init(self, len: usize) -> Self::Output {
let (init, uninit) = self.split_at_mut(len);
let init = unsafe { slice::from_raw_parts_mut(init.as_mut_ptr().cast::<T>(), init.len()) };
(init, uninit)
}
}
#[cfg(feature = "std")]
impl<'a> Buffer<u8> for &mut std::io::IoSliceMut<'a> {
type Output = usize;
#[inline]
fn buffer_ptr(&mut self) -> *mut u8 {
self.as_mut_ptr()
}
#[inline]
fn buffer_len(&self) -> usize {
self.len()
}
#[inline]
unsafe fn assume_init(self, len: usize) -> Self::Output {
len
}
}
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub struct SpareCapacity<'a, T>(&'a mut Vec<T>);
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub fn spare_capacity<'a, T>(v: &'a mut Vec<T>) -> SpareCapacity<'a, T> {
debug_assert_ne!(
v.capacity(),
0,
"`extend` uses spare capacity, and never allocates new memory, so the `Vec` passed to it \
should have some spare capacity."
);
SpareCapacity(v)
}
#[cfg(feature = "alloc")]
impl<'a, T> Buffer<T> for SpareCapacity<'a, T> {
type Output = usize;
#[inline]
fn buffer_ptr(&mut self) -> *mut T {
self.0.spare_capacity_mut().as_mut_ptr().cast::<T>()
}
#[inline]
fn buffer_len(&self) -> usize {
self.0.capacity() - self.0.len()
}
#[inline]
unsafe fn assume_init(self, len: usize) -> Self::Output {
unsafe {
self.0.set_len(self.0.len() + len);
}
len
}
}
pub struct Cursor<T, B: Buffer<T>> {
pos: usize,
b: B,
phantom: PhantomData<T>,
}
impl<T, B: Buffer<T>> Cursor<T, B> {
pub const fn new(b: B) -> Self {
Self {
pos: 0,
b,
phantom: PhantomData,
}
}
pub fn remaining(&self) -> usize {
self.b.buffer_len() - self.pos
}
pub fn write(&mut self, t: T) {
let ptr = self.b.buffer_ptr();
let len = self.b.buffer_len();
assert!(
self.pos < len,
"element would extend beyond the end of the buffer"
);
unsafe {
ptr.add(self.pos).write(t);
}
self.pos += 1;
}
pub fn write_slice(&mut self, t: &[T])
where
T: Copy,
{
let ptr = self.b.buffer_ptr();
let len = self.b.buffer_len();
assert!(
len - self.pos >= t.len(),
"elements would extend beyond the end of the buffer"
);
unsafe {
core::ptr::copy_nonoverlapping(t.as_ptr(), ptr, t.len());
}
self.pos += t.len();
}
pub fn finish(self) -> B::Output {
unsafe { self.b.assume_init(self.pos) }
}
}
impl<T, B: Buffer<T>> Buffer<T> for Cursor<T, B> {
type Output = B::Output;
#[inline]
fn buffer_ptr(&mut self) -> *mut T {
unsafe { self.b.buffer_ptr().add(self.pos) }
}
#[inline]
fn buffer_len(&self) -> usize {
self.remaining()
}
#[inline]
unsafe fn assume_init(mut self, len: usize) -> Self::Output {
self.pos += len;
self.finish()
}
}
impl<T, B: Buffer<T>> Buffer<T> for &mut Cursor<T, B> {
type Output = usize;
#[inline]
fn buffer_ptr(&mut self) -> *mut T {
unsafe { self.b.buffer_ptr().add(self.pos) }
}
#[inline]
fn buffer_len(&self) -> usize {
self.remaining()
}
#[inline]
unsafe fn assume_init(self, len: usize) -> Self::Output {
self.pos += len;
len
}
}
#[cfg(test)]
mod tests {
#[allow(unused_imports)]
use super::*;
#[cfg(not(windows))]
#[test]
fn test_compilation() {
use core::mem::MaybeUninit;
fn read<B: Buffer<u8>>(b: B) -> Result<B::Output, ()> {
Ok(b.cursor().finish())
}
let mut buf = vec![0_u8; 3];
buf.reserve(32);
let _x: usize = read(spare_capacity(&mut buf)).unwrap();
let _x: (&mut [u8], &mut [MaybeUninit<u8>]) = read(buf.spare_capacity_mut()).unwrap();
let _x: usize = read(&mut buf).unwrap();
let _x: usize = read(&mut *buf).unwrap();
let _x: usize = read(&mut buf[..]).unwrap();
let _x: usize = read(&mut (*buf)[..]).unwrap();
let mut buf = [0, 0, 0];
let _x: usize = read(&mut buf).unwrap();
let _x: usize = read(&mut buf[..]).unwrap();
let mut buf = vec![0, 0, 0];
let _x: usize = read(&mut buf).unwrap();
let _x: usize = read(&mut buf[..]).unwrap();
let mut buf = vec![0, 0, 0].into_boxed_slice();
let _x: usize = read(&mut buf).unwrap();
let _x: usize = read(&mut buf[..]).unwrap();
let mut buf = [
MaybeUninit::uninit(),
MaybeUninit::uninit(),
MaybeUninit::uninit(),
];
let _x: (&mut [u8], &mut [MaybeUninit<u8>]) = read(&mut buf).unwrap();
let _x: (&mut [u8], &mut [MaybeUninit<u8>]) = read(&mut buf[..]).unwrap();
let mut buf = vec![
MaybeUninit::uninit(),
MaybeUninit::uninit(),
MaybeUninit::uninit(),
];
let _x: (&mut [u8], &mut [MaybeUninit<u8>]) = read(&mut buf).unwrap();
let _x: (&mut [u8], &mut [MaybeUninit<u8>]) = read(&mut buf[..]).unwrap();
let mut buf = vec![
MaybeUninit::uninit(),
MaybeUninit::uninit(),
MaybeUninit::uninit(),
]
.into_boxed_slice();
let _x: (&mut [u8], &mut [MaybeUninit<u8>]) = read(&mut buf).unwrap();
let _x: (&mut [u8], &mut [MaybeUninit<u8>]) = read(&mut buf[..]).unwrap();
let mut buf = Cursor::new(&mut buf);
let _x: usize = read(&mut buf).unwrap();
let _x: (&mut [u8], &mut [MaybeUninit<u8>]) = buf.finish();
let mut buf = [0, 0, 0];
let mut io_slice = std::io::IoSliceMut::new(&mut buf);
let _x: usize = read(&mut io_slice).unwrap();
let _x: usize = read(&mut io_slice[..]).unwrap();
}
#[cfg(not(windows))]
#[test]
fn test_slice() {
use std::io::{Seek, SeekFrom};
let mut input = std::fs::File::open("src/lib.rs").unwrap();
let mut buf = [0_u8; 64];
let nread = read(&input, &mut buf).unwrap();
assert_eq!(nread, buf.len());
assert_eq!(
&buf[..54],
b"#![cfg_attr(doc, doc = include_str!(\"../README.md\"))]\n"
);
input.seek(SeekFrom::End(-1)).unwrap();
let nread = read(&input, &mut buf).unwrap();
assert_eq!(nread, 1);
assert_eq!(buf[0], b'\n');
input.seek(SeekFrom::End(0)).unwrap();
let nread = read(&input, &mut buf).unwrap();
assert_eq!(nread, 0);
}
#[cfg(not(windows))]
#[test]
fn test_slice_uninit() {
use core::mem::MaybeUninit;
use std::io::{Seek, SeekFrom};
let mut input = std::fs::File::open("src/lib.rs").unwrap();
let mut buf = [MaybeUninit::<u8>::uninit(); 64];
let (init, uninit) = read(&input, &mut buf).unwrap();
assert_eq!(uninit.len(), 0);
assert_eq!(
&init[..54],
b"#![cfg_attr(doc, doc = include_str!(\"../README.md\"))]\n"
);
assert_eq!(init.len(), buf.len());
assert_eq!(
unsafe { core::mem::transmute::<&mut [MaybeUninit<u8>], &mut [u8]>(&mut buf[..54]) },
b"#![cfg_attr(doc, doc = include_str!(\"../README.md\"))]\n"
);
input.seek(SeekFrom::End(-1)).unwrap();
let (init, uninit) = read(&input, &mut buf).unwrap();
assert_eq!(init.len(), 1);
assert_eq!(init[0], b'\n');
assert_eq!(uninit.len(), buf.len() - 1);
input.seek(SeekFrom::End(0)).unwrap();
let (init, uninit) = read(&input, &mut buf).unwrap();
assert_eq!(init.len(), 0);
assert_eq!(uninit.len(), buf.len());
}
#[cfg(not(windows))]
#[test]
fn test_spare_capacity() {
use std::io::{Seek, SeekFrom};
let mut input = std::fs::File::open("src/lib.rs").unwrap();
let mut buf = Vec::with_capacity(64);
let nread = read(&input, spare_capacity(&mut buf)).unwrap();
assert_eq!(nread, buf.capacity());
assert_eq!(nread, buf.len());
assert_eq!(
&buf[..54],
b"#![cfg_attr(doc, doc = include_str!(\"../README.md\"))]\n"
);
buf.clear();
input.seek(SeekFrom::End(-1)).unwrap();
let nread = read(&input, spare_capacity(&mut buf)).unwrap();
assert_eq!(nread, 1);
assert_eq!(buf.len(), 1);
assert_eq!(buf[0], b'\n');
buf.clear();
input.seek(SeekFrom::End(0)).unwrap();
let nread = read(&input, spare_capacity(&mut buf)).unwrap();
assert_eq!(nread, 0);
assert!(buf.is_empty());
}
#[test]
fn test_cursor_as_buffer() {
use std::io::{Seek, SeekFrom};
let mut input = std::fs::File::open("src/lib.rs").unwrap();
let mut total_read = 0;
let mut buf = Vec::with_capacity(256);
let mut cursor = Cursor::new(spare_capacity(&mut buf));
input.seek(SeekFrom::End(-39)).unwrap();
let nread = read(&input, &mut cursor).unwrap();
total_read += nread;
assert_eq!(cursor.remaining(), 256 - 39);
input.seek(SeekFrom::End(-39)).unwrap();
let nread = read(&input, &mut cursor).unwrap();
total_read += nread;
assert_eq!(cursor.remaining(), 256 - 39 * 2);
input.seek(SeekFrom::End(-39)).unwrap();
let nread = read(&input, &mut cursor).unwrap();
total_read += nread;
assert_eq!(cursor.remaining(), 256 - 39 * 3);
assert_eq!(total_read, 39 * 3);
let cursor_read = cursor.finish();
assert_eq!(cursor_read, total_read);
assert_eq!(buf.len(), 39 * 3);
assert_eq!(buf.capacity(), 256);
assert_eq!(
buf,
b"// The comment at the end of the file!\n// The comment at the end of the file!\n// The comment at the end of the file!\n"
);
}
#[test]
fn test_nesting() {
use std::io::{Seek, SeekFrom};
let mut input = std::fs::File::open("src/lib.rs").unwrap();
let mut total_read = 0;
let mut buf = Vec::with_capacity(256);
let mut cursor = Cursor::new(spare_capacity(&mut buf));
input.seek(SeekFrom::End(-39)).unwrap();
let nread = read(&input, &mut cursor).unwrap();
total_read += nread;
assert_eq!(cursor.remaining(), 256 - 39);
input.seek(SeekFrom::End(-39)).unwrap();
let mut nested_cursor = Cursor::new(&mut cursor);
let nested_nread = read(&input, &mut nested_cursor).unwrap();
assert_eq!(nested_nread, 39);
assert_eq!(nested_cursor.remaining(), 256 - 39 * 2);
input.seek(SeekFrom::End(-39)).unwrap();
let mut nested_nested_cursor = Cursor::new(&mut nested_cursor);
let nested_nested_nread = read(&input, &mut nested_nested_cursor).unwrap();
assert_eq!(nested_nested_nread, 39);
assert_eq!(nested_nested_cursor.remaining(), 256 - 39 * 3);
let inner_nread = nested_nested_cursor.finish();
assert_eq!(inner_nread, 39);
assert_eq!(nested_cursor.remaining(), 256 - 39 * 3);
let nread = nested_cursor.finish();
total_read += nread;
assert_eq!(total_read, 39 * 3);
assert_eq!(cursor.remaining(), 256 - 39 * 3);
let cursor_read = cursor.finish();
assert_eq!(cursor_read, total_read);
assert_eq!(buf.len(), 39 * 3);
assert_eq!(buf.capacity(), 256);
assert_eq!(
&buf[..39*3],
b"// The comment at the end of the file!\n// The comment at the end of the file!\n// The comment at the end of the file!\n"
);
}
#[test]
fn test_incremental() {
use std::io::{Seek, SeekFrom};
let mut input = std::fs::File::open("src/lib.rs").unwrap();
let mut total_read = 0;
let mut buf = [MaybeUninit::<u8>::zeroed(); 256];
let mut cursor = Cursor::new(&mut buf);
input.seek(SeekFrom::End(-39)).unwrap();
let nread = read(&input, &mut cursor).unwrap();
total_read += nread;
assert_eq!(cursor.remaining(), 256 - 39);
input.seek(SeekFrom::End(-39)).unwrap();
let nread = read(&input, &mut cursor).unwrap();
total_read += nread;
assert_eq!(cursor.remaining(), 256 - 39 * 2);
input.seek(SeekFrom::End(-39)).unwrap();
let nread = read(&input, &mut cursor).unwrap();
total_read += nread;
assert_eq!(cursor.remaining(), 256 - 39 * 3);
assert_eq!(total_read, 39 * 3);
let (init, uninit) = cursor.finish();
assert_eq!(init.len(), total_read);
assert_eq!(uninit.len(), 256 - total_read);
assert_eq!(
init,
b"// The comment at the end of the file!\n// The comment at the end of the file!\n// The comment at the end of the file!\n"
);
}
fn read<B: Buffer<u8>>(input: &std::fs::File, mut b: B) -> std::io::Result<B::Output> {
use std::os::fd::AsRawFd;
let ptr = b.buffer_ptr();
let len = b.buffer_len();
let n = unsafe { libc::read(input.as_raw_fd(), ptr.cast(), len) };
if let Ok(n) = usize::try_from(n) {
unsafe { Ok(b.assume_init(n)) }
} else {
Err(std::io::Error::last_os_error())
}
}
}