Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,162 @@
|
||||
module deflate
|
||||
|
||||
import hash.huffman
|
||||
|
||||
const deflate_hash_bits = 15
|
||||
const deflate_hash_size = 1 << deflate_hash_bits
|
||||
const deflate_max_chain = 64
|
||||
const deflate_min_match = 3
|
||||
const deflate_max_match = 258
|
||||
const deflate_window = 32768
|
||||
|
||||
// fixed_litlen_encode returns (reversed_codes, code_lengths) for fixed Huffman lit/len.
|
||||
// The LSB-first (bit-reversed) codes come straight from the shared canonical
|
||||
// builder, since the encoder writes bits LSB-first.
|
||||
fn fixed_litlen_encode() !([]u32, []int) {
|
||||
lens := fixed_litlen_lengths()
|
||||
t := huffman.build(lengths: lens, max_bits: 9, bit_order: .lsb_first)!
|
||||
return t.codes, lens
|
||||
}
|
||||
|
||||
// fixed_dist_encode returns (reversed_codes, code_lengths) for fixed Huffman distance.
|
||||
fn fixed_dist_encode() ([]u32, []int) {
|
||||
mut codes := []u32{len: 30}
|
||||
for i in 0 .. 30 {
|
||||
codes[i] = bit_reverse(u32(i), 5)
|
||||
}
|
||||
return codes, []int{len: 30, init: 5}
|
||||
}
|
||||
|
||||
fn length_code_info(length int) (int, int, int) {
|
||||
for i := length_bases.len - 1; i >= 0; i-- {
|
||||
if length >= length_bases[i] {
|
||||
return i, length - length_bases[i], length_extra_bits[i]
|
||||
}
|
||||
}
|
||||
return 0, 0, 0
|
||||
}
|
||||
|
||||
fn dist_code_info(distance int) (int, int, int) {
|
||||
for i := dist_bases.len - 1; i >= 0; i-- {
|
||||
if distance >= dist_bases[i] {
|
||||
return i, distance - dist_bases[i], dist_extra_bits[i]
|
||||
}
|
||||
}
|
||||
return 0, 0, 0
|
||||
}
|
||||
|
||||
fn hash3(data []u8, pos int) int {
|
||||
v := u32(data[pos]) | (u32(data[pos + 1]) << 8) | (u32(data[pos + 2]) << 16)
|
||||
return int((v * u32(2654435761)) >> u32(32 - deflate_hash_bits))
|
||||
}
|
||||
|
||||
@[direct_array_access]
|
||||
fn find_lz_match(data []u8, pos int, last []int, prev []int) (int, int) {
|
||||
if pos + deflate_min_match > data.len {
|
||||
return 0, 0
|
||||
}
|
||||
max_len := if pos + deflate_max_match < data.len { deflate_max_match } else { data.len - pos }
|
||||
mut best_len := 0
|
||||
mut best_off := 0
|
||||
mut i := last[hash3(data, pos)]
|
||||
mut chain := 0
|
||||
for i >= 0 && chain < deflate_max_chain {
|
||||
off := pos - i
|
||||
if off > deflate_window {
|
||||
break
|
||||
}
|
||||
mut l := 0
|
||||
for l < max_len && data[i + l] == data[pos + l] {
|
||||
l++
|
||||
}
|
||||
if l > best_len {
|
||||
best_len = l
|
||||
best_off = off
|
||||
if best_len == max_len {
|
||||
break
|
||||
}
|
||||
}
|
||||
i = prev[i]
|
||||
chain++
|
||||
}
|
||||
return best_off, best_len
|
||||
}
|
||||
|
||||
struct BitWriter {
|
||||
mut:
|
||||
buf []u8
|
||||
bits u32
|
||||
nbits int
|
||||
}
|
||||
|
||||
@[direct_array_access; inline]
|
||||
fn (mut w BitWriter) write_bits(value u32, nbits int) {
|
||||
if nbits == 0 {
|
||||
return
|
||||
}
|
||||
w.bits |= value << w.nbits
|
||||
w.nbits += nbits
|
||||
for w.nbits >= 8 {
|
||||
w.buf << u8(w.bits & 0xff)
|
||||
w.bits >>= 8
|
||||
w.nbits -= 8
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut w BitWriter) flush() {
|
||||
if w.nbits > 0 {
|
||||
w.buf << u8(w.bits & 0xff)
|
||||
w.bits = 0
|
||||
w.nbits = 0
|
||||
}
|
||||
}
|
||||
|
||||
// deflate_compress_fixed compresses data to RFC 1951 DEFLATE using fixed Huffman codes.
|
||||
@[direct_array_access]
|
||||
fn deflate_compress_fixed(data []u8) ![]u8 {
|
||||
ll_codes, ll_lens := fixed_litlen_encode()!
|
||||
d_codes, d_lens := fixed_dist_encode()
|
||||
mut w := BitWriter{}
|
||||
// BFINAL=1, BTYPE=01 (fixed Huffman)
|
||||
w.write_bits(1, 1)
|
||||
w.write_bits(1, 2)
|
||||
if data.len == 0 {
|
||||
w.write_bits(ll_codes[256], ll_lens[256])
|
||||
w.flush()
|
||||
return w.buf
|
||||
}
|
||||
mut last := []int{len: deflate_hash_size, init: -1}
|
||||
mut prev := []int{len: data.len, init: -1}
|
||||
mut pos := 0
|
||||
for pos < data.len {
|
||||
off, match_len := find_lz_match(data, pos, last, prev)
|
||||
if match_len >= deflate_min_match {
|
||||
li, lext, lext_bits := length_code_info(match_len)
|
||||
sym := 257 + li
|
||||
w.write_bits(ll_codes[sym], ll_lens[sym])
|
||||
w.write_bits(u32(lext), lext_bits)
|
||||
di, dext, dext_bits := dist_code_info(off)
|
||||
w.write_bits(d_codes[di], d_lens[di])
|
||||
w.write_bits(u32(dext), dext_bits)
|
||||
for i in pos .. pos + match_len {
|
||||
if i + deflate_min_match < data.len {
|
||||
h := hash3(data, i)
|
||||
prev[i] = last[h]
|
||||
last[h] = i
|
||||
}
|
||||
}
|
||||
pos += match_len
|
||||
} else {
|
||||
w.write_bits(ll_codes[int(data[pos])], ll_lens[int(data[pos])])
|
||||
if pos + deflate_min_match < data.len {
|
||||
h := hash3(data, pos)
|
||||
prev[pos] = last[h]
|
||||
last[h] = pos
|
||||
}
|
||||
pos++
|
||||
}
|
||||
}
|
||||
w.write_bits(ll_codes[256], ll_lens[256])
|
||||
w.flush()
|
||||
return w.buf
|
||||
}
|
||||
Reference in new issue
Block a user