Skip to content

Commit 47c5280

Browse files
committed
BufWriter: Make the soundness of BorrowedBuf usage clearer.
The previous safety comment was outdated as it was written before `BorrowedBuf::set_init` was changed to be a boolean. It also failed to address the possibility that `flush_buf` invalidated the assumption. Simplify the implementation of `flush_buf` to more obviously preserve the spare capacity, and document the need to preserve the spare capacity where necessary.
1 parent cf1817b commit 47c5280

3 files changed

Lines changed: 21 additions & 2 deletions

File tree

library/alloc/src/vec/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1784,6 +1784,9 @@ impl<T, A: Allocator> Vec<T, A> {
17841784
/// [`drain`]: Vec::drain
17851785
#[stable(feature = "rust1", since = "1.0.0")]
17861786
pub fn truncate(&mut self, len: usize) {
1787+
// SAFETY: `BufWriter::flush_buf` assumes that this preserves the spare
1788+
// capacity.
1789+
17871790
// This is safe because:
17881791
//
17891792
// * the slice passed to `drop_in_place` is valid; the `len > self.len`
@@ -1857,6 +1860,9 @@ impl<T, A: Allocator> Vec<T, A> {
18571860
#[rustc_diagnostic_item = "vec_as_mut_slice"]
18581861
#[rustc_const_stable(feature = "const_vec_string_slice", since = "1.87.0")]
18591862
pub const fn as_mut_slice(&mut self) -> &mut [T] {
1863+
// SAFETY: `BufWriter::flush_buf` assumes that this preserves the spare
1864+
// capacity.
1865+
18601866
// SAFETY: `slice::from_raw_parts_mut` requires pointee is a contiguous, aligned buffer of
18611867
// size `len` containing properly-initialized `T`s. Data must not be accessed through any
18621868
// other pointer for the returned lifetime. Further, `len * size_of::<T>` <=

library/std/src/io/buffered/bufwriter.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,9 @@ impl<W: ?Sized + Write> BufWriter<W> {
193193
/// `write`), any 0-length writes from `inner` must be reported as i/o
194194
/// errors from this method.
195195
pub(in crate::io) fn flush_buf(&mut self) -> io::Result<()> {
196+
// SAFETY: `<BufWriter as BufferedWriterSpec>::copy_from` assumes this
197+
// preserves `self.buf`'s spare capacity.
198+
196199
/// Helper struct to ensure the buffer is updated after all the writes
197200
/// are complete. It tracks the number of written bytes and drains them
198201
/// all from the front of the buffer when dropped.
@@ -225,7 +228,15 @@ impl<W: ?Sized + Write> BufWriter<W> {
225228
impl Drop for BufGuard<'_> {
226229
fn drop(&mut self) {
227230
if self.written > 0 {
228-
self.buffer.drain(..self.written);
231+
// Like `self.buffer.drain(..self.written)` but more obviously
232+
// preserving the spare capacity; see note above.
233+
let new_len = self.buffer.len() - self.written;
234+
// SAFETY: Assumes `<&mut [u8]>::copy_within` never writes
235+
// outside of the slice, so it preserves the spare capacity.
236+
self.buffer.as_mut_slice().copy_within(self.written.., 0);
237+
// SAFETY: Assumes `Vec::truncate` preserves the spare
238+
// capacity.
239+
self.buffer.truncate(new_len);
229240
}
230241
}
231242
}

library/std/src/io/copy.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,9 @@ impl<I: Write + ?Sized> BufferedWriterSpec for BufWriter<I> {
221221
let mut read_buf: BorrowedBuf<'_> = buf.spare_capacity_mut().into();
222222

223223
if init {
224-
// SAFETY: init is either 0 or the init_len from the previous iteration.
224+
// SAFETY: `init` is only true after `reader` initializes
225+
// `read_buf`. `flush_buf` preserves the spare capacity, so it
226+
// is OK to persist this across `flush_buf` calls.
225227
unsafe { read_buf.set_init() };
226228
}
227229

0 commit comments

Comments
 (0)