use super::bits::Bits;
use super::block::{self, Item, Symbols};
use super::{Error, ErrorKind};
pub trait Source {
fn view(&mut self, from: u64, want: usize) -> Result<(&[u8], bool), ErrorKind>;
fn release(&mut self, _upto: u64) {}
}
pub struct SliceSource<'a>(pub &'a [u8]);
impl Source for SliceSource<'_> {
fn view(&mut self, from: u64, _want: usize) -> Result<(&[u8], bool), ErrorKind> {
Ok((self.0.get(from as usize..).unwrap_or(&[]), true))
}
}
#[allow(clippy::large_enum_variant)] pub enum Event {
Block {
index: u64,
bit: u64,
end: u64,
stream: u64,
level: u32,
symbols: Symbols,
},
Done,
}
enum State {
Header { first: bool },
Stream { level: u32, combined: u32 },
Done,
}
const LOOKAHEAD: usize = 2 << 20;
const MAX_ITEM: usize = 64 << 20;
pub struct Scanner<S> {
src: S,
bit: u64,
state: State,
blocks: u64,
streams: u64,
lookahead: usize,
}
impl<S: Source> Scanner<S> {
pub fn new(src: S) -> Self {
Scanner {
src,
bit: 0,
state: State::Header { first: true },
blocks: 0,
streams: 0,
lookahead: LOOKAHEAD,
}
}
pub fn next(&mut self, mut syms: Vec<u16>) -> Result<Event, Error> {
loop {
match self.state {
State::Done => return Ok(Event::Done),
State::Header { first } => {
let at = self.bit;
let err = |kind| Error {
kind,
block: None,
bit: Some(at),
};
let (data, eof) = self.src.view(at / 8, 4).map_err(err)?;
if data.is_empty() && eof {
if first {
return Err(err(ErrorKind::Empty));
}
self.state = State::Done;
continue;
}
match data {
[b'B', b'Z', b'h', l @ b'1'..=b'9', ..] => {
self.state = State::Stream {
level: (l - b'0') as u32,
combined: 0,
};
self.bit += 32;
}
_ => {
return Err(err(if first {
ErrorKind::NotBz2
} else {
ErrorKind::TrailingData
}))
}
}
}
State::Stream { level, combined } => {
let at = self.bit;
let index = self.blocks;
let err = |kind| Error {
kind,
block: Some(index),
bit: Some(at),
};
let base = at / 8;
let (data, eof) = self.src.view(base, self.lookahead).map_err(err)?;
let mut br = Bits::at(data, at % 8);
let item = block::read_item(&mut br, level, syms);
let used = br.bit_pos() - at % 8;
if br.overran() {
if eof {
return Err(err(ErrorKind::Truncated));
}
if self.lookahead >= MAX_ITEM {
return Err(err(ErrorKind::BlockTooLarge));
}
self.lookahead = (self.lookahead * 2).max(data.len() + 1).min(MAX_ITEM);
syms = match item {
Ok(Item::Block(b)) => b.syms,
Ok(Item::End(_)) => Vec::new(),
Err((_, s)) => s,
};
continue;
}
self.bit += used;
self.src.release(self.bit / 8);
match item.map_err(|(kind, _)| err(kind))? {
Item::End(stored) => {
if stored != combined {
return Err(err(ErrorKind::StreamCrc {
stored,
computed: combined,
}));
}
self.bit = self.bit.div_ceil(8) * 8;
self.state = State::Header { first: false };
self.streams += 1;
syms = Vec::new();
}
Item::Block(symbols) => {
self.state = State::Stream {
level,
combined: combined.rotate_left(1) ^ symbols.crc,
};
self.blocks += 1;
let (end, stream) = (self.bit, self.streams);
return Ok(Event::Block {
index,
bit: at,
end,
stream,
level,
symbols,
});
}
}
}
}
}
}
}