Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
945823e
Adjust `std::io::Error` tests to only assess public API
bushrat011899 Apr 22, 2026
60bac02
Adjust `Error` documentation
bushrat011899 Apr 22, 2026
c651716
Change `repr` module definition to use `path` attribute
bushrat011899 May 18, 2026
1f0ccc7
Setup `alloc::io`
bushrat011899 Apr 22, 2026
53eba48
Make common `Error` constants public and hidden
bushrat011899 May 18, 2026
0092197
Allow use of incoherence
bushrat011899 May 18, 2026
9e42475
Make `Error::is_interrupted` public and hidden
bushrat011899 May 18, 2026
b1b790f
Remove usage of `Box` from `Error` internals
bushrat011899 May 18, 2026
b658c07
Delegate `RawOsError` functionality to `OsFunctions`
bushrat011899 May 18, 2026
6960ba3
Move `std::io::Error` and friends into `core` and `alloc`
bushrat011899 May 18, 2026
d593f3b
Reduce usage of unstable public items
bushrat011899 May 18, 2026
196072b
Simplify `core::io` documentation links
bushrat011899 May 18, 2026
cb843c4
Move `std::io::IoHandle` to `core::io`
bushrat011899 May 12, 2026
c7c595e
Move `std::io::SizeHint` to `core::io`
bushrat011899 May 12, 2026
8539914
Move `std::io::SeekFrom` to `core::io`
bushrat011899 May 21, 2026
3912cb1
Move `std::io::Seek` to `core::io`
bushrat011899 May 21, 2026
12dbdd5
Fix documentation links to `Seek`
bushrat011899 May 22, 2026
737b94f
Add `std::io::cursor::WriteThroughCursor`
bushrat011899 May 12, 2026
ccf3201
Move `std::io::default_write_vectored` to `core::io`
bushrat011899 May 21, 2026
b11844b
Move `std::io::cursor::slice_write` to `core::io`
bushrat011899 May 22, 2026
eb72e2a
Move `std::io::cursor::slice_write_vectored` to `core::io`
bushrat011899 May 22, 2026
0d118fb
Move `std::io::cursor::slice_write_all` to `core::io`
bushrat011899 May 22, 2026
861e21c
Move `std::io::cursor::slice_write_all_vectored` to `core::io`
bushrat011899 May 22, 2026
4f544a3
Move `std::io::Write` to `core::io`
bushrat011899 May 21, 2026
eb62ff9
Fix documentation links to `Write`
bushrat011899 May 22, 2026
b6e7141
Move `std::io::Read` internals to `alloc::io`
bushrat011899 May 21, 2026
e2b3594
Move `std::io::append_to_string` to `alloc::io`
bushrat011899 May 21, 2026
a221f45
Move `std::io::Read` to `alloc::io`
bushrat011899 May 21, 2026
aeee071
Move `std::io::read_to_string` to `alloc::io`
bushrat011899 May 12, 2026
3473829
Move `std::io::BufRead` to `alloc::io`
bushrat011899 May 21, 2026
12d0c90
Fix documentation links to `BufRead`
bushrat011899 May 22, 2026
304c07e
Move `std::io::buffered` to `alloc::io`
bushrat011899 May 12, 2026
8ce22e7
Move `std::io::copy` internals to `alloc::io`
bushrat011899 May 12, 2026
aaace09
Setup `core::io::prelude`
bushrat011899 May 21, 2026
f514aa4
Setup `alloc::io::prelude`
bushrat011899 May 21, 2026
93b8200
Reduce visibility of unstable internal items
bushrat011899 May 22, 2026
bc86e9f
Move general IO tests to `alloctests`
bushrat011899 May 21, 2026
59ccc3b
Move `std::io::util` tests to `alloctests`
bushrat011899 May 21, 2026
3a222b1
Move `std::io::cursor` tests to `alloctests`
bushrat011899 May 21, 2026
9294307
Move `std::io::buffered` tests to `alloctests`
bushrat011899 May 21, 2026
331e90b
Move `std::io::impls` benchmarks to `alloctests`
bushrat011899 May 25, 2026
df079fb
Reduce API surface of `Buffer`
bushrat011899 May 25, 2026
e5621e9
Note where and why documentation hard-links are used
bushrat011899 May 25, 2026
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
Comment thread
bushrat011899 marked this conversation as resolved.
/// [`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! {
Comment thread
bushrat011899 marked this conversation as resolved.
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 Expand Up @@ -580,6 +639,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,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)]
Comment thread
bushrat011899 marked this conversation as resolved.
/// 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
Loading
Loading