Skip to content

Commit 02a281f

Browse files
committed
optimize default read_to_end implementation
1 parent 9671eef commit 02a281f

File tree

1 file changed

+103
-9
lines changed

1 file changed

+103
-9
lines changed

src/lib.rs

Lines changed: 103 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
#![cfg_attr(not(doc), no_std)]
44
#![feature(doc_auto_cfg)]
5+
#![feature(maybe_uninit_slice)]
56

67
#[cfg(feature = "alloc")]
78
extern crate alloc;
@@ -22,6 +23,107 @@ use alloc::{string::String, vec::Vec};
2223

2324
use axerrno::ax_err;
2425

26+
/// Default [`Read::read_to_end`] implementation with optional size hint.
27+
///
28+
/// Adapted from [`std::io::default_read_to_end`].
29+
///
30+
/// [`std::io::default_read_to_end`]: https://github.com/rust-lang/rust/blob/30f168ef811aec63124eac677e14699baa9395bd/library/std/src/io/mod.rs#L409
31+
#[cfg(feature = "alloc")]
32+
pub fn default_read_to_end<R: Read + ?Sized>(
33+
r: &mut R,
34+
buf: &mut Vec<u8>,
35+
size_hint: Option<usize>,
36+
) -> Result<usize> {
37+
const DEFAULT_BUF_SIZE: usize = 1024;
38+
39+
let start_len = buf.len();
40+
let start_cap = buf.capacity();
41+
// Optionally limit the maximum bytes read on each iteration.
42+
// This adds an arbitrary fiddle factor to allow for more data than we expect.
43+
let mut max_read_size = size_hint
44+
.and_then(|s| {
45+
s.checked_add(1024)?
46+
.checked_next_multiple_of(DEFAULT_BUF_SIZE)
47+
})
48+
.unwrap_or(DEFAULT_BUF_SIZE);
49+
50+
const PROBE_SIZE: usize = 32;
51+
52+
fn small_probe_read<R: Read + ?Sized>(r: &mut R, buf: &mut Vec<u8>) -> Result<usize> {
53+
let mut probe = [0u8; PROBE_SIZE];
54+
55+
let n = r.read(&mut probe)?;
56+
buf.extend_from_slice(&probe[..n]);
57+
Ok(n)
58+
}
59+
60+
if (size_hint.is_none() || size_hint == Some(0)) && buf.capacity() - buf.len() < PROBE_SIZE {
61+
let read = small_probe_read(r, buf)?;
62+
63+
if read == 0 {
64+
return Ok(0);
65+
}
66+
}
67+
68+
let mut initialized = 0; // Extra initialized bytes from previous loop iteration
69+
70+
loop {
71+
if buf.len() == buf.capacity() && buf.capacity() == start_cap {
72+
// The buffer might be an exact fit. Let's read into a probe buffer
73+
// and see if it returns `Ok(0)`. If so, we've avoided an
74+
// unnecessary doubling of the capacity. But if not, append the
75+
// probe buffer to the primary buffer and let its capacity grow.
76+
let read = small_probe_read(r, buf)?;
77+
78+
if read == 0 {
79+
return Ok(buf.len() - start_len);
80+
}
81+
}
82+
83+
if buf.len() == buf.capacity() {
84+
// buf is full, need more space
85+
if let Err(e) = buf.try_reserve(PROBE_SIZE) {
86+
return ax_err!(NoMemory, e);
87+
}
88+
}
89+
90+
let mut spare = buf.spare_capacity_mut();
91+
let buf_len = spare.len().min(max_read_size);
92+
spare = &mut spare[..buf_len];
93+
94+
let uninit = &mut spare[initialized..];
95+
// SAFETY: 0 is a valid value for MaybeUninit<u8> and the length matches the allocation
96+
// since it is comes from a slice reference.
97+
unsafe {
98+
core::ptr::write_bytes(uninit.as_mut_ptr(), 0, uninit.len());
99+
}
100+
101+
// SAFETY: spare is fully initialized
102+
let unfilled = unsafe { spare.assume_init_mut() };
103+
104+
let bytes_read = r.read(unfilled)?;
105+
// SAFETY: bytes_read is guaranteed to be less than or equal to spare.len()
106+
unsafe {
107+
let new_len = buf.len() + bytes_read;
108+
buf.set_len(new_len);
109+
}
110+
111+
if bytes_read == 0 {
112+
return Ok(buf.len() - start_len);
113+
}
114+
115+
initialized = buf_len - bytes_read;
116+
117+
if size_hint.is_none() {
118+
// we have passed a larger buffer than previously and the
119+
// reader still hasn't returned a short read
120+
if buf_len >= max_read_size && bytes_read == buf_len {
121+
max_read_size = max_read_size.saturating_mul(2);
122+
}
123+
}
124+
}
125+
}
126+
25127
/// The `Read` trait allows for reading bytes from a source.
26128
pub trait Read {
27129
/// Pull some bytes from this source into the specified buffer, returning
@@ -31,15 +133,7 @@ pub trait Read {
31133
/// Read all bytes until EOF in this source, placing them into `buf`.
32134
#[cfg(feature = "alloc")]
33135
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> {
34-
let start_len = buf.len();
35-
let mut probe = [0u8; 32];
36-
loop {
37-
match self.read(&mut probe) {
38-
Ok(0) => return Ok(buf.len() - start_len),
39-
Ok(n) => buf.extend_from_slice(&probe[..n]),
40-
Err(e) => return Err(e),
41-
}
42-
}
136+
default_read_to_end(self, buf, None)
43137
}
44138

45139
/// Read all bytes until EOF in this source, appending them to `buf`.

0 commit comments

Comments
 (0)