From df42b1908861f652ada10911074070962043640a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20du=20Garreau?= Date: Thu, 18 Dec 2025 14:35:45 +0100 Subject: [PATCH] core: Make `BorrowedBuf::init` a boolean Co-authored-by: Josh Triplett --- library/core/src/io/borrowed_buf.rs | 124 ++++++++---------- library/coretests/tests/io/borrowed_buf.rs | 66 +++------- library/coretests/tests/lib.rs | 1 + library/std/src/fs/tests.rs | 2 +- library/std/src/io/buffered/bufreader.rs | 2 +- .../std/src/io/buffered/bufreader/buffer.rs | 24 ++-- library/std/src/io/buffered/tests.rs | 4 +- library/std/src/io/copy.rs | 12 +- library/std/src/io/mod.rs | 51 +++---- library/std/src/io/tests.rs | 4 +- library/std/src/io/util/tests.rs | 16 +-- library/std/src/lib.rs | 1 + library/std/src/net/tcp/tests.rs | 2 +- library/std/src/process/tests.rs | 4 +- 14 files changed, 133 insertions(+), 180 deletions(-) diff --git a/library/core/src/io/borrowed_buf.rs b/library/core/src/io/borrowed_buf.rs index e1ac57524660..db04c5533ce7 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` has explicitly been initialized. + init: bool, } impl Debug for BorrowedBuf<'_> { @@ -48,24 +46,20 @@ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 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 @@ fn from(buf: &'data mut [MaybeUninit]) -> 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 @@ pub fn len(&self) -> usize { } /// 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 @@ pub fn unfilled<'this>(&'this mut self) -> BorrowedCursor<'this> { /// 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 @@ pub fn written(&self) -> usize { 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 @@ pub unsafe fn as_mut(&mut self) -> &mut [MaybeUninit] { /// # 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 { // 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 @@ -293,38 +294,27 @@ pub fn advance(&mut self, n: usize) -> &mut Self { #[inline] pub unsafe fn advance_unchecked(&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 and returns them. + #[unstable(feature = "borrowed_buf_init", issue = "78485")] #[inline] 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..) }; + let unfilled = unsafe { self.buf.buf.get_unchecked_mut(self.buf.filled..) }; - // 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()); + 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.init_mut() - } - - /// 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 @@ pub fn append(&mut self, buf: &[u8]) { 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 @@ pub fn with_unfilled_buf(&mut self, f: impl FnOnce(&mut BorrowedBuf<'_>) -> T // 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 @@ pub fn with_unfilled_buf(&mut self, f: impl FnOnce(&mut BorrowedBuf<'_>) -> T // 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 aaa98d26ff8b..b63f5516e469 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] @@ -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); + assert!(cursor.is_init()); + assert_eq!(unsafe { cursor.as_mut().len() }, 16); - rbuf.unfilled().advance(4); + cursor.advance(4); - unsafe { - rbuf.unfilled().set_init(2); - } + assert_eq!(unsafe { cursor.as_mut().len() }, 12); - assert_eq!(rbuf.init_len(), 8); - - unsafe { - rbuf.unfilled().set_init(8); - } - - 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); }); - 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 72112f8b0113..328f2a0a8aff 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -8,6 +8,7 @@ #![feature(async_iter_from_iter)] #![feature(async_iterator)] #![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 42f3ccc340b2..cdb12cd0ba03 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -717,7 +717,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 40441dc057d0..ac461f4f0307 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -288,7 +288,7 @@ pub(in crate::io) fn discard_buffer(&mut self) { #[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 ad8608bf6190..6982000c4a02 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 @@ pub fn pos(&self) -> usize { // 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 @@ pub fn unconsume(&mut self, amt: usize) { /// 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 @@ pub fn fill_buf(&mut self, mut reader: impl Read) -> io::Result<&[u8]> { 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 6ad4158b9290..58be268573e6 100644 --- a/library/std/src/io/buffered/tests.rs +++ b/library/std/src/io/buffered/tests.rs @@ -1067,13 +1067,13 @@ fn read(&mut self, buf: &mut [u8]) -> crate::io::Result { } 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 2b558efb8885..0f3f890a964a 100644 --- a/library/std/src/io/copy.rs +++ b/library/std/src/io/copy.rs @@ -214,15 +214,15 @@ fn copy_from(&mut self, reader: &mut R) -> Result { } 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 @@ fn copy_from(&mut self, reader: &mut R) -> Result { 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 @@ fn copy_from(&mut self, reader: &mut R) -> Result { 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 c03da623dc8d..998be377b301 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 @@ fn small_probe_read(r: &mut R, buf: &mut Vec) -> Result(r: &mut R, buf: &mut Vec) -> Result = 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 @@ fn small_probe_read(r: &mut R, buf: &mut Vec) -> Result(r: &mut R, buf: &mut Vec) -> Result(r: &mut R, buf: &mut Vec) -> Result 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); } } @@ -3098,7 +3080,7 @@ fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> Result<()> { // 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 @@ fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> Result<()> { 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 + // SAFETY: filled bytes have been filled buf.advance_unchecked(filled); - // SAFETY: new_init bytes of buf's unfilled buffer have been initialized - buf.set_init(new_init); } self.limit -= filled as u64; diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs index b22988d4a8a9..00e52e6f33e6 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/tests.rs b/library/std/src/io/util/tests.rs index d0f106d7af41..fa1193f2b00f 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 6fcb28edc7d8..33089a941ab4 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -319,6 +319,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 e4a30b80e3df..af15009e665e 100644 --- a/library/std/src/net/tcp/tests.rs +++ b/library/std/src/net/tcp/tests.rs @@ -317,7 +317,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 12c5130defe5..68c62a861075 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()); }; }