use std::pin::Pin;
use std::task::ready;
use std::task::Poll;
use futures_lite::future::poll_fn;
use futures_lite::AsyncBufRead;
use futures_lite::AsyncReadExt;
use futures_lite::AsyncSeek;
use futures_lite::io::SeekFrom;
use futures_lite::AsyncSeekExt;
use crate::error::Result;
use crate::error::ZipError;
use crate::spec::headers1::EOCDRH;
use crate::spec::headers1::Signature;
use crate::spec::KnownSize;
const EOCDR_FIXED_SIZE: u64 = (Signature::SIZE + EOCDRH::SIZE) as u64;
const EOCDR_FURTHEST_BACK: u64 = EOCDR_FIXED_SIZE + u16::MAX as u64;
const EOCDR_COMM_LENGTH_OFFSET: u64 = EOCDR_FIXED_SIZE - 2;
pub(crate) struct Method0<R> {
reader: R,
}
impl<R: AsyncBufRead + AsyncSeek + Unpin> Method0<R> {
pub fn new(reader: R) -> Self {
Self { reader }
}
pub async fn locate(&mut self, eor: u64) -> Result<u64> {
if eor < EOCDR_FIXED_SIZE {
return Err(ZipError::UnableToLocateEOCDR);
}
let mut offset = eor.saturating_sub(EOCDR_FURTHEST_BACK);
self.reader.seek(SeekFrom::Start(offset)).await?;
let signature = u32::from(Signature::EOCDRH).to_le_bytes();
let mut matcher = Matcher { signature, matched: 0, candidates: Vec::new() };
loop {
let consumed = fill_and_consume(&mut self.reader, |buffer| matcher.next_buffer(buffer, offset)).await?;
if consumed == 0 {
break;
}
offset += consumed as u64;
}
matcher.candidates.retain(|offset| offset + EOCDR_FIXED_SIZE <= eor);
if matcher.candidates.len() > 1 {
for candidate in matcher.candidates.iter().rev() {
if self.comment_reaches_eof(*candidate, eor).await? {
return Ok(*candidate);
}
}
}
matcher.candidates.last().copied().ok_or(ZipError::UnableToLocateEOCDR)
}
async fn comment_reaches_eof(&mut self, offset: u64, length: u64) -> Result<bool> {
self.reader.seek(SeekFrom::Start(offset + EOCDR_COMM_LENGTH_OFFSET)).await?;
let mut buffer = [0; 2];
self.reader.read_exact(&mut buffer).await?;
Ok(offset + EOCDR_FIXED_SIZE + u16::from_le_bytes(buffer) as u64 == length)
}
}
async fn fill_and_consume<R>(reader: &mut R, mut handler: impl FnMut(&[u8])) -> std::io::Result<usize>
where
R: AsyncBufRead + Unpin + ?Sized,
{
poll_fn(|cx| {
let buffer = ready!(Pin::new(&mut *reader).poll_fill_buf(cx))?;
let consumed = buffer.len();
handler(buffer);
Pin::new(&mut *reader).consume(consumed);
Poll::Ready(Ok(consumed))
})
.await
}
struct Matcher {
signature: [u8; 4],
matched: usize,
candidates: Vec<u64>,
}
impl Matcher {
pub fn next_buffer(&mut self, buffer: &[u8], starting: u64) {
for (index, byte) in buffer.iter().enumerate() {
if self.next_byte(*byte) {
self.candidates.push(starting + index as u64 + 1 - Signature::SIZE as u64);
}
}
}
pub fn next_byte(&mut self, byte: u8) -> bool {
if byte == self.signature[self.matched] {
self.matched += 1;
if self.matched == self.signature.len() {
self.matched = 0;
return true;
}
return false;
}
if byte == self.signature[0] {
self.matched = 1;
return false;
}
self.matched = 0;
false
}
}