use std::fmt;
use std::io::{Error as IoError, ErrorKind, Result};
use crate::TRACING_ENABLED;
use super::block::{BLOCK_SIZE, Header};
#[derive(Debug)]
pub enum Error {
ExpectingHeader,
ExpectingEmptyBlock,
Eof,
}
impl Error {
#[inline]
pub fn kind(&self) -> ErrorKind {
ErrorKind::InvalidData
}
}
impl std::error::Error for Error {}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::ExpectingHeader => "expecting header".fmt(f),
Self::ExpectingEmptyBlock => "expecting empty block".fmt(f),
Self::Eof => "cannot process data after eof".fmt(f),
}
}
}
impl From<Error> for IoError {
#[inline]
fn from(value: Error) -> Self {
IoError::new(value.kind(), value)
}
}
impl<T> From<Error> for Result<T> {
#[inline]
fn from(value: Error) -> Self {
Err(value.into())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum State {
ExpectingHeader,
ReceivingHeader(usize, bool),
ReceivedHeader,
ReceivingData(u64),
ReceivedData,
AligningData(usize),
AlignedData,
ReceivingEof(usize),
ReceivedEof,
}
impl Default for State {
#[inline]
fn default() -> Self {
Self::ExpectingHeader
}
}
impl State {
#[inline]
pub fn is_terminal(&self) -> bool {
matches!(self, Self::ReceivedEof | Self::ReceivingEof(0))
}
#[inline]
pub fn is_marker(&self) -> bool {
matches!(
self,
Self::ExpectingHeader
| Self::ReceivedHeader
| Self::ReceivedData
| Self::AlignedData
| Self::ReceivedEof
)
}
#[inline]
pub fn take_marker(&mut self, header: Option<&Header>) -> Result<()> {
assert!(self.is_marker(), "not a marker: {self:?}");
let (state, pos) = self.next(&[], header)?;
debug_assert_eq!(pos, 0);
*self = state;
Ok(())
}
#[inline]
pub fn take_slices<'a, I>(self, slices: I, hdr: Option<&Header>) -> Result<(Self, usize)>
where
I: Iterator<Item = &'a [u8]>,
{
let stop = [Self::ReceivedHeader, Self::ReceivedEof];
let mut needs_next = true;
let mut state = self;
let mut cur = 0usize;
for buf in slices {
if buf.is_empty() {
continue;
}
let next = state.take_until(&stop, buf, hdr)?;
state = next.0;
cur += next.1;
needs_next = false;
if stop.contains(&state) {
break;
}
}
if needs_next {
let next = state.take_until(&stop, &[], hdr)?;
state = next.0;
cur += next.1;
}
Ok((state, cur))
}
#[inline]
pub fn take_until(
self,
stop: &[Self],
buf: &[u8],
header: Option<&Header>,
) -> Result<(Self, usize)> {
let mut state = self;
let mut cur = 0usize;
let mut buf = buf;
loop {
let next = state.next(buf, header)?;
state = next.0;
cur += next.1;
buf = &buf[next.1..];
if stop.contains(&state) || buf.is_empty() {
break;
}
}
Ok((state, cur))
}
pub fn next(self, buf: &[u8], header: Option<&Header>) -> Result<(Self, usize)> {
fn advance(buf: &[u8], max: usize) -> usize {
max.min(buf.len())
}
fn read(buf: &[u8], rem: usize) -> (usize, bool) {
let len = advance(buf, rem);
let empty = buf[..len].iter().all(|b| *b == 0);
(len, empty)
}
let mut cur = 0usize;
let state = match self {
Self::ExpectingHeader => Self::ReceivingHeader(BLOCK_SIZE, true),
Self::ReceivingHeader(mut rem, mut is_empty) => {
let (len, empty) = read(buf, rem);
cur += len;
rem -= len;
is_empty = is_empty && empty;
if rem == 0 {
if is_empty {
Self::ReceivingEof(BLOCK_SIZE)
} else {
Self::ReceivedHeader
}
} else {
Self::ReceivingHeader(rem, is_empty)
}
}
Self::ReceivedHeader => {
assert!(header.is_some(), "header cannot be nil for state {self:?}");
let rem = header.unwrap().entry_size()?;
Self::ReceivingData(rem)
}
Self::ReceivingData(mut rem) => {
let len = advance(buf, rem as usize);
cur += len;
rem -= len as u64;
if rem == 0 {
Self::ReceivedData
} else {
Self::ReceivingData(rem)
}
}
Self::ReceivedData => {
assert!(header.is_some(), "header cannot be nil for state {self:?}");
let rem = header.unwrap().entry_size()?;
let align = rem.next_multiple_of(BLOCK_SIZE as u64) - rem;
Self::AligningData(align as usize)
}
Self::AligningData(mut rem) => {
let len = advance(buf, rem);
cur += len;
rem -= len;
if rem == 0 {
Self::AlignedData
} else {
Self::AligningData(rem)
}
}
Self::AlignedData => Self::ExpectingHeader,
Self::ReceivingEof(mut rem) => {
let (len, empty) = read(buf, rem);
cur += len;
rem -= len;
if !empty {
return Error::ExpectingEmptyBlock.into();
}
if rem == 0 {
Self::ReceivedEof
} else {
Self::ReceivingEof(rem)
}
}
Self::ReceivedEof => return Error::Eof.into(),
};
if TRACING_ENABLED {
eprintln!(" | next: {self:?} -> {state:?}");
}
Ok((state, cur))
}
}
#[cfg(test)]
mod tests {
use crate::shared::block::Block;
use crate::shared::test::*;
use super::*;
#[test]
fn basic() {
let data = make_archive_data(&[("1000", 1000)]);
let mut hdr: Option<&Header> = None;
let state = State::default();
assert_eq!(state, State::ExpectingHeader);
let d = &data[..];
let (state, pos) = state.next(d, hdr).unwrap();
assert_eq!(state, State::ReceivingHeader(BLOCK_SIZE, true));
assert_eq!(pos, 0);
let n = 250usize;
let (state, pos) = state.next(&d[..n], hdr).unwrap();
assert_eq!(state, State::ReceivingHeader(BLOCK_SIZE - n, false));
assert_eq!(pos, n);
let d = &d[n..];
{
let n = 300usize;
let (state, pos) = state.next(&d[..n], hdr).unwrap();
assert_eq!((state, pos), (State::ReceivedHeader, 262));
}
let n = 262usize;
let (state, pos) = state.next(&d[..n], hdr).unwrap();
assert_eq!(state, State::ReceivedHeader);
assert_eq!(pos, n);
hdr = Some(Block::from_bytes(&data[..BLOCK_SIZE]).as_header().unwrap());
let d = &d[n..];
let (state, pos) = state.next(d, hdr).unwrap();
assert_eq!(state, State::ReceivingData(1000));
assert_eq!(pos, 0);
let n = 500usize;
let (state, pos) = state.next(&d[..n], hdr).unwrap();
assert_eq!(state, State::ReceivingData((1000 - n) as u64));
assert_eq!(pos, n);
let d = &d[n..];
let n = 500usize;
let (state, pos) = state.next(&d[..n], hdr).unwrap();
assert_eq!(state, State::ReceivedData);
assert_eq!(pos, n);
let d = &d[n..];
let (state, pos) = state.next(d, hdr).unwrap();
assert_eq!(state, State::AligningData(24));
assert_eq!(pos, 0);
let n = 10usize;
let (state, pos) = state.next(&d[..n], hdr).unwrap();
assert_eq!(state, State::AligningData(14));
assert_eq!(pos, n);
let d = &d[n..];
let n = 14usize;
let (state, pos) = state.next(&d[..n], hdr).unwrap();
assert_eq!(state, State::AlignedData);
assert_eq!(pos, n);
hdr = None;
let d = &d[n..];
let (state, pos) = state.next(d, hdr).unwrap();
assert_eq!(state, State::ExpectingHeader);
assert_eq!(pos, 0);
let (state, pos) = state.next(d, hdr).unwrap();
assert_eq!(state, State::ReceivingHeader(BLOCK_SIZE, true));
assert_eq!(pos, 0);
let n = 256usize;
let (state, pos) = state.next(&d[..n], hdr).unwrap();
assert_eq!(state, State::ReceivingHeader(BLOCK_SIZE - n, true));
assert_eq!(pos, n);
let d = &d[n..];
{
let n = 356usize;
let (state, pos) = state.next(&d[..n], hdr).unwrap();
assert_eq!(state, State::ReceivingEof(BLOCK_SIZE));
assert_eq!(pos, n - 100);
}
let n = 256usize;
let (state, pos) = state.next(&d[..n], hdr).unwrap();
assert_eq!(state, State::ReceivingEof(BLOCK_SIZE));
assert_eq!(pos, n);
let d = &d[n..];
let n = 256usize;
let (state, pos) = state.next(&d[..n], hdr).unwrap();
assert_eq!(state, State::ReceivingEof(BLOCK_SIZE - n));
assert_eq!(pos, n);
let d = &d[n..];
let n = 256usize;
let (state, pos) = state.next(&d[..n], hdr).unwrap();
assert_eq!(state, State::ReceivedEof);
assert_eq!(pos, n);
let d = &d[n..];
assert_eq!(d.len(), 0);
}
}