use alloc::vec::Vec;
use crate::error::Error;
use crate::traits::{Algorithm, RawDecoder, RawEncoder, RawProgress};
use super::block;
const MAGIC: u32 = 0x184D_2204;
const FLG_VERSION_MASK: u8 = 0b1100_0000;
const FLG_VERSION_BITS: u8 = 0b0100_0000;
const FLG_BLOCK_INDEP: u8 = 1 << 5;
const FLG_BLOCK_CHECKSUM: u8 = 1 << 4;
const FLG_CONTENT_SIZE: u8 = 1 << 3;
const FLG_CONTENT_CHECKSUM: u8 = 1 << 2;
const FLG_RESERVED: u8 = 1 << 1;
const FLG_DICT_ID: u8 = 1 << 0;
const BD_RESERVED_HIGH: u8 = 1 << 7;
const BD_RESERVED_LOW: u8 = 0b0000_1111;
const BD_BLOCK_MAX_SHIFT: u32 = 4;
const WINDOW_SIZE: usize = 64 * 1024;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum BlockMaxSize {
#[default]
Max64KB,
Max256KB,
Max1MB,
Max4MB,
}
impl BlockMaxSize {
pub const fn bd_value(self) -> u8 {
match self {
BlockMaxSize::Max64KB => 4,
BlockMaxSize::Max256KB => 5,
BlockMaxSize::Max1MB => 6,
BlockMaxSize::Max4MB => 7,
}
}
pub const fn bytes(self) -> usize {
match self {
BlockMaxSize::Max64KB => 64 * 1024,
BlockMaxSize::Max256KB => 256 * 1024,
BlockMaxSize::Max1MB => 1024 * 1024,
BlockMaxSize::Max4MB => 4 * 1024 * 1024,
}
}
fn from_bd_value(v: u8) -> Result<Self, Error> {
match v {
4 => Ok(BlockMaxSize::Max64KB),
5 => Ok(BlockMaxSize::Max256KB),
6 => Ok(BlockMaxSize::Max1MB),
7 => Ok(BlockMaxSize::Max4MB),
_ => Err(Error::Unsupported),
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct LZ4Frame;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EncoderConfig {
pub block_max_size: BlockMaxSize,
pub block_independence: bool,
pub block_checksum: bool,
pub content_checksum: bool,
}
impl Default for EncoderConfig {
fn default() -> Self {
Self {
block_max_size: BlockMaxSize::Max64KB,
block_independence: false,
block_checksum: false,
content_checksum: true,
}
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct DecoderConfig;
impl Algorithm for LZ4Frame {
const NAME: &'static str = "lz4-frame";
type Encoder = Encoder;
type Decoder = Decoder;
type EncoderConfig = EncoderConfig;
type DecoderConfig = DecoderConfig;
fn encoder_with(c: EncoderConfig) -> Encoder {
Encoder::with_config(c)
}
fn decoder_with(_: DecoderConfig) -> Decoder {
Decoder::new()
}
}
#[derive(Clone, Copy)]
struct XxHash32 {
total_len: u64,
v1: u32,
v2: u32,
v3: u32,
v4: u32,
buf: [u8; 16],
buf_len: u8,
primed: bool,
}
const XXH_PRIME32_1: u32 = 0x9E37_79B1;
const XXH_PRIME32_2: u32 = 0x85EB_CA77;
const XXH_PRIME32_3: u32 = 0xC2B2_AE3D;
const XXH_PRIME32_4: u32 = 0x27D4_EB2F;
const XXH_PRIME32_5: u32 = 0x1656_67B1;
impl XxHash32 {
fn new() -> Self {
Self {
total_len: 0,
v1: 0,
v2: 0,
v3: 0,
v4: 0,
buf: [0; 16],
buf_len: 0,
primed: false,
}
}
#[inline]
fn round(acc: u32, lane: u32) -> u32 {
acc.wrapping_add(lane.wrapping_mul(XXH_PRIME32_2))
.rotate_left(13)
.wrapping_mul(XXH_PRIME32_1)
}
fn process_stripe(&mut self, s: &[u8; 16]) {
if !self.primed {
self.v1 = XXH_PRIME32_1.wrapping_add(XXH_PRIME32_2);
self.v2 = XXH_PRIME32_2;
self.v3 = 0;
self.v4 = 0u32.wrapping_sub(XXH_PRIME32_1);
self.primed = true;
}
let lane = |i: usize| u32::from_le_bytes([s[i], s[i + 1], s[i + 2], s[i + 3]]);
self.v1 = Self::round(self.v1, lane(0));
self.v2 = Self::round(self.v2, lane(4));
self.v3 = Self::round(self.v3, lane(8));
self.v4 = Self::round(self.v4, lane(12));
}
fn update(&mut self, mut data: &[u8]) {
self.total_len = self.total_len.wrapping_add(data.len() as u64);
if self.buf_len > 0 {
let need = 16 - self.buf_len as usize;
let take = data.len().min(need);
self.buf[self.buf_len as usize..self.buf_len as usize + take]
.copy_from_slice(&data[..take]);
self.buf_len += take as u8;
data = &data[take..];
if self.buf_len < 16 {
return;
}
let stripe = self.buf;
self.process_stripe(&stripe);
self.buf_len = 0;
}
while data.len() >= 16 {
let mut stripe = [0u8; 16];
stripe.copy_from_slice(&data[..16]);
self.process_stripe(&stripe);
data = &data[16..];
}
if !data.is_empty() {
self.buf[..data.len()].copy_from_slice(data);
self.buf_len = data.len() as u8;
}
}
fn finish(self) -> u32 {
let mut h: u32;
if self.primed {
h = self
.v1
.rotate_left(1)
.wrapping_add(self.v2.rotate_left(7))
.wrapping_add(self.v3.rotate_left(12))
.wrapping_add(self.v4.rotate_left(18));
} else {
h = XXH_PRIME32_5;
}
h = h.wrapping_add(self.total_len as u32);
let tail = &self.buf[..self.buf_len as usize];
let mut i = 0;
while i + 4 <= tail.len() {
let lane = u32::from_le_bytes([tail[i], tail[i + 1], tail[i + 2], tail[i + 3]]);
h = h
.wrapping_add(lane.wrapping_mul(XXH_PRIME32_3))
.rotate_left(17)
.wrapping_mul(XXH_PRIME32_4);
i += 4;
}
while i < tail.len() {
h = h
.wrapping_add((tail[i] as u32).wrapping_mul(XXH_PRIME32_5))
.rotate_left(11)
.wrapping_mul(XXH_PRIME32_1);
i += 1;
}
h ^= h >> 15;
h = h.wrapping_mul(XXH_PRIME32_2);
h ^= h >> 13;
h = h.wrapping_mul(XXH_PRIME32_3);
h ^= h >> 16;
h
}
}
fn xxhash32(data: &[u8]) -> u32 {
let mut h = XxHash32::new();
h.update(data);
h.finish()
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum EncPhase {
Header,
Buffering,
Flushing,
Terminating,
Done,
}
pub struct Encoder {
cfg: EncoderConfig,
block_size: usize,
raw: Vec<u8>,
staged: Vec<u8>,
staged_idx: usize,
phase: EncPhase,
content_hash: XxHash32,
}
impl Encoder {
pub fn new() -> Self {
Self::with_config(EncoderConfig::default())
}
pub fn with_config(cfg: EncoderConfig) -> Self {
let bs = cfg.block_max_size.bytes();
let mut enc = Self {
cfg,
block_size: bs,
raw: Vec::with_capacity(bs),
staged: Vec::with_capacity(block::compress_bound(bs) + 16),
staged_idx: 0,
phase: EncPhase::Header,
content_hash: XxHash32::new(),
};
enc.build_header();
enc
}
fn build_header(&mut self) {
self.staged.clear();
self.staged.extend_from_slice(&MAGIC.to_le_bytes());
let mut flg = FLG_VERSION_BITS;
if self.cfg.block_independence {
flg |= FLG_BLOCK_INDEP;
}
if self.cfg.block_checksum {
flg |= FLG_BLOCK_CHECKSUM;
}
if self.cfg.content_checksum {
flg |= FLG_CONTENT_CHECKSUM;
}
let bd = self.cfg.block_max_size.bd_value() << BD_BLOCK_MAX_SHIFT;
let descriptor = [flg, bd];
let hc = (xxhash32(&descriptor) >> 8) as u8;
self.staged.extend_from_slice(&descriptor);
self.staged.push(hc);
self.staged_idx = 0;
}
fn build_block(&mut self) {
debug_assert!(!self.raw.is_empty());
if self.cfg.content_checksum {
self.content_hash.update(&self.raw);
}
let mut compressed = Vec::with_capacity(block::compress_bound(self.raw.len()));
block::encode_block(&self.raw, &mut compressed);
self.staged.clear();
let (payload_is_raw, payload): (bool, &[u8]) = if compressed.len() >= self.raw.len() {
(true, &self.raw[..])
} else {
(false, &compressed[..])
};
let mut size_word = payload.len() as u32;
if payload_is_raw {
size_word |= 0x8000_0000;
}
self.staged.extend_from_slice(&size_word.to_le_bytes());
self.staged.extend_from_slice(payload);
if self.cfg.block_checksum {
let h = xxhash32(payload);
self.staged.extend_from_slice(&h.to_le_bytes());
}
self.raw.clear();
self.staged_idx = 0;
self.phase = EncPhase::Flushing;
}
fn build_trailer(&mut self) {
self.staged.clear();
self.staged.extend_from_slice(&0u32.to_le_bytes());
if self.cfg.content_checksum {
let h = core::mem::replace(&mut self.content_hash, XxHash32::new()).finish();
self.staged.extend_from_slice(&h.to_le_bytes());
}
self.staged_idx = 0;
self.phase = EncPhase::Terminating;
}
fn drain_staged(&mut self, output: &mut [u8], written: &mut usize) {
let avail = self.staged.len() - self.staged_idx;
let space = output.len() - *written;
let n = avail.min(space);
if n > 0 {
output[*written..*written + n]
.copy_from_slice(&self.staged[self.staged_idx..self.staged_idx + n]);
self.staged_idx += n;
*written += n;
}
}
fn staged_done(&self) -> bool {
self.staged_idx == self.staged.len()
}
}
impl Default for Encoder {
fn default() -> Self {
Self::new()
}
}
impl RawEncoder for Encoder {
fn raw_encode(&mut self, input: &[u8], output: &mut [u8]) -> Result<RawProgress, Error> {
let mut consumed = 0usize;
let mut written = 0usize;
loop {
match self.phase {
EncPhase::Header => {
self.drain_staged(output, &mut written);
if !self.staged_done() {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
self.staged.clear();
self.staged_idx = 0;
self.phase = EncPhase::Buffering;
}
EncPhase::Flushing => {
self.drain_staged(output, &mut written);
if !self.staged_done() {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
self.staged.clear();
self.staged_idx = 0;
self.phase = EncPhase::Buffering;
}
EncPhase::Buffering => {
if consumed == input.len() {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
let room = self.block_size - self.raw.len();
let take = (input.len() - consumed).min(room);
self.raw
.extend_from_slice(&input[consumed..consumed + take]);
consumed += take;
if self.raw.len() == self.block_size {
self.build_block();
}
}
EncPhase::Terminating | EncPhase::Done => {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
}
}
}
fn raw_finish(&mut self, output: &mut [u8]) -> Result<RawProgress, Error> {
let mut written = 0usize;
loop {
match self.phase {
EncPhase::Header => {
self.drain_staged(output, &mut written);
if !self.staged_done() {
return Ok(RawProgress {
consumed: 0,
written,
done: false,
});
}
self.staged.clear();
self.staged_idx = 0;
self.phase = EncPhase::Buffering;
}
EncPhase::Buffering => {
if !self.raw.is_empty() {
self.build_block();
} else {
self.build_trailer();
}
}
EncPhase::Flushing => {
self.drain_staged(output, &mut written);
if !self.staged_done() {
return Ok(RawProgress {
consumed: 0,
written,
done: false,
});
}
self.staged.clear();
self.staged_idx = 0;
self.phase = EncPhase::Buffering;
}
EncPhase::Terminating => {
self.drain_staged(output, &mut written);
if !self.staged_done() {
return Ok(RawProgress {
consumed: 0,
written,
done: false,
});
}
self.staged.clear();
self.staged_idx = 0;
self.phase = EncPhase::Done;
}
EncPhase::Done => {
return Ok(RawProgress {
consumed: 0,
written,
done: true,
});
}
}
}
}
fn raw_reset(&mut self) {
self.raw.clear();
self.staged.clear();
self.staged_idx = 0;
self.content_hash = XxHash32::new();
self.phase = EncPhase::Header;
self.build_header();
}
}
const MAX_BLOCK_BYTES: usize = 4 * 1024 * 1024;
fn max_compressed_block(raw_max: usize) -> usize {
block::compress_bound(raw_max).max(raw_max)
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum DecPhase {
Magic,
FlgBd,
DescriptorTail,
Hc,
BlockSize,
BlockData,
BlockChecksum,
Decode,
Draining,
ContentChecksum,
Done,
}
pub struct Decoder {
phase: DecPhase,
accum: [u8; 12],
accum_len: u8,
accum_need: u8,
flg: u8,
bd: u8,
desc_remaining: u8,
descriptor: Vec<u8>,
block_max: BlockMaxSize,
block_independent: bool,
block_checksum: bool,
content_checksum: bool,
cur_block_len: u32,
cur_block_uncompressed: bool,
compressed: Vec<u8>,
decoded: Vec<u8>,
decoded_idx: usize,
window: Vec<u8>,
content_hash: XxHash32,
poisoned: bool,
}
impl Decoder {
pub fn new() -> Self {
Self {
phase: DecPhase::Magic,
accum: [0; 12],
accum_len: 0,
accum_need: 4,
flg: 0,
bd: 0,
desc_remaining: 0,
descriptor: Vec::with_capacity(14),
block_max: BlockMaxSize::Max64KB,
block_independent: false,
block_checksum: false,
content_checksum: false,
cur_block_len: 0,
cur_block_uncompressed: false,
compressed: Vec::new(),
decoded: Vec::new(),
decoded_idx: 0,
window: Vec::with_capacity(WINDOW_SIZE),
content_hash: XxHash32::new(),
poisoned: false,
}
}
fn poison<T>(&mut self, e: Error) -> Result<T, Error> {
self.poisoned = true;
Err(e)
}
fn fill_accum(&mut self, input: &[u8], consumed: &mut usize) -> bool {
while (self.accum_len as usize) < (self.accum_need as usize) && *consumed < input.len() {
self.accum[self.accum_len as usize] = input[*consumed];
self.accum_len += 1;
*consumed += 1;
}
self.accum_len == self.accum_need
}
fn drain_decoded(&mut self, output: &mut [u8], written: &mut usize) {
let avail = self.decoded.len() - self.decoded_idx;
let space = output.len() - *written;
let n = avail.min(space);
if n > 0 {
output[*written..*written + n]
.copy_from_slice(&self.decoded[self.decoded_idx..self.decoded_idx + n]);
self.decoded_idx += n;
*written += n;
}
}
fn push_window(&mut self, bytes: &[u8]) {
if self.block_independent {
return;
}
if bytes.len() >= WINDOW_SIZE {
self.window.clear();
self.window
.extend_from_slice(&bytes[bytes.len() - WINDOW_SIZE..]);
return;
}
let combined = self.window.len() + bytes.len();
if combined <= WINDOW_SIZE {
self.window.extend_from_slice(bytes);
} else {
let drop = combined - WINDOW_SIZE;
self.window.drain(..drop);
self.window.extend_from_slice(bytes);
}
}
fn parse_flg_bd(&mut self) -> Result<(), Error> {
let flg = self.accum[0];
let bd = self.accum[1];
if (flg & FLG_VERSION_MASK) != FLG_VERSION_BITS {
return Err(Error::Unsupported);
}
if (flg & FLG_RESERVED) != 0 {
return Err(Error::BadHeader);
}
if (bd & BD_RESERVED_HIGH) != 0 || (bd & BD_RESERVED_LOW) != 0 {
return Err(Error::BadHeader);
}
let bd_val = (bd >> BD_BLOCK_MAX_SHIFT) & 0b0111;
self.block_max = BlockMaxSize::from_bd_value(bd_val)?;
self.block_independent = (flg & FLG_BLOCK_INDEP) != 0;
self.block_checksum = (flg & FLG_BLOCK_CHECKSUM) != 0;
self.content_checksum = (flg & FLG_CONTENT_CHECKSUM) != 0;
self.flg = flg;
self.bd = bd;
self.descriptor.clear();
self.descriptor.push(flg);
self.descriptor.push(bd);
let mut tail = 0u8;
if (flg & FLG_CONTENT_SIZE) != 0 {
tail += 8;
}
if (flg & FLG_DICT_ID) != 0 {
tail += 4;
}
self.desc_remaining = tail;
if tail > 0 {
self.phase = DecPhase::DescriptorTail;
self.accum_len = 0;
self.accum_need = tail;
} else {
self.phase = DecPhase::Hc;
self.accum_len = 0;
self.accum_need = 1;
}
Ok(())
}
}
impl Default for Decoder {
fn default() -> Self {
Self::new()
}
}
impl RawDecoder for Decoder {
fn raw_decode(&mut self, input: &[u8], output: &mut [u8]) -> Result<RawProgress, Error> {
if self.poisoned {
return Err(Error::Corrupt);
}
let mut consumed = 0usize;
let mut written = 0usize;
loop {
match self.phase {
DecPhase::Magic => {
if !self.fill_accum(input, &mut consumed) {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
let m = u32::from_le_bytes([
self.accum[0],
self.accum[1],
self.accum[2],
self.accum[3],
]);
if m != MAGIC {
return self.poison(Error::BadHeader);
}
self.phase = DecPhase::FlgBd;
self.accum_len = 0;
self.accum_need = 2;
}
DecPhase::FlgBd => {
if !self.fill_accum(input, &mut consumed) {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
if let Err(e) = self.parse_flg_bd() {
return self.poison(e);
}
}
DecPhase::DescriptorTail => {
if !self.fill_accum(input, &mut consumed) {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
self.descriptor
.extend_from_slice(&self.accum[..self.accum_len as usize]);
self.phase = DecPhase::Hc;
self.accum_len = 0;
self.accum_need = 1;
}
DecPhase::Hc => {
if !self.fill_accum(input, &mut consumed) {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
let hc = self.accum[0];
let expected = (xxhash32(&self.descriptor) >> 8) as u8;
if hc != expected {
return self.poison(Error::BadHeader);
}
self.descriptor.clear();
self.phase = DecPhase::BlockSize;
self.accum_len = 0;
self.accum_need = 4;
}
DecPhase::BlockSize => {
if !self.fill_accum(input, &mut consumed) {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
let raw_word = u32::from_le_bytes([
self.accum[0],
self.accum[1],
self.accum[2],
self.accum[3],
]);
if raw_word == 0 {
if self.content_checksum {
self.phase = DecPhase::ContentChecksum;
self.accum_len = 0;
self.accum_need = 4;
} else {
self.phase = DecPhase::Done;
return Ok(RawProgress {
consumed,
written,
done: true,
});
}
continue;
}
let uncompressed = (raw_word & 0x8000_0000) != 0;
let len = (raw_word & 0x7FFF_FFFF) as usize;
let raw_max = self.block_max.bytes();
let bound = max_compressed_block(raw_max).min(MAX_BLOCK_BYTES);
if len == 0 {
return self.poison(Error::BadHeader);
}
if len > bound {
return self.poison(Error::Corrupt);
}
if uncompressed && len > raw_max {
return self.poison(Error::Corrupt);
}
self.cur_block_len = len as u32;
self.cur_block_uncompressed = uncompressed;
self.compressed.clear();
self.compressed.reserve(len);
self.phase = DecPhase::BlockData;
self.accum_len = 0;
}
DecPhase::BlockData => {
let need = self.cur_block_len as usize - self.compressed.len();
let avail = input.len() - consumed;
let take = need.min(avail);
if take > 0 {
self.compressed
.extend_from_slice(&input[consumed..consumed + take]);
consumed += take;
}
if self.compressed.len() < self.cur_block_len as usize {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
if self.block_checksum {
self.phase = DecPhase::BlockChecksum;
self.accum_len = 0;
self.accum_need = 4;
} else {
self.phase = DecPhase::Decode;
}
}
DecPhase::BlockChecksum => {
if !self.fill_accum(input, &mut consumed) {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
let expected = u32::from_le_bytes([
self.accum[0],
self.accum[1],
self.accum[2],
self.accum[3],
]);
let actual = xxhash32(&self.compressed);
if actual != expected {
return self.poison(Error::ChecksumMismatch);
}
self.phase = DecPhase::Decode;
}
DecPhase::Decode => {
self.decoded.clear();
if self.cur_block_uncompressed {
self.decoded.extend_from_slice(&self.compressed);
} else if self.block_independent {
if let Err(e) = block::decode_block(&self.compressed, &mut self.decoded) {
return self.poison(e);
}
} else {
if let Err(e) = decode_linked_block(
&self.compressed,
&self.window,
&mut self.decoded,
self.block_max.bytes(),
) {
return self.poison(e);
}
}
if self.content_checksum {
self.content_hash.update(&self.decoded);
}
if !self.block_independent {
let bytes = core::mem::take(&mut self.decoded);
self.push_window(&bytes);
self.decoded = bytes;
}
self.decoded_idx = 0;
self.phase = DecPhase::Draining;
}
DecPhase::Draining => {
self.drain_decoded(output, &mut written);
if self.decoded_idx < self.decoded.len() {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
self.decoded.clear();
self.decoded_idx = 0;
self.phase = DecPhase::BlockSize;
self.accum_len = 0;
self.accum_need = 4;
}
DecPhase::ContentChecksum => {
if !self.fill_accum(input, &mut consumed) {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
let expected = u32::from_le_bytes([
self.accum[0],
self.accum[1],
self.accum[2],
self.accum[3],
]);
let actual =
core::mem::replace(&mut self.content_hash, XxHash32::new()).finish();
if actual != expected {
return self.poison(Error::ChecksumMismatch);
}
self.phase = DecPhase::Done;
return Ok(RawProgress {
consumed,
written,
done: true,
});
}
DecPhase::Done => {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
}
}
}
fn raw_finish(&mut self, output: &mut [u8]) -> Result<RawProgress, Error> {
if self.poisoned {
return Err(Error::Corrupt);
}
let mut written = 0usize;
if self.phase == DecPhase::Draining {
self.drain_decoded(output, &mut written);
if self.decoded_idx < self.decoded.len() {
return Ok(RawProgress {
consumed: 0,
written,
done: false,
});
}
self.decoded.clear();
self.decoded_idx = 0;
self.phase = DecPhase::BlockSize;
self.accum_len = 0;
self.accum_need = 4;
}
match self.phase {
DecPhase::Done => Ok(RawProgress {
consumed: 0,
written,
done: true,
}),
DecPhase::Magic if self.accum_len == 0 => Ok(RawProgress {
consumed: 0,
written,
done: true,
}),
_ => Err(Error::UnexpectedEnd),
}
}
fn raw_reset(&mut self) {
self.phase = DecPhase::Magic;
self.accum_len = 0;
self.accum_need = 4;
self.flg = 0;
self.bd = 0;
self.desc_remaining = 0;
self.descriptor.clear();
self.block_max = BlockMaxSize::Max64KB;
self.block_independent = false;
self.block_checksum = false;
self.content_checksum = false;
self.cur_block_len = 0;
self.cur_block_uncompressed = false;
self.compressed.clear();
self.decoded.clear();
self.decoded_idx = 0;
self.window.clear();
self.content_hash = XxHash32::new();
self.poisoned = false;
}
}
fn decode_linked_block(
input: &[u8],
prefix: &[u8],
out: &mut Vec<u8>,
raw_max: usize,
) -> Result<(), Error> {
out.clear();
if input.is_empty() {
return Ok(());
}
let mut ip = 0usize;
let n = input.len();
loop {
if ip >= n {
return Err(Error::UnexpectedEnd);
}
let token = input[ip];
ip += 1;
let mut lit_len = (token >> 4) as usize;
if lit_len == 15 {
loop {
if ip >= n {
return Err(Error::UnexpectedEnd);
}
let b = input[ip];
ip += 1;
lit_len = lit_len.checked_add(b as usize).ok_or(Error::Corrupt)?;
if b != 255 {
break;
}
}
}
if lit_len > 0 {
if ip + lit_len > n {
return Err(Error::UnexpectedEnd);
}
if out.len() + lit_len > raw_max {
return Err(Error::Corrupt);
}
out.extend_from_slice(&input[ip..ip + lit_len]);
ip += lit_len;
}
if ip == n {
return Ok(());
}
if ip + 2 > n {
return Err(Error::UnexpectedEnd);
}
let offset = (input[ip] as usize) | ((input[ip + 1] as usize) << 8);
ip += 2;
if offset == 0 {
return Err(Error::InvalidDistance);
}
let in_block = out.len();
let total = in_block + prefix.len();
if offset > total {
return Err(Error::InvalidDistance);
}
let mut match_excess = (token & 0x0F) as usize;
if match_excess == 15 {
loop {
if ip >= n {
return Err(Error::UnexpectedEnd);
}
let b = input[ip];
ip += 1;
match_excess = match_excess.checked_add(b as usize).ok_or(Error::Corrupt)?;
if b != 255 {
break;
}
}
}
let match_len = 4 + match_excess;
if out.len() + match_len > raw_max {
return Err(Error::Corrupt);
}
for i in 0..match_len {
let cur_len = out.len();
let src_back = offset; let b = if src_back <= cur_len {
out[cur_len - src_back]
} else {
let into_prefix = src_back - cur_len;
if into_prefix > prefix.len() {
return Err(Error::InvalidDistance);
}
prefix[prefix.len() - into_prefix]
};
out.push(b);
let _ = i;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn xxhash32_empty() {
assert_eq!(xxhash32(b""), 0x02CC_5D05);
}
#[test]
fn xxhash32_known_short() {
assert_eq!(xxhash32(b"abc"), 0x32D1_53FF);
}
#[test]
fn xxhash32_incremental_matches_oneshot() {
let payload: Vec<u8> = (0..1000u32).map(|i| (i ^ 0x55) as u8).collect();
let one = xxhash32(&payload);
let mut h = XxHash32::new();
let mut i = 0;
let chunks = [1usize, 3, 7, 16, 5, 31, 13, 19];
let mut ci = 0;
while i < payload.len() {
let take = chunks[ci % chunks.len()].min(payload.len() - i);
h.update(&payload[i..i + take]);
i += take;
ci += 1;
}
assert_eq!(h.finish(), one);
}
}