///|
/// Vendored from mizchi/zlib (Apache-2.0) — DEFLATE fixed Huffman 压缩
pub enum DeflateToken {
Lit(Int)
Match(len~ : Int, dist~ : Int)
}
///|
let deflate_length_base : FixedArray[Int] = [
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83,
99, 115, 131, 163, 195, 227, 258,
]
///|
let deflate_length_extra : FixedArray[Int] = [
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5,
5, 0,
]
///|
let deflate_dist_base : FixedArray[Int] = [
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769,
1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577,
]
///|
let deflate_dist_extra : FixedArray[Int] = [
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
12, 12, 13, 13,
]
///|
let window_size : Int = 32768
///|
let max_match_len : Int = 258
///|
let min_match_len : Int = 3
///|
let hash_size : Int = 1 << 15
///|
let max_chain : Int = 64
///|
fn deflate_reverse_bits(code : Int, len : Int) -> Int {
let mut v = code
let mut out = 0
let mut n = len
while n > 0 {
out = (out << 1) | (v & 1)
v = v >> 1
n -= 1
}
out
}
///|
fn fixed_lit_code(sym : Int) -> (Int, Int) {
if sym <= 143 {
let code = sym + 0x30
(deflate_reverse_bits(code, 8), 8)
} else if sym <= 255 {
let code = sym - 144 + 0x190
(deflate_reverse_bits(code, 9), 9)
} else if sym <= 279 {
let code = sym - 256
(deflate_reverse_bits(code, 7), 7)
} else {
let code = sym - 280 + 0xC0
(deflate_reverse_bits(code, 8), 8)
}
}
///|
fn fixed_dist_code(sym : Int) -> (Int, Int) {
(deflate_reverse_bits(sym, 5), 5)
}
///|
fn length_code_info(len : Int) -> (Int, Int, Int) {
for i in 0.. (Int, Int, Int) {
for i in 0.. Int {
let b0 = data[pos].to_int()
let b1 = data[pos + 1].to_int()
let b2 = data[pos + 2].to_int()
((b0 << 8) ^ (b1 << 4) ^ b2) & (hash_size - 1)
}
///|
fn insert_hash(
data : Bytes,
pos : Int,
head : FixedArray[Int],
prev : FixedArray[Int],
) -> Unit {
if pos + 2 >= data.length() {
return
}
let h = hash3(data, pos)
prev[pos] = head[h]
head[h] = pos
}
///|
fn write_literal(writer : BitWriter, sym : Int) -> Unit {
let (code, len) = fixed_lit_code(sym)
writer.write_bits(code, len)
}
///|
fn write_match(writer : BitWriter, length : Int, distance : Int) -> Unit {
let (len_code, len_extra_bits, len_extra_val) = length_code_info(length)
let (code_bits, code_len) = fixed_lit_code(len_code)
writer.write_bits(code_bits, code_len)
if len_extra_bits > 0 {
writer.write_bits(len_extra_val, len_extra_bits)
}
let (dist_code, dist_extra_bits, dist_extra_val) = dist_code_info(distance)
let (dist_bits, dist_len) = fixed_dist_code(dist_code)
writer.write_bits(dist_bits, dist_len)
if dist_extra_bits > 0 {
writer.write_bits(dist_extra_val, dist_extra_bits)
}
}
///|
fn write_end_block(writer : BitWriter) -> Unit {
let (code, len) = fixed_lit_code(256)
writer.write_bits(code, len)
}
///|
fn write_tokens_fixed(tokens : Array[DeflateToken], writer : BitWriter) -> Unit {
for t in tokens {
match t {
Lit(sym) => write_literal(writer, sym)
Match(len~, dist~) => write_match(writer, len, dist)
}
}
}
///|
fn build_tokens(data : Bytes) -> Array[DeflateToken] {
let tokens : Array[DeflateToken] = []
let len = data.length()
let head : FixedArray[Int] = FixedArray::make(hash_size, -1)
let prev : FixedArray[Int] = FixedArray::make(len, -1)
let mut pos = 0
while pos < len {
let remaining = len - pos
let mut best_len = 0
let mut best_dist = 0
if remaining >= min_match_len {
let h = hash3(data, pos)
let mut candidate = head[h]
let limit = if pos > window_size { pos - window_size } else { 0 }
let max_len = if remaining < max_match_len {
remaining
} else {
max_match_len
}
let mut chain = 0
while candidate >= limit && chain < max_chain {
if data[candidate] == data[pos] &&
data[candidate + 1] == data[pos + 1] &&
data[candidate + 2] == data[pos + 2] {
let mut l = min_match_len
while l < max_len && data[candidate + l] == data[pos + l] {
l += 1
}
if l > best_len {
best_len = l
best_dist = pos - candidate
if l == max_len {
break
}
}
}
candidate = prev[candidate]
chain += 1
}
}
if best_len >= min_match_len {
tokens.push(Match(len=best_len, dist=best_dist))
for i = 0; i < best_len; i = i + 1 {
insert_hash(data, pos + i, head, prev)
}
pos = pos + best_len
} else {
tokens.push(Lit(data[pos].to_int()))
insert_hash(data, pos, head, prev)
pos = pos + 1
}
}
tokens
}
///|
///|
/// DEFLATE 固定 Huffman 压缩入口
fn deflate_compress_fixed_v(data : Bytes) -> Bytes {
let writer = BitWriter::new()
writer.write_bits(1, 1) // BFINAL=1
writer.write_bits(1, 2) // BTYPE=01 (fixed Huffman)
let tokens = build_tokens(data)
write_tokens_fixed(tokens, writer)
write_end_block(writer)
writer.finish()
}
// Bit writer for deflate streams (LSB-first)
//
///|
struct BitWriter {
buf : Array[Byte]
mut bit_buf : Int
mut bit_count : Int
}
//
///|
fn BitWriter::new() -> BitWriter {
{ buf: [], bit_buf: 0, bit_count: 0, }
}
//
///|
fn BitWriter::write_bits(self : BitWriter, bits : Int, count : Int) -> Unit {
let mut value = bits
let mut remaining = count
while remaining > 0 {
let available = 8 - self.bit_count
let take = if remaining < available { remaining } else { available }
let mask = (1 << take) - 1
self.bit_buf = self.bit_buf | ((value & mask) << self.bit_count)
self.bit_count = self.bit_count + take
value = value >> take
remaining = remaining - take
if self.bit_count == 8 {
self.buf.push((self.bit_buf & 0xff).to_byte())
self.bit_buf = 0
self.bit_count = 0
}
}
}
//
///|
fn BitWriter::align_byte(self : BitWriter) -> Unit {
if self.bit_count > 0 {
self.buf.push((self.bit_buf & 0xff).to_byte())
self.bit_buf = 0
self.bit_count = 0
}
}
//
///|
fn BitWriter::finish(self : BitWriter) -> Bytes {
self.align_byte()
Bytes::from_array(FixedArray::makei(self.buf.length(), fn(i) { self.buf[i] }))
}