use std::collections::TryReserveError;
use std::future::poll_fn;
use std::io;
use std::io::Error;
use std::io::ErrorKind;
use std::pin::Pin;
use std::task::Context;
use std::task::Poll;
use qubit_utils::SliceRange;
use crate::AsyncInput;
use crate::Buffer;
use crate::async_io::MAX_READY_OPERATIONS_PER_POLL;
use crate::buffered::DEFAULT_BUFFER_CAPACITY;
#[must_use]
#[derive(Debug)]
pub struct AsyncBufferedInput<I>
where
I: AsyncInput,
I::Item: Clone + Default,
{
inner: I,
buffer: Buffer<I::Item>,
}
impl<I> AsyncBufferedInput<I>
where
I: AsyncInput,
I::Item: Clone + Default,
{
#[inline(always)]
pub fn new(inner: I) -> Self {
Self::with_capacity(inner, DEFAULT_BUFFER_CAPACITY)
}
#[inline]
pub fn with_capacity(inner: I, capacity: usize) -> Self {
Self {
inner,
buffer: Buffer::with_capacity(capacity),
}
}
#[inline]
pub fn try_with_capacity(inner: I, capacity: usize) -> Result<Self, TryReserveError> {
Ok(Self {
inner,
buffer: Buffer::try_with_capacity(capacity)?,
})
}
#[inline(always)]
#[must_use]
pub const fn inner(&self) -> &I {
&self.inner
}
#[inline(always)]
#[must_use]
pub fn inner_mut(&mut self) -> &mut I {
&mut self.inner
}
#[inline(always)]
#[must_use = "the returned inner input and unread buffer must be handled"]
pub fn into_parts(self) -> (I, Buffer<I::Item>) {
(self.inner, self.buffer)
}
#[inline(always)]
#[must_use]
pub fn capacity(&self) -> usize {
self.buffer.capacity()
}
#[inline(always)]
#[must_use]
pub const fn unread_len(&self) -> usize {
self.buffer.available()
}
#[inline(always)]
#[must_use]
pub fn unread(&self) -> &[I::Item] {
self.buffer.readable()
}
#[inline(always)]
pub fn try_reserve_capacity(&mut self, capacity: usize) -> Result<(), TryReserveError> {
self.buffer.try_reserve_capacity(capacity)
}
#[inline(always)]
pub unsafe fn consume(&mut self, count: usize) {
unsafe {
self.buffer.consume(count);
}
}
#[inline]
pub unsafe fn copy_unread_to(&self, output: &mut [I::Item], output_index: usize, count: usize) {
debug_assert!(
SliceRange::range_fits(output.len(), output_index, count),
"unchecked unread destination range exceeds output buffer"
);
debug_assert!(
count <= self.buffer.available(),
"unchecked unread copy exceeds available buffer items"
);
unsafe {
let source = self.buffer.readable().get_unchecked(..count);
let output = output.get_unchecked_mut(output_index..output_index + count);
output.clone_from_slice(source);
}
}
pub fn poll_fill_more(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<bool>> {
let this = unsafe { self.as_mut().get_unchecked_mut() };
if this.buffer.available() == 0 {
this.buffer.clear();
} else if this.buffer.spare_capacity() == 0 {
this.buffer.compact();
if this.buffer.spare_capacity() == 0 {
return Poll::Ready(Err(Error::new(
ErrorKind::InvalidInput,
"buffered input is full; consume buffered items before refilling",
)));
}
}
let result = {
let inner = unsafe { Pin::new_unchecked(&mut this.inner) };
inner.poll_read(cx, this.buffer.spare_mut())
};
match result {
Poll::Ready(Ok(0)) => Poll::Ready(Ok(false)),
Poll::Ready(Ok(read)) => {
unsafe {
this.buffer.advance(read);
}
Poll::Ready(Ok(true))
}
Poll::Ready(Err(error)) => Poll::Ready(Err(error)),
Poll::Pending => Poll::Pending,
}
}
pub fn poll_fill_until(mut self: Pin<&mut Self>, cx: &mut Context<'_>, count: usize) -> Poll<io::Result<bool>> {
if count > self.as_ref().get_ref().buffer.capacity() {
return Poll::Ready(Err(Error::new(
ErrorKind::InvalidInput,
"requested available items exceed buffered input capacity",
)));
}
let mut ready_operations = 0;
while self.as_ref().get_ref().buffer.available() < count {
match self.as_mut().poll_fill_more(cx) {
Poll::Ready(Ok(true)) => {
ready_operations += 1;
if self.as_ref().get_ref().buffer.available() < count
&& ready_operations >= MAX_READY_OPERATIONS_PER_POLL
{
cx.waker().wake_by_ref();
return Poll::Pending;
}
}
Poll::Ready(Ok(false)) => return Poll::Ready(Ok(false)),
Poll::Ready(Err(error)) => return Poll::Ready(Err(error)),
Poll::Pending => return Poll::Pending,
}
}
Poll::Ready(Ok(true))
}
pub fn poll_ensure_available(mut self: Pin<&mut Self>, cx: &mut Context<'_>, count: usize) -> Poll<io::Result<()>> {
match self.as_mut().poll_fill_until(cx, count) {
Poll::Ready(Ok(true)) => Poll::Ready(Ok(())),
Poll::Ready(Ok(false)) => {
let this = unsafe { self.as_mut().get_unchecked_mut() };
let available = this.buffer.available();
unsafe {
this.buffer.consume(available);
}
Poll::Ready(Err(Error::new(ErrorKind::UnexpectedEof, "failed to fill whole buffer")))
}
Poll::Ready(Err(error)) => Poll::Ready(Err(error)),
Poll::Pending => Poll::Pending,
}
}
}
impl<I> AsyncBufferedInput<I>
where
I: AsyncInput + Unpin,
I::Item: Clone + Default + Unpin,
{
pub async fn fill_more_async(&mut self) -> io::Result<bool> {
poll_fn(|cx| Pin::new(&mut *self).poll_fill_more(cx)).await
}
pub async fn fill_until_async(&mut self, count: usize) -> io::Result<bool> {
poll_fn(|cx| Pin::new(&mut *self).poll_fill_until(cx, count)).await
}
pub async fn ensure_available_async(&mut self, count: usize) -> io::Result<()> {
poll_fn(|cx| Pin::new(&mut *self).poll_ensure_available(cx, count)).await
}
}
impl<I> AsyncInput for AsyncBufferedInput<I>
where
I: AsyncInput,
I::Item: Clone + Default,
{
type Item = I::Item;
#[inline(always)]
fn is_buffered(&self) -> bool {
true
}
unsafe fn poll_read_unchecked(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
output: &mut [Self::Item],
index: usize,
count: usize,
) -> Poll<io::Result<usize>> {
if count == 0 {
return Poll::Ready(Ok(0));
}
let this = unsafe { self.as_mut().get_unchecked_mut() };
if !this.buffer.is_empty() {
let read = count.min(this.buffer.available());
output[index..index + read].clone_from_slice(&this.buffer.readable()[..read]);
unsafe {
this.buffer.consume(read);
}
return Poll::Ready(Ok(read));
}
this.buffer.clear();
if count >= this.buffer.capacity() {
let destination = &mut output[index..index + count];
let inner = unsafe { Pin::new_unchecked(&mut this.inner) };
return inner.poll_read(cx, destination);
}
let result = {
let inner = unsafe { Pin::new_unchecked(&mut this.inner) };
inner.poll_read(cx, this.buffer.data_mut())
};
match result {
Poll::Ready(Ok(0)) => Poll::Ready(Ok(0)),
Poll::Ready(Ok(fetched)) => {
unsafe {
this.buffer.advance(fetched);
}
let read = count.min(fetched);
output[index..index + read].clone_from_slice(&this.buffer.readable()[..read]);
unsafe {
this.buffer.consume(read);
}
Poll::Ready(Ok(read))
}
Poll::Ready(Err(error)) => Poll::Ready(Err(error)),
Poll::Pending => Poll::Pending,
}
}
}