From 30a580846038ae0e5a314102a1134688b15adfb8 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 26 Jun 2021 20:40:39 +0200 Subject: [PATCH] ZLIB: Moar faster. --- core/compress/common.odin | 94 +++++++++---------------- core/compress/gzip/gzip.odin | 41 +++++------ core/compress/zlib/zlib.odin | 133 +++++++++++++++-------------------- 3 files changed, 109 insertions(+), 159 deletions(-) diff --git a/core/compress/common.odin b/core/compress/common.odin index 2b7c1afe1..47b80a995 100644 --- a/core/compress/common.odin +++ b/core/compress/common.odin @@ -127,10 +127,9 @@ Deflate_Error :: enum { // General I/O context for ZLIB, LZW, etc. -Context :: struct #packed { - input: io.Stream, +Context :: struct { input_data: []u8, - + input: io.Stream, output: ^bytes.Buffer, bytes_written: i64, @@ -140,14 +139,9 @@ Context :: struct #packed { size_packed: i64, size_unpacked: i64, - /* - Used to update hash as we write instead of all at once. - */ - rolling_hash: u32, - /* - Reserved - */ - reserved: [2]u32, + code_buffer: u64, + num_bits: u64, + /* Flags: `input_fully_in_memory` tells us whether we're EOF when `input_data` is empty. @@ -155,28 +149,8 @@ Context :: struct #packed { */ input_fully_in_memory: b8, input_refills_from_stream: b8, - output_to_stream: b8, - reserved_flag: b8, - - bit_buffer_stuff: [3]u64, - - } -// #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 /* @@ -290,26 +264,26 @@ peek_data :: #force_inline proc(z: ^Context, $T: typeid) -> (res: T, err: io.Err // Sliding window read back @(optimization_mode="speed") -peek_back_byte :: #force_inline proc(cb: ^Code_Buffer, offset: i64) -> (res: u8, err: io.Error) { +peek_back_byte :: #force_inline proc(z: ^Context, offset: i64) -> (res: u8, err: io.Error) { // Look back into the sliding window. - return cb.last[offset & cb.window_mask], .None; + return z.output.buf[z.bytes_written - offset], .None; } // Generalized bit reader LSB @(optimization_mode="speed") -refill_lsb :: proc(z: ^Context, cb: ^Code_Buffer, width := i8(24)) { +refill_lsb :: proc(z: ^Context, width := i8(24)) { refill := u64(width); for { - if cb.num_bits > refill { + if z.num_bits > refill { break; } - if cb.code_buffer == 0 && cb.num_bits > 63 { - cb.num_bits = 0; + if z.code_buffer == 0 && z.num_bits > 63 { + z.num_bits = 0; } - if cb.code_buffer >= 1 << uint(cb.num_bits) { + if z.code_buffer >= 1 << uint(z.num_bits) { // Code buffer is malformed. - cb.num_bits = max(u64); + z.num_bits = max(u64); return; } b, err := read_u8(z); @@ -317,48 +291,48 @@ refill_lsb :: proc(z: ^Context, cb: ^Code_Buffer, width := i8(24)) { // This is fine at the end of the file. return; } - cb.code_buffer |= (u64(b) << u8(cb.num_bits)); - cb.num_bits += 8; + z.code_buffer |= (u64(b) << u8(z.num_bits)); + z.num_bits += 8; } } @(optimization_mode="speed") -consume_bits_lsb :: #force_inline proc(cb: ^Code_Buffer, width: u8) { - cb.code_buffer >>= width; - cb.num_bits -= u64(width); +consume_bits_lsb :: #force_inline proc(z: ^Context, width: u8) { + z.code_buffer >>= width; + z.num_bits -= u64(width); } @(optimization_mode="speed") -peek_bits_lsb :: #force_inline proc(z: ^Context, cb: ^Code_Buffer, width: u8) -> u32 { - if cb.num_bits < u64(width) { - refill_lsb(z, cb); +peek_bits_lsb :: #force_inline proc(z: ^Context, width: u8) -> u32 { + if z.num_bits < u64(width) { + refill_lsb(z); } // assert(z.num_bits >= i8(width)); - return u32(cb.code_buffer & ~(~u64(0) << width)); + return u32(z.code_buffer & ~(~u64(0) << width)); } @(optimization_mode="speed") -peek_bits_no_refill_lsb :: #force_inline proc(z: ^Context, cb: ^Code_Buffer, width: u8) -> u32 { - assert(cb.num_bits >= u64(width)); - return u32(cb.code_buffer & ~(~u64(0) << width)); +peek_bits_no_refill_lsb :: #force_inline proc(z: ^Context, width: u8) -> u32 { + assert(z.num_bits >= u64(width)); + return u32(z.code_buffer & ~(~u64(0) << width)); } @(optimization_mode="speed") -read_bits_lsb :: #force_inline proc(z: ^Context, cb: ^Code_Buffer, width: u8) -> u32 { - k := peek_bits_lsb(z, cb, width); - consume_bits_lsb(cb, width); +read_bits_lsb :: #force_inline proc(z: ^Context, width: u8) -> u32 { + k := peek_bits_lsb(z, width); + consume_bits_lsb(z, width); return k; } @(optimization_mode="speed") -read_bits_no_refill_lsb :: #force_inline proc(z: ^Context, cb: ^Code_Buffer, width: u8) -> u32 { - k := peek_bits_no_refill_lsb(z, cb, width); - consume_bits_lsb(cb, width); +read_bits_no_refill_lsb :: #force_inline proc(z: ^Context, width: u8) -> u32 { + k := peek_bits_no_refill_lsb(z, width); + consume_bits_lsb(z, width); return k; } @(optimization_mode="speed") -discard_to_next_byte_lsb :: proc(cb: ^Code_Buffer) { - discard := u8(cb.num_bits & 7); - consume_bits_lsb(cb, discard); +discard_to_next_byte_lsb :: proc(z: ^Context) { + discard := u8(z.num_bits & 7); + consume_bits_lsb(z, discard); } diff --git a/core/compress/gzip/gzip.odin b/core/compress/gzip/gzip.odin index 3d3c28447..38cb77b20 100644 --- a/core/compress/gzip/gzip.odin +++ b/core/compress/gzip/gzip.odin @@ -133,13 +133,13 @@ load_from_file :: proc(filename: string, buf: ^bytes.Buffer, expected_output_siz return; } -load_from_stream :: proc(ctx: ^compress.Context, buf: ^bytes.Buffer, known_gzip_size := -1, expected_output_size := -1, allocator := context.allocator) -> (err: Error) { +load_from_stream :: proc(z: ^compress.Context, buf: ^bytes.Buffer, known_gzip_size := -1, expected_output_size := -1, allocator := context.allocator) -> (err: Error) { buf := buf; expected_output_size := expected_output_size; input_data_consumed := 0; - ctx.output = buf; + z.output = buf; if expected_output_size > GZIP_MAX_PAYLOAD_SIZE { return E_GZIP.Payload_Size_Exceeds_Max_Payload; @@ -151,7 +151,7 @@ load_from_stream :: proc(ctx: ^compress.Context, buf: ^bytes.Buffer, known_gzip_ b: []u8; - header, e := compress.read_data(ctx, Header); + header, e := compress.read_data(z, Header); if e != .None { return E_General.File_Too_Short; } @@ -180,7 +180,7 @@ load_from_stream :: proc(ctx: ^compress.Context, buf: ^bytes.Buffer, known_gzip_ // printf("os: %v\n", OS_Name[header.os]); if .extra in header.flags { - xlen, e_extra := compress.read_data(ctx, u16le); + xlen, e_extra := compress.read_data(z, u16le); input_data_consumed += 2; if e_extra != .None { @@ -198,7 +198,7 @@ load_from_stream :: proc(ctx: ^compress.Context, buf: ^bytes.Buffer, known_gzip_ for xlen >= 4 { // println("Parsing Extra field(s)."); - field_id, field_error = compress.read_data(ctx, [2]u8); + field_id, field_error = compress.read_data(z, [2]u8); if field_error != .None { // printf("Parsing Extra returned: %v\n", field_error); return E_General.Stream_Too_Short; @@ -206,7 +206,7 @@ load_from_stream :: proc(ctx: ^compress.Context, buf: ^bytes.Buffer, known_gzip_ xlen -= 2; input_data_consumed += 2; - field_length, field_error = compress.read_data(ctx, u16le); + field_length, field_error = compress.read_data(z, u16le); if field_error != .None { // printf("Parsing Extra returned: %v\n", field_error); return E_General.Stream_Too_Short; @@ -222,7 +222,7 @@ load_from_stream :: proc(ctx: ^compress.Context, buf: ^bytes.Buffer, known_gzip_ // printf(" Field \"%v\" of length %v found: ", string(field_id[:]), field_length); if field_length > 0 { - b, field_error = compress.read_slice(ctx, int(field_length)); + b, field_error = compress.read_slice(z, int(field_length)); if field_error != .None { // printf("Parsing Extra returned: %v\n", field_error); return E_General.Stream_Too_Short; @@ -246,7 +246,7 @@ load_from_stream :: proc(ctx: ^compress.Context, buf: ^bytes.Buffer, known_gzip_ name_error: io.Error; for i < len(name) { - b, name_error = compress.read_slice(ctx, 1); + b, name_error = compress.read_slice(z, 1); if name_error != .None { return E_General.Stream_Too_Short; } @@ -270,7 +270,7 @@ load_from_stream :: proc(ctx: ^compress.Context, buf: ^bytes.Buffer, known_gzip_ comment_error: io.Error; for i < len(comment) { - b, comment_error = compress.read_slice(ctx, 1); + b, comment_error = compress.read_slice(z, 1); if comment_error != .None { return E_General.Stream_Too_Short; } @@ -289,7 +289,7 @@ load_from_stream :: proc(ctx: ^compress.Context, buf: ^bytes.Buffer, known_gzip_ if .header_crc in header.flags { crc_error: io.Error; - _, crc_error = compress.read_slice(ctx, 2); + _, crc_error = compress.read_slice(z, 2); input_data_consumed += 2; if crc_error != .None { return E_General.Stream_Too_Short; @@ -303,9 +303,6 @@ load_from_stream :: proc(ctx: ^compress.Context, buf: ^bytes.Buffer, known_gzip_ /* We should have arrived at the ZLIB payload. */ - code_buffer := compress.Code_Buffer{}; - cb := &code_buffer; - payload_u32le: u32le; // fmt.printf("known_gzip_size: %v | expected_output_size: %v\n", known_gzip_size, expected_output_size); @@ -325,10 +322,10 @@ load_from_stream :: proc(ctx: ^compress.Context, buf: ^bytes.Buffer, known_gzip_ We'll still want to ensure there's capacity left in the output buffer when we write, of course. */ - if ctx.input_fully_in_memory && known_gzip_size > -1 { + if z.input_fully_in_memory && known_gzip_size > -1 { offset := known_gzip_size - input_data_consumed - 4; - if len(ctx.input_data) >= offset + 4 { - length_bytes := ctx.input_data[offset:][:4]; + if len(z.input_data) >= offset + 4 { + length_bytes := z.input_data[offset:][:4]; payload_u32le = (^u32le)(&length_bytes[0])^; expected_output_size = int(payload_u32le); } @@ -342,27 +339,27 @@ load_from_stream :: proc(ctx: ^compress.Context, buf: ^bytes.Buffer, known_gzip_ // fmt.printf("GZIP: Expected Payload Size: %v\n", expected_output_size); - zlib_error := zlib.inflate_raw(z=ctx, cb=&code_buffer, expected_output_size=expected_output_size); + zlib_error := zlib.inflate_raw(z=z, expected_output_size=expected_output_size); if zlib_error != nil { return zlib_error; } /* Read CRC32 using the ctx bit reader because zlib may leave bytes in there. */ - compress.discard_to_next_byte_lsb(cb); + compress.discard_to_next_byte_lsb(z); footer_error: io.Error; payload_crc_b: [4]u8; for _, i in payload_crc_b { - if cb.num_bits >= 8 { - payload_crc_b[i] = u8(compress.read_bits_lsb(ctx, cb, 8)); + if z.num_bits >= 8 { + payload_crc_b[i] = u8(compress.read_bits_lsb(z, 8)); } else { - payload_crc_b[i], footer_error = compress.read_u8(ctx); + payload_crc_b[i], footer_error = compress.read_u8(z); } } payload_crc := transmute(u32le)payload_crc_b; - payload_u32le, footer_error = compress.read_data(ctx, u32le); + payload_u32le, footer_error = compress.read_data(z, u32le); payload := bytes.buffer_to_bytes(buf); diff --git a/core/compress/zlib/zlib.odin b/core/compress/zlib/zlib.odin index 032c9c8e0..aaa549e7b 100644 --- a/core/compress/zlib/zlib.odin +++ b/core/compress/zlib/zlib.odin @@ -30,8 +30,7 @@ import "core:bytes" `Context.rolling_hash` if not inlining it is still faster. */ -Context :: compress.Context; -Code_Buffer :: compress.Code_Buffer; +Context :: compress.Context; Compression_Method :: enum u8 { DEFLATE = 8, @@ -166,7 +165,7 @@ grow_buffer :: proc(buf: ^[dynamic]u8) -> (err: compress.Error) { */ @(optimization_mode="speed") -write_byte :: #force_inline proc(z: ^Context, cb: ^Code_Buffer, c: u8) -> (err: io.Error) #no_bounds_check { +write_byte :: #force_inline proc(z: ^Context, c: u8) -> (err: io.Error) #no_bounds_check { /* Resize if needed. */ @@ -179,14 +178,13 @@ write_byte :: #force_inline proc(z: ^Context, cb: ^Code_Buffer, c: u8) -> (err: #no_bounds_check { z.output.buf[z.bytes_written] = c; - cb.last[z.bytes_written & cb.window_mask] = c; } z.bytes_written += 1; return .None; } @(optimization_mode="speed") -repl_byte :: proc(z: ^Context, cb: ^Code_Buffer, count: u16, c: u8) -> (err: io.Error) #no_bounds_check { +repl_byte :: proc(z: ^Context, count: u16, c: u8) -> (err: io.Error) #no_bounds_check { /* TODO(Jeroen): Once we have a magic ring buffer, we can just peek/write into it without having to worry about wrapping, so no need for a temp allocation to give to @@ -206,7 +204,6 @@ repl_byte :: proc(z: ^Context, cb: ^Code_Buffer, count: u16, c: u8) -> (err: io. #no_bounds_check { for _ in 0.. (err: io. } @(optimization_mode="speed") -repl_bytes :: proc(z: ^Context, cb: ^Code_Buffer, count: u16, distance: u16) -> (err: io.Error) { +repl_bytes :: proc(z: ^Context, count: u16, distance: u16) -> (err: io.Error) { /* TODO(Jeroen): Once we have a magic ring buffer, we can just peek/write into it without having to worry about wrapping, so no need for a temp allocation to give to the output stream, just give it _that_ slice. */ - offset := z.bytes_written - i64(distance); + offset := i64(distance); if int(z.bytes_written) + int(count) >= len(z.output.buf) { e := grow_buffer(&z.output.buf); @@ -233,10 +230,9 @@ repl_bytes :: proc(z: ^Context, cb: ^Code_Buffer, count: u16, distance: u16) -> #no_bounds_check { for _ in 0.. (err: Error) { } @(optimization_mode="speed") -decode_huffman_slowpath :: proc(z: ^Context, cb: ^Code_Buffer, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check { - code := u16(compress.peek_bits_lsb(z, cb, 16)); +decode_huffman_slowpath :: proc(z: ^Context, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check { + code := u16(compress.peek_bits_lsb(z,16)); k := int(z_bit_reverse(code, 16)); s: u8; @@ -332,41 +328,41 @@ decode_huffman_slowpath :: proc(z: ^Context, cb: ^Code_Buffer, t: ^Huffman_Table return 0, E_Deflate.Bad_Huffman_Code; } - compress.consume_bits_lsb(cb, s); + compress.consume_bits_lsb(z, s); r = t.value[b]; return r, nil; } @(optimization_mode="speed") -decode_huffman :: proc(z: ^Context, cb: ^Code_Buffer, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check { - if cb.num_bits < 16 { - if cb.num_bits > 63 { +decode_huffman :: proc(z: ^Context, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check { + if z.num_bits < 16 { + if z.num_bits > 63 { return 0, E_ZLIB.Code_Buffer_Malformed; } - compress.refill_lsb(z, cb); - if cb.num_bits > 63 { + compress.refill_lsb(z); + if z.num_bits > 63 { return 0, E_General.Stream_Too_Short; } } - #no_bounds_check b := t.fast[cb.code_buffer & ZFAST_MASK]; + #no_bounds_check b := t.fast[z.code_buffer & ZFAST_MASK]; if b != 0 { s := u8(b >> ZFAST_BITS); - compress.consume_bits_lsb(cb, s); + compress.consume_bits_lsb(z, s); return b & 511, nil; } - return decode_huffman_slowpath(z, cb, t); + return decode_huffman_slowpath(z, t); } @(optimization_mode="speed") -parse_huffman_block :: proc(z: ^Context, cb: ^Code_Buffer, z_repeat, z_offset: ^Huffman_Table) -> (err: Error) #no_bounds_check { +parse_huffman_block :: proc(z: ^Context, z_repeat, z_offset: ^Huffman_Table) -> (err: Error) #no_bounds_check { #no_bounds_check for { - value, e := decode_huffman(z, cb, z_repeat); + value, e := decode_huffman(z, z_repeat); if e != nil { return err; } if value < 256 { - e := write_byte(z, cb, u8(value)); + e := write_byte(z, u8(value)); if e != .None { return E_General.Output_Too_Short; } @@ -379,17 +375,17 @@ parse_huffman_block :: proc(z: ^Context, cb: ^Code_Buffer, z_repeat, z_offset: ^ value -= 257; length := Z_LENGTH_BASE[value]; if Z_LENGTH_EXTRA[value] > 0 { - length += u16(compress.read_bits_lsb(z, cb, Z_LENGTH_EXTRA[value])); + length += u16(compress.read_bits_lsb(z, Z_LENGTH_EXTRA[value])); } - value, e = decode_huffman(z, cb, z_offset); + value, e = decode_huffman(z, z_offset); if e != nil { return E_Deflate.Bad_Huffman_Code; } distance := Z_DIST_BASE[value]; if Z_DIST_EXTRA[value] > 0 { - distance += u16(compress.read_bits_lsb(z, cb, Z_DIST_EXTRA[value])); + distance += u16(compress.read_bits_lsb(z, Z_DIST_EXTRA[value])); } if z.bytes_written < i64(distance) { @@ -397,7 +393,6 @@ parse_huffman_block :: proc(z: ^Context, cb: ^Code_Buffer, z_repeat, z_offset: ^ return E_Deflate.Bad_Distance; } - offset := i64(z.bytes_written - i64(distance)); /* These might be sped up with a repl_byte call that copies from the already written output more directly, and that @@ -410,15 +405,15 @@ parse_huffman_block :: proc(z: ^Context, cb: ^Code_Buffer, z_repeat, z_offset: ^ Replicate the last outputted byte, length times. */ if length > 0 { - c := cb.last[offset & cb.window_mask]; - e := repl_byte(z, cb, length, c); + c := z.output.buf[z.bytes_written - i64(distance)]; + e := repl_byte(z, length, c); if e != .None { return E_General.Output_Too_Short; } } } else { if length > 0 { - e := repl_bytes(z, cb, length, distance); + e := repl_bytes(z, length, distance); if e != .None { return E_General.Output_Too_Short; } @@ -442,9 +437,6 @@ inflate_from_stream :: proc(using ctx: ^Context, raw := false, expected_output_s DEFLATE stream. */ - code_buffer := Code_Buffer{}; - cb := &code_buffer; - if !raw { data_size := io.size(ctx.input); if data_size < 6 { @@ -462,8 +454,6 @@ inflate_from_stream :: proc(using ctx: ^Context, raw := false, expected_output_s if cinfo > 7 { return E_ZLIB.Unsupported_Window_Size; } - cb.window_mask = i64((1 << (cinfo + 8) - 1)); - flg, _ := compress.read_u8(ctx); fcheck := flg & 0x1f; @@ -488,23 +478,21 @@ inflate_from_stream :: proc(using ctx: ^Context, raw := false, expected_output_s at the end to compare checksums. */ - // Seed the Adler32 rolling checksum. - ctx.rolling_hash = 1; } // Parse ZLIB stream without header. - err = inflate_raw(z=ctx, cb=cb, expected_output_size=expected_output_size); + err = inflate_raw(z=ctx, expected_output_size=expected_output_size); if err != nil { return err; } if !raw { - compress.discard_to_next_byte_lsb(cb); - 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); + compress.discard_to_next_byte_lsb(ctx); + 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); - ctx.rolling_hash = hash.adler32(ctx.output.buf[:]); + output_hash := hash.adler32(ctx.output.buf[:]); - if ctx.rolling_hash != u32(adler32) { + if output_hash != u32(adler32) { return E_General.Checksum_Failed; } } @@ -512,7 +500,7 @@ inflate_from_stream :: proc(using ctx: ^Context, raw := false, expected_output_s } @(optimization_mode="speed") -inflate_from_stream_raw :: proc(z: ^Context, cb: ^Code_Buffer, expected_output_size := -1, allocator := context.allocator) -> (err: Error) #no_bounds_check { +inflate_from_stream_raw :: proc(z: ^Context, expected_output_size := -1, allocator := context.allocator) -> (err: Error) #no_bounds_check { expected_output_size := expected_output_size; if expected_output_size <= 0 { @@ -536,8 +524,8 @@ inflate_from_stream_raw :: proc(z: ^Context, cb: ^Code_Buffer, expected_output_s return .Resize_Failed; } - cb.num_bits = 0; - cb.code_buffer = 0; + z.num_bits = 0; + z.code_buffer = 0; z_repeat: ^Huffman_Table; z_offset: ^Huffman_Table; @@ -559,21 +547,12 @@ inflate_from_stream_raw :: proc(z: ^Context, cb: ^Code_Buffer, expected_output_s defer free(z_offset); defer free(codelength_ht); - if cb.window_mask == 0 { - cb.window_mask = DEFLATE_MAX_DISTANCE - 1; - } - - // Allocate rolling window buffer. - cb.last = mem.make_dynamic_array_len_cap([dynamic]u8, cb.window_mask + 1, cb.window_mask + 1, allocator); - defer delete(cb.last); - - final := u32(0); type := u32(0); for { - final = compress.read_bits_lsb(z, cb, 1); - type = compress.read_bits_lsb(z, cb, 2); + final = compress.read_bits_lsb(z, 1); + type = compress.read_bits_lsb(z, 2); // fmt.printf("Final: %v | Type: %v\n", final, type); @@ -582,10 +561,10 @@ inflate_from_stream_raw :: proc(z: ^Context, cb: ^Code_Buffer, expected_output_s // Uncompressed block // Discard bits until next byte boundary - compress.discard_to_next_byte_lsb(cb); + compress.discard_to_next_byte_lsb(z); - uncompressed_len := i16(compress.read_bits_lsb(z, cb, 16)); - length_check := i16(compress.read_bits_lsb(z, cb, 16)); + uncompressed_len := i16(compress.read_bits_lsb(z, 16)); + length_check := i16(compress.read_bits_lsb(z, 16)); // fmt.printf("LEN: %v, ~LEN: %v, NLEN: %v, ~NLEN: %v\n", uncompressed_len, ~uncompressed_len, length_check, ~length_check); @@ -599,9 +578,9 @@ inflate_from_stream_raw :: proc(z: ^Context, cb: ^Code_Buffer, expected_output_s and a single Adler32 update after. */ #no_bounds_check for uncompressed_len > 0 { - compress.refill_lsb(z, cb); - lit := compress.read_bits_lsb(z, cb, 8); - write_byte(z, cb, u8(lit)); + compress.refill_lsb(z); + lit := compress.read_bits_lsb(z, 8); + write_byte(z, u8(lit)); uncompressed_len -= 1; } case 3: @@ -625,14 +604,14 @@ inflate_from_stream_raw :: proc(z: ^Context, cb: ^Code_Buffer, expected_output_s //i: u32; n: u32; - compress.refill_lsb(z, cb, 14); - hlit := compress.read_bits_no_refill_lsb(z, cb, 5) + 257; - hdist := compress.read_bits_no_refill_lsb(z, cb, 5) + 1; - hclen := compress.read_bits_no_refill_lsb(z, cb, 4) + 4; + compress.refill_lsb(z, 14); + hlit := compress.read_bits_no_refill_lsb(z, 5) + 257; + hdist := compress.read_bits_no_refill_lsb(z, 5) + 1; + hclen := compress.read_bits_no_refill_lsb(z, 4) + 4; ntot := hlit + hdist; #no_bounds_check for i in 0.. (err: Error) { +inflate_from_byte_array_raw :: proc(input: []u8, buf: ^bytes.Buffer, raw := false, expected_output_size := -1) -> (err: Error) { ctx := Context{}; r := bytes.Reader{}; @@ -746,7 +725,7 @@ inflate_from_byte_array_raw :: proc(input: []u8, buf: ^bytes.Buffer, cb: ^Code_B ctx.output = buf; - return inflate_from_stream_raw(z=&ctx, cb=cb, expected_output_size=expected_output_size); + return inflate_from_stream_raw(z=&ctx, expected_output_size=expected_output_size); } inflate :: proc{inflate_from_stream, inflate_from_byte_array};