diff --git a/examples/new_read.rs b/examples/new_read.rs new file mode 100644 index 000000000..a916aeb5f --- /dev/null +++ b/examples/new_read.rs @@ -0,0 +1,28 @@ +use rustix::io::read; +use rustix::stdio::stdin; + +fn main() { + let buf = Vec::new(); + let _x: Vec = read(stdin(), buf).unwrap(); + + let mut buf = Vec::new(); + let _x: usize = read(stdin(), &mut buf).unwrap(); + + let mut buf = [0, 0, 0]; + let _x: usize = read(stdin(), &mut buf).unwrap(); + + // Why doesn't this work? This is reduced from src/fs/inotify.rs line 177. + struct Wrapper<'a>(&'a mut [u8]); + impl<'a> Wrapper<'a> { + fn read(&mut self) { + let _x: usize = read(stdin(), self.0).unwrap(); + } + } + let mut buf = Vec::new(); + let mut wrapper = Wrapper(&mut buf); + wrapper.read(); + + // Why does this get two error messages? + let mut buf = [0, 0, 0]; + let _x = read(stdin(), buf).unwrap(); +} diff --git a/src/backend/libc/io/syscalls.rs b/src/backend/libc/io/syscalls.rs index 270aefd32..cd41db4a5 100644 --- a/src/backend/libc/io/syscalls.rs +++ b/src/backend/libc/io/syscalls.rs @@ -18,6 +18,7 @@ use crate::io::ReadWriteFlags; use crate::io::{self, FdFlags}; use crate::ioctl::{IoctlOutput, RawOpcode}; use core::cmp::min; +use core::mem::MaybeUninit; #[cfg(all(feature = "fs", feature = "net"))] use libc_errno::errno; #[cfg(not(any(target_os = "espidf", target_os = "horizon")))] @@ -26,8 +27,14 @@ use { crate::io::{IoSlice, IoSliceMut}, }; -pub(crate) unsafe fn read(fd: BorrowedFd<'_>, buf: *mut u8, len: usize) -> io::Result { - ret_usize(c::read(borrowed_fd(fd), buf.cast(), min(len, READ_LIMIT))) +pub(crate) fn read(fd: BorrowedFd<'_>, buf: &mut [MaybeUninit]) -> io::Result { + unsafe { + ret_usize(c::read( + borrowed_fd(fd), + buf.as_mut_ptr().cast(), + min(buf.len(), READ_LIMIT), + )) + } } pub(crate) fn write(fd: BorrowedFd<'_>, buf: &[u8]) -> io::Result { diff --git a/src/backend/linux_raw/io/syscalls.rs b/src/backend/linux_raw/io/syscalls.rs index 059ad7abd..96a1e856d 100644 --- a/src/backend/linux_raw/io/syscalls.rs +++ b/src/backend/linux_raw/io/syscalls.rs @@ -26,11 +26,19 @@ use crate::ioctl::{IoctlOutput, RawOpcode}; #[cfg(all(feature = "fs", feature = "net"))] use crate::net::{RecvFlags, SendFlags}; use core::cmp; +use core::mem::MaybeUninit; use linux_raw_sys::general::{F_DUPFD_CLOEXEC, F_GETFD, F_SETFD}; #[inline] -pub(crate) unsafe fn read(fd: BorrowedFd<'_>, buf: *mut u8, len: usize) -> io::Result { - ret_usize(syscall!(__NR_read, fd, buf, pass_usize(len))) +pub(crate) fn read(fd: BorrowedFd<'_>, buf: &mut [MaybeUninit]) -> io::Result { + unsafe { + ret_usize(syscall!( + __NR_read, + fd, + buf.as_mut_ptr(), + pass_usize(buf.len()) + )) + } } #[inline] diff --git a/src/buffer.rs b/src/buffer.rs index bb31ac056..e9ef194ac 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -2,9 +2,128 @@ #![allow(unsafe_code)] +#[cfg(feature = "alloc")] +use alloc::vec::Vec; use core::mem::MaybeUninit; use core::slice; +/// A memory buffer that may be uninitialized. +pub trait Buffer { + /// The result of the process operation. + type Result; + + /// Convert this buffer into a maybe-unitiailized view. + fn as_maybe_uninitialized(&mut self) -> &mut [MaybeUninit]; + + /// Convert a finished buffer pointer into its result. + /// + /// # Safety + /// + /// At least `len` bytes of the buffer must now be initialized. + unsafe fn finish(self, len: usize) -> Self::Result; +} + +/// Implements [`Buffer`] around the a slice of bytes. +/// +/// `Result` is a `usize` indicating how many bytes were written. +impl Buffer for &mut [T] { + type Result = usize; + + #[inline] + fn as_maybe_uninitialized(&mut self) -> &mut [MaybeUninit] { + // SAFETY: This just casts away the knowledge that the elements are + // initialized. + unsafe { core::mem::transmute::<&mut [T], &mut [MaybeUninit]>(self) } + } + + #[inline] + unsafe fn finish(self, len: usize) -> Self::Result { + len + } +} + +/// Implements [`Buffer`] around the a slice of bytes. +/// +/// `Result` is a `usize` indicating how many bytes were written. +impl Buffer for &mut [T; N] { + type Result = usize; + + #[inline] + fn as_maybe_uninitialized(&mut self) -> &mut [MaybeUninit] { + // SAFETY: This just casts away the knowledge that the elements are + // initialized. + unsafe { core::mem::transmute::<&mut [T], &mut [MaybeUninit]>(*self) } + } + + #[inline] + unsafe fn finish(self, len: usize) -> Self::Result { + len + } +} + +/// Implements [`Buffer`] around the a slice of bytes. +/// +/// `Result` is a `usize` indicating how many bytes were written. +impl Buffer for &mut Vec { + type Result = usize; + + #[inline] + fn as_maybe_uninitialized(&mut self) -> &mut [MaybeUninit] { + // SAFETY: This just casts away the knowledge that the elements are + // initialized. + unsafe { core::mem::transmute::<&mut [T], &mut [MaybeUninit]>(self) } + } + + #[inline] + unsafe fn finish(self, len: usize) -> Self::Result { + len + } +} + +/// Implements [`Buffer`] around the a slice of uninitialized bytes. +/// +/// `Result` is a pair of slices giving the initialized and uninitialized +/// subslices after the new data is written. +impl<'a, T> Buffer for &'a mut [MaybeUninit] { + type Result = (&'a mut [T], &'a mut [MaybeUninit]); + + #[inline] + fn as_maybe_uninitialized(&mut self) -> &mut [MaybeUninit] { + self + } + + #[inline] + unsafe fn finish(self, len: usize) -> Self::Result { + let (init, uninit) = self.split_at_mut(len); + + // SAFETY: The user asserts that the slice is now initialized. + let init = slice::from_raw_parts_mut(init.as_mut_ptr().cast::(), init.len()); + + (init, uninit) + } +} + +/// Implements [`Buffer`] around the `Vec` type. +/// +/// This implementation fills the buffer, overwriting any previous data, with +/// the new data data and sets the length. +#[cfg(feature = "alloc")] +impl Buffer for Vec { + type Result = Vec; + + #[inline] + fn as_maybe_uninitialized(&mut self) -> &mut [MaybeUninit] { + self.clear(); + self.spare_capacity_mut() + } + + #[inline] + unsafe fn finish(mut self, len: usize) -> Self::Result { + self.set_len(len); + self + } +} + /// Split an uninitialized byte slice into initialized and uninitialized parts. /// /// # Safety diff --git a/src/fs/inotify.rs b/src/fs/inotify.rs index f7dd3230a..8a3877ec5 100644 --- a/src/fs/inotify.rs +++ b/src/fs/inotify.rs @@ -40,6 +40,8 @@ //! # } #![allow(unused_qualifications)] +#![allow(dead_code)] // FIXME +#![allow(unused_imports)] // FIXME use super::inotify; pub use crate::backend::fs::inotify::{CreateFlags, ReadFlags, WatchFlags}; @@ -47,7 +49,7 @@ use crate::backend::fs::syscalls; use crate::fd::{AsFd, OwnedFd}; use crate::ffi::CStr; use crate::io; -use crate::io::{read_uninit, Errno}; +use crate::io::{read, Errno}; use core::mem::{align_of, size_of, MaybeUninit}; use linux_raw_sys::general::inotify_event; @@ -174,7 +176,9 @@ impl<'buf, Fd: AsFd> Reader<'buf, Fd> { #[allow(clippy::should_implement_trait)] pub fn next(&mut self) -> io::Result> { if self.is_buffer_empty() { - match read_uninit(self.fd.as_fd(), self.buf).map(|(init, _)| init.len()) { + todo!("FIXME: see \"Why doesn't this work?\" in examples/new_read.rs"); + /* + match read(self.fd.as_fd(), self.buf).map(|(init, _)| init.len()) { Ok(0) => return Err(Errno::INVAL), Ok(bytes_read) => { self.initialized = bytes_read; @@ -182,6 +186,7 @@ impl<'buf, Fd: AsFd> Reader<'buf, Fd> { } Err(e) => return Err(e), } + */ } let ptr = self.buf[self.offset..].as_ptr(); diff --git a/src/io/read_write.rs b/src/io/read_write.rs index fa587f5c4..c255af10c 100644 --- a/src/io/read_write.rs +++ b/src/io/read_write.rs @@ -2,7 +2,7 @@ #![allow(unsafe_code)] -use crate::buffer::split_init; +use crate::buffer::{split_init, Buffer}; use crate::{backend, io}; use backend::fd::AsFd; use core::mem::MaybeUninit; @@ -16,9 +16,6 @@ pub use backend::io::types::ReadWriteFlags; /// `read(fd, buf)`—Reads from a stream. /// -/// This takes a `&mut [u8]` which Rust requires to contain initialized memory. -/// To use an uninitialized buffer, use [`read_uninit`]. -/// /// # References /// - [POSIX] /// - [Linux] @@ -40,27 +37,10 @@ pub use backend::io::types::ReadWriteFlags; /// [illumos]: https://illumos.org/man/2/read /// [glibc]: https://sourceware.org/glibc/manual/latest/html_node/I_002fO-Primitives.html#index-reading-from-a-file-descriptor #[inline] -pub fn read(fd: Fd, buf: &mut [u8]) -> io::Result { - unsafe { backend::io::syscalls::read(fd.as_fd(), buf.as_mut_ptr(), buf.len()) } -} - -/// `read(fd, buf)`—Reads from a stream. -/// -/// This is equivalent to [`read`], except that it can read into uninitialized -/// memory. It returns the slice that was initialized by this function and the -/// slice that remains uninitialized. -#[inline] -pub fn read_uninit( - fd: Fd, - buf: &mut [MaybeUninit], -) -> io::Result<(&mut [u8], &mut [MaybeUninit])> { - // Get number of initialized bytes. - let length = unsafe { - backend::io::syscalls::read(fd.as_fd(), buf.as_mut_ptr().cast::(), buf.len()) - }; - - // Split into the initialized and uninitialized portions. - Ok(unsafe { split_init(buf, length?) }) +pub fn read>(fd: Fd, mut buf: Buf) -> io::Result { + let len = backend::io::syscalls::read(fd.as_fd(), buf.as_maybe_uninitialized())?; + // SAFETY: `read` works. + unsafe { Ok(buf.finish(len)) } } /// `write(fd, buf)`—Writes to a stream.