From 65b78b1aa9e8b696007a6a35adb4115c45d5259b Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 26 Jun 2021 15:11:15 +0200 Subject: [PATCH] So far, so good. --- core/compress/common.odin | 7 ++ core/compress/zlib/zlib.odin | 161 ++++++++++++++++++++++++++++------- 2 files changed, 138 insertions(+), 30 deletions(-) diff --git a/core/compress/common.odin b/core/compress/common.odin index a6ae230e5..e21339839 100644 --- a/core/compress/common.odin +++ b/core/compress/common.odin @@ -69,6 +69,13 @@ General_Error :: enum { Checksum_Failed, Incompatible_Options, Unimplemented, + + + /* + Memory errors + */ + Allocation_Failed, + Resize_Failed, } GZIP_Error :: enum { diff --git a/core/compress/zlib/zlib.odin b/core/compress/zlib/zlib.odin index b29e65007..25911ba88 100644 --- a/core/compress/zlib/zlib.odin +++ b/core/compress/zlib/zlib.odin @@ -142,43 +142,121 @@ z_bit_reverse :: #force_inline proc(n: u16, bits: u8) -> (r: u16) { return; } + +@(optimization_mode="speed") +grow_buffer :: proc(buf: ^[dynamic]u8) -> (err: compress.Error) { + /* + That we get here at all means that we didn't pass an expected output size, + or that it was too little. + */ + + /* + Double until we reach the maximum allowed. + */ + new_size := min(len(buf) << 1, compress.COMPRESS_OUTPUT_ALLOCATE_MAX); + + resize(buf, new_size); + if len(buf) != new_size { + /* + Resize failed. + */ + return .Resize_Failed; + } + + return nil; +} + +/* + TODO: Make these return compress.Error. +*/ + @(optimization_mode="speed") 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"); } c := c; - buf := transmute([]u8)mem.Raw_Slice{data=&c, len=1}; - when INLINE_ADLER { z.rolling_hash = hash.adler32(buf, z.rolling_hash); } - _, e := z.output->impl_write(buf); - if e != .None { - return e; + if !z.output_to_stream { + /* + Resize if needed. + */ + if int(z.bytes_written) + 1 >= len(z.output_buf) { + grow_buffer(z.output_buf); + } + + #no_bounds_check { + z.output_buf[z.bytes_written] = c; + cb.last[z.bytes_written & cb.window_mask] = c; + } + z.bytes_written += 1; + + when INLINE_ADLER { + buf := transmute([]u8)mem.Raw_Slice{data=&c, len=1}; + z.rolling_hash = hash.adler32(buf, z.rolling_hash); + } + } else { + buf := transmute([]u8)mem.Raw_Slice{data=&c, len=1}; + when INLINE_ADLER { + z.rolling_hash = hash.adler32(buf, z.rolling_hash); + } + + _, e := z.output->impl_write(buf); + if e != .None { + return e; + } + + #no_bounds_check cb.last[z.bytes_written & cb.window_mask] = c; + z.bytes_written += 1; } - 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) { +repl_byte :: proc(z: ^Context, cb: ^Code_Buffer, count: u16, c: u8) -> (err: io.Error) #no_bounds_check { 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 without having to worry about wrapping, so no need for a temp allocation to give to the output stream, just give it _that_ slice. */ - buf := make([]u8, count, context.temp_allocator); - #no_bounds_check for i in 0..impl_write(buf); - if e != .None { - return e; + if !z.output_to_stream { + /* + Resize if needed. + */ + if int(z.bytes_written) + int(count) >= len(z.output_buf) { + grow_buffer(z.output_buf); + } + + #no_bounds_check { + for _ in 0..impl_write(buf); + if e != .None { + return e; + } } + return .None; } @@ -190,22 +268,45 @@ repl_bytes :: proc(z: ^Context, cb: ^Code_Buffer, count: u16, distance: u16) -> without having to worry about wrapping, so no need for a temp allocation to give to the output stream, just give it _that_ slice. */ - buf := make([]u8, count, context.temp_allocator); offset := z.bytes_written - i64(distance); - #no_bounds_check for i in 0..= len(z.output_buf) { + grow_buffer(z.output_buf); + } - _, e := z.output->impl_write(buf); - if e != .None { - return e; + #no_bounds_check { + for _ in 0..impl_write(buf); + if e != .None { + return e; + } } + return .None; } @@ -495,7 +596,7 @@ inflate_from_stream_raw :: proc(z: ^Context, cb: ^Code_Buffer, expected_output_s if expected_output_size > -1 && expected_output_size <= compress.COMPRESS_OUTPUT_ALLOCATE_MAX { reserve(z.output_buf, expected_output_size); - // resize (z.output_buf, expected_output_size); + resize (z.output_buf, expected_output_size); } else { reserve(z.output_buf, compress.COMPRESS_OUTPUT_ALLOCATE_MIN); }