Skip to content
Draft
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
478 changes: 478 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,17 +1,21 @@
mod buffer;

use buffer::Buffer;
#[doc(hidden)]
#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
pub 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.
///
/// It can be excessively inefficient to work directly with a [`Read`] instance.
/// For example, every call to [`read`][`TcpStream::read`] on [`TcpStream`]
/// For example, every call to `read` on `TcpStream`
/// results in a system call. A `BufReader<R>` performs large, infrequent reads on
/// the underlying [`Read`] and maintains an in-memory buffer of the results.
///
Expand All @@ -27,9 +31,6 @@ 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
///
/// # Examples
///
/// ```no_run
Expand Down Expand Up @@ -74,11 +75,15 @@ impl<R: Read> 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 Down Expand Up @@ -279,14 +284,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 @@ -317,6 +325,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 @@ -579,6 +589,8 @@ impl<R: ?Sized + Seek> Seek for BufReader<R> {
}
}

#[doc(hidden)]
#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
impl<T: ?Sized> SizeHint for BufReader<T> {
#[inline]
fn lower_bound(&self) -> usize {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,14 @@
//! 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;

#[expect(missing_docs)]
#[expect(missing_debug_implementations)]
pub struct Buffer {
// The buffer.
buf: Box<[MaybeUninit<u8>]>,
Expand All @@ -29,12 +33,15 @@ pub struct Buffer {
}

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

#[doc(hidden)]
#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
#[inline]
pub fn try_with_capacity(capacity: usize) -> io::Result<Self> {
match Box::try_new_uninit_slice(capacity) {
Expand All @@ -45,40 +52,47 @@ impl Buffer {
}
}

#[expect(missing_docs)]
#[inline]
pub 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() }
}

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

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

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

// This is only used by a test which asserts that the initialization-tracking is correct.
#[cfg(test)]
#[doc(hidden)]
#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
pub fn initialized(&self) -> bool {
self.initialized
}

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

#[expect(missing_docs)]
#[inline]
pub fn consume(&mut self, amt: usize) {
self.pos = cmp::min(self.pos + amt, self.filled);
Expand All @@ -101,6 +115,7 @@ impl Buffer {
}
}

#[expect(missing_docs)]
#[inline]
pub fn unconsume(&mut self, amt: usize) {
self.pos = self.pos.saturating_sub(amt);
Expand Down Expand Up @@ -130,6 +145,7 @@ impl Buffer {
self.pos = 0;
}

#[expect(missing_docs)]
#[inline]
pub 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
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
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.
///
/// It can be excessively inefficient to work directly with something that
/// implements [`Write`]. For example, every call to
/// [`write`][`TcpStream::write`] on [`TcpStream`] results in a system call. A
/// `write` on `TcpStream` results in a system call. A
/// `BufWriter<W>` keeps an in-memory buffer of data and writes it to an underlying
/// writer in large, infrequent batches.
///
Expand All @@ -26,7 +28,7 @@ use crate::{error, fmt, ptr};
///
/// # Examples
///
/// Let's write the numbers one through ten to a [`TcpStream`]:
/// Let's write the numbers one through ten to a `TcpStream`:
///
/// ```no_run
/// use std::io::prelude::*;
Expand Down Expand Up @@ -60,8 +62,6 @@ 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
/// [`flush`]: BufWriter::flush
#[stable(feature = "rust1", since = "1.0.0")]
pub struct BufWriter<W: ?Sized + Write> {
Expand All @@ -87,20 +87,25 @@ 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());
/// ```
#[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,6 +120,7 @@ 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);
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
Expand All @@ -136,6 +142,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 +199,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 +296,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 +353,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 @@ -12,6 +12,8 @@ use crate::io::{self, BufWriter, IoSlice, Write};
/// implementation details of BufWriter. This also allows existing
/// `BufWriters` to be temporarily given line-buffering logic; this is what
/// enables Stdout to be alternately in line-buffered or block-buffered mode.
#[doc(hidden)]
#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
#[derive(Debug)]
pub struct LineWriterShim<'a, W: ?Sized + Write> {
buffer: &'a mut BufWriter<W>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,19 @@ mod bufwriter;
mod linewriter;
mod linewritershim;

#[cfg(test)]
mod tests;
use core::{error, fmt};

#[doc(hidden)]
#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
pub use self::bufreader::Buffer;
#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
pub use bufwriter::WriterPanicked;
use linewritershim::LineWriterShim;

pub use self::bufwriter::WriterPanicked;
#[doc(hidden)]
#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
pub use self::linewritershim::LineWriterShim;
#[stable(feature = "rust1", since = "1.0.0")]
pub use self::{bufreader::BufReader, bufwriter::BufWriter, linewriter::LineWriter};
use crate::io::Error;
use crate::{error, fmt};

/// An error returned by [`BufWriter::into_inner`] which combines an error that
/// happened while writing out the buffer, and the buffered writer object
Expand All @@ -27,6 +29,7 @@ use crate::{error, fmt};
/// use std::io::BufWriter;
/// use std::net::TcpStream;
///
/// # #[expect(unused_mut)]
/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
///
/// // do stuff with the stream
Expand Down Expand Up @@ -69,6 +72,7 @@ impl<W> IntoInnerError<W> {
/// use std::io::BufWriter;
/// use std::net::TcpStream;
///
/// # #[expect(unused_mut)]
/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
///
/// // do stuff with the stream
Expand Down Expand Up @@ -103,6 +107,7 @@ impl<W> IntoInnerError<W> {
/// use std::io::BufWriter;
/// use std::net::TcpStream;
///
/// # #[expect(unused_mut)]
/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
///
/// // do stuff with the stream
Expand Down
Loading
Loading