From 8663c64e47b96c7c03fc539766a4487ec94b39c2 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Tue, 22 Jun 2021 22:17:35 +0200 Subject: [PATCH] Refactor ZLIB structs. --- core/compress/common.odin | 129 ++++++++++++++++-------------- core/compress/gzip/example.odin | 14 ++-- core/compress/gzip/gzip.odin | 10 ++- core/compress/zlib/example.odin | 3 +- core/compress/zlib/zlib.odin | 137 +++++++++++++++++--------------- core/image/png/example.odin | 16 ++++ 6 files changed, 176 insertions(+), 133 deletions(-) diff --git a/core/compress/common.odin b/core/compress/common.odin index faac67f18..2a2ee8172 100644 --- a/core/compress/common.odin +++ b/core/compress/common.odin @@ -72,34 +72,46 @@ Deflate_Error :: enum { 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, - output: io.Stream, +// General I/O context for ZLIB, LZW, etc. +Context :: struct #packed { + input: io.Stream, + input_data: []u8, + + output: io.Stream, + output_buf: [dynamic]u8, 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. */ - rolling_hash: u32, - - // Sliding window buffer. Size must be a power of two. - window_size: i64, - window_mask: i64, - last: ^[dynamic]byte, - + rolling_hash: u32, /* - If we know the raw data size, we can optimize the reads. + Could put some useful bools in here. */ - uncompressed_size: i64, - input_data: []u8, + padding: [3]u32, } +#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 /* @@ -113,14 +125,17 @@ Context :: struct { read_data :: #force_inline proc(c: ^Context, $T: typeid) -> (res: T, err: io.Error) { when #config(TRACY_ENABLE, false) { tracy.ZoneN("Read Data"); } - b := make([]u8, size_of(T), context.temp_allocator); - r, e1 := io.to_reader(c.input); - _, e2 := io.read(r, b); - if !e1 || e2 != .None { - return T{}, e2; + when size_of(T) <= 128 { + b: [size_of(T)]u8; + } else { + b := make([]u8, size_of(T), context.temp_allocator); + } + _, e := c.input->impl_read(b[:]); + if e != .None { + return T{}, e; } - res = (^T)(raw_data(b))^; + res = (^T)(&b)^; 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 -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. - return c.last[offset % c.window_size], .None; + return cb.last[offset & cb.window_mask], .None; } // 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"); } for { - if z.num_bits > width { + if cb.num_bits > u64(width) { break; } - if z.code_buffer == 0 && z.num_bits == -1 { - z.num_bits = 0; + if cb.code_buffer == 0 && cb.num_bits > 63 { + 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. - z.num_bits = -100; + cb.num_bits = max(u64); return; } - c, err := read_u8(z); + b, err := read_u8(z); if err != .None { // This is fine at the end of the file. - z.num_bits = -42; - z.eof = true; return; } - z.code_buffer |= (u32(c) << u8(z.num_bits)); - z.num_bits += 8; + cb.code_buffer |= (u64(b) << u8(cb.num_bits)); + cb.num_bits += 8; } } -consume_bits_lsb :: #force_inline proc(z: ^Context, width: u8) { - z.code_buffer >>= width; - z.num_bits -= i8(width); +consume_bits_lsb :: #force_inline proc(cb: ^Code_Buffer, width: u8) { + cb.code_buffer >>= width; + cb.num_bits -= u64(width); } -peek_bits_lsb :: #force_inline proc(z: ^Context, width: u8) -> u32 { - if z.num_bits < i8(width) { - refill_lsb(z); +peek_bits_lsb :: #force_inline proc(z: ^Context, cb: ^Code_Buffer, width: u8) -> u32 { + if cb.num_bits < u64(width) { + refill_lsb(z, cb); } // 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 { - assert(z.num_bits >= i8(width)); - return z.code_buffer & ~(~u32(0) << width); +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)); } -read_bits_lsb :: #force_inline proc(z: ^Context, width: u8) -> u32 { - k := peek_bits_lsb(z, width); - consume_bits_lsb(z, width); +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); return k; } -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); +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); return k; } -discard_to_next_byte_lsb :: proc(z: ^Context) { - discard := u8(z.num_bits & 7); - consume_bits_lsb(z, discard); +discard_to_next_byte_lsb :: proc(cb: ^Code_Buffer) { + discard := u8(cb.num_bits & 7); + consume_bits_lsb(cb, discard); } diff --git a/core/compress/gzip/example.odin b/core/compress/gzip/example.odin index 81935a43a..80c630073 100644 --- a/core/compress/gzip/example.odin +++ b/core/compress/gzip/example.odin @@ -12,7 +12,6 @@ package gzip A small GZIP implementation as an example. */ -import "core:compress/gzip" import "core:bytes" import "core:os" @@ -31,7 +30,7 @@ TEST: []u8 = { main :: proc() { // Set up output buffer. - buf: bytes.Buffer; + buf := bytes.Buffer{}; stdout :: proc(s: string) { os.write_string(os.stdout, s); @@ -44,26 +43,27 @@ main :: proc() { if len(args) < 2 { stderr("No input file specified.\n"); - err := gzip.load(TEST, &buf); - if err != nil { + err := load(TEST, &buf); + if err == nil { stdout("Displaying test vector: "); stdout(bytes.buffer_to_string(&buf)); stdout("\n"); } bytes.buffer_destroy(&buf); + os.exit(0); } // The rest are all files. args = args[1:]; - err: gzip.Error; + err: Error; for file in args { if file == "-" { // Read from stdin s := os.stream_from_handle(os.stdin); - err = gzip.load(s, &buf); + err = load(s, &buf); } else { - err = gzip.load(file, &buf); + err = load(file, &buf); } if err != nil { if err != E_General.File_Not_Found { diff --git a/core/compress/gzip/gzip.odin b/core/compress/gzip/gzip.odin index 55e00198a..ccdd24489 100644 --- a/core/compress/gzip/gzip.odin +++ b/core/compress/gzip/gzip.odin @@ -281,8 +281,10 @@ load_from_stream :: proc(stream: io.Stream, buf: ^bytes.Buffer, allocator := con /* 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); @@ -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. */ - compress.discard_to_next_byte_lsb(&ctx); + compress.discard_to_next_byte_lsb(cb); payload_crc_b: [4]u8; payload_len_b: [4]u8; 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; 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); diff --git a/core/compress/zlib/example.odin b/core/compress/zlib/example.odin index 7c538b7af..4d951b2f4 100644 --- a/core/compress/zlib/example.odin +++ b/core/compress/zlib/example.odin @@ -11,7 +11,6 @@ package zlib An example of how to use `zlib.inflate`. */ -import "core:compress/zlib" import "core:bytes" import "core:fmt" @@ -40,7 +39,7 @@ main :: proc() { buf: bytes.Buffer; // 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); if err != nil { diff --git a/core/compress/zlib/zlib.odin b/core/compress/zlib/zlib.odin index 22d2e3f47..e6bde8535 100644 --- a/core/compress/zlib/zlib.odin +++ b/core/compress/zlib/zlib.odin @@ -23,7 +23,8 @@ import "core:hash" Returns: Error. */ -Context :: compress.Context; +Context :: compress.Context; +Code_Buffer :: compress.Code_Buffer; Compression_Method :: enum u8 { DEFLATE = 8, @@ -129,7 +130,7 @@ z_bit_reverse :: #force_inline proc(n: u16, bits: u8) -> (r: u16) { 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"); } c := c; 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 { return e; } - z.last[z.bytes_written & z.window_mask] = c; + cb.last[z.bytes_written & cb.window_mask] = c; z.bytes_written += 1; 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"); } /* 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); #no_bounds_check for i in 0.. (err: io.Error) { 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"); } /* 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); #no_bounds_check for i in 0.. (err: Error) { 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"); } - code := u16(compress.peek_bits_lsb(z, 16)); + code := u16(compress.peek_bits_lsb(z, cb, 16)); k := int(z_bit_reverse(code, 16)); 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; } - compress.consume_bits_lsb(z, s); + compress.consume_bits_lsb(cb, s); r = t.value[b]; 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"); } - if z.num_bits < 16 { - if z.num_bits == -100 { + if cb.num_bits < 16 { + if cb.num_bits > 63 { return 0, E_ZLIB.Code_Buffer_Malformed; } - compress.refill_lsb(z); - if z.eof { + compress.refill_lsb(z, cb); + if cb.num_bits > 63 { 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 { s := u8(b >> ZFAST_BITS); - compress.consume_bits_lsb(z, s); + compress.consume_bits_lsb(cb, s); 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"); } #no_bounds_check for { - value, e := decode_huffman(z, z_repeat); + value, e := decode_huffman(z, cb, z_repeat); if e != nil { return err; } if value < 256 { - e := write_byte(z, u8(value)); + e := write_byte(z, cb, u8(value)); if e != .None { return E_General.Output_Too_Short; } @@ -329,17 +330,17 @@ parse_huffman_block :: proc(z: ^Context, z_repeat, z_offset: ^Huffman_Table) -> value -= 257; length := Z_LENGTH_BASE[value]; 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 { return E_Deflate.Bad_Huffman_Code; } distance := Z_DIST_BASE[value]; 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) { @@ -360,15 +361,15 @@ parse_huffman_block :: proc(z: ^Context, z_repeat, z_offset: ^Huffman_Table) -> Replicate the last outputted byte, length times. */ if length > 0 { - c := z.last[offset & z.window_mask]; - e := repl_byte(z, length, c); + c := cb.last[offset & cb.window_mask]; + e := repl_byte(z, cb, length, c); if e != .None { return E_General.Output_Too_Short; } } } else { if length > 0 { - e := repl_bytes(z, length, distance); + e := repl_bytes(z, cb, length, distance); if e != .None { return E_General.Output_Too_Short; } @@ -391,6 +392,9 @@ inflate_from_stream :: proc(using ctx: ^Context, raw := false, allocator := cont DEFLATE stream. */ + code_buffer := Code_Buffer{}; + cb := &code_buffer; + if !raw { data_size := io.size(ctx.input); if data_size < 6 { @@ -408,7 +412,7 @@ inflate_from_stream :: proc(using ctx: ^Context, raw := false, allocator := cont if cinfo > 7 { 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); @@ -439,15 +443,15 @@ inflate_from_stream :: proc(using ctx: ^Context, raw := false, allocator := cont } // Parse ZLIB stream without header. - err = inflate_raw(ctx); + err = inflate_raw(ctx, cb); if err != nil { return err; } 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) { return E_General.Checksum_Failed; } @@ -456,13 +460,13 @@ inflate_from_stream :: proc(using ctx: ^Context, raw := false, allocator := cont } // @(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"); } final := u32(0); type := u32(0); - z.num_bits = 0; - z.code_buffer = 0; + cb.num_bits = 0; + cb.code_buffer = 0; z_repeat: ^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(codelength_ht); - if z.window_size == 0 { - z.window_size = DEFLATE_MAX_DISTANCE; + if cb.window_mask == 0 { + cb.window_mask = DEFLATE_MAX_DISTANCE - 1; } - z.window_mask = z.window_size - 1; // Allocate rolling window buffer. - last_b := mem.make_dynamic_array_len_cap([dynamic]u8, z.window_size, z.window_size, allocator); - z.last = &last_b; - defer delete(last_b); + cb.last = mem.make_dynamic_array_len_cap([dynamic]u8, cb.window_mask + 1, cb.window_mask + 1, allocator); + defer delete(cb.last); for { - final = compress.read_bits_lsb(z, 1); - type = compress.read_bits_lsb(z, 2); + final = compress.read_bits_lsb(z, cb, 1); + type = compress.read_bits_lsb(z, cb, 2); // 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 // 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)); - length_check := 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, cb, 16)); // 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. */ #no_bounds_check for uncompressed_len > 0 { - compress.refill_lsb(z); - lit := compress.read_bits_lsb(z, 8); - write_byte(z, u8(lit)); + compress.refill_lsb(z, cb); + lit := compress.read_bits_lsb(z, cb, 8); + write_byte(z, cb, u8(lit)); uncompressed_len -= 1; } case 3: @@ -550,14 +552,14 @@ inflate_from_stream_raw :: proc(z: ^Context, allocator := context.allocator) -> //i: u32; n: u32; - 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; + 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; ntot := hlit + hdist; #no_bounds_check for i in 0.. c: u16; for n < ntot { - c, err = decode_huffman(z, codelength_ht); + c, err = decode_huffman(z, cb, codelength_ht); if err != nil { return err; } @@ -582,18 +584,18 @@ inflate_from_stream_raw :: proc(z: ^Context, allocator := context.allocator) -> n += 1; } else { fill := u8(0); - compress.refill_lsb(z, 7); + compress.refill_lsb(z, cb, 7); switch c { 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 { return E_Deflate.Huffman_Bad_Code_Lengths; } fill = lencodes[n - 1]; 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: - c = u16(compress.read_bits_no_refill_lsb(z, 7) + 11); + c = u16(compress.read_bits_no_refill_lsb(z, cb, 7) + 11); case: return E_Deflate.Huffman_Bad_Code_Lengths; } @@ -623,7 +625,7 @@ inflate_from_stream_raw :: proc(z: ^Context, allocator := context.allocator) -> 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); if err != nil { return err; @@ -653,8 +655,19 @@ inflate_from_byte_array :: proc(input: []u8, buf: ^bytes.Buffer, raw := false) - return err; } -inflate_from_byte_array_raw :: proc(input: []u8, buf: ^bytes.Buffer, raw := false) -> (err: Error) { - return inflate_from_byte_array(input, buf, true); +inflate_from_byte_array_raw :: proc(input: []u8, buf: ^bytes.Buffer, cb: ^Code_Buffer, raw := false) -> (err: Error) { + 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}; diff --git a/core/image/png/example.odin b/core/image/png/example.odin index f96ef01b2..8fca684ab 100644 --- a/core/image/png/example.odin +++ b/core/image/png/example.odin @@ -23,6 +23,22 @@ import "core:mem" import "core:os" 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; options := image.Options{.return_metadata};