use core::future::poll_fn;
use core::pin::Pin;
use core::task::{Context, Poll, ready};
use std::io;
use super::{AsyncRead, AsyncWrite};
const DEFAULT_BUF_SIZE: usize = 8 * 1024;
pub struct BufReader<R> {
inner: R,
buf: Vec<u8>,
pos: usize,
filled: usize,
}
impl<R: AsyncRead> BufReader<R> {
pub fn new(inner: R) -> Self {
Self::with_capacity(DEFAULT_BUF_SIZE, inner)
}
pub fn with_capacity(capacity: usize, inner: R) -> Self {
Self {
inner,
buf: vec![0; capacity],
pos: 0,
filled: 0,
}
}
pub fn get_ref(&self) -> &R {
&self.inner
}
pub fn get_mut(&mut self) -> &mut R {
&mut self.inner
}
pub fn into_inner(self) -> R {
self.inner
}
pub fn buffer(&self) -> &[u8] {
&self.buf[self.pos..self.filled]
}
}
impl<R: AsyncRead + Unpin> BufReader<R> {
pub async fn fill_buf(&mut self) -> io::Result<&[u8]> {
if self.pos == self.filled {
let filled =
poll_fn(|cx| Pin::new(&mut self.inner).poll_read(cx, &mut self.buf)).await?;
self.pos = 0;
self.filled = filled;
}
Ok(self.buffer())
}
pub fn consume(&mut self, amount: usize) {
self.pos = self.filled.min(self.pos.saturating_add(amount));
}
pub async fn read_line(&mut self, buf: &mut String) -> io::Result<usize> {
let mut bytes = Vec::new();
loop {
let available = self.fill_buf().await?;
if available.is_empty() {
break;
}
let take = match available.iter().position(|byte| *byte == b'\n') {
Some(newline) => newline + 1,
None => available.len(),
};
let found_newline = available[..take].last() == Some(&b'\n');
bytes.extend_from_slice(&available[..take]);
self.consume(take);
if found_newline {
break;
}
}
let read = bytes.len();
let line = String::from_utf8(bytes)
.map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?;
buf.push_str(&line);
Ok(read)
}
}
impl<R: AsyncRead + Unpin> AsyncRead for BufReader<R> {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
if buf.is_empty() {
return Poll::Ready(Ok(0));
}
let this = self.get_mut();
if this.pos < this.filled {
let read = buf.len().min(this.filled - this.pos);
buf[..read].copy_from_slice(&this.buf[this.pos..this.pos + read]);
this.pos += read;
return Poll::Ready(Ok(read));
}
if buf.len() >= this.buf.len() {
return Pin::new(&mut this.inner).poll_read(cx, buf);
}
let filled = ready!(Pin::new(&mut this.inner).poll_read(cx, &mut this.buf))?;
this.pos = 0;
this.filled = filled;
let read = buf.len().min(this.filled);
buf[..read].copy_from_slice(&this.buf[..read]);
this.pos = read;
Poll::Ready(Ok(read))
}
}
impl<R: AsyncRead + AsyncWrite + Unpin> AsyncWrite for BufReader<R> {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
Pin::new(&mut self.get_mut().inner).poll_write(cx, buf)
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut self.get_mut().inner).poll_flush(cx)
}
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut self.get_mut().inner).poll_close(cx)
}
}
pub struct BufWriter<W> {
inner: W,
buf: Vec<u8>,
written: usize,
}
impl<W: AsyncWrite> BufWriter<W> {
pub fn new(inner: W) -> Self {
Self::with_capacity(DEFAULT_BUF_SIZE, inner)
}
pub fn with_capacity(capacity: usize, inner: W) -> Self {
Self {
inner,
buf: Vec::with_capacity(capacity),
written: 0,
}
}
pub fn get_ref(&self) -> &W {
&self.inner
}
pub fn get_mut(&mut self) -> &mut W {
&mut self.inner
}
pub fn into_inner(self) -> W {
self.inner
}
}
impl<W: AsyncWrite + Unpin> BufWriter<W> {
fn poll_flush_buf(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
while self.written < self.buf.len() {
let written =
ready!(Pin::new(&mut self.inner).poll_write(cx, &self.buf[self.written..]))?;
if written == 0 {
return Poll::Ready(Err(io::Error::new(
io::ErrorKind::WriteZero,
"failed to write buffered data",
)));
}
self.written += written;
}
self.buf.clear();
self.written = 0;
Poll::Ready(Ok(()))
}
}
impl<W: AsyncWrite + Unpin> AsyncWrite for BufWriter<W> {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
if buf.is_empty() {
return Poll::Ready(Ok(0));
}
let this = self.get_mut();
if this.buf.capacity() == 0 {
return Pin::new(&mut this.inner).poll_write(cx, buf);
}
if this.written > 0 || buf.len() > this.buf.capacity() - this.buf.len() {
ready!(this.poll_flush_buf(cx))?;
}
if buf.len() >= this.buf.capacity() {
return Pin::new(&mut this.inner).poll_write(cx, buf);
}
this.buf.extend_from_slice(buf);
Poll::Ready(Ok(buf.len()))
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
let this = self.get_mut();
ready!(this.poll_flush_buf(cx))?;
Pin::new(&mut this.inner).poll_flush(cx)
}
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
let this = self.get_mut();
ready!(this.poll_flush_buf(cx))?;
Pin::new(&mut this.inner).poll_close(cx)
}
}
#[cfg(test)]
mod tests {
use std::cell::RefCell;
use std::io;
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll};
use crate::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
use crate::{run, spawn};
use super::{BufReader, BufWriter};
struct ChunkedReader {
data: Vec<u8>,
pos: usize,
max_read: usize,
reads: Rc<RefCell<usize>>,
}
impl AsyncRead for ChunkedReader {
fn poll_read(
mut self: Pin<&mut Self>,
_cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
*self.reads.borrow_mut() += 1;
let remaining = self.data.len() - self.pos;
let read = remaining.min(buf.len()).min(self.max_read);
let pos = self.pos;
buf[..read].copy_from_slice(&self.data[pos..pos + read]);
self.pos += read;
Poll::Ready(Ok(read))
}
}
#[derive(Clone)]
struct RecordingWriter {
data: Rc<RefCell<Vec<u8>>>,
writes: Rc<RefCell<usize>>,
closes: Rc<RefCell<usize>>,
max_write: usize,
}
impl AsyncWrite for RecordingWriter {
fn poll_write(
self: Pin<&mut Self>,
_cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
*self.writes.borrow_mut() += 1;
let written = buf.len().min(self.max_write);
self.data.borrow_mut().extend_from_slice(&buf[..written]);
Poll::Ready(Ok(written))
}
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(()))
}
fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
*self.closes.borrow_mut() += 1;
Poll::Ready(Ok(()))
}
}
#[test]
fn buf_reader_serves_small_reads_from_one_inner_read() {
let reads = Rc::new(RefCell::new(0));
let observed = Rc::new(RefCell::new(Vec::new()));
let observed_for_task = Rc::clone(&observed);
let reads_for_task = Rc::clone(&reads);
spawn(async move {
let inner = ChunkedReader {
data: b"abcdef".to_vec(),
pos: 0,
max_read: 4,
reads: reads_for_task,
};
let mut reader = BufReader::with_capacity(4, inner);
let mut first = [0; 1];
let mut second = [0; 1];
reader.read_exact(&mut first).await.unwrap();
reader.read_exact(&mut second).await.unwrap();
observed_for_task.borrow_mut().extend_from_slice(&first);
observed_for_task.borrow_mut().extend_from_slice(&second);
});
run();
assert_eq!(&*observed.borrow(), b"ab");
assert_eq!(*reads.borrow(), 1);
}
#[test]
fn buf_reader_fill_buf_and_consume_expose_buffered_bytes() {
let reads = Rc::new(RefCell::new(0));
let observed = Rc::new(RefCell::new(Vec::new()));
let observed_for_task = Rc::clone(&observed);
spawn({
let reads = Rc::clone(&reads);
async move {
let inner = ChunkedReader {
data: b"line\nrest".to_vec(),
pos: 0,
max_read: 8,
reads,
};
let mut reader = BufReader::with_capacity(8, inner);
let filled = reader.fill_buf().await.unwrap();
let newline = filled.iter().position(|byte| *byte == b'\n').unwrap();
observed_for_task
.borrow_mut()
.extend_from_slice(&filled[..=newline]);
reader.consume(newline + 1);
observed_for_task
.borrow_mut()
.extend_from_slice(reader.buffer());
}
});
run();
assert_eq!(&*observed.borrow(), b"line\nres");
assert_eq!(*reads.borrow(), 1);
}
#[test]
fn buf_reader_read_line_uses_internal_buffer() {
let reads = Rc::new(RefCell::new(0));
let observed = Rc::new(RefCell::new(Vec::new()));
let observed_for_task = Rc::clone(&observed);
spawn({
let reads = Rc::clone(&reads);
async move {
let inner = ChunkedReader {
data: b"alpha\nbeta\n".to_vec(),
pos: 0,
max_read: 11,
reads,
};
let mut reader = BufReader::with_capacity(11, inner);
let mut first = String::new();
let mut second = String::new();
assert_eq!(reader.read_line(&mut first).await.unwrap(), 6);
assert_eq!(reader.read_line(&mut second).await.unwrap(), 5);
observed_for_task.borrow_mut().push(first);
observed_for_task.borrow_mut().push(second);
}
});
run();
assert_eq!(
*observed.borrow(),
vec!["alpha\n".to_string(), "beta\n".to_string()]
);
assert_eq!(*reads.borrow(), 1);
}
#[test]
fn buf_writer_batches_small_writes_until_flush() {
let data = Rc::new(RefCell::new(Vec::new()));
let writes = Rc::new(RefCell::new(0));
spawn({
let data = Rc::clone(&data);
let writes = Rc::clone(&writes);
async move {
let inner = RecordingWriter {
data,
writes,
closes: Rc::new(RefCell::new(0)),
max_write: usize::MAX,
};
let mut writer = BufWriter::with_capacity(8, inner);
writer.write_all(b"ab").await.unwrap();
writer.write_all(b"cd").await.unwrap();
writer.flush().await.unwrap();
}
});
run();
assert_eq!(&*data.borrow(), b"abcd");
assert_eq!(*writes.borrow(), 1);
}
#[test]
fn buf_writer_flushes_before_close() {
let data = Rc::new(RefCell::new(Vec::new()));
let writes = Rc::new(RefCell::new(0));
let closes = Rc::new(RefCell::new(0));
spawn({
let data = Rc::clone(&data);
let writes = Rc::clone(&writes);
let closes = Rc::clone(&closes);
async move {
let inner = RecordingWriter {
data,
writes,
closes,
max_write: usize::MAX,
};
let mut writer = BufWriter::with_capacity(8, inner);
writer.write_all(b"close me").await.unwrap();
writer.close().await.unwrap();
}
});
run();
assert_eq!(&*data.borrow(), b"close me");
assert_eq!(*writes.borrow(), 1);
assert_eq!(*closes.borrow(), 1);
}
#[test]
fn buf_reader_read_line_rejects_invalid_utf8() {
let observed = Rc::new(RefCell::new(None::<io::ErrorKind>));
spawn({
let observed = Rc::clone(&observed);
async move {
let inner = ChunkedReader {
data: b"bad \xff\n".to_vec(),
pos: 0,
max_read: 8,
reads: Rc::new(RefCell::new(0)),
};
let mut reader = BufReader::with_capacity(8, inner);
let mut line = String::new();
let error = reader
.read_line(&mut line)
.await
.expect_err("invalid UTF-8 should fail");
*observed.borrow_mut() = Some(error.kind());
}
});
run();
assert_eq!(*observed.borrow(), Some(io::ErrorKind::InvalidData));
}
#[test]
fn buf_reader_consume_past_buffer_exhausts_available_bytes() {
let observed = Rc::new(RefCell::new(None::<Vec<u8>>));
spawn({
let observed = Rc::clone(&observed);
async move {
let inner = ChunkedReader {
data: b"abcd".to_vec(),
pos: 0,
max_read: 4,
reads: Rc::new(RefCell::new(0)),
};
let mut reader = BufReader::with_capacity(4, inner);
assert_eq!(reader.fill_buf().await.unwrap(), b"abcd");
reader.consume(99);
observed.borrow_mut().replace(reader.buffer().to_vec());
}
});
run();
assert_eq!(observed.borrow().as_deref(), Some(&[][..]));
}
#[test]
fn buf_writer_flush_handles_partial_inner_writes() {
let data = Rc::new(RefCell::new(Vec::new()));
let writes = Rc::new(RefCell::new(0));
spawn({
let data = Rc::clone(&data);
let writes = Rc::clone(&writes);
async move {
let inner = RecordingWriter {
data,
writes,
closes: Rc::new(RefCell::new(0)),
max_write: 2,
};
let mut writer = BufWriter::with_capacity(8, inner);
writer.write_all(b"abcdef").await.unwrap();
writer.flush().await.unwrap();
}
});
run();
assert_eq!(&*data.borrow(), b"abcdef");
assert_eq!(*writes.borrow(), 3);
}
#[test]
fn buf_writer_zero_capacity_delegates_write_all_to_inner() {
let data = Rc::new(RefCell::new(Vec::new()));
let writes = Rc::new(RefCell::new(0));
spawn({
let data = Rc::clone(&data);
let writes = Rc::clone(&writes);
async move {
let inner = RecordingWriter {
data,
writes,
closes: Rc::new(RefCell::new(0)),
max_write: 2,
};
let mut writer = BufWriter::with_capacity(0, inner);
writer.write_all(b"abcde").await.unwrap();
}
});
run();
assert_eq!(&*data.borrow(), b"abcde");
assert_eq!(*writes.borrow(), 3);
}
struct ParkingReader {
chunks: std::collections::VecDeque<&'static [u8]>,
park_next: bool,
}
impl ParkingReader {
fn new(chunks: impl IntoIterator<Item = &'static [u8]>) -> Self {
Self {
chunks: chunks.into_iter().collect(),
park_next: false,
}
}
}
impl AsyncRead for ParkingReader {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
if self.park_next {
self.park_next = false;
cx.waker().wake_by_ref();
return Poll::Pending;
}
match self.chunks.pop_front() {
Some(chunk) => {
self.park_next = true;
let read = chunk.len().min(buf.len());
buf[..read].copy_from_slice(&chunk[..read]);
Poll::Ready(Ok(read))
}
None => Poll::Ready(Ok(0)),
}
}
}
#[test]
fn buf_reader_does_not_replay_bytes_when_inner_parks() {
let observed = Rc::new(RefCell::new(Vec::new()));
let observed_for_task = Rc::clone(&observed);
spawn(async move {
let inner = ParkingReader::new([b"ab".as_slice(), b"cd".as_slice()]);
let mut reader = BufReader::with_capacity(2, inner);
for _ in 0..4 {
let mut byte = [0u8; 1];
reader.read_exact(&mut byte).await.unwrap();
observed_for_task.borrow_mut().push(byte[0]);
}
});
run();
assert_eq!(&*observed.borrow(), b"abcd");
}
#[test]
fn buf_reader_read_line_handles_parking_inner() {
let observed = Rc::new(RefCell::new(Vec::new()));
let observed_for_task = Rc::clone(&observed);
spawn(async move {
let inner = ParkingReader::new([b"foo\n".as_slice(), b"bar\n".as_slice()]);
let mut reader = BufReader::with_capacity(4, inner);
let mut first = String::new();
let mut second = String::new();
reader.read_line(&mut first).await.unwrap();
reader.read_line(&mut second).await.unwrap();
observed_for_task.borrow_mut().push(first);
observed_for_task.borrow_mut().push(second);
});
run();
assert_eq!(
&*observed.borrow(),
&["foo\n".to_string(), "bar\n".to_string()]
);
}
}