use alloc::vec::Vec;
use crate::error::Error;
use crate::traits::{RawEncoder, RawProgress};
use super::{MAX_DISTANCE, MIN_MATCH};
const HASH_BITS: u32 = 13;
const HASH_SIZE: usize = 1 << HASH_BITS;
const HASH_EMPTY: u32 = u32::MAX;
#[derive(Clone, Copy, PartialEq, Eq)]
enum EncPhase {
Buffering,
Draining,
Done,
}
pub struct Encoder {
raw: Vec<u8>,
staged: Vec<u8>,
staged_idx: usize,
phase: EncPhase,
}
impl Default for Encoder {
fn default() -> Self {
Self::new()
}
}
impl Encoder {
pub const fn new() -> Self {
Self {
raw: Vec::new(),
staged: Vec::new(),
staged_idx: 0,
phase: EncPhase::Buffering,
}
}
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;
}
if self.staged_idx == self.staged.len() {
self.staged.clear();
self.staged_idx = 0;
self.phase = EncPhase::Done;
}
}
fn build_stream(&mut self) {
self.staged.clear();
self.staged
.extend_from_slice(&(self.raw.len() as u64).to_le_bytes());
if self.raw.is_empty() {
return;
}
encode_payload(&self.raw, &mut self.staged);
}
}
impl RawEncoder for Encoder {
fn raw_encode(&mut self, input: &[u8], output: &mut [u8]) -> Result<RawProgress, Error> {
let _ = output;
if self.phase != EncPhase::Buffering {
return Ok(RawProgress {
consumed: 0,
written: 0,
done: false,
});
}
self.raw.extend_from_slice(input);
Ok(RawProgress {
consumed: input.len(),
written: 0,
done: false,
})
}
fn raw_finish(&mut self, output: &mut [u8]) -> Result<RawProgress, Error> {
let mut written = 0usize;
loop {
match self.phase {
EncPhase::Buffering => {
self.build_stream();
self.staged_idx = 0;
self.phase = EncPhase::Draining;
}
EncPhase::Draining => {
self.drain_staged(output, &mut written);
if self.phase == EncPhase::Draining {
return Ok(RawProgress {
consumed: 0,
written,
done: false,
});
}
}
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.phase = EncPhase::Buffering;
}
}
#[inline]
fn hash3(b: [u8; 3]) -> usize {
let v = (u32::from(b[0]) << 16) | (u32::from(b[1]) << 8) | u32::from(b[2]);
(v.wrapping_mul(2_654_435_761) >> (32 - HASH_BITS)) as usize
}
fn encode_payload(input: &[u8], out: &mut Vec<u8>) {
let mut table: [u32; HASH_SIZE] = [HASH_EMPTY; HASH_SIZE];
let mut i: usize = 0;
let mut half_byte_owner: Option<usize> = None;
while i < input.len() {
let flag_pos = out.len();
out.extend_from_slice(&[0u8; 4]);
let mut flag_word: u32 = 0;
let mut emitted: u32 = 0;
while emitted < 32 && i < input.len() {
let match_found = if i + MIN_MATCH <= input.len() {
let key = hash3([input[i], input[i + 1], input[i + 2]]);
let prev = table[key];
table[key] = i as u32;
if prev != HASH_EMPTY {
let p = prev as usize;
if p < i && i - p <= MAX_DISTANCE && i - p >= 1 {
if input[p] == input[i]
&& input[p + 1] == input[i + 1]
&& input[p + 2] == input[i + 2]
{
let max_len = input.len() - i;
let mut len = 3usize;
while len < max_len && input[p + len] == input[i + len] {
len += 1;
}
Some((p, len))
} else {
None
}
} else {
None
}
} else {
None
}
} else {
None
};
match match_found {
Some((p, len)) => {
flag_word |= 1u32 << (31 - emitted);
let distance = i - p;
write_match(out, distance as u32, len as u32, &mut half_byte_owner);
i += len;
}
None => {
out.push(input[i]);
i += 1;
}
}
emitted += 1;
}
if emitted < 32 {
let trailing_ones = 32 - emitted;
let mask: u32 = if trailing_ones == 32 {
u32::MAX
} else {
(1u32 << trailing_ones) - 1
};
flag_word |= mask;
}
out[flag_pos..flag_pos + 4].copy_from_slice(&flag_word.to_le_bytes());
}
}
fn write_match(out: &mut Vec<u8>, distance: u32, length: u32, half_byte_owner: &mut Option<usize>) {
debug_assert!(distance >= 1 && distance <= MAX_DISTANCE as u32);
debug_assert!(length >= MIN_MATCH as u32);
let dist_field = distance - 1; let length_minus_3 = length - 3;
let lc_field: u16 = if length_minus_3 < 7 {
length_minus_3 as u16
} else {
7
};
let sym: u16 = ((dist_field as u16) << 3) | lc_field;
out.extend_from_slice(&sym.to_le_bytes());
if length_minus_3 < 7 {
return;
}
let remainder = length - 10; if remainder < 15 {
write_half_byte(out, remainder as u8, half_byte_owner);
return;
}
write_half_byte(out, 15, half_byte_owner);
let after_hb = length - (15 + 7 + 3); if after_hb < 255 {
out.push(after_hb as u8);
return;
}
out.push(255);
let biased = length - 3;
if biased <= 0xFFFF {
out.extend_from_slice(&(biased as u16).to_le_bytes());
} else {
out.extend_from_slice(&0u16.to_le_bytes());
out.extend_from_slice(&biased.to_le_bytes());
}
}
fn write_half_byte(out: &mut Vec<u8>, nibble: u8, owner: &mut Option<usize>) {
debug_assert!(nibble <= 0x0F);
match *owner {
Some(idx) => {
out[idx] |= nibble << 4;
*owner = None;
}
None => {
let idx = out.len();
out.push(nibble);
*owner = Some(idx);
}
}
}