Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
5f65064
Make `std::sys::io::io_slice` compatible with `core`
bushrat011899 May 13, 2026
90baad8
Move `std::io::IoSlice(Mut)` to `core::io`
bushrat011899 May 13, 2026
f5e861f
Move `IoSlice(Mut)` tests from `std` to `coretests`
bushrat011899 May 13, 2026
d42c603
Adjust `std::io::Error` tests to only assess public API
bushrat011899 Apr 22, 2026
e502783
Adjust `Error` documentation
bushrat011899 Apr 22, 2026
5c965e2
Change `repr` module definition to use `path` attribute
bushrat011899 May 18, 2026
c451cd7
Setup `alloc::io`
bushrat011899 Apr 22, 2026
c251787
Make common `Error` constants public and hidden
bushrat011899 May 18, 2026
d39934f
Allow use of incoherence
bushrat011899 May 18, 2026
8c7cdbb
Make `Error::is_interrupted` public and hidden
bushrat011899 May 18, 2026
14f2cf5
Remove usage of `Box` from `Error` internals
bushrat011899 May 18, 2026
4e3015d
Delegate `RawOsError` functionality to `OsFunctions`
bushrat011899 May 18, 2026
ae2715d
Move `std::io::Error` and friends into `core` and `alloc`
bushrat011899 May 18, 2026
16a2094
Reduce usage of unstable public items
bushrat011899 May 18, 2026
3ce7e32
Simplify `core::io` documentation links
bushrat011899 May 18, 2026
ccb4dd6
Move `std::io::IoHandle` to `core::io`
bushrat011899 May 12, 2026
079018c
Move `std::io::SizeHint` to `core::io`
bushrat011899 May 12, 2026
22d466c
Move `std::io::SeekFrom` to `core::io`
bushrat011899 May 21, 2026
6e3b1f4
Move `std::io::Seek` to `core::io`
bushrat011899 May 21, 2026
be9ec82
Add `std::io::cursor::WriteThroughCursor`
bushrat011899 May 12, 2026
65eee2c
Move `std::io::default_write_vectored` to `core::io`
bushrat011899 May 21, 2026
93b399a
Move `std::io::cursor` default `Write` methods to `core::io`
bushrat011899 May 21, 2026
d33ed84
Move `std::io::Write` to `core::io`
bushrat011899 May 21, 2026
a8a3fa2
Move `std::io::Read` internals to `alloc::io`
bushrat011899 May 21, 2026
60a6005
Move `std::io::append_to_string` to `alloc::io`
bushrat011899 May 21, 2026
eee9394
Move `std::io::Read` to `alloc::io`
bushrat011899 May 21, 2026
6ff2491
Move `std::io::read_to_string` to `alloc::io`
bushrat011899 May 12, 2026
e76099c
Move `std::io::BufRead` to `alloc::io`
bushrat011899 May 21, 2026
b15cb7b
Move `std::io::buffered` to `alloc::io`
bushrat011899 May 12, 2026
0970a09
Move `std::io::copy` internals to `alloc::io`
bushrat011899 May 12, 2026
7dd0680
Move general IO tests to `alloctests`
bushrat011899 May 21, 2026
8b32e91
Move `std::io::util` tests to `alloctests`
bushrat011899 May 21, 2026
b4fe0a4
Move `std::io::cursor` tests to `alloctests`
bushrat011899 May 21, 2026
bb6879a
Setup `core::io::prelude`
bushrat011899 May 21, 2026
eba62e7
Setup `alloc::io::prelude`
bushrat011899 May 21, 2026
564f3c8
Fix documentation links to `Write`
bushrat011899 May 22, 2026
3027557
Fix documentation links to `Seek`
bushrat011899 May 22, 2026
4eecaf2
Fix documentation links to `BufRead`
bushrat011899 May 22, 2026
4d33dcc
Fix documentation links to `BufReader`
bushrat011899 May 22, 2026
24a44c3
Reduce visibility of unstable internal items
bushrat011899 May 22, 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,8 @@ 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
/// [`TcpStream::read`]: ../../std/net/struct.TcpStream.html#method.read
/// [`TcpStream`]: ../../std/net/struct.TcpStream.html
///
/// # Examples
///
Expand Down Expand Up @@ -70,16 +72,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 +106,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 +288,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 +329,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 +424,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 +477,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 +638,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,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;

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

impl Buffer {
#[cfg(not(no_global_oom_handling))]
#[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 Down Expand Up @@ -68,7 +74,8 @@ impl Buffer {
}

// 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
}
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,8 @@ 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
/// [`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 +89,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 +123,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 +146,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 +203,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 +300,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 +357,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