diff --git a/library/core/src/io/borrowed_buf.rs b/library/core/src/io/borrowed_buf.rs index 088dea7812945..5df4f044b0b95 100644 --- a/library/core/src/io/borrowed_buf.rs +++ b/library/core/src/io/borrowed_buf.rs @@ -2,20 +2,18 @@ use crate::fmt::{self, Debug, Formatter}; use crate::mem::{self, MaybeUninit}; -use crate::{cmp, ptr}; +use crate::ptr; -/// A borrowed byte buffer which is incrementally filled and initialized. +/// A borrowed byte buffer which is incrementally filled. /// -/// 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. +/// This type makes it safer to work with `MaybeUninit` buffers, such as to read into a buffer +/// without having to initialize it first. It tracks the region of bytes that have been filled and +/// whether the unfilled region was initialized. /// /// In summary, the contents of the buffer can be visualized as: /// ```not_rust -/// [ capacity ] -/// [ filled | unfilled ] -/// [ initialized | uninitialized ] +/// [ capacity ] +/// [ filled | unfilled (may be initialized) ] /// ``` /// /// A `BorrowedBuf` is created around some existing data (or capacity for data) via a unique reference @@ -30,8 +28,8 @@ pub struct BorrowedBuf<'data> { buf: &'data mut [MaybeUninit], /// The length of `self.buf` which is known to be filled. filled: usize, - /// The length of `self.buf` which is known to be initialized. - init: usize, + /// Whether the entire unfilled part of `self.buf` is known to be initialized. + init: bool, } impl Debug for BorrowedBuf<'_> { @@ -48,24 +46,20 @@ impl Debug for BorrowedBuf<'_> { impl<'data> From<&'data mut [u8]> for BorrowedBuf<'data> { #[inline] fn from(slice: &'data mut [u8]) -> BorrowedBuf<'data> { - let len = slice.len(); - BorrowedBuf { // SAFETY: initialized data never becoming uninitialized is an invariant of BorrowedBuf - buf: unsafe { (slice as *mut [u8]).as_uninit_slice_mut().unwrap() }, + buf: unsafe { &mut *(slice as *mut [u8] as *mut [MaybeUninit]) }, filled: 0, - init: len, + init: true, } } } /// Creates a new `BorrowedBuf` from an uninitialized buffer. -/// -/// Use `set_init` if part of the buffer is known to be already initialized. impl<'data> From<&'data mut [MaybeUninit]> for BorrowedBuf<'data> { #[inline] fn from(buf: &'data mut [MaybeUninit]) -> BorrowedBuf<'data> { - BorrowedBuf { buf, filled: 0, init: 0 } + BorrowedBuf { buf, filled: 0, init: false } } } @@ -74,14 +68,13 @@ impl<'data> From<&'data mut [MaybeUninit]> for BorrowedBuf<'data> { /// Use `BorrowedCursor::with_unfilled_buf` instead for a safer alternative. impl<'data> From> for BorrowedBuf<'data> { #[inline] - fn from(mut buf: BorrowedCursor<'data>) -> BorrowedBuf<'data> { - let init = buf.init_mut().len(); + fn from(buf: BorrowedCursor<'data>) -> BorrowedBuf<'data> { BorrowedBuf { // SAFETY: no initialized byte is ever uninitialized as per // `BorrowedBuf`'s invariant buf: unsafe { buf.buf.buf.get_unchecked_mut(buf.buf.filled..) }, filled: 0, - init, + init: buf.buf.init, } } } @@ -100,8 +93,9 @@ impl<'data> BorrowedBuf<'data> { } /// Returns the length of the initialized part of the buffer. + #[unstable(feature = "borrowed_buf_init", issue = "78485")] #[inline] - pub fn init_len(&self) -> usize { + pub fn is_init(&self) -> bool { self.init } @@ -159,32 +153,29 @@ impl<'data> BorrowedBuf<'data> { /// 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. + /// The contents of the buffer are not modified. #[inline] pub fn clear(&mut self) -> &mut Self { self.filled = 0; self } - /// Asserts that the first `n` bytes of the buffer are initialized. - /// - /// `BorrowedBuf` assumes that bytes are never de-initialized, so this method does nothing when called with fewer - /// bytes than are already known to be initialized. + /// Asserts that the unfilled part of the buffer is initialized. /// /// # Safety /// - /// The caller must ensure that the first `n` unfilled bytes of the buffer have already been initialized. + /// All the bytes of the buffer must be initialized. + #[unstable(feature = "borrowed_buf_init", issue = "78485")] #[inline] - pub unsafe fn set_init(&mut self, n: usize) -> &mut Self { - self.init = cmp::max(self.init, n); + pub unsafe fn set_init(&mut self) -> &mut Self { + self.init = true; self } } /// A writeable view of the unfilled portion of a [`BorrowedBuf`]. /// -/// The unfilled portion consists of an initialized and an uninitialized part; see [`BorrowedBuf`] -/// for details. +/// The unfilled portion may be uninitialized; see [`BorrowedBuf`] for details. /// /// Data can be written directly to the cursor by using [`append`](BorrowedCursor::append) or /// indirectly by getting a slice of part or all of the cursor and writing into the slice. In the @@ -238,21 +229,29 @@ impl<'a> BorrowedCursor<'a> { self.buf.filled } - /// Returns a mutable reference to the initialized portion of the cursor. + /// Returns `true` if the buffer is initialized. + #[unstable(feature = "borrowed_buf_init", issue = "78485")] #[inline] - pub fn init_mut(&mut self) -> &mut [u8] { - // SAFETY: We only slice the initialized part of the buffer, which is always valid - unsafe { - let buf = self.buf.buf.get_unchecked_mut(self.buf.filled..self.buf.init); - buf.assume_init_mut() - } + pub fn is_init(&self) -> bool { + self.buf.init + } + + /// Set the buffer as fully initialized. + /// + /// # Safety + /// + /// All the bytes of the cursor must be initialized. + #[unstable(feature = "borrowed_buf_init", issue = "78485")] + #[inline] + pub unsafe fn set_init(&mut self) { + self.buf.init = true; } /// Returns a mutable reference to the whole cursor. /// /// # Safety /// - /// The caller must not uninitialize any bytes in the initialized portion of the cursor. + /// The caller must not uninitialize any bytes of the cursor if it is initialized. #[inline] pub unsafe fn as_mut(&mut self) -> &mut [MaybeUninit] { // SAFETY: always in bounds @@ -271,10 +270,12 @@ impl<'a> BorrowedCursor<'a> { /// # Panics /// /// Panics if there are less than `n` bytes initialized. + #[unstable(feature = "borrowed_buf_init", issue = "78485")] #[inline] - pub fn advance(&mut self, n: usize) -> &mut Self { + pub fn advance_checked(&mut self, n: usize) -> &mut Self { // The subtraction cannot underflow by invariant of this type. - assert!(n <= self.buf.init - self.buf.filled); + let init_unfilled = if self.buf.init { self.buf.buf.len() - self.buf.filled } else { 0 }; + assert!(n <= init_unfilled); self.buf.filled += n; self @@ -291,40 +292,29 @@ impl<'a> BorrowedCursor<'a> { /// The caller must ensure that the first `n` bytes of the cursor have been properly /// initialised. #[inline] - pub unsafe fn advance_unchecked(&mut self, n: usize) -> &mut Self { + pub unsafe fn advance(&mut self, n: usize) -> &mut Self { self.buf.filled += n; - self.buf.init = cmp::max(self.buf.init, self.buf.filled); self } - /// Initializes all bytes in the cursor. + /// Initializes all bytes in the cursor and returns them. + #[unstable(feature = "borrowed_buf_init", issue = "78485")] #[inline] - pub fn ensure_init(&mut self) -> &mut Self { + pub fn ensure_init(&mut self) -> &mut [u8] { // SAFETY: always in bounds and we never uninitialize these bytes. - let uninit = unsafe { self.buf.buf.get_unchecked_mut(self.buf.init..) }; - - // SAFETY: 0 is a valid value for MaybeUninit and the length matches the allocation - // since it is comes from a slice reference. - unsafe { - ptr::write_bytes(uninit.as_mut_ptr(), 0, uninit.len()); + let unfilled = unsafe { self.buf.buf.get_unchecked_mut(self.buf.filled..) }; + + if !self.buf.init { + // SAFETY: 0 is a valid value for MaybeUninit and the length matches the allocation + // since it is comes from a slice reference. + unsafe { + ptr::write_bytes(unfilled.as_mut_ptr(), 0, unfilled.len()); + } + self.buf.init = true; } - self.buf.init = self.buf.capacity(); - - self - } - /// Asserts that the first `n` unfilled bytes of the cursor are initialized. - /// - /// `BorrowedBuf` 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 the first `n` bytes of the buffer have already been initialized. - #[inline] - pub unsafe fn set_init(&mut self, n: usize) -> &mut Self { - self.buf.init = cmp::max(self.buf.init, self.buf.filled + n); - self + // SAFETY: these bytes have just been initialized if they weren't before + unsafe { unfilled.assume_init_mut() } } /// Appends data to the cursor, advancing position within its buffer. @@ -341,10 +331,6 @@ impl<'a> BorrowedCursor<'a> { self.as_mut()[..buf.len()].write_copy_of_slice(buf); } - // SAFETY: We just added the entire contents of buf to the filled section. - unsafe { - self.set_init(buf.len()); - } self.buf.filled += buf.len(); } @@ -365,7 +351,7 @@ impl<'a> BorrowedCursor<'a> { // Check that the caller didn't replace the `BorrowedBuf`. // This is necessary for the safety of the code below: if the check wasn't // there, one could mark some bytes as initialized even though there aren't. - assert!(core::ptr::addr_eq(prev_ptr, buf.buf)); + assert!(core::ptr::eq(prev_ptr, buf.buf)); let filled = buf.filled; let init = buf.init; @@ -376,7 +362,7 @@ impl<'a> BorrowedCursor<'a> { // SAFETY: These amounts of bytes were initialized/filled in the `BorrowedBuf`, // and therefore they are initialized/filled in the cursor too, because the // buffer wasn't replaced. - self.buf.init = self.buf.filled + init; + self.buf.init = init; self.buf.filled += filled; res diff --git a/library/coretests/tests/io/borrowed_buf.rs b/library/coretests/tests/io/borrowed_buf.rs index aaa98d26ff8b9..de241e0cb85c9 100644 --- a/library/coretests/tests/io/borrowed_buf.rs +++ b/library/coretests/tests/io/borrowed_buf.rs @@ -8,7 +8,7 @@ fn new() { let mut rbuf: BorrowedBuf<'_> = buf.into(); assert_eq!(rbuf.filled().len(), 0); - assert_eq!(rbuf.init_len(), 16); + assert!(rbuf.is_init()); assert_eq!(rbuf.capacity(), 16); assert_eq!(rbuf.unfilled().capacity(), 16); } @@ -20,7 +20,7 @@ fn uninit() { let mut rbuf: BorrowedBuf<'_> = buf.into(); assert_eq!(rbuf.filled().len(), 0); - assert_eq!(rbuf.init_len(), 0); + assert!(!rbuf.is_init()); assert_eq!(rbuf.capacity(), 16); assert_eq!(rbuf.unfilled().capacity(), 16); } @@ -32,7 +32,7 @@ fn initialize_unfilled() { rbuf.unfilled().ensure_init(); - assert_eq!(rbuf.init_len(), 16); + assert!(rbuf.is_init()); } #[test] @@ -40,7 +40,7 @@ fn advance_filled() { let buf: &mut [_] = &mut [0; 16]; let mut rbuf: BorrowedBuf<'_> = buf.into(); - rbuf.unfilled().advance(1); + rbuf.unfilled().advance_checked(1); assert_eq!(rbuf.filled().len(), 1); assert_eq!(rbuf.unfilled().capacity(), 15); @@ -51,7 +51,7 @@ fn clear() { let buf: &mut [_] = &mut [255; 16]; let mut rbuf: BorrowedBuf<'_> = buf.into(); - rbuf.unfilled().advance(16); + rbuf.unfilled().advance_checked(16); assert_eq!(rbuf.filled().len(), 16); assert_eq!(rbuf.unfilled().capacity(), 0); @@ -61,7 +61,7 @@ fn clear() { assert_eq!(rbuf.filled().len(), 0); assert_eq!(rbuf.unfilled().capacity(), 16); - assert_eq!(rbuf.unfilled().init_mut(), [255; 16]); + assert_eq!(rbuf.unfilled().ensure_init(), [255; 16]); } #[test] @@ -70,24 +70,10 @@ fn set_init() { let mut rbuf: BorrowedBuf<'_> = buf.into(); unsafe { - rbuf.set_init(8); + rbuf.set_init(); } - assert_eq!(rbuf.init_len(), 8); - - rbuf.unfilled().advance(4); - - unsafe { - rbuf.set_init(2); - } - - assert_eq!(rbuf.init_len(), 8); - - unsafe { - rbuf.set_init(8); - } - - assert_eq!(rbuf.init_len(), 8); + assert!(rbuf.is_init()); } #[test] @@ -97,7 +83,7 @@ fn append() { rbuf.unfilled().append(&[0; 8]); - assert_eq!(rbuf.init_len(), 8); + assert!(!rbuf.is_init()); assert_eq!(rbuf.filled().len(), 8); assert_eq!(rbuf.filled(), [0; 8]); @@ -105,7 +91,7 @@ fn append() { rbuf.unfilled().append(&[1; 16]); - assert_eq!(rbuf.init_len(), 16); + assert!(!rbuf.is_init()); assert_eq!(rbuf.filled().len(), 16); assert_eq!(rbuf.filled(), [1; 16]); } @@ -125,7 +111,7 @@ fn reborrow_written() { assert_eq!(cursor.written(), 32); assert_eq!(buf.unfilled().written(), 32); - assert_eq!(buf.init_len(), 32); + assert!(!buf.is_init()); assert_eq!(buf.filled().len(), 32); let filled = buf.filled(); assert_eq!(&filled[..16], [1; 16]); @@ -136,30 +122,20 @@ fn reborrow_written() { fn cursor_set_init() { let buf: &mut [_] = &mut [MaybeUninit::zeroed(); 16]; let mut rbuf: BorrowedBuf<'_> = buf.into(); + let mut cursor = rbuf.unfilled(); unsafe { - rbuf.unfilled().set_init(8); + cursor.set_init(); } - assert_eq!(rbuf.init_len(), 8); - assert_eq!(rbuf.unfilled().init_mut().len(), 8); - assert_eq!(unsafe { rbuf.unfilled().as_mut().len() }, 16); - - rbuf.unfilled().advance(4); - - unsafe { - rbuf.unfilled().set_init(2); - } + assert!(cursor.is_init()); + assert_eq!(unsafe { cursor.as_mut().len() }, 16); - assert_eq!(rbuf.init_len(), 8); + cursor.advance_checked(4); - unsafe { - rbuf.unfilled().set_init(8); - } + assert_eq!(unsafe { cursor.as_mut().len() }, 12); - assert_eq!(rbuf.init_len(), 12); - assert_eq!(rbuf.unfilled().init_mut().len(), 8); - assert_eq!(unsafe { rbuf.unfilled().as_mut().len() }, 12); + assert!(rbuf.is_init()); } #[test] @@ -173,26 +149,26 @@ fn cursor_with_unfilled_buf() { assert_eq!(buf.filled(), &[1, 2, 3]); }); - assert_eq!(cursor.init_mut().len(), 0); + assert!(!cursor.is_init()); assert_eq!(cursor.written(), 3); cursor.with_unfilled_buf(|buf| { assert_eq!(buf.capacity(), 13); - assert_eq!(buf.init_len(), 0); + assert!(!buf.is_init()); buf.unfilled().ensure_init(); - buf.unfilled().advance(4); + buf.unfilled().advance_checked(4); }); - assert_eq!(cursor.init_mut().len(), 9); + assert!(cursor.is_init()); assert_eq!(cursor.written(), 7); cursor.with_unfilled_buf(|buf| { assert_eq!(buf.capacity(), 9); - assert_eq!(buf.init_len(), 9); + assert!(buf.is_init()); }); - assert_eq!(cursor.init_mut().len(), 9); + assert!(cursor.is_init()); assert_eq!(cursor.written(), 7); assert_eq!(rbuf.filled(), &[1, 2, 3, 0, 0, 0, 0]); diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 0387b442562db..762baea57b881 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -11,6 +11,7 @@ #![feature(async_iterator)] #![feature(bigint_helper_methods)] #![feature(bool_to_result)] +#![feature(borrowed_buf_init)] #![feature(bstr)] #![feature(cfg_target_has_reliable_f16_f128)] #![feature(char_internals)] diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 0a5d1153d860c..8838fe3be8f09 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -710,7 +710,7 @@ fn file_test_read_buf() { check!(file.read_buf(buf.unfilled())); assert_eq!(buf.filled(), &[1, 2, 3, 4]); // File::read_buf should omit buffer initialization. - assert_eq!(buf.init_len(), 4); + assert!(!buf.is_init()); check!(fs::remove_file(filename)); } diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs index 40441dc057d0d..ac461f4f0307e 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -288,7 +288,7 @@ impl BufReader { #[cfg(test)] impl BufReader { #[allow(missing_docs)] - pub fn initialized(&self) -> usize { + pub fn initialized(&self) -> bool { self.buf.initialized() } } diff --git a/library/std/src/io/buffered/bufreader/buffer.rs b/library/std/src/io/buffered/bufreader/buffer.rs index 9b600cd55758b..55fe33aaa800b 100644 --- a/library/std/src/io/buffered/bufreader/buffer.rs +++ b/library/std/src/io/buffered/bufreader/buffer.rs @@ -26,20 +26,20 @@ pub struct Buffer { // defensive initialization as possible. Note that while this often the same as `filled`, it // doesn't need to be. Calls to `fill_buf` are not required to actually fill the buffer, and // omitting this is a huge perf regression for `Read` impls that do not. - initialized: usize, + initialized: bool, } impl Buffer { #[inline] pub fn with_capacity(capacity: usize) -> Self { let buf = Box::new_uninit_slice(capacity); - Self { buf, pos: 0, filled: 0, initialized: 0 } + Self { buf, pos: 0, filled: 0, initialized: false } } #[inline] pub fn try_with_capacity(capacity: usize) -> io::Result { match Box::try_new_uninit_slice(capacity) { - Ok(buf) => Ok(Self { buf, pos: 0, filled: 0, initialized: 0 }), + Ok(buf) => Ok(Self { buf, pos: 0, filled: 0, initialized: false }), Err(_) => { Err(io::const_error!(ErrorKind::OutOfMemory, "failed to allocate read buffer")) } @@ -70,7 +70,7 @@ impl Buffer { // This is only used by a test which asserts that the initialization-tracking is correct. #[cfg(test)] - pub fn initialized(&self) -> usize { + pub fn initialized(&self) -> bool { self.initialized } @@ -110,13 +110,14 @@ impl Buffer { /// Read more bytes into the buffer without discarding any of its contents pub fn read_more(&mut self, mut reader: impl Read) -> io::Result { let mut buf = BorrowedBuf::from(&mut self.buf[self.filled..]); - let old_init = self.initialized - self.filled; - unsafe { - buf.set_init(old_init); + + if self.initialized { + unsafe { buf.set_init() }; } + reader.read_buf(buf.unfilled())?; self.filled += buf.len(); - self.initialized += buf.init_len() - old_init; + self.initialized = buf.is_init(); Ok(buf.len()) } @@ -138,15 +139,16 @@ impl Buffer { let mut buf = BorrowedBuf::from(&mut *self.buf); // SAFETY: `self.filled` bytes will always have been initialized. - unsafe { - buf.set_init(self.initialized); + + if self.initialized { + unsafe { buf.set_init() }; } let result = reader.read_buf(buf.unfilled()); self.pos = 0; self.filled = buf.len(); - self.initialized = buf.init_len(); + self.initialized = buf.is_init(); result?; } diff --git a/library/std/src/io/buffered/tests.rs b/library/std/src/io/buffered/tests.rs index 6ad4158b92904..58be268573e66 100644 --- a/library/std/src/io/buffered/tests.rs +++ b/library/std/src/io/buffered/tests.rs @@ -1067,13 +1067,13 @@ fn bufreader_full_initialize() { } let mut reader = BufReader::new(OneByteReader); // Nothing is initialized yet. - assert_eq!(reader.initialized(), 0); + assert!(!reader.initialized()); let buf = reader.fill_buf().unwrap(); // We read one byte... assert_eq!(buf.len(), 1); // But we initialized the whole buffer! - assert_eq!(reader.initialized(), reader.capacity()); + assert!(reader.initialized()); } /// This is a regression test for https://github.com/rust-lang/rust/issues/127584. diff --git a/library/std/src/io/copy.rs b/library/std/src/io/copy.rs index 2b558efb8885e..0f3f890a964ad 100644 --- a/library/std/src/io/copy.rs +++ b/library/std/src/io/copy.rs @@ -214,15 +214,15 @@ impl BufferedWriterSpec for BufWriter { } let mut len = 0; - let mut init = 0; + let mut init = false; loop { let buf = self.buffer_mut(); let mut read_buf: BorrowedBuf<'_> = buf.spare_capacity_mut().into(); - unsafe { + if init { // SAFETY: init is either 0 or the init_len from the previous iteration. - read_buf.set_init(init); + unsafe { read_buf.set_init() }; } if read_buf.capacity() >= DEFAULT_BUF_SIZE { @@ -235,7 +235,7 @@ impl BufferedWriterSpec for BufWriter { return Ok(len); } - init = read_buf.init_len() - bytes_read; + init = read_buf.is_init(); len += bytes_read as u64; // SAFETY: BorrowedBuf guarantees all of its filled bytes are init @@ -248,10 +248,6 @@ impl BufferedWriterSpec for BufWriter { Err(e) => return Err(e), } } else { - // All the bytes that were already in the buffer are initialized, - // treat them as such when the buffer is flushed. - init += buf.len(); - self.flush_buf()?; } } diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index b7756befa11e9..4442c4ccb0648 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -419,8 +419,6 @@ pub(crate) fn default_read_to_end( .and_then(|s| s.checked_add(1024)?.checked_next_multiple_of(DEFAULT_BUF_SIZE)) .unwrap_or(DEFAULT_BUF_SIZE); - let mut initialized = 0; // Extra initialized bytes from previous loop iteration - const PROBE_SIZE: usize = 32; fn small_probe_read(r: &mut R, buf: &mut Vec) -> Result { @@ -449,8 +447,6 @@ pub(crate) fn default_read_to_end( } } - let mut consecutive_short_reads = 0; - loop { if buf.len() == buf.capacity() && buf.capacity() == start_cap { // The buffer might be an exact fit. Let's read into a probe buffer @@ -474,11 +470,8 @@ pub(crate) fn default_read_to_end( spare = &mut spare[..buf_len]; let mut read_buf: BorrowedBuf<'_> = spare.into(); - // SAFETY: These bytes were initialized but not filled in the previous loop - unsafe { - read_buf.set_init(initialized); - } - + // Note that we don't track already initialized bytes here, but this is fine + // because we explicitly limit the read size let mut cursor = read_buf.unfilled(); let result = loop { match r.read_buf(cursor.reborrow()) { @@ -489,9 +482,8 @@ pub(crate) fn default_read_to_end( } }; - let unfilled_but_initialized = cursor.init_mut().len(); let bytes_read = cursor.written(); - let was_fully_initialized = read_buf.init_len() == buf_len; + let is_init = read_buf.is_init(); // SAFETY: BorrowedBuf's invariants mean this much memory is initialized. unsafe { @@ -506,15 +498,6 @@ pub(crate) fn default_read_to_end( return Ok(buf.len() - start_len); } - if bytes_read < buf_len { - consecutive_short_reads += 1; - } else { - consecutive_short_reads = 0; - } - - // store how much was initialized but not filled - initialized = unfilled_but_initialized; - // Use heuristics to determine the max read size if no initial size hint was provided if size_hint.is_none() { // The reader is returning short reads but it doesn't call ensure_init(). @@ -523,13 +506,12 @@ pub(crate) fn default_read_to_end( // When reading from disk we usually don't get any short reads except at EOF. // So we wait for at least 2 short reads before uncapping the read buffer; // this helps with the Windows issue. - if !was_fully_initialized && consecutive_short_reads > 1 { + if !is_init { max_read_size = usize::MAX; } - // we have passed a larger buffer than previously and the // reader still hasn't returned a short read - if buf_len >= max_read_size && bytes_read == buf_len { + else if buf_len >= max_read_size && bytes_read == buf_len { max_read_size = max_read_size.saturating_mul(2); } } @@ -587,8 +569,8 @@ pub(crate) fn default_read_buf(read: F, mut cursor: BorrowedCursor<'_>) -> Re where F: FnOnce(&mut [u8]) -> Result, { - let n = read(cursor.ensure_init().init_mut())?; - cursor.advance(n); + let n = read(cursor.ensure_init())?; + cursor.advance_checked(n); Ok(()) } @@ -3098,7 +3080,7 @@ impl Read for Take { // The condition above guarantees that `self.limit` fits in `usize`. let limit = self.limit as usize; - let extra_init = cmp::min(limit, buf.init_mut().len()); + let is_init = buf.is_init(); // SAFETY: no uninit data is written to ibuf let ibuf = unsafe { &mut buf.as_mut()[..limit] }; @@ -3106,23 +3088,32 @@ impl Read for Take { let mut sliced_buf: BorrowedBuf<'_> = ibuf.into(); // SAFETY: extra_init bytes of ibuf are known to be initialized - unsafe { - sliced_buf.set_init(extra_init); + if is_init { + unsafe { sliced_buf.set_init() }; } let mut cursor = sliced_buf.unfilled(); let result = self.inner.read_buf(cursor.reborrow()); - let new_init = cursor.init_mut().len(); + let should_init = cursor.is_init(); let filled = sliced_buf.len(); // cursor / sliced_buf / ibuf must drop here + // Avoid accidentally quadratic behaviour by initializing the whole + // cursor if only part of it was initialized. + if should_init { + // SAFETY: no uninit data is written + let uninit = unsafe { &mut buf.as_mut()[limit..] }; + uninit.write_filled(0); + // SAFETY: all bytes that were not initialized by `T::read_buf` + // have just been written to. + unsafe { buf.set_init() }; + } + unsafe { - // SAFETY: filled bytes have been filled and therefore initialized - buf.advance_unchecked(filled); - // SAFETY: new_init bytes of buf's unfilled buffer have been initialized - buf.set_init(new_init); + // SAFETY: filled bytes have been filled + buf.advance(filled); } self.limit -= filled as u64; diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs index b22988d4a8a9d..00e52e6f33e62 100644 --- a/library/std/src/io/tests.rs +++ b/library/std/src/io/tests.rs @@ -214,8 +214,8 @@ fn read_buf_exact() { fn borrowed_cursor_advance_overflow() { let mut buf = [0; 512]; let mut buf = BorrowedBuf::from(&mut buf[..]); - buf.unfilled().advance(1); - buf.unfilled().advance(usize::MAX); + buf.unfilled().advance_checked(1); + buf.unfilled().advance_checked(usize::MAX); } #[test] diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs index 0410df3ef1a3e..a09c8bc069306 100644 --- a/library/std/src/io/util.rs +++ b/library/std/src/io/util.rs @@ -283,7 +283,7 @@ impl Read for Repeat { // SAFETY: No uninit bytes are being written. unsafe { buf.as_mut() }.write_filled(self.byte); // SAFETY: the entire unfilled portion of buf has been initialized. - unsafe { buf.advance_unchecked(buf.capacity()) }; + unsafe { buf.advance(buf.capacity()) }; Ok(()) } diff --git a/library/std/src/io/util/tests.rs b/library/std/src/io/util/tests.rs index d0f106d7af416..fa1193f2b00fa 100644 --- a/library/std/src/io/util/tests.rs +++ b/library/std/src/io/util/tests.rs @@ -75,43 +75,43 @@ fn empty_reads() { let mut buf: BorrowedBuf<'_> = buf.into(); e.read_buf(buf.unfilled()).unwrap(); assert_eq!(buf.len(), 0); - assert_eq!(buf.init_len(), 0); + assert!(!buf.is_init()); let buf: &mut [_] = &mut [MaybeUninit::uninit()]; let mut buf: BorrowedBuf<'_> = buf.into(); e.read_buf(buf.unfilled()).unwrap(); assert_eq!(buf.len(), 0); - assert_eq!(buf.init_len(), 0); + assert!(!buf.is_init()); let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024]; let mut buf: BorrowedBuf<'_> = buf.into(); e.read_buf(buf.unfilled()).unwrap(); assert_eq!(buf.len(), 0); - assert_eq!(buf.init_len(), 0); + assert!(!buf.is_init()); let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024]; let mut buf: BorrowedBuf<'_> = buf.into(); Read::by_ref(&mut e).read_buf(buf.unfilled()).unwrap(); assert_eq!(buf.len(), 0); - assert_eq!(buf.init_len(), 0); + assert!(!buf.is_init()); let buf: &mut [MaybeUninit<_>] = &mut []; let mut buf: BorrowedBuf<'_> = buf.into(); e.read_buf_exact(buf.unfilled()).unwrap(); assert_eq!(buf.len(), 0); - assert_eq!(buf.init_len(), 0); + assert!(!buf.is_init()); let buf: &mut [_] = &mut [MaybeUninit::uninit()]; let mut buf: BorrowedBuf<'_> = buf.into(); assert_eq!(e.read_buf_exact(buf.unfilled()).unwrap_err().kind(), ErrorKind::UnexpectedEof); assert_eq!(buf.len(), 0); - assert_eq!(buf.init_len(), 0); + assert!(!buf.is_init()); let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024]; let mut buf: BorrowedBuf<'_> = buf.into(); assert_eq!(e.read_buf_exact(buf.unfilled()).unwrap_err().kind(), ErrorKind::UnexpectedEof); assert_eq!(buf.len(), 0); - assert_eq!(buf.init_len(), 0); + assert!(!buf.is_init()); let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024]; let mut buf: BorrowedBuf<'_> = buf.into(); @@ -120,7 +120,7 @@ fn empty_reads() { ErrorKind::UnexpectedEof, ); assert_eq!(buf.len(), 0); - assert_eq!(buf.init_len(), 0); + assert!(!buf.is_init()); let mut buf = Vec::new(); assert_eq!(e.read_to_end(&mut buf).unwrap(), 0); diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 8fb1b1b05d20c..1aa79ebcccc62 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -316,6 +316,7 @@ // // Library features (core): // tidy-alphabetical-start +#![feature(borrowed_buf_init)] #![feature(bstr)] #![feature(bstr_internals)] #![feature(cast_maybe_uninit)] diff --git a/library/std/src/net/tcp/tests.rs b/library/std/src/net/tcp/tests.rs index 7c7ef7b2f7018..80d2aa6e58307 100644 --- a/library/std/src/net/tcp/tests.rs +++ b/library/std/src/net/tcp/tests.rs @@ -316,7 +316,7 @@ fn read_buf() { t!(s.read_buf(buf.unfilled())); assert_eq!(buf.filled(), &[1, 2, 3, 4]); // TcpStream::read_buf should omit buffer initialization. - assert_eq!(buf.init_len(), 4); + assert!(!buf.is_init()); t.join().ok().expect("thread panicked"); }) diff --git a/library/std/src/process/tests.rs b/library/std/src/process/tests.rs index 12c5130defe5a..68c62a861075f 100644 --- a/library/std/src/process/tests.rs +++ b/library/std/src/process/tests.rs @@ -188,10 +188,10 @@ fn child_stdout_read_buf() { // ChildStdout::read_buf should omit buffer initialization. if cfg!(target_os = "windows") { assert_eq!(buf.filled(), b"abc\r\n"); - assert_eq!(buf.init_len(), 5); + assert!(!buf.is_init()); } else { assert_eq!(buf.filled(), b"abc\n"); - assert_eq!(buf.init_len(), 4); + assert!(!buf.is_init()); }; } diff --git a/library/std/src/sys/fd/hermit.rs b/library/std/src/sys/fd/hermit.rs index 2666da16420c4..28fafdaf57d8a 100644 --- a/library/std/src/sys/fd/hermit.rs +++ b/library/std/src/sys/fd/hermit.rs @@ -33,7 +33,7 @@ impl FileDesc { ) })?; // SAFETY: Exactly `result` bytes have been filled. - unsafe { buf.advance_unchecked(result as usize) }; + unsafe { buf.advance(result as usize) }; Ok(()) } diff --git a/library/std/src/sys/fd/unix.rs b/library/std/src/sys/fd/unix.rs index bb6c0ac9e18e6..c5e8646dada1e 100644 --- a/library/std/src/sys/fd/unix.rs +++ b/library/std/src/sys/fd/unix.rs @@ -185,7 +185,7 @@ impl FileDesc { // SAFETY: `ret` bytes were written to the initialized portion of the buffer unsafe { - cursor.advance_unchecked(ret as usize); + cursor.advance(ret as usize); } Ok(()) } @@ -203,7 +203,7 @@ impl FileDesc { // SAFETY: `ret` bytes were written to the initialized portion of the buffer unsafe { - cursor.advance_unchecked(ret as usize); + cursor.advance(ret as usize); } Ok(()) } diff --git a/library/std/src/sys/fs/solid.rs b/library/std/src/sys/fs/solid.rs index f6d5d3b784d3b..ec1db262855ad 100644 --- a/library/std/src/sys/fs/solid.rs +++ b/library/std/src/sys/fs/solid.rs @@ -401,7 +401,7 @@ impl File { // Safety: `num_bytes_read` bytes were written to the unfilled // portion of the buffer - cursor.advance_unchecked(num_bytes_read); + cursor.advance(num_bytes_read); Ok(()) } diff --git a/library/std/src/sys/net/connection/socket/hermit.rs b/library/std/src/sys/net/connection/socket/hermit.rs index c32f8dcc8de86..8350d2b5fe4a0 100644 --- a/library/std/src/sys/net/connection/socket/hermit.rs +++ b/library/std/src/sys/net/connection/socket/hermit.rs @@ -143,7 +143,7 @@ impl Socket { ) })?; unsafe { - buf.advance_unchecked(ret as usize); + buf.advance(ret as usize); } Ok(()) } diff --git a/library/std/src/sys/net/connection/socket/solid.rs b/library/std/src/sys/net/connection/socket/solid.rs index 673d75046d3f2..ac06cdc00c8f0 100644 --- a/library/std/src/sys/net/connection/socket/solid.rs +++ b/library/std/src/sys/net/connection/socket/solid.rs @@ -190,7 +190,7 @@ impl Socket { netc::recv(self.as_raw_fd(), buf.as_mut().as_mut_ptr().cast(), buf.capacity(), flags) })?; unsafe { - buf.advance_unchecked(ret as usize); + buf.advance(ret as usize); } Ok(()) } diff --git a/library/std/src/sys/net/connection/socket/unix.rs b/library/std/src/sys/net/connection/socket/unix.rs index 6d06a8d86bf16..323d6214347e7 100644 --- a/library/std/src/sys/net/connection/socket/unix.rs +++ b/library/std/src/sys/net/connection/socket/unix.rs @@ -294,7 +294,7 @@ impl Socket { ) })?; unsafe { - buf.advance_unchecked(ret as usize); + buf.advance(ret as usize); } Ok(()) } diff --git a/library/std/src/sys/net/connection/socket/windows.rs b/library/std/src/sys/net/connection/socket/windows.rs index 7355f0ce6bf5e..4da51d78ea69b 100644 --- a/library/std/src/sys/net/connection/socket/windows.rs +++ b/library/std/src/sys/net/connection/socket/windows.rs @@ -243,7 +243,7 @@ impl Socket { } } _ => { - unsafe { buf.advance_unchecked(result as usize) }; + unsafe { buf.advance(result as usize) }; Ok(()) } } diff --git a/library/std/src/sys/pal/sgx/abi/usercalls/mod.rs b/library/std/src/sys/pal/sgx/abi/usercalls/mod.rs index 5041770faf661..f1e4a5a42577a 100644 --- a/library/std/src/sys/pal/sgx/abi/usercalls/mod.rs +++ b/library/std/src/sys/pal/sgx/abi/usercalls/mod.rs @@ -46,7 +46,7 @@ pub fn read_buf(fd: Fd, mut buf: BorrowedCursor<'_>) -> IoResult<()> { let mut userbuf = alloc::User::<[u8]>::uninitialized(buf.capacity()); let len = raw::read(fd, userbuf.as_mut_ptr().cast(), userbuf.len()).from_sgx_result()?; userbuf[..len].copy_to_enclave(&mut buf.as_mut()[..len]); - buf.advance_unchecked(len); + buf.advance(len); Ok(()) } } diff --git a/library/std/src/sys/pal/windows/handle.rs b/library/std/src/sys/pal/windows/handle.rs index 90e243e1aa038..ffa8507831acf 100644 --- a/library/std/src/sys/pal/windows/handle.rs +++ b/library/std/src/sys/pal/windows/handle.rs @@ -117,7 +117,7 @@ impl Handle { Ok(read) => { // Safety: `read` bytes were written to the initialized portion of the buffer unsafe { - cursor.advance_unchecked(read); + cursor.advance(read); } Ok(()) } @@ -140,7 +140,7 @@ impl Handle { // SAFETY: `read` bytes were written to the initialized portion of the buffer unsafe { - cursor.advance_unchecked(read); + cursor.advance(read); } Ok(()) } diff --git a/library/std/src/sys/process/windows/child_pipe.rs b/library/std/src/sys/process/windows/child_pipe.rs index da7a86ca072e3..b848435ac275f 100644 --- a/library/std/src/sys/process/windows/child_pipe.rs +++ b/library/std/src/sys/process/windows/child_pipe.rs @@ -260,7 +260,7 @@ impl ChildPipe { Err(e) => Err(e), Ok(n) => { unsafe { - buf.advance_unchecked(n); + buf.advance(n); } Ok(()) } diff --git a/library/std/src/sys/stdio/zkvm.rs b/library/std/src/sys/stdio/zkvm.rs index f31c6c26e87cd..84496ac937363 100644 --- a/library/std/src/sys/stdio/zkvm.rs +++ b/library/std/src/sys/stdio/zkvm.rs @@ -19,7 +19,7 @@ impl io::Read for Stdin { fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> io::Result<()> { unsafe { let n = abi::sys_read(fileno::STDIN, buf.as_mut().as_mut_ptr().cast(), buf.capacity()); - buf.advance_unchecked(n); + buf.advance(n); } Ok(()) }