Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
496 changes: 496 additions & 0 deletions library/alloc/src/io/buf_read.rs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
mod buffer;

use buffer::Buffer;
pub(super) use buffer::Buffer;

use crate::fmt;
use crate::io::{
self, BorrowedCursor, BufRead, DEFAULT_BUF_SIZE, IoSliceMut, Read, Seek, SeekFrom, SizeHint,
SpecReadByte, uninlined_slow_read_byte,
};
use crate::string::String;
use crate::vec::Vec;

/// The `BufReader<R>` struct adds buffering to any reader.
///
Expand All @@ -27,8 +29,9 @@ use crate::io::{
/// unwrapping the `BufReader<R>` with [`BufReader::into_inner`] can also cause
/// data loss.
///
/// [`TcpStream::read`]: crate::net::TcpStream::read
/// [`TcpStream`]: crate::net::TcpStream
// FIXME(#74481): Hard-links required to link from `alloc` to `std`
/// [`TcpStream::read`]: ../../std/net/struct.TcpStream.html#method.read
/// [`TcpStream`]: ../../std/net/struct.TcpStream.html
///
/// # Examples
///
Expand Down Expand Up @@ -70,16 +73,21 @@ impl<R: Read> BufReader<R> {
/// Ok(())
/// }
/// ```
#[cfg(not(no_global_oom_handling))]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn new(inner: R) -> BufReader<R> {
BufReader::with_capacity(DEFAULT_BUF_SIZE, inner)
}

pub(crate) fn try_new_buffer() -> io::Result<Buffer> {
#[doc(hidden)]
#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
pub fn try_new_buffer() -> io::Result<Buffer> {
Buffer::try_with_capacity(DEFAULT_BUF_SIZE)
}

pub(crate) fn with_buffer(inner: R, buf: Buffer) -> Self {
#[doc(hidden)]
#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
pub fn with_buffer(inner: R, buf: Buffer) -> Self {
Self { inner, buf }
}

Expand All @@ -99,6 +107,7 @@ impl<R: Read> BufReader<R> {
/// Ok(())
/// }
/// ```
#[cfg(not(no_global_oom_handling))]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn with_capacity(capacity: usize, inner: R) -> BufReader<R> {
BufReader { inner, buf: Buffer::with_capacity(capacity) }
Expand Down Expand Up @@ -280,14 +289,17 @@ impl<R: ?Sized> BufReader<R> {

/// Invalidates all data in the internal buffer.
#[inline]
pub(in crate::io) fn discard_buffer(&mut self) {
#[doc(hidden)]
#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
pub fn discard_buffer(&mut self) {
self.buf.discard_buffer()
}
}

// This is only used by a test which asserts that the initialization-tracking is correct.
#[cfg(test)]
impl<R: ?Sized> BufReader<R> {
#[doc(hidden)]
#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
#[allow(missing_docs)]
pub fn initialized(&self) -> bool {
self.buf.initialized()
Expand Down Expand Up @@ -318,6 +330,8 @@ impl<R: ?Sized + Seek> BufReader<R> {
}
}

#[doc(hidden)]
#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
impl<R> SpecReadByte for BufReader<R>
where
Self: Read,
Expand Down Expand Up @@ -411,7 +425,28 @@ impl<R: ?Sized + Read> Read for BufReader<R> {
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
let inner_buf = self.buffer();
buf.try_reserve(inner_buf.len())?;
buf.extend_from_slice(inner_buf);

cfg_select! {
no_global_oom_handling => {
// SAFETY:
// * inner_buf and buf are non-overlapping
// * buf[..len] is already initialized
// * buf[len..len + count] is initialized by copy_nonoverlapping
// * len + count is within the capacity of buf based on the reservation completed above
unsafe {
let count = inner_buf.len();
let len = buf.len();
let src = inner_buf.as_ptr();
let dst = buf.as_mut_ptr().add(len);
core::ptr::copy_nonoverlapping(src, dst, count);
buf.set_len(len + count);
}
}
_ => {
buf.extend_from_slice(inner_buf);
}
}

let nread = inner_buf.len();
self.discard_buffer();
Ok(nread + self.inner.read_to_end(buf)?)
Expand Down Expand Up @@ -443,7 +478,31 @@ impl<R: ?Sized + Read> Read for BufReader<R> {
let mut bytes = Vec::new();
self.read_to_end(&mut bytes)?;
let string = crate::str::from_utf8(&bytes).map_err(|_| io::Error::INVALID_UTF8)?;
*buf += string;

cfg_select! {
no_global_oom_handling => {
// SAFETY:
// * string and buf are non-overlapping
// * buf[..len] is already initialized
// * buf[len..len + count] is initialized by copy_nonoverlapping
// * len + count is within the capacity of buf based on the reservation completed above
// * buf is appended with valid UTF-8 data and is initially valid UTF-8, therefore
// it is valid UTF-8 at all times.
unsafe {
let buf = buf.as_mut_vec();
let count = string.len();
let len = buf.len();
let src = string.as_ptr();
let dst = buf.as_mut_ptr().add(len);
core::ptr::copy_nonoverlapping(src, dst, count);
buf.set_len(len + count);
}
}
_ => {
*buf += string;
}
}

Ok(string.len())
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,15 @@
//! that user code which wants to do reads from a `BufReader` via `buffer` + `consume` can do so
//! without encountering any runtime bounds checks.

use crate::cmp;
use core::cmp;
use core::mem::MaybeUninit;

use crate::boxed::Box;
use crate::io::{self, BorrowedBuf, ErrorKind, Read};
use crate::mem::MaybeUninit;

#[doc(hidden)]
#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
#[derive(Debug)]
pub struct Buffer {
// The buffer.
buf: Box<[MaybeUninit<u8>]>,
Expand All @@ -29,14 +34,15 @@ pub struct Buffer {
}

impl Buffer {
#[cfg(not(no_global_oom_handling))]
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
pub(super) fn with_capacity(capacity: usize) -> Self {
let buf = Box::new_uninit_slice(capacity);
Self { buf, pos: 0, filled: 0, initialized: false }
}

#[inline]
pub fn try_with_capacity(capacity: usize) -> io::Result<Self> {
pub(super) fn try_with_capacity(capacity: usize) -> io::Result<Self> {
match Box::try_new_uninit_slice(capacity) {
Ok(buf) => Ok(Self { buf, pos: 0, filled: 0, initialized: false }),
Err(_) => {
Expand All @@ -46,48 +52,47 @@ impl Buffer {
}

#[inline]
pub fn buffer(&self) -> &[u8] {
pub(super) fn buffer(&self) -> &[u8] {
// SAFETY: self.pos and self.filled are valid, and self.filled >= self.pos, and
// that region is initialized because those are all invariants of this type.
unsafe { self.buf.get_unchecked(self.pos..self.filled).assume_init_ref() }
}

#[inline]
pub fn capacity(&self) -> usize {
pub(super) fn capacity(&self) -> usize {
self.buf.len()
}

#[inline]
pub fn filled(&self) -> usize {
pub(super) fn filled(&self) -> usize {
self.filled
}

#[inline]
pub fn pos(&self) -> usize {
pub(super) fn pos(&self) -> usize {
self.pos
}

// This is only used by a test which asserts that the initialization-tracking is correct.
#[cfg(test)]
pub fn initialized(&self) -> bool {
pub(super) fn initialized(&self) -> bool {
self.initialized
}

#[inline]
pub fn discard_buffer(&mut self) {
pub(super) fn discard_buffer(&mut self) {
self.pos = 0;
self.filled = 0;
}

#[inline]
pub fn consume(&mut self, amt: usize) {
pub(super) fn consume(&mut self, amt: usize) {
self.pos = cmp::min(self.pos + amt, self.filled);
}

/// If there are `amt` bytes available in the buffer, pass a slice containing those bytes to
/// `visitor` and return true. If there are not enough bytes available, return false.
#[inline]
pub fn consume_with<V>(&mut self, amt: usize, mut visitor: V) -> bool
pub(super) fn consume_with<V>(&mut self, amt: usize, mut visitor: V) -> bool
where
V: FnMut(&[u8]),
{
Expand All @@ -102,12 +107,12 @@ impl Buffer {
}

#[inline]
pub fn unconsume(&mut self, amt: usize) {
pub(super) fn unconsume(&mut self, amt: usize) {
self.pos = self.pos.saturating_sub(amt);
}

/// Read more bytes into the buffer without discarding any of its contents
pub fn read_more(&mut self, mut reader: impl Read) -> io::Result<usize> {
pub(super) fn read_more(&mut self, mut reader: impl Read) -> io::Result<usize> {
let mut buf = BorrowedBuf::from(&mut self.buf[self.filled..]);

if self.initialized {
Expand All @@ -124,14 +129,14 @@ impl Buffer {
}

/// Remove bytes that have already been read from the buffer.
pub fn backshift(&mut self) {
pub(super) fn backshift(&mut self) {
self.buf.copy_within(self.pos..self.filled, 0);
self.filled -= self.pos;
self.pos = 0;
}

#[inline]
pub fn fill_buf(&mut self, mut reader: impl Read) -> io::Result<&[u8]> {
pub(super) fn fill_buf(&mut self, mut reader: impl Read) -> io::Result<&[u8]> {
// If we've reached the end of our internal buffer then we need to fetch
// some more data from the reader.
// Branch using `>=` instead of the more correct `==`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use core::mem::{self, ManuallyDrop};
use core::{error, fmt, ptr};

use crate::io::{
self, DEFAULT_BUF_SIZE, ErrorKind, IntoInnerError, IoSlice, Seek, SeekFrom, Write,
};
use crate::mem::{self, ManuallyDrop};
use crate::{error, fmt, ptr};
use crate::vec::Vec;

/// Wraps a writer and buffers its output.
///
Expand Down Expand Up @@ -60,8 +62,9 @@ use crate::{error, fmt, ptr};
/// together by the buffer and will all be written out in one system call when
/// the `stream` is flushed.
///
/// [`TcpStream::write`]: crate::net::TcpStream::write
/// [`TcpStream`]: crate::net::TcpStream
// FIXME(#74481): Hard-links required to link from `alloc` to `std`
/// [`TcpStream::write`]: ../../std/net/struct.TcpStream.html#method.write
/// [`TcpStream`]: ../../std/net/struct.TcpStream.html
/// [`flush`]: BufWriter::flush
#[stable(feature = "rust1", since = "1.0.0")]
pub struct BufWriter<W: ?Sized + Write> {
Expand All @@ -87,20 +90,26 @@ impl<W: Write> BufWriter<W> {
/// use std::io::BufWriter;
/// use std::net::TcpStream;
///
/// # #[expect(unused_mut)]
/// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
/// ```
#[cfg(not(no_global_oom_handling))]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn new(inner: W) -> BufWriter<W> {
BufWriter::with_capacity(DEFAULT_BUF_SIZE, inner)
}

pub(crate) fn try_new_buffer() -> io::Result<Vec<u8>> {
#[doc(hidden)]
#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
pub fn try_new_buffer() -> io::Result<Vec<u8>> {
Vec::try_with_capacity(DEFAULT_BUF_SIZE).map_err(|_| {
io::const_error!(ErrorKind::OutOfMemory, "failed to allocate write buffer")
})
}

pub(crate) fn with_buffer(inner: W, buf: Vec<u8>) -> Self {
#[doc(hidden)]
#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
pub fn with_buffer(inner: W, buf: Vec<u8>) -> Self {
Self { inner, buf, panicked: false }
}

Expand All @@ -115,8 +124,10 @@ impl<W: Write> BufWriter<W> {
/// use std::net::TcpStream;
///
/// let stream = TcpStream::connect("127.0.0.1:34254").unwrap();
/// # #[expect(unused_mut)]
/// let mut buffer = BufWriter::with_capacity(100, stream);
/// ```
#[cfg(not(no_global_oom_handling))]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn with_capacity(capacity: usize, inner: W) -> BufWriter<W> {
BufWriter { inner, buf: Vec::with_capacity(capacity), panicked: false }
Expand All @@ -136,6 +147,7 @@ impl<W: Write> BufWriter<W> {
/// use std::io::BufWriter;
/// use std::net::TcpStream;
///
/// # #[expect(unused_mut)]
/// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
///
/// // unwrap the TcpStream and flush the buffer
Expand Down Expand Up @@ -192,7 +204,9 @@ impl<W: ?Sized + Write> BufWriter<W> {
/// "successfully written" (by returning nonzero success values from
/// `write`), any 0-length writes from `inner` must be reported as i/o
/// errors from this method.
pub(in crate::io) fn flush_buf(&mut self) -> io::Result<()> {
#[doc(hidden)]
#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
pub fn flush_buf(&mut self) -> io::Result<()> {
// SAFETY: `<BufWriter as BufferedWriterSpec>::copy_from` assumes that
// this will not de-initialize any elements of `self.buf`'s spare
// capacity.
Expand Down Expand Up @@ -287,6 +301,7 @@ impl<W: ?Sized + Write> BufWriter<W> {
/// use std::io::BufWriter;
/// use std::net::TcpStream;
///
/// # #[expect(unused_mut)]
/// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
///
/// // we can use reference just like buffer
Expand Down Expand Up @@ -343,7 +358,9 @@ impl<W: ?Sized + Write> BufWriter<W> {
/// That the buffer is a `Vec` is an implementation detail.
/// Callers should not modify the capacity as there currently is no public API to do so
/// and thus any capacity changes would be unexpected by the user.
pub(in crate::io) fn buffer_mut(&mut self) -> &mut Vec<u8> {
#[doc(hidden)]
#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
pub fn buffer_mut(&mut self) -> &mut Vec<u8> {
&mut self.buf
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ impl<W: Write> LineWriter<W> {
/// Ok(())
/// }
/// ```
#[cfg(not(no_global_oom_handling))]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn new(inner: W) -> LineWriter<W> {
// Lines typically aren't that long, don't use a giant buffer
Expand All @@ -105,6 +106,7 @@ impl<W: Write> LineWriter<W> {
/// Ok(())
/// }
/// ```
#[cfg(not(no_global_oom_handling))]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn with_capacity(capacity: usize, inner: W) -> LineWriter<W> {
LineWriter { inner: BufWriter::with_capacity(capacity, inner) }
Expand Down
Loading
Loading