use crate::error::{Error, Result};
use crate::model::Model;
use crate::rc::{CarrylessRangeDecoder, RangeDecoder, RangeInput, SevenZipRangeDecoder};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Stop {
OutputFull,
Margin,
EndMarker,
Escape,
}
pub(crate) trait Cursor: RangeDecoder {
fn position(&self) -> usize;
fn padding(&self) -> u32;
}
impl<I: RangeInput> Cursor for SevenZipRangeDecoder<I> {
#[inline(always)]
fn position(&self) -> usize {
SevenZipRangeDecoder::position(self)
}
#[inline(always)]
fn padding(&self) -> u32 {
self.zero_bytes_past_eof()
}
}
impl<I: RangeInput> Cursor for CarrylessRangeDecoder<I> {
#[inline(always)]
fn position(&self) -> usize {
CarrylessRangeDecoder::position(self)
}
#[inline(always)]
fn padding(&self) -> u32 {
self.zero_bytes_past_eof()
}
}
#[inline]
pub(crate) fn fast<R: Cursor, const ESC: bool>(
model: &mut Model,
rc: &mut R,
out: &mut [u8],
fast_end: usize,
margin: usize,
esc: u8,
) -> (usize, Result<Stop>) {
debug_assert!(margin > 0);
let mut produced = 0;
while produced < out.len() {
let Some(room) = fast_end.checked_sub(rc.position()) else {
return (produced, Ok(Stop::Margin));
};
let batch = (room / margin + 1).min(out.len() - produced);
for slot in &mut out[produced..produced + batch] {
match model.decode_symbol(rc) {
Ok(Some(byte)) => {
debug_assert_eq!(rc.padding(), 0, "a symbol outran the input margin");
if ESC && byte == esc {
return (produced, Ok(Stop::Escape));
}
*slot = byte;
produced += 1;
}
Ok(None) => return (produced, Ok(Stop::EndMarker)),
Err(e) => return (produced, Err(e)),
}
}
}
(produced, Ok(Stop::OutputFull))
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct Padding {
pub(crate) before: u32,
pub(crate) allowance: u32,
pub(crate) truncates_errors: bool,
}
pub(crate) fn edge<R: Cursor, const ESC: bool>(
model: &mut Model,
rc: &mut R,
out: &mut [u8],
esc: u8,
rule: Padding,
) -> (usize, Result<Stop>) {
let mut produced = 0;
for slot in out.iter_mut() {
let result = model.decode_symbol(rc);
let padded = rule.before.saturating_add(rc.padding());
match result {
Ok(Some(byte)) => {
if padded > rule.allowance {
return (produced, Err(Error::truncated()));
}
if ESC && byte == esc {
return (produced, Ok(Stop::Escape));
}
*slot = byte;
produced += 1;
}
Ok(None) => return (produced, Ok(Stop::EndMarker)),
Err(e) => {
let e = if rule.truncates_errors && padded > rule.allowance {
Error::truncated()
} else {
e
};
return (produced, Err(e));
}
}
}
(produced, Ok(Stop::OutputFull))
}