mirror of
https://github.com/async-rs/async-std.git
synced 2025-03-29 20:46:41 +00:00
* Cleanup: replace cfg-if with our macros * Prefix macros with cfg_ * Remove #[macro_export] from internal macros
361 lines
11 KiB
Rust
361 lines
11 KiB
Rust
mod lines;
|
|
mod read_line;
|
|
mod read_until;
|
|
mod split;
|
|
|
|
pub use lines::Lines;
|
|
pub use split::Split;
|
|
|
|
use read_line::ReadLineFuture;
|
|
use read_until::ReadUntilFuture;
|
|
|
|
use std::mem;
|
|
use std::pin::Pin;
|
|
|
|
use crate::io;
|
|
use crate::task::{Context, Poll};
|
|
|
|
extension_trait! {
|
|
use std::ops::{Deref, DerefMut};
|
|
|
|
#[doc = r#"
|
|
Allows reading from a buffered byte stream.
|
|
|
|
This trait is a re-export of [`futures::io::AsyncBufRead`] and is an async version of
|
|
[`std::io::BufRead`].
|
|
|
|
The [provided methods] do not really exist in the trait itself, but they become
|
|
available when the prelude is imported:
|
|
|
|
```
|
|
# #[allow(unused_imports)]
|
|
use async_std::prelude::*;
|
|
```
|
|
|
|
[`std::io::BufRead`]: https://doc.rust-lang.org/std/io/trait.BufRead.html
|
|
[`futures::io::AsyncBufRead`]:
|
|
https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncBufRead.html
|
|
[provided methods]: #provided-methods
|
|
"#]
|
|
pub trait BufRead {
|
|
#[doc = r#"
|
|
Returns the contents of the internal buffer, filling it with more data from the
|
|
inner reader if it is empty.
|
|
|
|
This function is a lower-level call. It needs to be paired with the [`consume`]
|
|
method to function properly. When calling this method, none of the contents will be
|
|
"read" in the sense that later calling `read` may return the same contents. As
|
|
such, [`consume`] must be called with the number of bytes that are consumed from
|
|
this buffer to ensure that the bytes are never returned twice.
|
|
|
|
[`consume`]: #tymethod.consume
|
|
|
|
An empty buffer returned indicates that the stream has reached EOF.
|
|
"#]
|
|
// TODO: write a proper doctest with `consume`
|
|
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>>;
|
|
|
|
#[doc = r#"
|
|
Tells this buffer that `amt` bytes have been consumed from the buffer, so they
|
|
should no longer be returned in calls to `read`.
|
|
"#]
|
|
fn consume(self: Pin<&mut Self>, amt: usize);
|
|
}
|
|
|
|
pub trait BufReadExt: futures_io::AsyncBufRead {
|
|
#[doc = r#"
|
|
Reads all bytes into `buf` until the delimiter `byte` or EOF is reached.
|
|
|
|
This function will read bytes from the underlying stream until the delimiter or EOF
|
|
is found. Once found, all bytes up to, and including, the delimiter (if found) will
|
|
be appended to `buf`.
|
|
|
|
If successful, this function will return the total number of bytes read.
|
|
|
|
# Examples
|
|
|
|
```no_run
|
|
# fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
|
#
|
|
use async_std::fs::File;
|
|
use async_std::io::BufReader;
|
|
use async_std::prelude::*;
|
|
|
|
let mut file = BufReader::new(File::open("a.txt").await?);
|
|
|
|
let mut buf = Vec::with_capacity(1024);
|
|
let n = file.read_until(b'\n', &mut buf).await?;
|
|
#
|
|
# Ok(()) }) }
|
|
```
|
|
|
|
Multiple successful calls to `read_until` append all bytes up to and including to
|
|
`buf`:
|
|
```
|
|
# fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
|
#
|
|
use async_std::io::BufReader;
|
|
use async_std::prelude::*;
|
|
|
|
let from: &[u8] = b"append\nexample\n";
|
|
let mut reader = BufReader::new(from);
|
|
let mut buf = vec![];
|
|
|
|
let mut size = reader.read_until(b'\n', &mut buf).await?;
|
|
assert_eq!(size, 7);
|
|
assert_eq!(buf, b"append\n");
|
|
|
|
size += reader.read_until(b'\n', &mut buf).await?;
|
|
assert_eq!(size, from.len());
|
|
|
|
assert_eq!(buf, from);
|
|
#
|
|
# Ok(()) }) }
|
|
```
|
|
"#]
|
|
fn read_until<'a>(
|
|
&'a mut self,
|
|
byte: u8,
|
|
buf: &'a mut Vec<u8>,
|
|
) -> impl Future<Output = usize> + 'a [ReadUntilFuture<'a, Self>]
|
|
where
|
|
Self: Unpin,
|
|
{
|
|
ReadUntilFuture {
|
|
reader: self,
|
|
byte,
|
|
buf,
|
|
read: 0,
|
|
}
|
|
}
|
|
|
|
#[doc = r#"
|
|
Reads all bytes and appends them into `buf` until a newline (the 0xA byte) is
|
|
reached.
|
|
|
|
This function will read bytes from the underlying stream until the newline
|
|
delimiter (the 0xA byte) or EOF is found. Once found, all bytes up to, and
|
|
including, the delimiter (if found) will be appended to `buf`.
|
|
|
|
If successful, this function will return the total number of bytes read.
|
|
|
|
If this function returns `Ok(0)`, the stream has reached EOF.
|
|
|
|
# Errors
|
|
|
|
This function has the same error semantics as [`read_until`] and will also return
|
|
an error if the read bytes are not valid UTF-8. If an I/O error is encountered then
|
|
`buf` may contain some bytes already read in the event that all data read so far
|
|
was valid UTF-8.
|
|
|
|
[`read_until`]: #method.read_until
|
|
|
|
# Examples
|
|
|
|
```no_run
|
|
# fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
|
#
|
|
use async_std::fs::File;
|
|
use async_std::io::BufReader;
|
|
use async_std::prelude::*;
|
|
|
|
let mut file = BufReader::new(File::open("a.txt").await?);
|
|
|
|
let mut buf = String::new();
|
|
file.read_line(&mut buf).await?;
|
|
#
|
|
# Ok(()) }) }
|
|
```
|
|
"#]
|
|
fn read_line<'a>(
|
|
&'a mut self,
|
|
buf: &'a mut String,
|
|
) -> impl Future<Output = io::Result<usize>> + 'a [ReadLineFuture<'a, Self>]
|
|
where
|
|
Self: Unpin,
|
|
{
|
|
ReadLineFuture {
|
|
reader: self,
|
|
bytes: unsafe { mem::replace(buf.as_mut_vec(), Vec::new()) },
|
|
buf,
|
|
read: 0,
|
|
}
|
|
}
|
|
|
|
#[doc = r#"
|
|
Returns a stream over the lines of this byte stream.
|
|
|
|
The stream returned from this function will yield instances of
|
|
[`io::Result`]`<`[`String`]`>`. Each string returned will *not* have a newline byte
|
|
(the 0xA byte) or CRLF (0xD, 0xA bytes) at the end.
|
|
|
|
[`io::Result`]: type.Result.html
|
|
[`String`]: https://doc.rust-lang.org/std/string/struct.String.html
|
|
|
|
# Examples
|
|
|
|
```no_run
|
|
# fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
|
#
|
|
use async_std::fs::File;
|
|
use async_std::io::BufReader;
|
|
use async_std::prelude::*;
|
|
|
|
let file = File::open("a.txt").await?;
|
|
let mut lines = BufReader::new(file).lines();
|
|
let mut count = 0;
|
|
|
|
while let Some(line) = lines.next().await {
|
|
line?;
|
|
count += 1;
|
|
}
|
|
#
|
|
# Ok(()) }) }
|
|
```
|
|
"#]
|
|
fn lines(self) -> Lines<Self>
|
|
where
|
|
Self: Unpin + Sized,
|
|
{
|
|
Lines {
|
|
reader: self,
|
|
buf: String::new(),
|
|
bytes: Vec::new(),
|
|
read: 0,
|
|
}
|
|
}
|
|
|
|
#[doc = r#"
|
|
Returns a stream over the contents of this reader split on the byte `byte`.
|
|
|
|
The stream returned from this function will return instances of
|
|
[`io::Result`]`<`[`Vec<u8>`]`>`. Each vector returned will *not* have
|
|
the delimiter byte at the end.
|
|
|
|
This function will yield errors whenever [`read_until`] would have
|
|
also yielded an error.
|
|
|
|
[`io::Result`]: type.Result.html
|
|
[`Vec<u8>`]: ../vec/struct.Vec.html
|
|
[`read_until`]: #method.read_until
|
|
|
|
# Examples
|
|
|
|
[`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In
|
|
this example, we use [`Cursor`] to iterate over all hyphen delimited
|
|
segments in a byte slice
|
|
|
|
[`Cursor`]: struct.Cursor.html
|
|
|
|
```
|
|
# fn main() -> std::io::Result<()> { async_std::task::block_on(async {
|
|
#
|
|
use async_std::prelude::*;
|
|
use async_std::io;
|
|
|
|
let cursor = io::Cursor::new(b"lorem-ipsum-dolor");
|
|
|
|
let mut split_iter = cursor.split(b'-').map(|l| l.unwrap());
|
|
assert_eq!(split_iter.next().await, Some(b"lorem".to_vec()));
|
|
assert_eq!(split_iter.next().await, Some(b"ipsum".to_vec()));
|
|
assert_eq!(split_iter.next().await, Some(b"dolor".to_vec()));
|
|
assert_eq!(split_iter.next().await, None);
|
|
#
|
|
# Ok(()) }) }
|
|
```
|
|
"#]
|
|
fn split(self, byte: u8) -> Split<Self>
|
|
where
|
|
Self: Sized,
|
|
{
|
|
Split {
|
|
reader: self,
|
|
buf: Vec::new(),
|
|
delim: byte,
|
|
read: 0,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T: BufRead + Unpin + ?Sized> BufRead for Box<T> {
|
|
fn poll_fill_buf(
|
|
self: Pin<&mut Self>,
|
|
cx: &mut Context<'_>,
|
|
) -> Poll<io::Result<&[u8]>> {
|
|
unreachable!("this impl only appears in the rendered docs")
|
|
}
|
|
|
|
fn consume(self: Pin<&mut Self>, amt: usize) {
|
|
unreachable!("this impl only appears in the rendered docs")
|
|
}
|
|
}
|
|
|
|
impl<T: BufRead + Unpin + ?Sized> BufRead for &mut T {
|
|
fn poll_fill_buf(
|
|
self: Pin<&mut Self>,
|
|
cx: &mut Context<'_>,
|
|
) -> Poll<io::Result<&[u8]>> {
|
|
unreachable!("this impl only appears in the rendered docs")
|
|
}
|
|
|
|
fn consume(self: Pin<&mut Self>, amt: usize) {
|
|
unreachable!("this impl only appears in the rendered docs")
|
|
}
|
|
}
|
|
|
|
impl<P> BufRead for Pin<P>
|
|
where
|
|
P: DerefMut + Unpin,
|
|
<P as Deref>::Target: BufRead,
|
|
{
|
|
fn poll_fill_buf(
|
|
self: Pin<&mut Self>,
|
|
cx: &mut Context<'_>,
|
|
) -> Poll<io::Result<&[u8]>> {
|
|
unreachable!("this impl only appears in the rendered docs")
|
|
}
|
|
|
|
fn consume(self: Pin<&mut Self>, amt: usize) {
|
|
unreachable!("this impl only appears in the rendered docs")
|
|
}
|
|
}
|
|
|
|
impl BufRead for &[u8] {
|
|
fn poll_fill_buf(
|
|
self: Pin<&mut Self>,
|
|
cx: &mut Context<'_>,
|
|
) -> Poll<io::Result<&[u8]>> {
|
|
unreachable!()
|
|
}
|
|
|
|
fn consume(self: Pin<&mut Self>, amt: usize) {
|
|
unreachable!("this impl only appears in the rendered docs")
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn read_until_internal<R: BufReadExt + ?Sized>(
|
|
mut reader: Pin<&mut R>,
|
|
cx: &mut Context<'_>,
|
|
byte: u8,
|
|
buf: &mut Vec<u8>,
|
|
read: &mut usize,
|
|
) -> Poll<io::Result<usize>> {
|
|
loop {
|
|
let (done, used) = {
|
|
let available = futures_core::ready!(reader.as_mut().poll_fill_buf(cx))?;
|
|
if let Some(i) = memchr::memchr(byte, available) {
|
|
buf.extend_from_slice(&available[..=i]);
|
|
(true, i + 1)
|
|
} else {
|
|
buf.extend_from_slice(available);
|
|
(false, available.len())
|
|
}
|
|
};
|
|
reader.as_mut().consume(used);
|
|
*read += used;
|
|
if done || used == 0 {
|
|
return Poll::Ready(Ok(mem::replace(read, 0)));
|
|
}
|
|
}
|
|
}
|