use crate::algorithm::Algorithm;
use crate::core::error::{ErrorContext, Res, ResultBuilder, SliceErrors, WithContext};
use crate::core::pointer::{Pointer, PointerDest};
use crate::core::{pointer, Core};
#[derive(Default)]
pub(crate) struct KrakenLzTable {
cmd_stream: Pointer<{ PointerDest::SCRATCH }>,
cmd_stream_size: usize,
offs_stream: Vec<i32>,
lit_stream: Pointer<{ PointerDest::SCRATCH }>,
lit_stream_size: usize,
len_stream: Vec<i32>,
}
impl ErrorContext for KrakenLzTable {}
#[derive(Debug)]
pub(crate) struct Kraken;
impl Algorithm for Kraken {
fn process(
&self,
core: &mut Core,
mode: usize,
src: Pointer<{ PointerDest::INPUT }>,
src_used: usize,
dst_start: Pointer<{ PointerDest::OUTPUT }>,
dst: Pointer<{ PointerDest::OUTPUT }>,
dst_size: usize,
) -> Res<()> {
let mut lz = KrakenLzTable::default();
lz.assert_le(mode, 1)?;
let offset = dst - dst_start;
lz.read_lz_table(core, src, src + src_used, dst, dst_size, offset)?;
if mode == 0 {
lz.process_lz_runs::<true>(core, dst, dst_size, offset)
} else {
lz.process_lz_runs::<false>(core, dst, dst_size, offset)
}
}
}
impl KrakenLzTable {
fn read_lz_table(
&mut self,
core: &mut Core,
mut src: Pointer<{ PointerDest::INPUT }>,
src_end: Pointer<{ PointerDest::INPUT }>,
mut dst: Pointer<{ PointerDest::OUTPUT }>,
dst_size: usize,
offset: usize,
) -> Res<()> {
let mut out;
let mut decode_count = 0;
let mut n;
let mut packed_offs_stream;
let mut packed_len_stream;
let mut scratch = pointer::scratch(0);
self.assert_le(13, src_end - src)?;
if offset == 0 {
core.copy_bytes(dst, src, 8).at(self)?;
dst += 8;
src += 8;
}
let flag = core.get_byte(src).at(self)? as usize;
if flag & 0x80 != 0 {
src += 1;
self.assert_eq(flag & 0xc0, 0x80)
.message(|_| format!("reserved flag set {:X}", flag))?;
self.assert_eq(flag & 0x80, 0)
.msg_of(&"excess bytes not supported")?;
}
out = scratch;
n = core
.decode_bytes(
&mut out,
src,
src_end,
&mut decode_count,
dst_size,
false,
scratch,
)
.at(self)?;
self.lit_stream = out;
self.lit_stream_size = decode_count;
src += n;
scratch += decode_count;
out = scratch;
n = core
.decode_bytes(
&mut out,
src,
src_end,
&mut decode_count,
dst_size,
false,
scratch,
)
.at(self)?;
src += n;
self.cmd_stream = out;
self.cmd_stream_size = decode_count;
scratch += decode_count;
self.assert_le(3, src_end - src)?;
let mut offs_scaling = 0;
let mut packed_offs_stream_extra = None;
let mut offs_stream_size = 0;
if (core.get_byte(src).at(self)? as usize) & 0x80 != 0 {
offs_scaling = i32::from(core.get_byte(src).at(self)?) - 127;
src += 1;
packed_offs_stream = scratch;
n = core
.decode_bytes(
&mut packed_offs_stream,
src,
src_end,
&mut offs_stream_size,
self.cmd_stream_size,
false,
scratch,
)
.at(self)?;
src += n;
scratch += offs_stream_size;
if offs_scaling != 1 {
packed_offs_stream_extra = Some(scratch);
n = core
.decode_bytes(
packed_offs_stream_extra
.as_mut()
.expect("value assigned just above"),
src,
src_end,
&mut decode_count,
offs_stream_size,
false,
scratch,
)
.at(self)?;
self.assert_eq(decode_count, offs_stream_size)?;
src += n;
scratch += decode_count;
}
} else {
packed_offs_stream = scratch;
n = core
.decode_bytes(
&mut packed_offs_stream,
src,
src_end,
&mut offs_stream_size,
self.cmd_stream_size,
false,
scratch,
)
.at(self)?;
src += n;
scratch += offs_stream_size;
}
packed_len_stream = scratch;
let mut len_stream_size = 0;
n = core
.decode_bytes(
&mut packed_len_stream,
src,
src_end,
&mut len_stream_size,
dst_size >> 2,
false,
scratch,
)
.at(self)?;
src += n;
scratch += len_stream_size;
self.offs_stream = vec![0; offs_stream_size];
self.len_stream = vec![0; len_stream_size];
if let Some(extra) = packed_offs_stream_extra {
core.unpack_offsets(
src,
src_end,
packed_offs_stream,
extra,
offs_scaling,
packed_len_stream,
self.offs_stream.as_mut(),
self.len_stream.as_mut(),
false,
)
.at(self)?
} else {
core.unpack_offsets(
src,
src_end,
packed_offs_stream,
pointer::null(),
offs_scaling,
packed_len_stream,
self.offs_stream.as_mut(),
self.len_stream.as_mut(),
false,
)
.at(self)?
}
Ok(())
}
fn process_lz_runs<const MODE_0: bool>(
&mut self,
core: &mut Core,
mut dst: Pointer<{ PointerDest::OUTPUT }>,
dst_size: usize,
offset: usize,
) -> Res<()> {
let dst_end = dst + dst_size;
if offset == 0 {
dst += 8
};
let mut cmd_stream = self.cmd_stream;
let cmd_stream_end = cmd_stream + self.cmd_stream_size;
let mut len_stream = self.len_stream.iter().copied();
let mut offs_stream = self.offs_stream.iter().copied().peekable();
let mut lit_stream = self.lit_stream;
let lit_stream_end = self.lit_stream + self.lit_stream_size;
let mut copyfrom;
let mut offset;
let mut recent_offs = [0; 7];
let mut last_offset: i32;
recent_offs[3] = -8;
recent_offs[4] = -8;
recent_offs[5] = -8;
last_offset = -8;
while cmd_stream < cmd_stream_end {
let f = core.get_byte(cmd_stream).at(self)? as usize;
cmd_stream += 1;
let mut litlen = f & 3;
let offs_index = f >> 6;
let mut matchlen = (f >> 2) & 0xF;
if litlen == 3 {
litlen = len_stream.next().err()? as _
}
recent_offs[6] = offs_stream.peek().copied().unwrap_or_default();
if MODE_0 {
core.copy_64_add(dst, lit_stream, dst + last_offset, litlen)
.at(self)?;
} else {
core.copy_bytes(dst, lit_stream, litlen).at(self)?;
}
dst += litlen;
lit_stream += litlen;
offset = recent_offs.get_copy(offs_index + 3)?;
recent_offs.copy_within(offs_index..offs_index + 3, offs_index + 1);
recent_offs[3] = offset;
last_offset = offset;
if offs_index == 3 {
offs_stream.next();
}
copyfrom = dst + offset;
if matchlen != 15 {
core.repeat_copy_64(dst, copyfrom, matchlen + 2).at(self)?;
dst += matchlen + 2;
} else {
matchlen = (14 + len_stream.next().err()?) as usize;
core.repeat_copy_64(dst, copyfrom, matchlen).at(self)?;
dst += matchlen;
}
}
self.assert_eq(offs_stream.len(), 0)?;
self.assert_eq(len_stream.len(), 0)?;
let final_len = dst_end - dst;
self.assert_eq(final_len, lit_stream_end - lit_stream)?;
if MODE_0 {
core.copy_64_add(dst, lit_stream, dst + last_offset, final_len)
.at(self)?;
} else {
core.copy_bytes(dst, lit_stream, final_len).at(self)?;
}
Ok(())
}
}