#[cfg(feature = "async")]
pub mod reader {
use crate::explode::ExplodeState;
use crate::{CompressionMode, PkLibError, Result};
use bytes::Bytes;
use futures::stream::Stream;
use futures::Future;
use pin_project::pin_project;
use std::collections::VecDeque;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::io::{AsyncRead, AsyncReadExt};
#[pin_project]
#[derive(Debug)]
pub struct AsyncExplodeReader<R: AsyncRead + Unpin> {
#[pin]
reader: R,
state: ExplodeState,
initialized: bool,
finished: bool,
output_buffer: VecDeque<Bytes>,
buffer_size: usize,
read_buffer: Vec<u8>,
decompress_buffer: Vec<u8>,
pending_read: Option<Vec<u8>>,
}
impl<R: AsyncRead + Unpin> AsyncExplodeReader<R> {
pub fn new(reader: R) -> Result<Self> {
Self::with_buffer_size(reader, 64 * 1024) }
pub fn with_buffer_size(reader: R, buffer_size: usize) -> Result<Self> {
Ok(Self {
reader,
state: ExplodeState::new(),
initialized: false,
finished: false,
output_buffer: VecDeque::new(),
buffer_size,
read_buffer: vec![0u8; buffer_size],
decompress_buffer: Vec::new(),
pending_read: None,
})
}
async fn initialize(&mut self) -> Result<()> {
if self.initialized {
return Ok(());
}
let mut header_buf = [0u8; 8];
let bytes_read = self.reader.read(&mut header_buf).await?;
if bytes_read < 3 {
return Err(PkLibError::InvalidData("Header too short".to_string()));
}
self.state.ctype = match header_buf[0] {
0 => CompressionMode::Binary,
1 => CompressionMode::ASCII,
_ => return Err(PkLibError::InvalidCompressionMode(header_buf[0])),
};
self.state.dsize_bits = header_buf[1] as u32;
if self.state.dsize_bits < 4 || self.state.dsize_bits > 6 {
return Err(PkLibError::InvalidDictionaryBits(
self.state.dsize_bits as u8,
));
}
self.state.dsize_mask = 0xFFFF >> (16 - self.state.dsize_bits);
self.state.bit_buff = header_buf[2] as u32;
self.state.extra_bits = 0;
if bytes_read > 3 {
self.state.in_buff[..bytes_read - 3].copy_from_slice(&header_buf[3..bytes_read]);
self.state.in_bytes = bytes_read - 3;
self.state.in_pos = 0;
}
let header_data = &header_buf[..bytes_read];
self.state.initialize(header_data)?;
self.initialized = true;
Ok(())
}
async fn process_chunk(&mut self) -> Result<Option<Bytes>> {
if !self.initialized {
self.initialize().await?;
}
if self.finished {
return Ok(None);
}
if self.state.in_pos >= self.state.in_bytes {
let bytes_read = self.reader.read(&mut self.state.in_buff).await?;
if bytes_read == 0 {
self.finished = true;
return Ok(None);
}
self.state.in_bytes = bytes_read;
self.state.in_pos = 0;
}
let chunk_size = (self.state.in_bytes - self.state.in_pos).min(1024);
if chunk_size > 0 {
let chunk =
self.state.in_buff[self.state.in_pos..self.state.in_pos + chunk_size].to_vec();
self.state.in_pos += chunk_size;
Ok(Some(Bytes::from(chunk)))
} else {
self.finished = true;
Ok(None)
}
}
}
impl<R: AsyncRead + Unpin> Stream for AsyncExplodeReader<R> {
type Item = Result<Bytes>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
{
let this = self.as_mut().project();
if let Some(chunk) = this.output_buffer.pop_front() {
return Poll::Ready(Some(Ok(chunk)));
}
if *this.finished {
return Poll::Ready(None);
}
}
let this_unpinned = unsafe { self.as_mut().get_unchecked_mut() };
let poll_result = Box::pin(this_unpinned.process_chunk()).as_mut().poll(cx);
match poll_result {
Poll::Ready(Ok(Some(chunk))) => Poll::Ready(Some(Ok(chunk))),
Poll::Ready(Ok(None)) => {
let this = self.project();
*this.finished = true;
Poll::Ready(None)
}
Poll::Ready(Err(e)) => Poll::Ready(Some(Err(e))),
Poll::Pending => Poll::Pending,
}
}
}
}
#[cfg(feature = "async")]
pub use reader::AsyncExplodeReader;