Skip to content

Commit 45459a3

Browse files
committed
BufWriter: Note non-obvious safety assumption in BorrowedBuf::set_init usage.
1 parent 12f35ad commit 45459a3

4 files changed

Lines changed: 20 additions & 1 deletion

File tree

library/alloc/src/raw_vec/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ const unsafe fn new_cap<T>(cap: usize) -> Cap {
7171
/// `Box<[T]>`, since `capacity()` won't yield the length.
7272
#[allow(missing_debug_implementations)]
7373
pub(crate) struct RawVec<T, A: Allocator = Global> {
74+
// FIXME: Despite "Its uninitialized memory is scratch space that it may use however it wants"
75+
// in `Vec`'s documentation, `BufWriter::flush_buf` relies on the scratch space being never
76+
// de-initialized by several methods.
7477
inner: RawVecInner<A>,
7578
_marker: PhantomData<T>,
7679
}
@@ -83,6 +86,9 @@ pub(crate) struct RawVec<T, A: Allocator = Global> {
8386
/// as most operations don't need the actual type, just its layout.
8487
#[allow(missing_debug_implementations)]
8588
struct RawVecInner<A: Allocator = Global> {
89+
// FIXME: Despite "Its uninitialized memory is scratch space that it may use however it wants"
90+
// in `Vec`'s documentation, `BufWriter::flush_buf` relies on the scratch space being never
91+
// de-initialized by several methods.
8692
ptr: Unique<u8>,
8793
/// Never used for ZSTs; it's `capacity()`'s responsibility to return usize::MAX in that case.
8894
///

library/alloc/src/vec/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,9 @@ mod spec_extend;
436436
#[doc(alias = "list")]
437437
#[doc(alias = "vector")]
438438
pub struct Vec<T, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global> {
439+
// FIXME: Despite "Its uninitialized memory is scratch space that it may use however it wants"
440+
// mentioned above, `BufWriter::flush_buf` relies on the sctach space being never
441+
// de-initialized by several methods.
439442
buf: RawVec<T, A>,
440443
len: usize,
441444
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,13 @@ 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` requires that `self.buf`'s spare
197+
// capacity is preserved by this function. This function does not grow `self.buf` or
198+
// explicitly shrink its capacity; `Vec` guarantees that this is sufficient to avoid it
199+
// re-allocating. We never de-initialize the spare capacity of `self.buf` and we assume none
200+
// of the `Vec` methods used here will do so either, though nothing in `Vec`'s documentation
201+
// guarantees this.
202+
196203
/// Helper struct to ensure the buffer is updated after all the writes
197204
/// are complete. It tracks the number of written bytes and drains them
198205
/// all from the front of the buffer when dropped.

library/std/src/io/copy.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,10 @@ 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 `read_buf`. `flush_buf`
225+
// guarantees that it won't cause any part of the spare capacity to become
226+
// uninitialized or cause `self.buf` to reallocate, so it is OK to persist this
227+
// across `flush_buf` calls.
225228
unsafe { read_buf.set_init() };
226229
}
227230

0 commit comments

Comments
 (0)