v2 / vlib / compress / compress.c.v
101 lines · 87 sloc · 3.62 KB · e2e5cf8db56f3562c7baa735061690be936bdf3e
Raw
1module compress
2
3#flag -I @VEXEROOT/thirdparty/zip
4#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES
5#include "miniz.h"
6
7pub const max_size = u64(1 << 31)
8
9fn C.tdefl_compress_mem_to_heap(source_buf voidptr, source_buf_len usize, out_len &usize, flags i32) voidptr
10fn C.tinfl_decompress_mem_to_heap(source_buf voidptr, source_buf_len usize, out_len &usize, flags i32) voidptr
11
12// compresses an array of bytes based on providing flags and returns the compressed bytes in a new array
13// NB: this is a low level api, a high level implementation like zlib/gzip should be preferred
14@[manualfree]
15pub fn compress(data []u8, flags int) ![]u8 {
16 if u64(data.len) > max_size {
17 return error('data too large (${data.len} > ${max_size})')
18 }
19 mut out_len := usize(0)
20
21 address := C.tdefl_compress_mem_to_heap(data.data, data.len, &out_len, flags)
22 if address == 0 {
23 return error('compression failed')
24 }
25 if u64(out_len) > max_size {
26 return error('compressed data is too large (${out_len} > ${max_size})')
27 }
28 unsafe {
29 ret := address.vbytes(int(out_len)).clone()
30 C.free(address)
31 return ret
32 }
33}
34
35// decompresses an array of bytes based on providing flags and returns the decompressed bytes in a new array
36// NB: this is a low level api, a high level implementation like zlib/gzip should be preferred
37@[manualfree]
38pub fn decompress(data []u8, flags int) ![]u8 {
39 mut out_len := usize(0)
40
41 address := C.tinfl_decompress_mem_to_heap(data.data, data.len, &out_len, flags)
42 if address == 0 {
43 return error('decompression failed')
44 }
45 if u64(out_len) > max_size {
46 return error('decompressed data is too large (${out_len} > ${max_size})')
47 }
48
49 unsafe {
50 ret := address.vbytes(int(out_len)).clone()
51 C.free(address)
52 return ret
53 }
54}
55
56// ChunkCallback is used to receive decompressed chunks of maximum 32768 bytes.
57// After processing the chunk this function should return the chunk's length to indicate
58// the decompressor to send more chunks, otherwise the decompression stops.
59// The userdata parameter comes from the call to decompress_with_callback/4, and can be used
60// to pass arbitrary data, without having to create a closure.
61pub type ChunkCallback = fn (chunk []u8, userdata voidptr) int
62
63// decompress_with_callback decompresses an array of bytes, based on the provided flags, and a V fn callback to receive decompressed chunks, of at most 32 kilobytes each.
64// It returns the total decompressed length, or a decompression error.
65// NB: this is a low level api, a high level implementation like zlib/gzip should be preferred.
66pub fn decompress_with_callback(data []u8, cb ChunkCallback, userdata voidptr, flags int) !u64 {
67 cbdata := DecompressionCallBackData{
68 data: data.data
69 size: usize(data.len)
70 cb: cb
71 userdata: userdata
72 }
73 status := C.tinfl_decompress_mem_to_callback(cbdata.data, &cbdata.size,
74 c_cb_for_decompress_mem, &cbdata, flags)
75 if status == 0 {
76 return error('decompression error')
77 }
78 return cbdata.decompressed_size
79}
80
81struct DecompressionCallBackData {
82mut:
83 data voidptr
84 size usize
85 decompressed_size u64
86 userdata voidptr
87 cb ChunkCallback = unsafe { nil }
88}
89
90fn c_cb_for_decompress_mem(buf &char, len int, pdcbd voidptr) int {
91 mut cbdata := unsafe { &DecompressionCallBackData(pdcbd) }
92 if cbdata.cb(unsafe { voidptr(buf).vbytes(len) }, cbdata.userdata) == len {
93 cbdata.decompressed_size += u64(len)
94 return 1 // continue decompressing
95 }
96 return 0 // stop decompressing
97}
98
99type DecompressCallback = fn (const_buffer voidptr, len int, userdata voidptr) int
100
101fn C.tinfl_decompress_mem_to_callback(const_input_buffer voidptr, psize &usize, put_buf_cb DecompressCallback, userdata voidptr, flags i32) i32
102