diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index c6c78dc3939e7..1c39028f435ee 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -13,7 +13,9 @@ mod tests; use crate::ffi::OsString; use crate::fmt; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write}; +use crate::io::{ + self, BorrowedCursor, BorrowedSliceCursor, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write, +}; use crate::path::{Path, PathBuf}; use crate::sys::fs as fs_imp; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; @@ -736,6 +738,10 @@ impl Read for File { self.inner.read_buf(cursor) } + fn read_buf_vectored(&mut self, cursor: BorrowedSliceCursor<'_>) -> io::Result<()> { + self.inner.read_buf_vectored(cursor) + } + #[inline] fn is_read_vectored(&self) -> bool { self.inner.is_read_vectored() @@ -792,6 +798,10 @@ impl Read for &File { self.inner.read_vectored(bufs) } + fn read_buf_vectored(&mut self, cursor: BorrowedSliceCursor<'_>) -> io::Result<()> { + self.inner.read_buf_vectored(cursor) + } + #[inline] fn is_read_vectored(&self) -> bool { self.inner.is_read_vectored() diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs index 88ad92d8a9859..d9caf5e1be7a0 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -2,7 +2,8 @@ mod buffer; use crate::fmt; use crate::io::{ - self, BorrowedCursor, BufRead, IoSliceMut, Read, Seek, SeekFrom, SizeHint, DEFAULT_BUF_SIZE, + self, BorrowedCursor, BorrowedSliceCursor, BufRead, IoSliceMut, Read, Seek, SeekFrom, SizeHint, + DEFAULT_BUF_SIZE, }; use buffer::Buffer; @@ -311,6 +312,22 @@ impl Read for BufReader { Ok(nread) } + fn read_buf_vectored(&mut self, mut cursor: BorrowedSliceCursor<'_>) -> io::Result<()> { + if self.buf.pos() == self.buf.filled() && cursor.capacity() >= self.capacity() { + self.discard_buffer(); + return self.inner.read_buf_vectored(cursor); + } + + let prev = cursor.written(); + + let mut rem = self.fill_buf()?; + rem.read_buf_vectored(cursor.reborrow())?; + + self.consume(cursor.written() - prev); + + Ok(()) + } + fn is_read_vectored(&self) -> bool { self.inner.is_read_vectored() } diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index d98ab021cadb1..d9a3c6d9e383a 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -5,7 +5,10 @@ use crate::io::prelude::*; use crate::alloc::Allocator; use crate::cmp; -use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut, SeekFrom}; +use crate::io::{ + self, BorrowedBuf, BorrowedCursor, BorrowedSliceCursor, ErrorKind, IoSlice, IoSliceMut, + SeekFrom, +}; /// A `Cursor` wraps an in-memory buffer and provides it with a /// [`Seek`] implementation. @@ -345,6 +348,29 @@ where Ok(nread) } + fn read_buf_vectored(&mut self, mut cursor: BorrowedSliceCursor<'_>) -> io::Result<()> { + let mut nread = 0; + while let Some(slice) = unsafe { cursor.next_mut() } { + let mut buf: BorrowedBuf<'_> = slice.into(); + let mut cursor = buf.unfilled(); + Read::read_buf(&mut self.fill_buf()?, cursor.reborrow())?; + + let n = cursor.written(); + self.pos += n as u64; + nread += n; + + if n < slice.len() { + break; + } + } + + unsafe { + cursor.advance(nread); + } + + Ok(()) + } + fn is_read_vectored(&self) -> bool { true } diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs index e5048dcc8acd9..a6e6cf1e6cd6b 100644 --- a/library/std/src/io/impls.rs +++ b/library/std/src/io/impls.rs @@ -6,7 +6,8 @@ use crate::cmp; use crate::collections::VecDeque; use crate::fmt; use crate::io::{ - self, BorrowedCursor, BufRead, ErrorKind, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write, + self, BorrowedCursor, BorrowedSliceCursor, BufRead, ErrorKind, IoSlice, IoSliceMut, Read, Seek, + SeekFrom, Write, }; use crate::mem; @@ -30,6 +31,11 @@ impl Read for &mut R { (**self).read_vectored(bufs) } + #[inline] + fn read_buf_vectored(&mut self, cursor: BorrowedSliceCursor<'_>) -> io::Result<()> { + (**self).read_buf_vectored(cursor) + } + #[inline] fn is_read_vectored(&self) -> bool { (**self).is_read_vectored() @@ -134,6 +140,11 @@ impl Read for Box { (**self).read_vectored(bufs) } + #[inline] + fn read_buf_vectored(&mut self, cursor: BorrowedSliceCursor<'_>) -> io::Result<()> { + (**self).read_buf_vectored(cursor) + } + #[inline] fn is_read_vectored(&self) -> bool { (**self).is_read_vectored() @@ -272,6 +283,17 @@ impl Read for &[u8] { Ok(nread) } + #[inline] + fn read_buf_vectored(&mut self, mut cursor: BorrowedSliceCursor<'_>) -> io::Result<()> { + let amt = cmp::min(cursor.capacity(), self.len()); + let (a, b) = self.split_at(amt); + + cursor.append(a); + + *self = b; + Ok(()) + } + #[inline] fn is_read_vectored(&self) -> bool { true diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 01a3873c75cec..c11e490412c67 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -278,7 +278,9 @@ pub use self::{ }; #[unstable(feature = "read_buf", issue = "78485")] -pub use self::readbuf::{BorrowedBuf, BorrowedCursor}; +pub use self::readbuf::{ + BorrowedBuf, BorrowedCursor, BorrowedSliceBuf, BorrowedSliceCursor, IoSliceMaybeUninit, +}; pub(crate) use error::const_io_error; mod buffered; @@ -474,6 +476,27 @@ where Ok(()) } +pub(crate) fn default_read_buf_vectored( + read: F, + mut cursors: BorrowedSliceCursor<'_>, +) -> Result<()> +where + F: FnOnce(&mut [u8]) -> Result, +{ + cursors.ensure_next_init(); + if let Some(buf) = cursors.next_init_mut() { + let n = read(buf)?; + unsafe { + // SAFETY: we initialised using `ensure_init` so there is no uninit data to advance to. + cursors.advance(n); + } + } else { + read(&mut [])?; + } + + Ok(()) +} + /// The `Read` trait allows for reading bytes from a source. /// /// Implementors of the `Read` trait are called 'readers'. @@ -838,6 +861,12 @@ pub trait Read { Ok(()) } + /// TODO docs + #[unstable(feature = "read_buf", issue = "78485")] + fn read_buf_vectored(&mut self, bufs: BorrowedSliceCursor<'_>) -> Result<()> { + default_read_buf_vectored(|b| self.read(b), bufs) + } + /// Creates a "by reference" adaptor for this instance of `Read`. /// /// The returned adapter also implements `Read` and will simply borrow this diff --git a/library/std/src/io/readbuf.rs b/library/std/src/io/readbuf.rs index b1a84095f13fa..ab2ea3c51b8a8 100644 --- a/library/std/src/io/readbuf.rs +++ b/library/std/src/io/readbuf.rs @@ -6,7 +6,10 @@ mod tests; use crate::cmp; use crate::fmt::{self, Debug, Formatter}; use crate::io::{Result, Write}; +use crate::marker::PhantomData; use crate::mem::{self, MaybeUninit}; +use crate::ops::{Deref, DerefMut}; +use crate::slice; /// A borrowed byte buffer which is incrementally filled and initialized. /// @@ -304,3 +307,882 @@ impl<'a> Write for BorrowedCursor<'a> { Ok(()) } } + +use libc::{c_void, iovec}; + +// TODO non-unix versions too +/// A buffer type used with `Read::read_buf_vectored`. Unlike `IoSliceMut`, there is no guarantee +/// that its memory has been initialised. +/// +/// It is semantically a wrapper around an &mut [MaybeUninit], but is guaranteed to be ABI +/// compatible with the `iovec` type on Unix platforms and WSABUF on Windows. +#[repr(transparent)] +pub struct IoSliceMaybeUninit<'a> { + vec: iovec, + _p: PhantomData<&'a mut [MaybeUninit]>, +} + +impl Debug for IoSliceMaybeUninit<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("IoSliceMaybeUninit").field("len", &self.vec.iov_len).finish() + } +} + +/// Create a new `IoSliceMaybeUninit` from an uninitialized buffer. +impl<'a> From<&'a mut [MaybeUninit]> for IoSliceMaybeUninit<'a> { + #[inline] + fn from(buf: &'a mut [MaybeUninit]) -> IoSliceMaybeUninit<'a> { + IoSliceMaybeUninit { + vec: iovec { iov_base: buf.as_mut_ptr() as *mut c_void, iov_len: buf.len() }, + _p: PhantomData, + } + } +} + +impl<'a> IoSliceMaybeUninit<'a> { + /// Create a new IoSliceMaybeUninit from an existing mutable slice of `u8`s. + /// + /// SAFETY: all bytes in the slice must be initialized by the time the IoSliceMaybeUninit is + /// destroyed and thus access to the data is restored for `slice` or other views of the data. + #[inline] + pub unsafe fn from_slice(slice: &'a mut [u8]) -> IoSliceMaybeUninit<'a> { + IoSliceMaybeUninit { + vec: iovec { iov_base: slice.as_mut_ptr() as *mut c_void, iov_len: slice.len() }, + _p: PhantomData, + } + } + + /// Create a new IoSliceMaybeUninit with its internal cursor advanced. + #[inline] + pub fn advance(&self, n: usize) -> Self { + if self.vec.iov_len < n { + panic!("advancing IoSliceMaybeUninit beyond its length"); + } + + IoSliceMaybeUninit { + vec: iovec { + iov_base: unsafe { self.vec.iov_base.add(n) }, + iov_len: self.vec.iov_len - n, + }, + _p: PhantomData, + } + } + + /// View the slice as a slice of `u8`s. + /// + /// # Safety + /// + /// The caller must ensure that all elements of the slice have been initialized. + #[inline] + pub unsafe fn as_slice(&self) -> &[u8] { + slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) + } + + /// View the slice as a mutable slice of `u8`s. + /// + /// # Safety + /// + /// The caller must ensure that all elements of the slice have been initialized. + #[inline] + pub unsafe fn as_mut_slice(&mut self) -> &mut [u8] { + slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) + } + + /// View the slice as a mutable slice of `MaybeUninit`. + #[inline] + pub fn as_maybe_init_slice(&mut self) -> &mut [MaybeUninit] { + // SAFETY: Slice invariants follow from iovec. Lifetime of the returned type ensures + // unique access. + unsafe { + slice::from_raw_parts_mut(self.vec.iov_base as *mut MaybeUninit, self.vec.iov_len) + } + } + + /// Returns the number of elements in the slice. + #[inline] + pub fn len(&self) -> usize { + self.vec.iov_len + } +} + +/// A borrowed byte buffer, consisting of multiple underlying buffers, which is incrementally filled +/// and initialized. Primarily designed for vectored IO. +/// +/// This type is a sort of "double cursor". It tracks three regions in the buffer: a region at the beginning of the +/// buffer that has been logically filled with data, a region that has been initialized at some point but not yet +/// logically filled, and a region at the end that is fully uninitialized. The filled region is guaranteed to be a +/// subset of the initialized region. +/// +/// In summary, the contents of the buffer can be visualized as: +/// ```not_rust +/// [ | | | | | | ] Underlying buffers +/// [ capacity ] +/// [ filled | unfilled ] +/// [ initialized | uninitialized ] +/// ``` +/// +/// A `BorrowedSliceBuf` is created around some existing data (or capacity for data) via a unique reference +/// (`&mut`). The `BorrowedSliceBuf` can be configured (e.g., using `clear` or `set_init`), but otherwise +/// is read-only. To write into the buffer, use `unfilled` to create a `BorrowedSliceCursor`. The cursor +/// has write-only access to the unfilled portion of the buffer (you can think of it as a +/// write-only iterator). +/// +/// The lifetime `'a` is a bound on the lifetime of the underlying data. +#[derive(Debug)] +pub struct BorrowedSliceBuf<'a> { + // Tracks the initialized portion. The first part of the tuple is the index of the slice, the + // second is the index within that slice. + // + // Invariant: init.0 == bufs.len() => init.1 == 0 + // Invariant: init.0 < bufs.len() => init.1 < bufs[init.0].len() + // Note that these invariants are the same as those for filled. + init: (usize, usize), + + // Tracks the filled portion. The first part of the tuple is the index of the slice, the + // second is the index within that slice. + // + // Invariant: filled.0 == bufs.len() => filled.1 == 0 + // Invariant: filled.0 < bufs.len() => filled.1 < bufs[filled.0].len() + // Note that these invariants are the same as those for init. + filled: (usize, usize), + // Tracks the number of bytes written by the currently or previously active cursor. + written: usize, + + // The underlying buffers. + // Safety invariant: we treat the type of bufs as covariant in the lifetime of `IoSliceMaybeUninit` + // when we create a `BorrowedSliceBuf`. This is only safe if we never replace `bufs` by assigning + // into it, so don't do that! + bufs: &'a mut [IoSliceMaybeUninit<'a>], +} + +impl<'a> BorrowedSliceBuf<'a> { + /// Create a new `BorrowedSliceBuf` from a slice of possibly initialized io slices. + #[inline] + pub fn new<'b: 'a>(bufs: &'a mut [IoSliceMaybeUninit<'b>]) -> BorrowedSliceBuf<'a> { + // Re-establish the init and filled invariants. + let mut init = (0, 0); + let mut filled = (0, 0); + while init.0 < bufs.len() && init.1 >= bufs[init.0].len() { + init.0 += 1; + filled.0 += 1; + } + + BorrowedSliceBuf { + init, + filled, + written: 0, + // SAFETY: we never assign into `BorrowedSliceBuf::bufs`, so treating its + // lifetime covariantly is safe. + bufs: unsafe { + mem::transmute::<&'a mut [IoSliceMaybeUninit<'b>], &'a mut [IoSliceMaybeUninit<'a>]>( + bufs, + ) + }, + } + } + + /// Returns the length of the filled part of the buffer. + #[inline] + pub fn len(&self) -> usize { + self.iter_filled_slices().map(|s| s.len()).sum() + } + + /// Returns the number of completely filled slices in the buffer. + #[inline] + pub fn len_filled_slices(&self) -> usize { + self.filled.0 + } + + /// Returns the number of filled elements in any partially filled slice. + /// + /// If there are no partially filled slices, then this method returns `0`. + #[inline] + pub fn len_partial_filled_slice(&self) -> usize { + self.filled.1 + } + + /// Iterate over the filled portion of the buffer. + #[inline] + pub fn iter_filled_slices(&self) -> FilledSliceIterator<'_, 'a> { + FilledSliceIterator { bufs: self, next: 0 } + } + + /// Returns a cursor over the unfilled part of the buffer. + #[inline] + pub fn unfilled<'this>(&'this mut self) -> BorrowedSliceCursor<'this> { + self.written = 0; + BorrowedSliceCursor { + // SAFETY: we never assign into `BorrowedSliceCursor::bufs`, so treating its + // lifetime covariantly is safe. + bufs: unsafe { + mem::transmute::<&'this mut BorrowedSliceBuf<'a>, &'this mut BorrowedSliceBuf<'this>>( + self, + ) + }, + } + } + + /// Clears the buffer, resetting the filled region to empty. + /// + /// The number of initialized bytes is not changed, and the contents of the buffer are not modified. + #[inline] + pub fn clear(&mut self) -> &mut Self { + self.filled = (0, 0); + + // Re-establish the filled invariant. + while self.filled.0 < self.bufs.len() && self.filled.1 >= self.bufs[self.filled.0].len() { + self.filled.0 += 1; + } + + self + } + + /// Asserts that a prefix of the underlying buffers are initialized. The initialized prefix is + /// all of the first `b - 1` buffers and the first `n` bytes of the `b`th buffer. In other words, + /// `(b, n)` is the coordinates of the first uninitialized byte in the buffers. + /// + /// `BorrowedSliceBuf` assumes that bytes are never de-initialized, so this method does nothing when called with fewer + /// bytes than are already known to be initialized. + /// + /// # Safety + /// + /// The caller must ensure that all of the `(b, n)` prefix has already been initialized. + #[inline] + pub unsafe fn set_init(&mut self, b: usize, n: usize) -> &mut Self { + if b == self.init.0 { + self.init.1 = cmp::max(self.init.1, n); + } else if b > self.init.0 { + self.init.0 = b; + self.init.1 = n; + } + + // Re-establish the init invariant. + while self.init.0 < self.bufs.len() && self.init.1 >= self.bufs[self.init.0].len() { + self.init.0 += 1; + } + + self + } +} + +/// A writeable view of the unfilled portion of a [`BorrowedSliceBuf`](BorrowedSliceBuf). +/// +/// Provides access to the initialized and uninitialized parts of the underlying `BorrowedSliceBuf`. +/// Data can be written directly to the cursor by using [`append`](BorrowedSliceCursor::append) or +/// indirectly by writing into a view of the cursor (obtained by calling `as_mut`, `next_init_mut`, +/// etc.) and then calling `advance`. +/// +/// Once data is written to the cursor, it becomes part of the filled portion of the underlying +/// `BorrowedSliceBuf` and can no longer be accessed or re-written by the cursor. I.e., the cursor tracks +/// the unfilled part of the underlying `BorrowedSliceBuf`. +/// +/// The lifetime `'a` is a bound on the lifetime of the underlying data. +#[derive(Debug)] +pub struct BorrowedSliceCursor<'a> { + /// The underlying buffers. + // Safety invariant: we treat the type of bufs as covariant in the lifetime of `BorrowedSliceBuf` + // when we create a `BorrowedSliceCursor`. This is only safe if we never replace `bufs` by assigning + // into it, so don't do that! + bufs: &'a mut BorrowedSliceBuf<'a>, +} + +impl<'a> BorrowedSliceCursor<'a> { + /// Clone this cursor. + /// + /// Since a cursor maintains unique access to its underlying buffer, the cloned cursor is not + /// accessible while the clone is alive. + #[inline] + pub fn reborrow<'this>(&'this mut self) -> BorrowedSliceCursor<'this> { + BorrowedSliceCursor { + // SAFETY: we never assign into `BorrowedSliceCursor::bufs`, so treating its + // lifetime covariantly is safe. + bufs: unsafe { + mem::transmute::<&'this mut BorrowedSliceBuf<'a>, &'this mut BorrowedSliceBuf<'this>>( + self.bufs, + ) + }, + } + } + + /// Returns the available space in the cursor. + #[inline] + pub fn capacity(&self) -> usize { + if self.bufs.filled.0 >= self.bufs.bufs.len() { + return 0; + } + + let mut result = self.bufs.bufs[self.bufs.filled.0].len() - self.bufs.filled.1; + + for buf in &self.bufs.bufs[(self.bufs.filled.0 + 1)..] { + result += buf.len(); + } + + result + } + + /// Returns the number of bytes written to this cursor since it was created from a `BorrowBuf`. + /// + /// Note that if this cursor is a reborrow of another, then the count returned is the count written + /// via either cursor, not the count since the cursor was reborrowed. + #[inline] + pub fn written(&self) -> usize { + self.bufs.written + } + + /// Returns a mutable reference to the whole cursor. + /// + /// Returns a guard type which dereferences to a `&mut [IoSliceMaybeUninit<'a>]` + /// + /// # Safety + /// + /// The caller must not uninitialize any bytes in the initialized portion of the cursor. + #[inline] + pub unsafe fn as_mut<'this>(&'this mut self) -> BorrowedSliceGuard<'this, 'a> { + let prev = if self.bufs.filled.1 == 0 { + None + } else { + let mut new = self.bufs.bufs[self.bufs.filled.0].advance(self.bufs.filled.1); + mem::swap(&mut self.bufs.bufs[self.bufs.filled.0], &mut new); + Some(new) + }; + + BorrowedSliceGuard { bufs: &mut self.bufs, prev } + } + + /// Returns a shared reference to the initialized portion of the first (at least partially) + /// initialised buffer in the cursor. + /// + /// Returns a reference to a slice of a single underlying buffer. That buffer will be the first + /// unfilled buffer which is at least partially initialized. The returned slice is the part of + /// that buffer which is initialized. If there is no part of any buffer which is both unfilled + /// and initialised, then this method returns `None`. + /// + /// Does not iterate over buffers in any way. Calling this method multiple times will return + /// the same slice unless data is either filled or initialized (e.g., by calling `advance`). + #[inline] + pub fn next_init_ref(&self) -> Option<&[u8]> { + //SAFETY: We only slice the initialized part of the buffer, which is always valid + Some(unsafe { + if self.bufs.filled.0 == self.bufs.init.0 { + &self.bufs.bufs.get(self.bufs.filled.0)?.as_slice() + [self.bufs.filled.1..self.bufs.init.1] + } else { + &self.bufs.bufs.get(self.bufs.filled.0)?.as_slice()[self.bufs.filled.1..] + } + }) + } + + /// Returns a mutable reference to the initialized portion of the first (at least partially) + /// initialised buffer in the cursor. + /// + /// Returns a reference to a slice of a single underlying buffer. That buffer will be the first + /// unfilled buffer which is at least partially initialized. The returned slice is the part of + /// that buffer which is initialized. If there is no part of any buffer which is both unfilled + /// and initialised, then this method returns `None`. + /// + /// Does not iterate over buffers in any way. Calling this method multiple times will return + /// the same slice unless data is either filled or initialized (e.g., by calling `advance`). + #[inline] + pub fn next_init_mut(&mut self) -> Option<&mut [u8]> { + //SAFETY: We only slice the initialized part of the buffer, which is always valid + Some(unsafe { + if self.bufs.filled.0 == self.bufs.init.0 { + &mut self.bufs.bufs.get_mut(self.bufs.filled.0)?.as_mut_slice() + [self.bufs.filled.1..self.bufs.init.1] + } else { + &mut self.bufs.bufs.get_mut(self.bufs.filled.0)?.as_mut_slice() + [self.bufs.filled.1..] + } + }) + } + + /// Returns a mutable reference to the uninitialized portion of the first (at least partially) + /// uninitialised buffer in the cursor. + /// + /// It is safe to uninitialize any of these bytes. + /// + /// Returns `None` if `self` is entirely initialized. + /// + /// Does not iterate over buffers in any way. Calling this method multiple times will return + /// the same slice unless data is either filled or initialized (e.g., by calling `advance`). + #[inline] + pub fn next_uninit_mut(&mut self) -> Option<&mut [MaybeUninit]> { + if self.bufs.init.0 != self.bufs.filled.0 { + return Some(&mut []); + } + + Some( + &mut self.bufs.bufs.get_mut(self.bufs.init.0)?.as_maybe_init_slice() + [self.bufs.init.1..], + ) + } + + /// Returns a mutable reference to the first buffer in the cursor. + /// + /// Returns `None` if `self` is empty. + /// + /// Does not iterate over buffers in any way. Calling this method multiple times will return + /// the same slice unless data is filled (e.g., by calling `advance`). + /// + /// # Safety + /// + /// The caller must not uninitialize any bytes in the initialized portion of the cursor. + #[inline] + pub unsafe fn next_mut(&mut self) -> Option<&mut [MaybeUninit]> { + Some( + &mut self.bufs.bufs.get_mut(self.bufs.filled.0)?.as_maybe_init_slice() + [self.bufs.filled.1..], + ) + } + + /// Initializes all bytes in the cursor. + #[inline] + pub fn ensure_init(&mut self) -> &mut Self { + // Already initialized. + if self.bufs.init.0 >= self.bufs.bufs.len() { + return self; + } + + // `first_uninit` is the index of the first wholly uninitialized buffer. + let first_uninit = if self.bufs.init.1 > 0 { + // Initialize any partially initialized buffer. + for byte in + &mut self.bufs.bufs[self.bufs.init.0].as_maybe_init_slice()[self.bufs.init.1..] + { + byte.write(0); + } + self.bufs.init.0 + 1 + } else { + self.bufs.init.0 + }; + + // Initialize any wholly uninitialized buffers. + for buf in &mut self.bufs.bufs[first_uninit..] { + for byte in buf.as_maybe_init_slice() { + byte.write(0); + } + } + + // Record that we're fully initialized. + self.bufs.init.0 = self.bufs.bufs.len(); + self.bufs.init.1 = 0; + + self + } + + /// Initializes all bytes in the first (at least partially unfilled) buffer in the cursor. + #[inline] + pub fn ensure_next_init(&mut self) -> &mut Self { + // The whole buffer is initialized. + if self.bufs.init.0 >= self.bufs.bufs.len() { + return self; + } + + for byte in &mut self.bufs.bufs[self.bufs.init.0].as_maybe_init_slice()[self.bufs.init.1..] + { + byte.write(0); + } + + self.bufs.init.0 += 1; + self.bufs.init.1 = 0; + + // Re-establish the init invariant. + while self.bufs.init.0 < self.bufs.bufs.len() + && self.bufs.init.1 >= self.bufs.bufs[self.bufs.init.0].len() + { + self.bufs.init.0 += 1; + } + + self + } + + /// Advance the cursor by asserting that `n` bytes have been filled. + /// + /// After advancing, the `n` bytes are no longer accessible via the cursor and can only be + /// accessed via the underlying `BorrowedSliceBuf`. I.e., the `BorrowedSliceBuf`'s filled portion + /// grows by `n` elements and its unfilled portion (and the capacity of this cursor) shrinks by + /// `n` elements. + /// + /// # Safety + /// + /// The caller must ensure that the first `n` bytes of the cursor have been properly + /// initialised. + #[inline] + pub unsafe fn advance(&mut self, mut n: usize) -> &mut Self { + self.bufs.written += n; + + while n > 0 && self.bufs.filled.0 < self.bufs.bufs.len() { + let buf = &mut self.bufs.bufs[self.bufs.filled.0]; + let capacity = buf.len() - self.bufs.filled.1; + if n < capacity { + self.bufs.filled.1 += n; + n = 0; + } else { + n -= capacity; + + self.bufs.filled.0 += 1; + self.bufs.filled.1 = 0; + } + } + + assert_eq!(0, n, "advancing borrowed buffers beyond their length"); + + // Re-establish the filled invariant. + while self.bufs.filled.0 < self.bufs.bufs.len() + && self.bufs.filled.1 >= self.bufs.bufs[self.bufs.filled.0].len() + { + self.bufs.filled.0 += 1; + } + + // SAFETY the filled region has grown by a maximum of `n` and the caller must + // ensure those `n` bytes are initialized. + self.update_init_to_filled(); + + self + } + + /// Appends data to the cursor, advancing position within its buffer. + /// + /// # Panics + /// + /// Panics if `self.capacity()` is less than `buf.len()`. + #[inline] + pub fn append(&mut self, mut buf: &[u8]) { + self.bufs.written += buf.len(); + + while !buf.is_empty() && self.bufs.filled.0 < self.bufs.bufs.len() { + // SAFETY: we do not de-initialize any of the elements of the slice + let next = unsafe { self.next_mut().unwrap() }; + if buf.len() < next.len() { + MaybeUninit::write_slice(&mut next[..buf.len()], buf); + self.bufs.filled.1 += buf.len(); + buf = &[]; + } else { + MaybeUninit::write_slice(next, &buf[..next.len()]); + buf = &buf[next.len()..]; + self.bufs.filled.0 += 1; + self.bufs.filled.1 = 0; + } + } + + assert!(buf.is_empty(), "appending to borrowed buffers beyond their length"); + + // Re-establish the filled invariant. + while self.bufs.filled.0 < self.bufs.bufs.len() + && self.bufs.filled.1 >= self.bufs.bufs[self.bufs.filled.0].len() + { + self.bufs.filled.0 += 1; + } + + // SAFETY: we have filled (and thus initialized) the filled region in the loop above. + unsafe { + self.update_init_to_filled(); + } + } + + /// Sets the initialized region to the minimum of the currently initialized region + /// and the filled region. + /// + /// The caller must ensure all invariants for `self.bufs.filled` are satisfied (see the field definition) + /// + /// # Safety + /// + /// The caller must ensure that the filled region is entirely initialized. + #[inline] + unsafe fn update_init_to_filled(&mut self) { + if self.bufs.init.0 == self.bufs.filled.0 { + self.bufs.init.1 = cmp::max(self.bufs.init.1, self.bufs.filled.1); + } else if self.bufs.init.0 < self.bufs.filled.0 { + self.bufs.init.0 = self.bufs.filled.0; + self.bufs.init.1 = self.bufs.filled.1; + } + } +} + +impl<'a> Write for BorrowedSliceCursor<'a> { + fn write(&mut self, buf: &[u8]) -> Result { + self.append(buf); + Ok(buf.len()) + } + + fn flush(&mut self) -> Result<()> { + Ok(()) + } +} + +/// An iterator over the filled slices of a `BorrowedSliceBuf`. +/// +/// See `BorrowedSliceBuf::iter_filled`. +#[derive(Debug)] +pub struct FilledSliceIterator<'buf, 'data> { + bufs: &'buf BorrowedSliceBuf<'data>, + next: usize, +} + +impl<'buf, 'data> Iterator for FilledSliceIterator<'buf, 'data> { + type Item = &'buf [u8]; + + fn next(&mut self) -> Option<&'buf [u8]> { + if self.next > self.bufs.filled.0 || self.next >= self.bufs.bufs.len() { + return None; + } + + let mut result = unsafe { self.bufs.bufs[self.next].as_slice() }; + if self.next == self.bufs.filled.0 { + result = &result[..self.bufs.filled.1]; + } + + self.next += 1; + Some(result) + } +} + +/// Guard type used by `BorrowedSliceCursor::as_mut`. +/// +/// Presents a view of the cursor containing only the filled data (via the `Deref` impls). Also +/// resets the state of the underlying BorrowedSliceBuf to a view of the complete +/// buffer when dropped. +#[derive(Debug)] +pub struct BorrowedSliceGuard<'buf, 'data> { + bufs: &'buf mut BorrowedSliceBuf<'data>, + // One of the io slices in self.bufs may have been sliced so as only to contain filled data. In + // that case we keep a copy of the original so we can restore it. + prev: Option>, +} + +impl<'buf, 'data> Drop for BorrowedSliceGuard<'buf, 'data> { + fn drop(&mut self) { + if let Some(prev) = self.prev.take() { + self.bufs.bufs[self.bufs.filled.0] = prev; + } + } +} + +impl<'buf, 'data> Deref for BorrowedSliceGuard<'buf, 'data> { + type Target = [IoSliceMaybeUninit<'data>]; + + fn deref(&self) -> &[IoSliceMaybeUninit<'data>] { + &self.bufs.bufs[self.bufs.filled.0..] + } +} + +impl<'buf, 'data> DerefMut for BorrowedSliceGuard<'buf, 'data> { + fn deref_mut(&mut self) -> &mut [IoSliceMaybeUninit<'data>] { + &mut self.bufs.bufs[self.bufs.filled.0..] + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_capacity() { + // Empty buffers + let mut buf = BorrowedSliceBuf::new(&mut []); + let cursor = buf.unfilled(); + assert_eq!(0, cursor.capacity()); + + let a: IoSliceMaybeUninit<'_> = (&mut [] as &mut [MaybeUninit]).into(); + let b: IoSliceMaybeUninit<'_> = (&mut [] as &mut [MaybeUninit]).into(); + let slices = &mut [a, b]; + let mut buf = BorrowedSliceBuf::new(slices); + let cursor = buf.unfilled(); + assert_eq!(0, cursor.capacity()); + + // Partially empty buffers + let mut a = [MaybeUninit::uninit()]; + let a: IoSliceMaybeUninit<'_> = (&mut a as &mut [MaybeUninit]).into(); + let b: IoSliceMaybeUninit<'_> = (&mut [] as &mut [MaybeUninit]).into(); + let slices = &mut [a, b]; + let mut buf = BorrowedSliceBuf::new(slices); + let cursor = buf.unfilled(); + assert_eq!(1, cursor.capacity()); + + let a: IoSliceMaybeUninit<'_> = (&mut [] as &mut [MaybeUninit]).into(); + let mut b = [MaybeUninit::uninit(), MaybeUninit::uninit()]; + let b: IoSliceMaybeUninit<'_> = (&mut b as &mut [MaybeUninit]).into(); + let slices = &mut [a, b]; + let mut buf = BorrowedSliceBuf::new(slices); + let cursor = buf.unfilled(); + assert_eq!(2, cursor.capacity()); + + // Non-empty. + let mut a = [MaybeUninit::uninit()]; + let a: IoSliceMaybeUninit<'_> = (&mut a as &mut [MaybeUninit]).into(); + let mut b = [MaybeUninit::uninit(), MaybeUninit::uninit()]; + let b: IoSliceMaybeUninit<'_> = (&mut b as &mut [MaybeUninit]).into(); + let slices = &mut [a, b]; + let mut buf = BorrowedSliceBuf::new(slices); + let cursor = buf.unfilled(); + assert_eq!(3, cursor.capacity()); + + // Filled and cleared + buf.filled = (0, 1); + let cursor = buf.unfilled(); + assert_eq!(2, cursor.capacity()); + buf.filled = (1, 0); + let cursor = buf.unfilled(); + assert_eq!(2, cursor.capacity()); + buf.filled = (1, 1); + let cursor = buf.unfilled(); + assert_eq!(1, cursor.capacity()); + buf.filled = (1, 2); + let cursor = buf.unfilled(); + assert_eq!(0, cursor.capacity()); + buf.filled = (2, 0); + let cursor = buf.unfilled(); + assert_eq!(0, cursor.capacity()); + buf.clear(); + let cursor = buf.unfilled(); + assert_eq!(3, cursor.capacity()); + + // set_init does not affect capacity + unsafe { + buf.set_init(0, 1); + let cursor = buf.unfilled(); + assert_eq!(3, cursor.capacity()); + buf.set_init(3, 42); + let cursor = buf.unfilled(); + assert_eq!(3, cursor.capacity()); + } + } + + fn as_mut_len(slices: &[IoSliceMaybeUninit<'_>]) -> usize { + slices.iter().map(|s| s.len()).sum() + } + + fn bufs_len(bufs: &BorrowedSliceBuf<'_>) -> usize { + bufs.bufs.iter().map(|s| s.len()).sum() + } + + #[test] + fn test_as_mut() { + // Empty buf + let mut buf = BorrowedSliceBuf::new(&mut []); + let mut cursor = buf.unfilled(); + let mut_view = unsafe { cursor.as_mut() }; + assert_eq!(0, mut_view.len()); + + // Check data and modifications are preserved + let a: IoSliceMaybeUninit<'_> = (&mut [] as &mut [MaybeUninit]).into(); + let mut b = [MaybeUninit::new(0u8), MaybeUninit::new(1)]; + let b: IoSliceMaybeUninit<'_> = (&mut b as &mut [MaybeUninit]).into(); + let slices = &mut [a, b]; + let mut buf = BorrowedSliceBuf::new(slices); + let mut cursor = buf.unfilled(); + { + let mut mut_view = unsafe { cursor.as_mut() }; + + assert_eq!(2, as_mut_len(&mut_view)); + assert_eq!(0, unsafe { mut_view[0].as_slice()[0] }); + assert_eq!(1, unsafe { mut_view[0].as_slice()[1] }); + + unsafe { + mut_view[0].as_mut_slice()[0] = 42; + } + } + unsafe { + assert_eq!(42, slices[1].as_slice()[0]); + } + + macro_rules! test_filled { + ($buf: ident, $filled: expr, $mut_len: literal, $buf_len: literal) => { + $buf.filled = $filled; + { + let mut cursor = $buf.unfilled(); + let mut_view = unsafe { cursor.as_mut() }; + assert_eq!($mut_len, as_mut_len(&mut_view)); + } + assert_eq!($buf_len, $buf.len()); + assert_eq!(6, bufs_len(&$buf)); + }; + } + + // Check that filled data is not recorded, and that the original buffer is not affected. + let mut a = [MaybeUninit::new(0u8), MaybeUninit::new(1), MaybeUninit::new(1)]; + let a: IoSliceMaybeUninit<'_> = (&mut a as &mut [MaybeUninit]).into(); + let mut b = [MaybeUninit::new(0u8), MaybeUninit::new(1), MaybeUninit::new(1)]; + let b: IoSliceMaybeUninit<'_> = (&mut b as &mut [MaybeUninit]).into(); + let slices = &mut [a, b]; + let mut buf = BorrowedSliceBuf::new(slices); + + // Nothing filled + test_filled!(buf, (0, 0), 6, 0); + // All filled + test_filled!(buf, (2, 0), 0, 6); + test_filled!(buf, (1, 3), 0, 6); + // One buffer filled + test_filled!(buf, (1, 0), 3, 3); + test_filled!(buf, (0, 3), 3, 3); + // Part of buffer filled + test_filled!(buf, (1, 1), 2, 4); + test_filled!(buf, (0, 1), 5, 1); + + // With middle buffer empty + let mut a = [MaybeUninit::new(0u8), MaybeUninit::new(1), MaybeUninit::new(1)]; + let a: IoSliceMaybeUninit<'_> = (&mut a as &mut [MaybeUninit]).into(); + let b: IoSliceMaybeUninit<'_> = (&mut [] as &mut [MaybeUninit]).into(); + let mut c = [MaybeUninit::new(0u8), MaybeUninit::new(1), MaybeUninit::new(1)]; + let c: IoSliceMaybeUninit<'_> = (&mut c as &mut [MaybeUninit]).into(); + let slices = &mut [a, b, c]; + let mut buf = BorrowedSliceBuf::new(slices); + + // Nothing filled + test_filled!(buf, (0, 0), 6, 0); + // All filled + test_filled!(buf, (3, 0), 0, 6); + test_filled!(buf, (2, 3), 0, 6); + // One buffer filled + test_filled!(buf, (0, 3), 3, 3); + test_filled!(buf, (1, 0), 3, 3); + test_filled!(buf, (2, 0), 3, 3); + // Part of buffer filled + test_filled!(buf, (0, 1), 5, 1); + test_filled!(buf, (2, 1), 2, 4); + + // With first buffer empty + let a: IoSliceMaybeUninit<'_> = (&mut [] as &mut [MaybeUninit]).into(); + let mut b = [MaybeUninit::new(0u8), MaybeUninit::new(1), MaybeUninit::new(1)]; + let b: IoSliceMaybeUninit<'_> = (&mut b as &mut [MaybeUninit]).into(); + let mut c = [MaybeUninit::new(0u8), MaybeUninit::new(1), MaybeUninit::new(1)]; + let c: IoSliceMaybeUninit<'_> = (&mut c as &mut [MaybeUninit]).into(); + let slices = &mut [a, b, c]; + let mut buf = BorrowedSliceBuf::new(slices); + + // Nothing filled + test_filled!(buf, (0, 0), 6, 0); + test_filled!(buf, (1, 0), 6, 0); + // All filled + test_filled!(buf, (3, 0), 0, 6); + test_filled!(buf, (2, 3), 0, 6); + // One buffer filled + test_filled!(buf, (1, 3), 3, 3); + test_filled!(buf, (2, 0), 3, 3); + // Part of buffer filled + test_filled!(buf, (1, 1), 5, 1); + test_filled!(buf, (2, 1), 2, 4); + + // With last buffer empty + let mut a = [MaybeUninit::new(0u8), MaybeUninit::new(1), MaybeUninit::new(1)]; + let a: IoSliceMaybeUninit<'_> = (&mut a as &mut [MaybeUninit]).into(); + let mut b = [MaybeUninit::new(0u8), MaybeUninit::new(1), MaybeUninit::new(1)]; + let b: IoSliceMaybeUninit<'_> = (&mut b as &mut [MaybeUninit]).into(); + let c: IoSliceMaybeUninit<'_> = (&mut [] as &mut [MaybeUninit]).into(); + let slices = &mut [a, b, c]; + let mut buf = BorrowedSliceBuf::new(slices); + + // Nothing filled + test_filled!(buf, (0, 0), 6, 0); + // All filled + test_filled!(buf, (2, 0), 0, 6); + test_filled!(buf, (3, 0), 0, 6); + test_filled!(buf, (1, 3), 0, 6); + // One buffer filled + test_filled!(buf, (1, 0), 3, 3); + test_filled!(buf, (0, 3), 3, 3); + // Part of buffer filled + test_filled!(buf, (1, 1), 2, 4); + test_filled!(buf, (0, 1), 5, 1); + } +} diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index 2dc12a18a8a66..5ac8cd109cd10 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -7,7 +7,7 @@ use crate::io::prelude::*; use crate::cell::{Cell, RefCell}; use crate::fmt; -use crate::io::{self, BufReader, IoSlice, IoSliceMut, LineWriter, Lines}; +use crate::io::{self, BorrowedSliceCursor, BufReader, IoSlice, IoSliceMut, LineWriter, Lines}; use crate::sync::atomic::{AtomicBool, Ordering}; use crate::sync::{Arc, Mutex, MutexGuard, OnceLock}; use crate::sys::stdio; @@ -101,6 +101,10 @@ impl Read for StdinRaw { handle_ebadf(self.0.read_vectored(bufs), 0) } + fn read_buf_vectored(&mut self, cursor: BorrowedSliceCursor<'_>) -> io::Result<()> { + handle_ebadf(self.0.read_buf_vectored(cursor), ()) + } + #[inline] fn is_read_vectored(&self) -> bool { self.0.is_read_vectored() @@ -421,6 +425,9 @@ impl Read for Stdin { fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { self.lock().read_vectored(bufs) } + fn read_buf_vectored(&mut self, cursor: BorrowedSliceCursor<'_>) -> io::Result<()> { + self.lock().read_buf_vectored(cursor) + } #[inline] fn is_read_vectored(&self) -> bool { self.lock().is_read_vectored() @@ -454,6 +461,10 @@ impl Read for StdinLock<'_> { self.inner.read_vectored(bufs) } + fn read_buf_vectored(&mut self, cursor: BorrowedSliceCursor<'_>) -> io::Result<()> { + self.inner.read_buf_vectored(cursor) + } + #[inline] fn is_read_vectored(&self) -> bool { self.inner.is_read_vectored() diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs index f076ee0923c80..ba0330ce7a21b 100644 --- a/library/std/src/io/util.rs +++ b/library/std/src/io/util.rs @@ -5,7 +5,8 @@ mod tests; use crate::fmt; use crate::io::{ - self, BorrowedCursor, BufRead, IoSlice, IoSliceMut, Read, Seek, SeekFrom, SizeHint, Write, + self, BorrowedCursor, BorrowedSliceCursor, BufRead, IoSlice, IoSliceMut, Read, Seek, SeekFrom, + SizeHint, Write, }; /// A reader which is always at EOF. @@ -155,6 +156,26 @@ impl Read for Repeat { Ok(nwritten) } + #[inline] + fn read_buf_vectored(&mut self, mut cursor: BorrowedSliceCursor<'_>) -> io::Result<()> { + unsafe { + while let Some(buf) = cursor.next_mut() { + for slot in buf { + slot.write(self.byte); + } + } + } + + let remaining = cursor.capacity(); + + // SAFETY: the entire unfilled portion of cursor has been initialized + unsafe { + cursor.advance(remaining); + } + + Ok(()) + } + #[inline] fn is_read_vectored(&self) -> bool { true diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index f13500de01431..bb9d38ed6652f 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -310,6 +310,7 @@ #![feature(raw_os_nonzero)] #![feature(slice_internals)] #![feature(slice_ptr_get)] +#![feature(slice_take)] #![feature(std_internals)] #![feature(str_internals)] #![feature(strict_provenance)] diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs index 69b72a81c5b6d..03626fbd4f7b3 100644 --- a/library/std/src/net/tcp.rs +++ b/library/std/src/net/tcp.rs @@ -6,7 +6,7 @@ mod tests; use crate::io::prelude::*; use crate::fmt; -use crate::io::{self, IoSlice, IoSliceMut}; +use crate::io::{self, BorrowedSliceCursor, IoSlice, IoSliceMut}; use crate::iter::FusedIterator; use crate::net::{Shutdown, SocketAddr, ToSocketAddrs}; use crate::sys_common::net as net_imp; @@ -623,6 +623,10 @@ impl Read for TcpStream { self.0.read_vectored(bufs) } + fn read_buf_vectored(&mut self, cursor: BorrowedSliceCursor<'_>) -> io::Result<()> { + self.0.read_buf_vectored(cursor) + } + #[inline] fn is_read_vectored(&self) -> bool { self.0.is_read_vectored() @@ -657,6 +661,10 @@ impl Read for &TcpStream { self.0.read_vectored(bufs) } + fn read_buf_vectored(&mut self, cursor: BorrowedSliceCursor<'_>) -> io::Result<()> { + self.0.read_buf_vectored(cursor) + } + #[inline] fn is_read_vectored(&self) -> bool { self.0.is_read_vectored() diff --git a/library/std/src/net/tcp/tests.rs b/library/std/src/net/tcp/tests.rs index 8c0adcfb0ebbb..5bf1b75d53c54 100644 --- a/library/std/src/net/tcp/tests.rs +++ b/library/std/src/net/tcp/tests.rs @@ -1,6 +1,6 @@ use crate::fmt; use crate::io::prelude::*; -use crate::io::{ErrorKind, IoSlice, IoSliceMut}; +use crate::io::{BorrowedSliceBuf, ErrorKind, IoSlice, IoSliceMaybeUninit, IoSliceMut}; use crate::net::test::{next_test_ip4, next_test_ip6}; use crate::net::*; use crate::sync::mpsc::channel; @@ -304,6 +304,38 @@ fn read_vectored() { }) } +#[test] +fn read_buf_vectored() { + each_ip(&mut |addr| { + let srv = t!(TcpListener::bind(&addr)); + let mut s1 = t!(TcpStream::connect(&addr)); + let mut s2 = t!(srv.accept()).0; + + let len = s1.write(&[10, 11, 12]).unwrap(); + assert_eq!(len, 3); + + let a: &mut [u8] = &mut []; + let b: &mut [u8] = &mut [0]; + let c: &mut [u8] = &mut [0; 3]; + let mut bufs: [IoSliceMaybeUninit<'_>; 3] = unsafe { + [ + IoSliceMaybeUninit::from_slice(a), + IoSliceMaybeUninit::from_slice(b), + IoSliceMaybeUninit::from_slice(c), + ] + }; + let mut bufs = BorrowedSliceBuf::new(&mut bufs); + let mut cursor = bufs.unfilled(); + t!(s2.read_buf_vectored(cursor.reborrow())); + + let len = cursor.written(); + assert!(len > 0); + assert_eq!(b, &[10]); + // some implementations don't support readv, so we may only fill the first buffer + assert!(len == 1 || c == &[11, 12, 0]); + }) +} + #[test] fn write_vectored() { each_ip(&mut |addr| { diff --git a/library/std/src/process.rs b/library/std/src/process.rs index d91d4fa64caa5..44e7180b3b87e 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -110,7 +110,7 @@ use crate::convert::Infallible; use crate::ffi::OsStr; use crate::fmt; use crate::fs; -use crate::io::{self, IoSlice, IoSliceMut}; +use crate::io::{self, BorrowedSliceCursor, IoSlice, IoSliceMut}; use crate::num::NonZeroI32; use crate::path::Path; use crate::str; @@ -358,6 +358,10 @@ impl Read for ChildStdout { self.inner.read_vectored(bufs) } + fn read_buf_vectored(&mut self, cursor: BorrowedSliceCursor<'_>) -> io::Result<()> { + self.inner.read_buf_vectored(cursor) + } + #[inline] fn is_read_vectored(&self) -> bool { self.inner.is_read_vectored() @@ -419,6 +423,10 @@ impl Read for ChildStderr { self.inner.read_vectored(bufs) } + fn read_buf_vectored(&mut self, cursor: BorrowedSliceCursor<'_>) -> io::Result<()> { + self.inner.read_buf_vectored(cursor) + } + #[inline] fn is_read_vectored(&self) -> bool { self.inner.is_read_vectored() diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs index dbaa3c33e2e57..58a2bb0dd9a29 100644 --- a/library/std/src/sys/unix/fd.rs +++ b/library/std/src/sys/unix/fd.rs @@ -4,7 +4,7 @@ mod tests; use crate::cmp; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read}; +use crate::io::{self, BorrowedCursor, BorrowedSliceCursor, IoSlice, IoSliceMut, Read}; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; use crate::sys::cvt; use crate::sys_common::{AsInner, FromInner, IntoInner}; @@ -147,6 +147,30 @@ impl FileDesc { Ok(()) } + #[cfg(not(any(target_os = "espidf", target_os = "horizon")))] + pub fn read_buf_vectored(&self, mut cursor: BorrowedSliceCursor<'_>) -> io::Result<()> { + let ret = unsafe { + let bufs = cursor.as_mut(); + cvt(libc::readv( + self.as_raw_fd(), + bufs.as_ptr() as *const libc::iovec, + cmp::min(bufs.len(), max_iov()) as libc::c_int, + ))? + }; + + // SAFETY: we've just read `ret` bytes into the cursor. + unsafe { + cursor.advance(ret as usize); + } + + Ok(()) + } + + #[cfg(any(target_os = "espidf", target_os = "horizon"))] + pub fn read_buf_vectored(&self, bufs: BorrowedSliceCursor<'_>) -> io::Result<()> { + crate::io::default_read_buf_vectored(|b| self.read(b), bufs) + } + pub fn write(&self, buf: &[u8]) -> io::Result { let ret = cvt(unsafe { libc::write( diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index cc347e3586ae3..0174f4cafcacf 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -2,7 +2,7 @@ use crate::os::unix::prelude::*; use crate::ffi::{CStr, CString, OsStr, OsString}; use crate::fmt; -use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; +use crate::io::{self, BorrowedCursor, BorrowedSliceCursor, Error, IoSlice, IoSliceMut, SeekFrom}; use crate::mem; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd}; use crate::path::{Path, PathBuf}; @@ -1041,6 +1041,10 @@ impl File { self.0.read_buf(cursor) } + pub fn read_buf_vectored(&self, bufs: BorrowedSliceCursor<'_>) -> io::Result<()> { + self.0.read_buf_vectored(bufs) + } + pub fn write(&self, buf: &[u8]) -> io::Result { self.0.write(buf) } diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs index b84bf8f9264a4..fe40b12431d19 100644 --- a/library/std/src/sys/unix/net.rs +++ b/library/std/src/sys/unix/net.rs @@ -1,6 +1,6 @@ use crate::cmp; use crate::ffi::CStr; -use crate::io::{self, IoSlice, IoSliceMut}; +use crate::io::{self, BorrowedSliceCursor, IoSlice, IoSliceMut}; use crate::mem; use crate::net::{Shutdown, SocketAddr}; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; @@ -259,6 +259,10 @@ impl Socket { self.0.read_vectored(bufs) } + pub fn read_buf_vectored(&self, cursor: BorrowedSliceCursor<'_>) -> io::Result<()> { + self.0.read_buf_vectored(cursor) + } + #[inline] pub fn is_read_vectored(&self) -> bool { self.0.is_read_vectored() diff --git a/library/std/src/sys/unix/pipe.rs b/library/std/src/sys/unix/pipe.rs index a56c275c94207..d42b1879593cf 100644 --- a/library/std/src/sys/unix/pipe.rs +++ b/library/std/src/sys/unix/pipe.rs @@ -1,4 +1,4 @@ -use crate::io::{self, IoSlice, IoSliceMut}; +use crate::io::{self, BorrowedSliceCursor, IoSlice, IoSliceMut}; use crate::mem; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::sys::fd::FileDesc; @@ -66,6 +66,10 @@ impl AnonPipe { self.0.write_vectored(bufs) } + pub fn read_buf_vectored(&self, cursor: BorrowedSliceCursor<'_>) -> io::Result<()> { + self.0.read_buf_vectored(cursor) + } + #[inline] pub fn is_write_vectored(&self) -> bool { self.0.is_write_vectored() diff --git a/library/std/src/sys/unix/stdio.rs b/library/std/src/sys/unix/stdio.rs index 329f9433dba0e..42e70092e8157 100644 --- a/library/std/src/sys/unix/stdio.rs +++ b/library/std/src/sys/unix/stdio.rs @@ -1,4 +1,4 @@ -use crate::io::{self, IoSlice, IoSliceMut}; +use crate::io::{self, BorrowedSliceCursor, IoSlice, IoSliceMut}; use crate::mem::ManuallyDrop; use crate::os::unix::io::{AsFd, BorrowedFd, FromRawFd}; use crate::sys::fd::FileDesc; @@ -22,6 +22,12 @@ impl io::Read for Stdin { unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDIN_FILENO)).read_vectored(bufs) } } + fn read_buf_vectored(&mut self, cursor: BorrowedSliceCursor<'_>) -> io::Result<()> { + unsafe { + ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDIN_FILENO)).read_buf_vectored(cursor) + } + } + #[inline] fn is_read_vectored(&self) -> bool { true diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs index 3ad802afa8f7a..0310d529aa69f 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys_common/net.rs @@ -4,7 +4,7 @@ mod tests; use crate::cmp; use crate::ffi::CString; use crate::fmt; -use crate::io::{self, ErrorKind, IoSlice, IoSliceMut}; +use crate::io::{self, BorrowedSliceCursor, ErrorKind, IoSlice, IoSliceMut}; use crate::mem; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; use crate::ptr; @@ -273,6 +273,10 @@ impl TcpStream { self.inner.read_vectored(bufs) } + pub fn read_buf_vectored(&self, cursor: BorrowedSliceCursor<'_>) -> io::Result<()> { + self.inner.read_buf_vectored(cursor) + } + #[inline] pub fn is_read_vectored(&self) -> bool { self.inner.is_read_vectored()