mirror of
https://github.com/Noratrieb/zwergli.git
synced 2026-07-12 09:07:48 +02:00
continue
This commit is contained in:
parent
00a411a728
commit
8e1d90e0ce
2 changed files with 223 additions and 46 deletions
266
src/lib.rs
266
src/lib.rs
|
|
@ -4,30 +4,60 @@ struct Bitstream<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bitstream<'_> {
|
impl Bitstream<'_> {
|
||||||
fn read_bits_normal(&mut self, mut len: usize) -> Option<u64> {
|
fn read_bits(&mut self, mut len: usize) -> Result<u16, Error> {
|
||||||
assert!((len as u32) < u64::BITS);
|
assert!((len as u32) < u16::BITS);
|
||||||
|
|
||||||
let mut result = 0;
|
let mut result = 0;
|
||||||
|
|
||||||
while len > 0 {
|
while len > 0 {
|
||||||
if self.data.is_empty() {
|
if self.data.is_empty() {
|
||||||
return None;
|
return Err(Error::Incomplete);
|
||||||
}
|
}
|
||||||
|
|
||||||
let to_read_from_current_byte = std::cmp::min(len % 8, 8 - self.pos_bit);
|
let to_read_from_current_byte = std::cmp::min(len % 8, 8 - self.pos_bit);
|
||||||
result <<= to_read_from_current_byte;
|
result <<= to_read_from_current_byte;
|
||||||
result |=
|
result |=
|
||||||
((self.data[0] >> self.pos_bit) & ((1 << to_read_from_current_byte) - 1)) as u64;
|
((self.data[0] >> self.pos_bit) & ((1 << to_read_from_current_byte) - 1)) as u16;
|
||||||
len -= to_read_from_current_byte;
|
len -= to_read_from_current_byte;
|
||||||
self.pos_bit += to_read_from_current_byte;
|
self.pos_bit += to_read_from_current_byte;
|
||||||
|
|
||||||
if self.pos_bit == 8 {
|
if self.pos_bit == 8 {
|
||||||
self.data = &self.data[1..];
|
self.data = &self.data.get(1..).ok_or(Error::Incomplete)?;
|
||||||
self.pos_bit = 0;
|
self.pos_bit = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(result)
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn skip_current_byte(&mut self) -> Result<(), Error> {
|
||||||
|
if self.pos_bit > 0 {
|
||||||
|
self.data = &self.data.get(1..).ok_or(Error::Incomplete)?;
|
||||||
|
self.pos_bit = 0;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_aligned_u16(&mut self) -> Result<u16, Error> {
|
||||||
|
assert_eq!(self.pos_bit, 0);
|
||||||
|
let value = u16::from_le_bytes(
|
||||||
|
self.data
|
||||||
|
.get(..2)
|
||||||
|
.ok_or(Error::Incomplete)?
|
||||||
|
.try_into()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
self.data = self.data.get(2..).ok_or(Error::Incomplete)?;
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_aligned_bytes(&mut self, len: usize) -> Result<&[u8], Error> {
|
||||||
|
if self.data.len() < len {
|
||||||
|
return Err(Error::Incomplete);
|
||||||
|
}
|
||||||
|
let (data, rest) = self.data.split_at(len);
|
||||||
|
self.data = rest;
|
||||||
|
Ok(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -38,6 +68,7 @@ struct HuffmanTree {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum HuffmanNode {
|
enum HuffmanNode {
|
||||||
Leaf(u16),
|
Leaf(u16),
|
||||||
|
// todo optimize this into an array index lol
|
||||||
Cont { zero: usize, one: usize },
|
Cont { zero: usize, one: usize },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -47,7 +78,7 @@ enum HuffmanLookupResult {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HuffmanTree {
|
impl HuffmanTree {
|
||||||
fn fixed_code_lengths() -> [u8; 288] {
|
fn fixed_lit_len_code_lengths() -> [u8; 288] {
|
||||||
let mut codes = [0; 288];
|
let mut codes = [0; 288];
|
||||||
codes[0..144].fill(8);
|
codes[0..144].fill(8);
|
||||||
codes[144..256].fill(9);
|
codes[144..256].fill(9);
|
||||||
|
|
@ -56,6 +87,12 @@ impl HuffmanTree {
|
||||||
codes
|
codes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fixed_dist_code_lengths() -> [u8; 32] {
|
||||||
|
let mut codes = [0; 32];
|
||||||
|
codes.fill(5);
|
||||||
|
codes
|
||||||
|
}
|
||||||
|
|
||||||
fn from_lengths(lengths: &[u8]) -> Self {
|
fn from_lengths(lengths: &[u8]) -> Self {
|
||||||
const MAX_BITS: usize = 10;
|
const MAX_BITS: usize = 10;
|
||||||
|
|
||||||
|
|
@ -114,7 +151,7 @@ impl HuffmanTree {
|
||||||
Self { nodes }
|
Self { nodes }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lookup_with_state(&self, state: usize, bit: u64) -> HuffmanLookupResult {
|
fn lookup_with_state(&self, state: usize, bit: u16) -> HuffmanLookupResult {
|
||||||
let HuffmanNode::Cont { zero, one } = self.nodes[state] else {
|
let HuffmanNode::Cont { zero, one } = self.nodes[state] else {
|
||||||
unreachable!("invalid state, should point at continuation node");
|
unreachable!("invalid state, should point at continuation node");
|
||||||
};
|
};
|
||||||
|
|
@ -125,6 +162,20 @@ impl HuffmanTree {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_value(&self, data: &mut Bitstream) -> Result<u16, Error> {
|
||||||
|
let mut node_state = 0;
|
||||||
|
loop {
|
||||||
|
let bit = data.read_bits(1)?;
|
||||||
|
let result = self.lookup_with_state(node_state, bit);
|
||||||
|
match result {
|
||||||
|
HuffmanLookupResult::Done(value) => {
|
||||||
|
return Ok(value);
|
||||||
|
}
|
||||||
|
HuffmanLookupResult::Incomplete { next_state } => node_state = next_state,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn to_dot(&self) -> String {
|
fn to_dot(&self) -> String {
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
|
@ -184,49 +235,174 @@ impl HuffmanTree {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
ReservedBlockType,
|
||||||
|
Incomplete,
|
||||||
|
InvalidCode,
|
||||||
|
}
|
||||||
|
|
||||||
// https://datatracker.ietf.org/doc/html/rfc1951
|
// https://datatracker.ietf.org/doc/html/rfc1951
|
||||||
pub fn inflate(data: &[u8], out: &mut Vec<u8>) {
|
pub fn inflate(data: &[u8], out: &mut Vec<u8>) -> Result<(), Error> {
|
||||||
std::fs::write(
|
std::fs::write(
|
||||||
"output.dot",
|
"output.dot",
|
||||||
HuffmanTree::from_lengths(&HuffmanTree::fixed_code_lengths()).to_dot(),
|
HuffmanTree::from_lengths(&HuffmanTree::fixed_lit_len_code_lengths()).to_dot(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut data = Bitstream { data, pos_bit: 0 };
|
let mut data = Bitstream { data, pos_bit: 0 };
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let bfinal = data.read_bits_normal(1).unwrap();
|
// 3.2.3. Details of block format
|
||||||
|
// Each block of compressed data begins with 3 header bits containing the following data:
|
||||||
|
// first bit BFINAL
|
||||||
|
// next 2 bits BTYPE
|
||||||
|
let bfinal = data.read_bits(1)?;
|
||||||
|
let btype = data.read_bits(2)?;
|
||||||
|
|
||||||
let btype = data.read_bits_normal(2).unwrap();
|
'end_block: {
|
||||||
|
// BTYPE specifies how the data are compressed, as follows:
|
||||||
|
// 00 - no compression
|
||||||
|
// 01 - compressed with fixed Huffman codes
|
||||||
|
// 10 - compressed with dynamic Huffman codes
|
||||||
|
// 11 - reserved (error)
|
||||||
|
let (code_lengths_lit_len, code_lengths_dist) = match btype {
|
||||||
|
0b00 => {
|
||||||
|
// Any bits of input up to the next byte boundary are ignored.
|
||||||
|
// The rest of the block consists of the following information:
|
||||||
|
|
||||||
assert_eq!(btype, 1, "not a static huffman tree construction");
|
// 0 1 2 3 4...
|
||||||
|
// +---+---+---+---+================================+
|
||||||
|
// | LEN | NLEN |... LEN bytes of literal data...|
|
||||||
|
// +---+---+---+---+================================+
|
||||||
|
|
||||||
let tree = HuffmanTree::from_lengths(&HuffmanTree::fixed_code_lengths());
|
// LEN is the number of data bytes in the block. NLEN is the
|
||||||
|
// one's complement of LEN.
|
||||||
let mut node_state = 0;
|
data.skip_current_byte()?;
|
||||||
loop {
|
let len = data.read_aligned_u16()?;
|
||||||
let bit = data.read_bits_normal(1).unwrap();
|
let _nlen = data.read_aligned_u16()?;
|
||||||
let result = tree.lookup_with_state(node_state, bit);
|
out.extend_from_slice(data.read_aligned_bytes(len as usize)?);
|
||||||
match result {
|
break 'end_block;
|
||||||
HuffmanLookupResult::Done(value) => {
|
}
|
||||||
dbg!(value);
|
0b01 => (
|
||||||
node_state = 0;
|
&HuffmanTree::fixed_lit_len_code_lengths(),
|
||||||
|
&HuffmanTree::fixed_dist_code_lengths(),
|
||||||
match value {
|
),
|
||||||
0..256 => {
|
0b10 => todo!("dynamic huffman codes"),
|
||||||
out.push(value as u8);
|
0b11 => return Err(Error::ReservedBlockType),
|
||||||
}
|
_ => unreachable!("only 2 bits"),
|
||||||
256 => break,
|
};
|
||||||
257..286 => {
|
|
||||||
let length = match value {
|
let lit_len_tree = HuffmanTree::from_lengths(code_lengths_lit_len);
|
||||||
257..265 => value - (257 - 3),
|
let dist_tree = HuffmanTree::from_lengths(code_lengths_dist);
|
||||||
_ => todo!("lz77 more"),
|
|
||||||
};
|
loop {
|
||||||
}
|
let lit_len = lit_len_tree.read_value(&mut data)?;
|
||||||
286.. => unreachable!("invalid byte"),
|
match lit_len {
|
||||||
}
|
0..=255 => {
|
||||||
|
out.push(lit_len as /* literal */ u8);
|
||||||
|
}
|
||||||
|
256 => break 'end_block,
|
||||||
|
257..286 => {
|
||||||
|
let mut compute_value =
|
||||||
|
|first_code, extra_bits_amount, value_start| -> Result<u16, Error> {
|
||||||
|
let extra_value = if extra_bits_amount == 0 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
data.read_bits(extra_bits_amount)? as u16
|
||||||
|
};
|
||||||
|
|
||||||
|
let extra_values_amount = 2_u16.pow(extra_bits_amount as u32);
|
||||||
|
|
||||||
|
Ok(((lit_len - first_code) * extra_values_amount)
|
||||||
|
+ value_start
|
||||||
|
+ extra_value)
|
||||||
|
};
|
||||||
|
// Extra Extra Extra
|
||||||
|
// Code Bits Length(s) Code Bits Lengths Code Bits Length(s)
|
||||||
|
// ---- ---- ------ ---- ---- ------- ---- ---- -------
|
||||||
|
// 257 0 3 267 1 15,16 277 4 67-82
|
||||||
|
// 258 0 4 268 1 17,18 278 4 83-98
|
||||||
|
// 259 0 5 269 2 19-22 279 4 99-114
|
||||||
|
// 260 0 6 270 2 23-26 280 4 115-130
|
||||||
|
// 261 0 7 271 2 27-30 281 5 131-162
|
||||||
|
// 262 0 8 272 2 31-34 282 5 163-194
|
||||||
|
// 263 0 9 273 3 35-42 283 5 195-226
|
||||||
|
// 264 0 10 274 3 43-50 284 5 227-257
|
||||||
|
// 265 1 11,12 275 3 51-58 285 0 258
|
||||||
|
// 266 1 13,14 276 3 59-66
|
||||||
|
let length = match lit_len {
|
||||||
|
257..=264 => compute_value(257, 0, 3),
|
||||||
|
265..=268 => compute_value(265, 1, 11),
|
||||||
|
269..=272 => compute_value(269, 2, 19),
|
||||||
|
273..=276 => compute_value(273, 3, 35),
|
||||||
|
277..=280 => compute_value(277, 4, 67),
|
||||||
|
281..=284 => compute_value(281, 5, 131),
|
||||||
|
285 => Ok(258),
|
||||||
|
_ => return Err(Error::InvalidCode),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
let dist_value = dist_tree.read_value(&mut data)?;
|
||||||
|
|
||||||
|
let mut compute_value =
|
||||||
|
|first_code, extra_bits_amount, value_start| -> Result<u16, Error> {
|
||||||
|
assert_eq!(extra_bits_amount, 3);
|
||||||
|
dbg!(data.read_bits(1).unwrap());
|
||||||
|
dbg!(data.read_bits(1).unwrap());
|
||||||
|
dbg!(data.read_bits(1).unwrap());
|
||||||
|
std::process::exit(1);
|
||||||
|
|
||||||
|
// TODO: read_bits is broken (i suspect the byte parts are reversed)
|
||||||
|
|
||||||
|
let extra_value = if extra_bits_amount == 0 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
data.read_bits(extra_bits_amount)? as u16
|
||||||
|
};
|
||||||
|
|
||||||
|
dbg!(extra_value);
|
||||||
|
|
||||||
|
let extra_values_amount = 2_u16.pow(extra_bits_amount as u32);
|
||||||
|
|
||||||
|
Ok(((dist_value - first_code) * extra_values_amount)
|
||||||
|
+ value_start
|
||||||
|
+ extra_value)
|
||||||
|
};
|
||||||
|
// Extra Extra Extra
|
||||||
|
// Code Bits Dist Code Bits Dist Code Bits Distance
|
||||||
|
// ---- ---- ---- ---- ---- ------ ---- ---- --------
|
||||||
|
// 0 0 1 10 4 33-48 20 9 1025-1536
|
||||||
|
// 1 0 2 11 4 49-64 21 9 1537-2048
|
||||||
|
// 2 0 3 12 5 65-96 22 10 2049-3072
|
||||||
|
// 3 0 4 13 5 97-128 23 10 3073-4096
|
||||||
|
// 4 1 5,6 14 6 129-192 24 11 4097-6144
|
||||||
|
// 5 1 7,8 15 6 193-256 25 11 6145-8192
|
||||||
|
// 6 2 9-12 16 7 257-384 26 12 8193-12288
|
||||||
|
// 7 2 13-16 17 7 385-512 27 12 12289-16384
|
||||||
|
// 8 3 17-24 18 8 513-768 28 13 16385-24576
|
||||||
|
// 9 3 25-32 19 8 769-1024 29 13 24577-32768
|
||||||
|
let distance = match dist_value {
|
||||||
|
0..=3 => compute_value(0, 0, 1),
|
||||||
|
4..=5 => compute_value(4, 1, 5),
|
||||||
|
6..=7 => compute_value(6, 2, 9),
|
||||||
|
8..=9 => compute_value(8, 3, 17),
|
||||||
|
10..=11 => compute_value(10, 4, 33),
|
||||||
|
12..=13 => compute_value(12, 5, 65),
|
||||||
|
14..=15 => compute_value(14, 6, 129),
|
||||||
|
16..=17 => compute_value(16, 7, 257),
|
||||||
|
18..=19 => compute_value(18, 8, 513),
|
||||||
|
20..=21 => compute_value(20, 9, 1025),
|
||||||
|
22..=23 => compute_value(22, 10, 2049),
|
||||||
|
24..=25 => compute_value(24, 11, 4097),
|
||||||
|
26..=27 => compute_value(26, 12, 8193),
|
||||||
|
28..=29 => compute_value(28, 13, 16385),
|
||||||
|
_ => return Err(Error::InvalidCode),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
dbg!((length, dist_value, distance));
|
||||||
|
}
|
||||||
|
286.. => return Err(Error::InvalidCode),
|
||||||
}
|
}
|
||||||
HuffmanLookupResult::Incomplete { next_state } => node_state = next_state,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -234,6 +410,8 @@ pub fn inflate(data: &[u8], out: &mut Vec<u8>) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
@ -247,16 +425,16 @@ mod tests {
|
||||||
data: &bytes,
|
data: &bytes,
|
||||||
pos_bit: 0,
|
pos_bit: 0,
|
||||||
};
|
};
|
||||||
assert_eq!(stream.read_bits_normal(2).unwrap(), 0b01);
|
assert_eq!(stream.read_bits(2).unwrap(), 0b01);
|
||||||
assert_eq!(stream.read_bits_normal(3).unwrap(), 0b010);
|
assert_eq!(stream.read_bits(3).unwrap(), 0b010);
|
||||||
assert_eq!(stream.read_bits_normal(5).unwrap(), 0b11001);
|
assert_eq!(stream.read_bits(5).unwrap(), 0b11001);
|
||||||
assert_eq!(stream.read_bits_normal(3).unwrap(), 0b111);
|
assert_eq!(stream.read_bits(3).unwrap(), 0b111);
|
||||||
assert_eq!(stream.read_bits_normal(3).unwrap(), 0b010);
|
assert_eq!(stream.read_bits(3).unwrap(), 0b010);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn decode() {
|
fn decode() {
|
||||||
let lengths = HuffmanTree::fixed_code_lengths();
|
let lengths = HuffmanTree::fixed_lit_len_code_lengths();
|
||||||
HuffmanTree::from_lengths(&lengths);
|
HuffmanTree::from_lengths(&lengths);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let gz = std::env::args().nth(1).unwrap();
|
let gz = std::env::args().nth(1).expect("must provide a cli argument");
|
||||||
let gz = std::fs::read(gz).unwrap();
|
let gz = std::fs::read(gz).unwrap();
|
||||||
|
|
||||||
assert_eq!(gz[0], 31, "ID");
|
assert_eq!(gz[0], 31, "ID");
|
||||||
|
|
@ -27,6 +27,5 @@ fn main() {
|
||||||
|
|
||||||
zwergli::inflate(blocks, &mut out);
|
zwergli::inflate(blocks, &mut out);
|
||||||
|
|
||||||
dbg!(&out);
|
|
||||||
dbg!(String::from_utf8(out)).ok();
|
dbg!(String::from_utf8(out)).ok();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue