Refactor ZLIB structs.

This commit is contained in:
Jeroen van Rijn
2021-06-23 16:32:47 +02:00
parent bb3ffdbdfb
commit 8663c64e47
6 changed files with 176 additions and 133 deletions
+71 -58
View File
@@ -72,34 +72,46 @@ Deflate_Error :: enum {
BType_3, BType_3,
} }
// General context for ZLIB, LZW, etc.
Context :: struct {
code_buffer: u32,
num_bits: i8,
/*
num_bits will be set to -100 if the buffer is malformed
*/
eof: b8,
input: io.Stream, // General I/O context for ZLIB, LZW, etc.
output: io.Stream, Context :: struct #packed {
input: io.Stream,
input_data: []u8,
output: io.Stream,
output_buf: [dynamic]u8,
bytes_written: i64, bytes_written: i64,
/*
If we know the data size, we can optimize the reads and writes.
*/
size_packed: i64,
size_unpacked: i64,
/* /*
Used to update hash as we write instead of all at once. Used to update hash as we write instead of all at once.
*/ */
rolling_hash: u32, rolling_hash: u32,
// Sliding window buffer. Size must be a power of two.
window_size: i64,
window_mask: i64,
last: ^[dynamic]byte,
/* /*
If we know the raw data size, we can optimize the reads. Could put some useful bools in here.
*/ */
uncompressed_size: i64, padding: [3]u32,
input_data: []u8,
} }
#assert(size_of(Context) == 128);
/*
Compression algorithm context
*/
Code_Buffer :: struct #packed {
code_buffer: u64,
num_bits: u64,
/*
Sliding window buffer. Size must be a power of two.
*/
window_mask: i64,
last: [dynamic]u8,
}
#assert(size_of(Code_Buffer) == 64);
// Stream helpers // Stream helpers
/* /*
@@ -113,14 +125,17 @@ Context :: struct {
read_data :: #force_inline proc(c: ^Context, $T: typeid) -> (res: T, err: io.Error) { read_data :: #force_inline proc(c: ^Context, $T: typeid) -> (res: T, err: io.Error) {
when #config(TRACY_ENABLE, false) { tracy.ZoneN("Read Data"); } when #config(TRACY_ENABLE, false) { tracy.ZoneN("Read Data"); }
b := make([]u8, size_of(T), context.temp_allocator); when size_of(T) <= 128 {
r, e1 := io.to_reader(c.input); b: [size_of(T)]u8;
_, e2 := io.read(r, b); } else {
if !e1 || e2 != .None { b := make([]u8, size_of(T), context.temp_allocator);
return T{}, e2; }
_, e := c.input->impl_read(b[:]);
if e != .None {
return T{}, e;
} }
res = (^T)(raw_data(b))^; res = (^T)(&b)^;
return res, .None; return res, .None;
} }
@@ -151,69 +166,67 @@ peek_data :: #force_inline proc(c: ^Context, $T: typeid) -> (res: T, err: io.Err
} }
// Sliding window read back // Sliding window read back
peek_back_byte :: proc(c: ^Context, offset: i64) -> (res: u8, err: io.Error) { peek_back_byte :: #force_inline proc(cb: ^Code_Buffer, offset: i64) -> (res: u8, err: io.Error) {
// Look back into the sliding window. // Look back into the sliding window.
return c.last[offset % c.window_size], .None; return cb.last[offset & cb.window_mask], .None;
} }
// Generalized bit reader LSB // Generalized bit reader LSB
refill_lsb :: proc(z: ^Context, width := i8(24)) { refill_lsb :: proc(z: ^Context, cb: ^Code_Buffer, width := i8(24)) {
when #config(TRACY_ENABLE, false) { tracy.ZoneN("Refill LSB"); } when #config(TRACY_ENABLE, false) { tracy.ZoneN("Refill LSB"); }
for { for {
if z.num_bits > width { if cb.num_bits > u64(width) {
break; break;
} }
if z.code_buffer == 0 && z.num_bits == -1 { if cb.code_buffer == 0 && cb.num_bits > 63 {
z.num_bits = 0; cb.num_bits = 0;
} }
if z.code_buffer >= 1 << uint(z.num_bits) { if cb.code_buffer >= 1 << uint(cb.num_bits) {
// Code buffer is malformed. // Code buffer is malformed.
z.num_bits = -100; cb.num_bits = max(u64);
return; return;
} }
c, err := read_u8(z); b, err := read_u8(z);
if err != .None { if err != .None {
// This is fine at the end of the file. // This is fine at the end of the file.
z.num_bits = -42;
z.eof = true;
return; return;
} }
z.code_buffer |= (u32(c) << u8(z.num_bits)); cb.code_buffer |= (u64(b) << u8(cb.num_bits));
z.num_bits += 8; cb.num_bits += 8;
} }
} }
consume_bits_lsb :: #force_inline proc(z: ^Context, width: u8) { consume_bits_lsb :: #force_inline proc(cb: ^Code_Buffer, width: u8) {
z.code_buffer >>= width; cb.code_buffer >>= width;
z.num_bits -= i8(width); cb.num_bits -= u64(width);
} }
peek_bits_lsb :: #force_inline proc(z: ^Context, width: u8) -> u32 { peek_bits_lsb :: #force_inline proc(z: ^Context, cb: ^Code_Buffer, width: u8) -> u32 {
if z.num_bits < i8(width) { if cb.num_bits < u64(width) {
refill_lsb(z); refill_lsb(z, cb);
} }
// assert(z.num_bits >= i8(width)); // assert(z.num_bits >= i8(width));
return z.code_buffer & ~(~u32(0) << width); return u32(cb.code_buffer & ~(~u64(0) << width));
} }
peek_bits_no_refill_lsb :: #force_inline proc(z: ^Context, width: u8) -> u32 { peek_bits_no_refill_lsb :: #force_inline proc(z: ^Context, cb: ^Code_Buffer, width: u8) -> u32 {
assert(z.num_bits >= i8(width)); assert(cb.num_bits >= u64(width));
return z.code_buffer & ~(~u32(0) << width); return u32(cb.code_buffer & ~(~u64(0) << width));
} }
read_bits_lsb :: #force_inline proc(z: ^Context, width: u8) -> u32 { read_bits_lsb :: #force_inline proc(z: ^Context, cb: ^Code_Buffer, width: u8) -> u32 {
k := peek_bits_lsb(z, width); k := peek_bits_lsb(z, cb, width);
consume_bits_lsb(z, width); consume_bits_lsb(cb, width);
return k; return k;
} }
read_bits_no_refill_lsb :: #force_inline proc(z: ^Context, width: u8) -> u32 { read_bits_no_refill_lsb :: #force_inline proc(z: ^Context, cb: ^Code_Buffer, width: u8) -> u32 {
k := peek_bits_no_refill_lsb(z, width); k := peek_bits_no_refill_lsb(z, cb, width);
consume_bits_lsb(z, width); consume_bits_lsb(cb, width);
return k; return k;
} }
discard_to_next_byte_lsb :: proc(z: ^Context) { discard_to_next_byte_lsb :: proc(cb: ^Code_Buffer) {
discard := u8(z.num_bits & 7); discard := u8(cb.num_bits & 7);
consume_bits_lsb(z, discard); consume_bits_lsb(cb, discard);
} }
+7 -7
View File
@@ -12,7 +12,6 @@ package gzip
A small GZIP implementation as an example. A small GZIP implementation as an example.
*/ */
import "core:compress/gzip"
import "core:bytes" import "core:bytes"
import "core:os" import "core:os"
@@ -31,7 +30,7 @@ TEST: []u8 = {
main :: proc() { main :: proc() {
// Set up output buffer. // Set up output buffer.
buf: bytes.Buffer; buf := bytes.Buffer{};
stdout :: proc(s: string) { stdout :: proc(s: string) {
os.write_string(os.stdout, s); os.write_string(os.stdout, s);
@@ -44,26 +43,27 @@ main :: proc() {
if len(args) < 2 { if len(args) < 2 {
stderr("No input file specified.\n"); stderr("No input file specified.\n");
err := gzip.load(TEST, &buf); err := load(TEST, &buf);
if err != nil { if err == nil {
stdout("Displaying test vector: "); stdout("Displaying test vector: ");
stdout(bytes.buffer_to_string(&buf)); stdout(bytes.buffer_to_string(&buf));
stdout("\n"); stdout("\n");
} }
bytes.buffer_destroy(&buf); bytes.buffer_destroy(&buf);
os.exit(0);
} }
// The rest are all files. // The rest are all files.
args = args[1:]; args = args[1:];
err: gzip.Error; err: Error;
for file in args { for file in args {
if file == "-" { if file == "-" {
// Read from stdin // Read from stdin
s := os.stream_from_handle(os.stdin); s := os.stream_from_handle(os.stdin);
err = gzip.load(s, &buf); err = load(s, &buf);
} else { } else {
err = gzip.load(file, &buf); err = load(file, &buf);
} }
if err != nil { if err != nil {
if err != E_General.File_Not_Found { if err != E_General.File_Not_Found {
+6 -4
View File
@@ -281,8 +281,10 @@ load_from_stream :: proc(stream: io.Stream, buf: ^bytes.Buffer, allocator := con
/* /*
We should have arrived at the ZLIB payload. We should have arrived at the ZLIB payload.
*/ */
code_buffer := compress.Code_Buffer{};
cb := &code_buffer;
zlib_error := zlib.inflate_raw(&ctx); zlib_error := zlib.inflate_raw(&ctx, &code_buffer);
// fmt.printf("ZLIB returned: %v\n", zlib_error); // fmt.printf("ZLIB returned: %v\n", zlib_error);
@@ -293,16 +295,16 @@ load_from_stream :: proc(stream: io.Stream, buf: ^bytes.Buffer, allocator := con
/* /*
Read CRC32 using the ctx bit reader because zlib may leave bytes in there. Read CRC32 using the ctx bit reader because zlib may leave bytes in there.
*/ */
compress.discard_to_next_byte_lsb(&ctx); compress.discard_to_next_byte_lsb(cb);
payload_crc_b: [4]u8; payload_crc_b: [4]u8;
payload_len_b: [4]u8; payload_len_b: [4]u8;
for _, i in payload_crc_b { for _, i in payload_crc_b {
payload_crc_b[i] = u8(compress.read_bits_lsb(&ctx, 8)); payload_crc_b[i] = u8(compress.read_bits_lsb(&ctx, cb, 8));
} }
payload_crc := transmute(u32le)payload_crc_b; payload_crc := transmute(u32le)payload_crc_b;
for _, i in payload_len_b { for _, i in payload_len_b {
payload_len_b[i] = u8(compress.read_bits_lsb(&ctx, 8)); payload_len_b[i] = u8(compress.read_bits_lsb(&ctx, cb, 8));
} }
payload_len := int(transmute(u32le)payload_len_b); payload_len := int(transmute(u32le)payload_len_b);
+1 -2
View File
@@ -11,7 +11,6 @@ package zlib
An example of how to use `zlib.inflate`. An example of how to use `zlib.inflate`.
*/ */
import "core:compress/zlib"
import "core:bytes" import "core:bytes"
import "core:fmt" import "core:fmt"
@@ -40,7 +39,7 @@ main :: proc() {
buf: bytes.Buffer; buf: bytes.Buffer;
// We can pass ", true" to inflate a raw DEFLATE stream instead of a ZLIB wrapped one. // We can pass ", true" to inflate a raw DEFLATE stream instead of a ZLIB wrapped one.
err := zlib.inflate(ODIN_DEMO, &buf); err := inflate(ODIN_DEMO, &buf);
defer bytes.buffer_destroy(&buf); defer bytes.buffer_destroy(&buf);
if err != nil { if err != nil {
+75 -62
View File
@@ -23,7 +23,8 @@ import "core:hash"
Returns: Error. Returns: Error.
*/ */
Context :: compress.Context; Context :: compress.Context;
Code_Buffer :: compress.Code_Buffer;
Compression_Method :: enum u8 { Compression_Method :: enum u8 {
DEFLATE = 8, DEFLATE = 8,
@@ -129,7 +130,7 @@ z_bit_reverse :: #force_inline proc(n: u16, bits: u8) -> (r: u16) {
return; return;
} }
write_byte :: #force_inline proc(z: ^Context, c: u8) -> (err: io.Error) #no_bounds_check { write_byte :: #force_inline proc(z: ^Context, cb: ^Code_Buffer, c: u8) -> (err: io.Error) #no_bounds_check {
when #config(TRACY_ENABLE, false) { tracy.ZoneN("Write Byte"); } when #config(TRACY_ENABLE, false) { tracy.ZoneN("Write Byte"); }
c := c; c := c;
buf := transmute([]u8)mem.Raw_Slice{data=&c, len=1}; buf := transmute([]u8)mem.Raw_Slice{data=&c, len=1};
@@ -139,13 +140,13 @@ write_byte :: #force_inline proc(z: ^Context, c: u8) -> (err: io.Error) #no_boun
if e != .None { if e != .None {
return e; return e;
} }
z.last[z.bytes_written & z.window_mask] = c; cb.last[z.bytes_written & cb.window_mask] = c;
z.bytes_written += 1; z.bytes_written += 1;
return .None; return .None;
} }
repl_byte :: proc(z: ^Context, count: u16, c: u8) -> (err: io.Error) { repl_byte :: proc(z: ^Context, cb: ^Code_Buffer, count: u16, c: u8) -> (err: io.Error) {
when #config(TRACY_ENABLE, false) { tracy.ZoneN("Repl Byte"); } when #config(TRACY_ENABLE, false) { tracy.ZoneN("Repl Byte"); }
/* /*
TODO(Jeroen): Once we have a magic ring buffer, we can just peek/write into it TODO(Jeroen): Once we have a magic ring buffer, we can just peek/write into it
@@ -155,7 +156,7 @@ repl_byte :: proc(z: ^Context, count: u16, c: u8) -> (err: io.Error) {
buf := make([]u8, count, context.temp_allocator); buf := make([]u8, count, context.temp_allocator);
#no_bounds_check for i in 0..<count { #no_bounds_check for i in 0..<count {
buf[i] = c; buf[i] = c;
z.last[z.bytes_written & z.window_mask] = c; cb.last[z.bytes_written & cb.window_mask] = c;
z.bytes_written += 1; z.bytes_written += 1;
} }
z.rolling_hash = hash.adler32(buf, z.rolling_hash); z.rolling_hash = hash.adler32(buf, z.rolling_hash);
@@ -167,7 +168,7 @@ repl_byte :: proc(z: ^Context, count: u16, c: u8) -> (err: io.Error) {
return .None; return .None;
} }
repl_bytes :: proc(z: ^Context, count: u16, distance: u16) -> (err: io.Error) { repl_bytes :: proc(z: ^Context, cb: ^Code_Buffer, count: u16, distance: u16) -> (err: io.Error) {
when #config(TRACY_ENABLE, false) { tracy.ZoneN("Repl Bytes"); } when #config(TRACY_ENABLE, false) { tracy.ZoneN("Repl Bytes"); }
/* /*
TODO(Jeroen): Once we have a magic ring buffer, we can just peek/write into it TODO(Jeroen): Once we have a magic ring buffer, we can just peek/write into it
@@ -178,9 +179,9 @@ repl_bytes :: proc(z: ^Context, count: u16, distance: u16) -> (err: io.Error) {
offset := z.bytes_written - i64(distance); offset := z.bytes_written - i64(distance);
#no_bounds_check for i in 0..<count { #no_bounds_check for i in 0..<count {
c := z.last[offset & z.window_mask]; c := cb.last[offset & cb.window_mask];
z.last[z.bytes_written & z.window_mask] = c; cb.last[z.bytes_written & cb.window_mask] = c;
buf[i] = c; buf[i] = c;
z.bytes_written += 1; offset += 1; z.bytes_written += 1; offset += 1;
} }
@@ -257,9 +258,9 @@ build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) {
return nil; return nil;
} }
decode_huffman_slowpath :: proc(z: ^Context, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check { decode_huffman_slowpath :: proc(z: ^Context, cb: ^Code_Buffer, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check {
when #config(TRACY_ENABLE, false) { tracy.ZoneN("Decode Huffman Slow"); } when #config(TRACY_ENABLE, false) { tracy.ZoneN("Decode Huffman Slow"); }
code := u16(compress.peek_bits_lsb(z, 16)); code := u16(compress.peek_bits_lsb(z, cb, 16));
k := int(z_bit_reverse(code, 16)); k := int(z_bit_reverse(code, 16));
s: u8; s: u8;
@@ -282,41 +283,41 @@ decode_huffman_slowpath :: proc(z: ^Context, t: ^Huffman_Table) -> (r: u16, err:
return 0, E_Deflate.Bad_Huffman_Code; return 0, E_Deflate.Bad_Huffman_Code;
} }
compress.consume_bits_lsb(z, s); compress.consume_bits_lsb(cb, s);
r = t.value[b]; r = t.value[b];
return r, nil; return r, nil;
} }
decode_huffman :: proc(z: ^Context, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check { decode_huffman :: proc(z: ^Context, cb: ^Code_Buffer, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check {
when #config(TRACY_ENABLE, false) { tracy.ZoneN("Decode Huffman"); } when #config(TRACY_ENABLE, false) { tracy.ZoneN("Decode Huffman"); }
if z.num_bits < 16 { if cb.num_bits < 16 {
if z.num_bits == -100 { if cb.num_bits > 63 {
return 0, E_ZLIB.Code_Buffer_Malformed; return 0, E_ZLIB.Code_Buffer_Malformed;
} }
compress.refill_lsb(z); compress.refill_lsb(z, cb);
if z.eof { if cb.num_bits > 63 {
return 0, E_General.Stream_Too_Short; return 0, E_General.Stream_Too_Short;
} }
} }
#no_bounds_check b := t.fast[z.code_buffer & ZFAST_MASK]; #no_bounds_check b := t.fast[cb.code_buffer & ZFAST_MASK];
if b != 0 { if b != 0 {
s := u8(b >> ZFAST_BITS); s := u8(b >> ZFAST_BITS);
compress.consume_bits_lsb(z, s); compress.consume_bits_lsb(cb, s);
return b & 511, nil; return b & 511, nil;
} }
return decode_huffman_slowpath(z, t); return decode_huffman_slowpath(z, cb, t);
} }
parse_huffman_block :: proc(z: ^Context, z_repeat, z_offset: ^Huffman_Table) -> (err: Error) #no_bounds_check { parse_huffman_block :: proc(z: ^Context, cb: ^Code_Buffer, z_repeat, z_offset: ^Huffman_Table) -> (err: Error) #no_bounds_check {
when #config(TRACY_ENABLE, false) { tracy.ZoneN("Parse Huffman Block"); } when #config(TRACY_ENABLE, false) { tracy.ZoneN("Parse Huffman Block"); }
#no_bounds_check for { #no_bounds_check for {
value, e := decode_huffman(z, z_repeat); value, e := decode_huffman(z, cb, z_repeat);
if e != nil { if e != nil {
return err; return err;
} }
if value < 256 { if value < 256 {
e := write_byte(z, u8(value)); e := write_byte(z, cb, u8(value));
if e != .None { if e != .None {
return E_General.Output_Too_Short; return E_General.Output_Too_Short;
} }
@@ -329,17 +330,17 @@ parse_huffman_block :: proc(z: ^Context, z_repeat, z_offset: ^Huffman_Table) ->
value -= 257; value -= 257;
length := Z_LENGTH_BASE[value]; length := Z_LENGTH_BASE[value];
if Z_LENGTH_EXTRA[value] > 0 { if Z_LENGTH_EXTRA[value] > 0 {
length += u16(compress.read_bits_lsb(z, Z_LENGTH_EXTRA[value])); length += u16(compress.read_bits_lsb(z, cb, Z_LENGTH_EXTRA[value]));
} }
value, e = decode_huffman(z, z_offset); value, e = decode_huffman(z, cb, z_offset);
if e != nil { if e != nil {
return E_Deflate.Bad_Huffman_Code; return E_Deflate.Bad_Huffman_Code;
} }
distance := Z_DIST_BASE[value]; distance := Z_DIST_BASE[value];
if Z_DIST_EXTRA[value] > 0 { if Z_DIST_EXTRA[value] > 0 {
distance += u16(compress.read_bits_lsb(z, Z_DIST_EXTRA[value])); distance += u16(compress.read_bits_lsb(z, cb, Z_DIST_EXTRA[value]));
} }
if z.bytes_written < i64(distance) { if z.bytes_written < i64(distance) {
@@ -360,15 +361,15 @@ parse_huffman_block :: proc(z: ^Context, z_repeat, z_offset: ^Huffman_Table) ->
Replicate the last outputted byte, length times. Replicate the last outputted byte, length times.
*/ */
if length > 0 { if length > 0 {
c := z.last[offset & z.window_mask]; c := cb.last[offset & cb.window_mask];
e := repl_byte(z, length, c); e := repl_byte(z, cb, length, c);
if e != .None { if e != .None {
return E_General.Output_Too_Short; return E_General.Output_Too_Short;
} }
} }
} else { } else {
if length > 0 { if length > 0 {
e := repl_bytes(z, length, distance); e := repl_bytes(z, cb, length, distance);
if e != .None { if e != .None {
return E_General.Output_Too_Short; return E_General.Output_Too_Short;
} }
@@ -391,6 +392,9 @@ inflate_from_stream :: proc(using ctx: ^Context, raw := false, allocator := cont
DEFLATE stream. DEFLATE stream.
*/ */
code_buffer := Code_Buffer{};
cb := &code_buffer;
if !raw { if !raw {
data_size := io.size(ctx.input); data_size := io.size(ctx.input);
if data_size < 6 { if data_size < 6 {
@@ -408,7 +412,7 @@ inflate_from_stream :: proc(using ctx: ^Context, raw := false, allocator := cont
if cinfo > 7 { if cinfo > 7 {
return E_ZLIB.Unsupported_Window_Size; return E_ZLIB.Unsupported_Window_Size;
} }
ctx.window_size = 1 << (cinfo + 8); cb.window_mask = i64((1 << (cinfo + 8) - 1));
flg, _ := compress.read_u8(ctx); flg, _ := compress.read_u8(ctx);
@@ -439,15 +443,15 @@ inflate_from_stream :: proc(using ctx: ^Context, raw := false, allocator := cont
} }
// Parse ZLIB stream without header. // Parse ZLIB stream without header.
err = inflate_raw(ctx); err = inflate_raw(ctx, cb);
if err != nil { if err != nil {
return err; return err;
} }
if !raw { if !raw {
compress.discard_to_next_byte_lsb(ctx); compress.discard_to_next_byte_lsb(cb);
adler32 := compress.read_bits_lsb(ctx, 8) << 24 | compress.read_bits_lsb(ctx, 8) << 16 | compress.read_bits_lsb(ctx, 8) << 8 | compress.read_bits_lsb(ctx, 8); adler32 := compress.read_bits_lsb(ctx, cb, 8) << 24 | compress.read_bits_lsb(ctx, cb, 8) << 16 | compress.read_bits_lsb(ctx, cb, 8) << 8 | compress.read_bits_lsb(ctx, cb, 8);
if ctx.rolling_hash != u32(adler32) { if ctx.rolling_hash != u32(adler32) {
return E_General.Checksum_Failed; return E_General.Checksum_Failed;
} }
@@ -456,13 +460,13 @@ inflate_from_stream :: proc(using ctx: ^Context, raw := false, allocator := cont
} }
// @(optimization_mode="speed") // @(optimization_mode="speed")
inflate_from_stream_raw :: proc(z: ^Context, allocator := context.allocator) -> (err: Error) #no_bounds_check { inflate_from_stream_raw :: proc(z: ^Context, cb: ^Code_Buffer, allocator := context.allocator) -> (err: Error) #no_bounds_check {
when #config(TRACY_ENABLE, false) { tracy.ZoneN("Inflate Raw"); } when #config(TRACY_ENABLE, false) { tracy.ZoneN("Inflate Raw"); }
final := u32(0); final := u32(0);
type := u32(0); type := u32(0);
z.num_bits = 0; cb.num_bits = 0;
z.code_buffer = 0; cb.code_buffer = 0;
z_repeat: ^Huffman_Table; z_repeat: ^Huffman_Table;
z_offset: ^Huffman_Table; z_offset: ^Huffman_Table;
@@ -484,19 +488,17 @@ inflate_from_stream_raw :: proc(z: ^Context, allocator := context.allocator) ->
defer free(z_offset); defer free(z_offset);
defer free(codelength_ht); defer free(codelength_ht);
if z.window_size == 0 { if cb.window_mask == 0 {
z.window_size = DEFLATE_MAX_DISTANCE; cb.window_mask = DEFLATE_MAX_DISTANCE - 1;
} }
z.window_mask = z.window_size - 1;
// Allocate rolling window buffer. // Allocate rolling window buffer.
last_b := mem.make_dynamic_array_len_cap([dynamic]u8, z.window_size, z.window_size, allocator); cb.last = mem.make_dynamic_array_len_cap([dynamic]u8, cb.window_mask + 1, cb.window_mask + 1, allocator);
z.last = &last_b; defer delete(cb.last);
defer delete(last_b);
for { for {
final = compress.read_bits_lsb(z, 1); final = compress.read_bits_lsb(z, cb, 1);
type = compress.read_bits_lsb(z, 2); type = compress.read_bits_lsb(z, cb, 2);
// fmt.printf("Final: %v | Type: %v\n", final, type); // fmt.printf("Final: %v | Type: %v\n", final, type);
@@ -506,10 +508,10 @@ inflate_from_stream_raw :: proc(z: ^Context, allocator := context.allocator) ->
// Uncompressed block // Uncompressed block
// Discard bits until next byte boundary // Discard bits until next byte boundary
compress.discard_to_next_byte_lsb(z); compress.discard_to_next_byte_lsb(cb);
uncompressed_len := i16(compress.read_bits_lsb(z, 16)); uncompressed_len := i16(compress.read_bits_lsb(z, cb, 16));
length_check := i16(compress.read_bits_lsb(z, 16)); length_check := i16(compress.read_bits_lsb(z, cb, 16));
// fmt.printf("LEN: %v, ~LEN: %v, NLEN: %v, ~NLEN: %v\n", uncompressed_len, ~uncompressed_len, length_check, ~length_check); // fmt.printf("LEN: %v, ~LEN: %v, NLEN: %v, ~NLEN: %v\n", uncompressed_len, ~uncompressed_len, length_check, ~length_check);
@@ -523,9 +525,9 @@ inflate_from_stream_raw :: proc(z: ^Context, allocator := context.allocator) ->
and a single Adler32 update after. and a single Adler32 update after.
*/ */
#no_bounds_check for uncompressed_len > 0 { #no_bounds_check for uncompressed_len > 0 {
compress.refill_lsb(z); compress.refill_lsb(z, cb);
lit := compress.read_bits_lsb(z, 8); lit := compress.read_bits_lsb(z, cb, 8);
write_byte(z, u8(lit)); write_byte(z, cb, u8(lit));
uncompressed_len -= 1; uncompressed_len -= 1;
} }
case 3: case 3:
@@ -550,14 +552,14 @@ inflate_from_stream_raw :: proc(z: ^Context, allocator := context.allocator) ->
//i: u32; //i: u32;
n: u32; n: u32;
compress.refill_lsb(z, 14); compress.refill_lsb(z, cb, 14);
hlit := compress.read_bits_no_refill_lsb(z, 5) + 257; hlit := compress.read_bits_no_refill_lsb(z, cb, 5) + 257;
hdist := compress.read_bits_no_refill_lsb(z, 5) + 1; hdist := compress.read_bits_no_refill_lsb(z, cb, 5) + 1;
hclen := compress.read_bits_no_refill_lsb(z, 4) + 4; hclen := compress.read_bits_no_refill_lsb(z, cb, 4) + 4;
ntot := hlit + hdist; ntot := hlit + hdist;
#no_bounds_check for i in 0..<hclen { #no_bounds_check for i in 0..<hclen {
s := compress.read_bits_lsb(z, 3); s := compress.read_bits_lsb(z, cb, 3);
codelength_sizes[Z_LENGTH_DEZIGZAG[i]] = u8(s); codelength_sizes[Z_LENGTH_DEZIGZAG[i]] = u8(s);
} }
err = build_huffman(codelength_ht, codelength_sizes[:]); err = build_huffman(codelength_ht, codelength_sizes[:]);
@@ -569,7 +571,7 @@ inflate_from_stream_raw :: proc(z: ^Context, allocator := context.allocator) ->
c: u16; c: u16;
for n < ntot { for n < ntot {
c, err = decode_huffman(z, codelength_ht); c, err = decode_huffman(z, cb, codelength_ht);
if err != nil { if err != nil {
return err; return err;
} }
@@ -582,18 +584,18 @@ inflate_from_stream_raw :: proc(z: ^Context, allocator := context.allocator) ->
n += 1; n += 1;
} else { } else {
fill := u8(0); fill := u8(0);
compress.refill_lsb(z, 7); compress.refill_lsb(z, cb, 7);
switch c { switch c {
case 16: case 16:
c = u16(compress.read_bits_no_refill_lsb(z, 2) + 3); c = u16(compress.read_bits_no_refill_lsb(z, cb, 2) + 3);
if n == 0 { if n == 0 {
return E_Deflate.Huffman_Bad_Code_Lengths; return E_Deflate.Huffman_Bad_Code_Lengths;
} }
fill = lencodes[n - 1]; fill = lencodes[n - 1];
case 17: case 17:
c = u16(compress.read_bits_no_refill_lsb(z, 3) + 3); c = u16(compress.read_bits_no_refill_lsb(z, cb, 3) + 3);
case 18: case 18:
c = u16(compress.read_bits_no_refill_lsb(z, 7) + 11); c = u16(compress.read_bits_no_refill_lsb(z, cb, 7) + 11);
case: case:
return E_Deflate.Huffman_Bad_Code_Lengths; return E_Deflate.Huffman_Bad_Code_Lengths;
} }
@@ -623,7 +625,7 @@ inflate_from_stream_raw :: proc(z: ^Context, allocator := context.allocator) ->
return err; return err;
} }
} }
err = parse_huffman_block(z, z_repeat, z_offset); err = parse_huffman_block(z, cb, z_repeat, z_offset);
// log.debugf("Err: %v | Final: %v | Type: %v\n", err, final, type); // log.debugf("Err: %v | Final: %v | Type: %v\n", err, final, type);
if err != nil { if err != nil {
return err; return err;
@@ -653,8 +655,19 @@ inflate_from_byte_array :: proc(input: []u8, buf: ^bytes.Buffer, raw := false) -
return err; return err;
} }
inflate_from_byte_array_raw :: proc(input: []u8, buf: ^bytes.Buffer, raw := false) -> (err: Error) { inflate_from_byte_array_raw :: proc(input: []u8, buf: ^bytes.Buffer, cb: ^Code_Buffer, raw := false) -> (err: Error) {
return inflate_from_byte_array(input, buf, true); ctx := Context{};
r := bytes.Reader{};
bytes.reader_init(&r, input);
rs := bytes.reader_to_stream(&r);
ctx.input = rs;
buf := buf;
ws := bytes.buffer_to_stream(buf);
ctx.output = ws;
return inflate_from_stream_raw(&ctx, cb);
} }
inflate :: proc{inflate_from_stream, inflate_from_byte_array}; inflate :: proc{inflate_from_stream, inflate_from_byte_array};
+16
View File
@@ -23,6 +23,22 @@ import "core:mem"
import "core:os" import "core:os"
main :: proc() { main :: proc() {
track := mem.Tracking_Allocator{};
mem.tracking_allocator_init(&track, context.allocator);
context.allocator = mem.tracking_allocator(&track);
demo();
if len(track.allocation_map) > 0 {
fmt.println("Leaks:");
for _, v in track.allocation_map {
fmt.printf("\t%v\n\n", v);
}
}
}
demo :: proc() {
file: string; file: string;
options := image.Options{.return_metadata}; options := image.Options{.return_metadata};