Skip to content

Commit 9cdf2cc

Browse files
authored
refactor: buffered (#9)
1 parent 511805d commit 9cdf2cc

11 files changed

Lines changed: 1275 additions & 158 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ continue-on-interrupt = []
1818

1919
[dependencies]
2020
axerrno = "0.2"
21+
heapless = "0.9"
2122
memchr = { version = "2", default-features = false }
2223

2324
[build-dependencies]

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
- Enables extra methods on `Read`: `read_to_end`, `read_to_string`.
1515
- Enables extra methods on `BufRead`: `read_until`, `read_line`, `split`, `lines`.
1616
- Enables implementations of axio traits for `alloc` types like `Vec<u8>`, `Box<T>`, etc.
17+
- Enables `BufWriter::with_capacity`. (If `alloc` is disabled, only `BufWriter::new` is available.)
18+
- Removes the capacity limit on `BufReader`. (If `alloc` is disabled, `BufReader::with_capacity` will panic if the capacity is larger than a fixed limit.)
1719

1820
### Differences to `std::io`
1921

build.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use std::{env, fs, path::PathBuf};
22

33
fn main() {
4-
autocfg::emit_possibility("borrowedbuf_init");
54
autocfg::rerun_path("build.rs");
65

76
let ac = autocfg::new();
7+
8+
autocfg::emit_possibility("borrowedbuf_init");
89
let code = r#"
910
#![no_std]
1011
#![feature(core_io_borrowed_buf)]
@@ -16,6 +17,17 @@ fn main() {
1617
autocfg::emit("borrowedbuf_init");
1718
}
1819

20+
autocfg::emit_possibility("maybe_uninit_slice");
21+
let code = r#"
22+
#![no_std]
23+
pub fn probe() {
24+
let _ = <[core::mem::MaybeUninit<()>]>::assume_init_mut;
25+
}
26+
"#;
27+
if ac.probe_raw(code).is_ok() {
28+
autocfg::emit("maybe_uninit_slice");
29+
}
30+
1931
let buf_size = env::var("AXIO_DEFAULT_BUF_SIZE")
2032
.map(|v| v.parse::<usize>().expect("Invalid AXIO_DEFAULT_BUF_SIZE"))
2133
.unwrap_or(1024 * 2);

src/buffered/bufreader.rs

Lines changed: 0 additions & 156 deletions
This file was deleted.

src/buffered/bufreader/buffer.rs

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
#[cfg(feature = "alloc")]
2+
use alloc::boxed::Box;
3+
use core::{cmp, io::BorrowedBuf, mem::MaybeUninit};
4+
5+
#[cfg(not(feature = "alloc"))]
6+
use heapless::Vec;
7+
8+
#[cfg(not(feature = "alloc"))]
9+
use crate::DEFAULT_BUF_SIZE;
10+
use crate::{Read, Result};
11+
12+
pub struct Buffer {
13+
// The buffer.
14+
#[cfg(feature = "alloc")]
15+
buf: Box<[MaybeUninit<u8>]>,
16+
// The buffer.
17+
#[cfg(not(feature = "alloc"))]
18+
buf: Vec<MaybeUninit<u8>, DEFAULT_BUF_SIZE, u16>,
19+
20+
// The current seek offset into `buf`, must always be <= `filled`.
21+
pos: usize,
22+
// Each call to `fill_buf` sets `filled` to indicate how many bytes at the start of `buf` are
23+
// initialized with bytes from a read.
24+
filled: usize,
25+
#[cfg(borrowedbuf_init)]
26+
// This is the max number of bytes returned across all `fill_buf` calls. We track this so that
27+
// we can accurately tell `read_buf` how many bytes of buf are initialized, to bypass as much
28+
// of its defensive initialization as possible. Note that while this often the same as
29+
// `filled`, it doesn't need to be. Calls to `fill_buf` are not required to actually fill the
30+
// buffer, and omitting this is a huge perf regression for `Read` impls that do not.
31+
initialized: usize,
32+
}
33+
34+
impl Buffer {
35+
#[inline]
36+
pub fn with_capacity(capacity: usize) -> Self {
37+
#[cfg(feature = "alloc")]
38+
let buf = Box::new_uninit_slice(capacity);
39+
#[cfg(not(feature = "alloc"))]
40+
let buf = {
41+
let mut buf = Vec::new();
42+
assert!(capacity <= buf.capacity());
43+
unsafe { buf.set_len(capacity) };
44+
buf
45+
};
46+
Self {
47+
buf,
48+
pos: 0,
49+
filled: 0,
50+
#[cfg(borrowedbuf_init)]
51+
initialized: 0,
52+
}
53+
}
54+
55+
#[inline]
56+
pub fn buffer(&self) -> &[u8] {
57+
// SAFETY: self.pos and self.filled are valid, and self.filled >= self.pos, and
58+
// that region is initialized because those are all invariants of this type.
59+
unsafe {
60+
self.buf
61+
.get_unchecked(self.pos..self.filled)
62+
.assume_init_ref()
63+
}
64+
}
65+
66+
#[inline]
67+
pub fn capacity(&self) -> usize {
68+
self.buf.len()
69+
}
70+
71+
#[inline]
72+
pub fn filled(&self) -> usize {
73+
self.filled
74+
}
75+
76+
#[inline]
77+
pub fn pos(&self) -> usize {
78+
self.pos
79+
}
80+
81+
#[cfg(borrowedbuf_init)]
82+
#[inline]
83+
pub fn initialized(&self) -> usize {
84+
self.initialized
85+
}
86+
87+
#[inline]
88+
pub fn discard_buffer(&mut self) {
89+
self.pos = 0;
90+
self.filled = 0;
91+
}
92+
93+
#[inline]
94+
pub fn consume(&mut self, amt: usize) {
95+
self.pos = cmp::min(self.pos + amt, self.filled);
96+
}
97+
98+
/// If there are `amt` bytes available in the buffer, pass a slice containing those bytes to
99+
/// `visitor` and return true. If there are not enough bytes available, return false.
100+
#[inline]
101+
pub fn consume_with<V>(&mut self, amt: usize, mut visitor: V) -> bool
102+
where
103+
V: FnMut(&[u8]),
104+
{
105+
if let Some(claimed) = self.buffer().get(..amt) {
106+
visitor(claimed);
107+
// If the indexing into self.buffer() succeeds, amt must be a valid increment.
108+
self.pos += amt;
109+
true
110+
} else {
111+
false
112+
}
113+
}
114+
115+
#[inline]
116+
pub fn unconsume(&mut self, amt: usize) {
117+
self.pos = self.pos.saturating_sub(amt);
118+
}
119+
120+
/// Read more bytes into the buffer without discarding any of its contents
121+
pub fn read_more(&mut self, mut reader: impl Read) -> Result<usize> {
122+
let mut buf = BorrowedBuf::from(&mut self.buf[self.filled..]);
123+
#[cfg(borrowedbuf_init)]
124+
let old_init = self.initialized - self.filled;
125+
#[cfg(borrowedbuf_init)]
126+
unsafe {
127+
buf.set_init(old_init);
128+
}
129+
reader.read_buf(buf.unfilled())?;
130+
self.filled += buf.len();
131+
#[cfg(borrowedbuf_init)]
132+
{
133+
self.initialized += buf.init_len() - old_init;
134+
}
135+
Ok(buf.len())
136+
}
137+
138+
/// Remove bytes that have already been read from the buffer.
139+
pub fn backshift(&mut self) {
140+
self.buf.copy_within(self.pos.., 0);
141+
self.filled -= self.pos;
142+
self.pos = 0;
143+
}
144+
145+
#[inline]
146+
pub fn fill_buf(&mut self, mut reader: impl Read) -> Result<&[u8]> {
147+
// If we've reached the end of our internal buffer then we need to fetch
148+
// some more data from the reader.
149+
// Branch using `>=` instead of the more correct `==`
150+
// to tell the compiler that the pos..cap slice is always valid.
151+
if self.pos >= self.filled {
152+
debug_assert!(self.pos == self.filled);
153+
154+
#[cfg(feature = "alloc")]
155+
let mut buf = BorrowedBuf::from(&mut *self.buf);
156+
#[cfg(not(feature = "alloc"))]
157+
let mut buf = BorrowedBuf::from(self.buf.as_mut_slice());
158+
#[cfg(borrowedbuf_init)]
159+
// SAFETY: `self.filled` bytes will always have been initialized.
160+
unsafe {
161+
buf.set_init(self.initialized);
162+
}
163+
164+
let result = reader.read_buf(buf.unfilled());
165+
166+
self.pos = 0;
167+
self.filled = buf.len();
168+
#[cfg(borrowedbuf_init)]
169+
{
170+
self.initialized = buf.init_len();
171+
}
172+
173+
result?;
174+
}
175+
Ok(self.buffer())
176+
}
177+
}

0 commit comments

Comments
 (0)