mod async_sys;
mod underlying;
pub use async_sys::*;
pub use underlying::*;
use pin_project_lite::pin_project;
use std::{
fmt::{Debug, Formatter, Result as FmtResult},
future::Future,
pin::Pin,
string::FromUtf8Error,
task::{Context, Poll},
};
use tokio::io::{AsyncBufRead, Error as IoError, ErrorKind as IoErrorKind, Result as IoResult};
macro_rules! ready {
($e:expr $(,)?) => {
match $e {
std::task::Poll::Ready(t) => t,
std::task::Poll::Pending => return std::task::Poll::Pending,
}
};
}
pin_project! {
#[derive(Debug)]
#[must_use = "streams do nothing unless polled"]
pub struct SerialLines<ReaderTy> {
#[pin]
reader: ReaderTy,
buf: String,
bytes: Vec<u8>,
read: usize,
}
}
impl<ReaderTy> SerialLines<ReaderTy>
where
ReaderTy: AsyncBufRead + Unpin,
{
pub async fn next_line(&mut self) -> IoResult<Option<String>> {
poll_fn(|cx| Pin::new(&mut *self).poll_next_line(cx)).await
}
}
impl<ReaderTy> SerialLines<ReaderTy>
where
ReaderTy: AsyncBufRead,
{
pub fn new(reader: ReaderTy) -> Self {
SerialLines {
reader,
buf: String::new(),
bytes: Vec::new(),
read: 0,
}
}
#[must_use]
pub const fn get_ref(&self) -> &ReaderTy {
&self.reader
}
#[must_use]
pub const fn get_mut(&mut self) -> &mut ReaderTy {
&mut self.reader
}
pub fn poll_next_line(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<IoResult<Option<String>>> {
let me = self.project();
let n = ready!(read_line_internal(me.reader, cx, me.buf, me.bytes, me.read))?;
debug_assert_eq!(*me.read, 0);
if n == 0 && me.buf.is_empty() {
return Poll::Ready(Ok(None));
}
if me.buf.ends_with('\r') {
me.buf.pop();
}
Poll::Ready(Ok(Some(std::mem::take(me.buf))))
}
}
fn read_line_internal<ReaderTy: AsyncBufRead + ?Sized>(
reader: Pin<&mut ReaderTy>,
cx: &mut Context<'_>,
output: &mut String,
buf: &mut Vec<u8>,
read: &mut usize,
) -> Poll<IoResult<usize>> {
let io_res = ready!(read_until_internal(reader, cx, b'\r', buf, read));
let utf8_res = String::from_utf8(std::mem::take(buf));
debug_assert!(buf.is_empty());
debug_assert!(output.is_empty());
finish_string_read(io_res, utf8_res, *read, output, false)
}
fn read_until_internal<ReaderTy: AsyncBufRead + ?Sized>(
mut reader: Pin<&mut ReaderTy>,
cx: &mut Context<'_>,
delimiter: u8,
buf: &mut Vec<u8>,
read: &mut usize,
) -> Poll<IoResult<usize>> {
loop {
let (done, used) = {
let available = ready!(reader.as_mut().poll_fill_buf(cx))?;
if let Some(i) = memchr(delimiter, 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(std::mem::replace(read, 0)));
}
}
}
#[cfg(not(unix))]
fn memchr(needle: u8, haystack: &[u8]) -> Option<usize> {
haystack.iter().position(|val| needle == *val)
}
#[cfg(unix)]
#[allow(clippy::cast_lossless)]
fn memchr(needle: u8, haystack: &[u8]) -> Option<usize> {
let start = haystack.as_ptr();
let ptr = unsafe { libc::memchr(start.cast(), needle as _, haystack.len()) };
if ptr.is_null() {
None
} else {
Some(ptr as usize - start as usize)
}
}
struct PollFn<F> {
f: F,
}
fn poll_fn<T, F>(f: F) -> PollFn<F>
where
F: FnMut(&mut Context<'_>) -> Poll<T>,
{
PollFn { f }
}
impl<F> Debug for PollFn<F> {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.debug_struct("PollFn").finish()
}
}
impl<T, F> Future for PollFn<F>
where
F: FnMut(&mut Context<'_>) -> Poll<T>,
{
type Output = T;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
let me = unsafe { Pin::into_inner_unchecked(self) };
(me.f)(cx)
}
}
fn put_back_original_data(output: &mut String, mut vector: Vec<u8>, num_bytes_read: usize) {
let original_len = vector.len() - num_bytes_read;
vector.truncate(original_len);
*output = String::from_utf8(vector).expect("The original data must be valid utf-8.");
}
fn finish_string_read(
io_res: IoResult<usize>,
utf8_res: Result<String, FromUtf8Error>,
read: usize,
output: &mut String,
truncate_on_io_error: bool,
) -> Poll<IoResult<usize>> {
match (io_res, utf8_res) {
(Ok(num_bytes), Ok(string)) => {
debug_assert_eq!(read, 0);
*output = string;
Poll::Ready(Ok(num_bytes))
}
(Err(io_err), Ok(string)) => {
*output = string;
if truncate_on_io_error {
let original_len = output.len() - read;
output.truncate(original_len);
}
Poll::Ready(Err(io_err))
}
(Ok(num_bytes), Err(utf8_err)) => {
debug_assert_eq!(read, 0);
put_back_original_data(output, utf8_err.into_bytes(), num_bytes);
Poll::Ready(Err(IoError::new(
IoErrorKind::InvalidData,
"stream did not contain valid UTF-8",
)))
}
(Err(io_err), Err(utf8_err)) => {
put_back_original_data(output, utf8_err.into_bytes(), read);
Poll::Ready(Err(io_err))
}
}
}
#[cfg(test)]
mod unit_tests {
use super::*;
#[test]
fn memchr_test() {
let haystack = b"123abc456\0\xffabc\n";
assert_eq!(memchr(b'1', haystack), Some(0));
assert_eq!(memchr(b'2', haystack), Some(1));
assert_eq!(memchr(b'3', haystack), Some(2));
assert_eq!(memchr(b'4', haystack), Some(6));
assert_eq!(memchr(b'5', haystack), Some(7));
assert_eq!(memchr(b'6', haystack), Some(8));
assert_eq!(memchr(b'7', haystack), None);
assert_eq!(memchr(b'a', haystack), Some(3));
assert_eq!(memchr(b'b', haystack), Some(4));
assert_eq!(memchr(b'c', haystack), Some(5));
assert_eq!(memchr(b'd', haystack), None);
assert_eq!(memchr(b'A', haystack), None);
assert_eq!(memchr(0, haystack), Some(9));
assert_eq!(memchr(0xff, haystack), Some(10));
assert_eq!(memchr(0xfe, haystack), None);
assert_eq!(memchr(1, haystack), None);
assert_eq!(memchr(b'\n', haystack), Some(14));
assert_eq!(memchr(b'\r', haystack), None);
}
#[test]
fn memchr_all() {
let mut arr = Vec::new();
for b in 0..=255 {
arr.push(b);
}
for b in 0..=255 {
assert_eq!(memchr(b, &arr), Some(b as usize));
}
arr.reverse();
for b in 0..=255 {
assert_eq!(memchr(b, &arr), Some(255 - b as usize));
}
}
#[test]
fn memchr_empty() {
for b in 0..=255 {
assert_eq!(memchr(b, b""), None);
}
}
}