Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ 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,17 +73,20 @@ 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> {
Buffer::try_with_capacity(DEFAULT_BUF_SIZE)
}

pub(crate) fn with_buffer(inner: R, buf: Buffer) -> Self {
Self { inner, buf }
/// Attempts to allocate an internal buffer, _then_ calls the provided function
/// to retrieve the inner reader `R`.
#[doc(hidden)]
#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
pub fn try_new_with(f: impl FnOnce() -> io::Result<R>) -> io::Result<Self> {
let buf = Buffer::try_with_capacity(DEFAULT_BUF_SIZE)?;
let inner = f()?;
Ok(Self { inner, buf })
}

/// Creates a new `BufReader<R>` with the specified buffer capacity.
Expand All @@ -99,6 +105,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 +287,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 @@ -413,7 +423,16 @@ 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 => {
buf.try_extend_from_slice_of_bytes(inner_buf)?;
}
_ => {
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 @@ -445,7 +464,16 @@ 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 => {
buf.try_push_str(string)?;
}
_ => {
buf.push_str(string);
}
}

Ok(string.len())
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@
//! 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;

pub struct Buffer {
pub(super) struct Buffer {
// The buffer.
buf: Box<[MaybeUninit<u8>]>,
// The current seek offset into `buf`, must always be <= `filled`.
Expand All @@ -29,14 +31,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 +49,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 +104,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 +126,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,21 +90,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());
/// ```
#[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>> {
Vec::try_with_capacity(DEFAULT_BUF_SIZE).map_err(|_| {
/// Attempts to allocate an internal buffer, _then_ calls the provided function
/// to retrieve the inner writer `W`.
#[doc(hidden)]
#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
pub fn try_new_with(f: impl FnOnce() -> io::Result<W>) -> io::Result<Self> {
let buf = 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 {
Self { inner, buf, panicked: false }
})?;
let inner = f()?;
Ok(Self { inner, buf, panicked: false })
}

/// Creates a new `BufWriter<W>` with at least the specified buffer capacity.
Expand All @@ -115,8 +122,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 +145,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 +202,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 +299,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 +356,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
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ use crate::io::{self, BufWriter, IoSlice, Write};
/// `BufWriters` to be temporarily given line-buffering logic; this is what
/// enables Stdout to be alternately in line-buffered or block-buffered mode.
#[derive(Debug)]
pub struct LineWriterShim<'a, W: ?Sized + Write> {
pub(super) struct LineWriterShim<'a, W: ?Sized + Write> {
buffer: &'a mut BufWriter<W>,
}

impl<'a, W: ?Sized + Write> LineWriterShim<'a, W> {
pub fn new(buffer: &'a mut BufWriter<W>) -> Self {
pub(super) fn new(buffer: &'a mut BufWriter<W>) -> Self {
Self { buffer }
}

Expand Down
Loading
Loading