Files
zlib/zlib_helper.h
2022-01-27 11:55:55 +01:00

196 lines
4.5 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "zlib.h"
// --- BEGIN ZRES HELPER ---
typedef struct {
int len;
char* bytes;
} ZLibZBytes;
typedef struct {
int which;
union {
int err;
ZLibZBytes* out;
};
} ZRes;
#define ZRES_OK 0
#define ZRES_ERR 1
bool ZRes_is_ok(ZRes* r) {
return r->which == ZRES_OK;
}
ZLibZBytes ZRes_bytes(ZRes r) {
assert(r.which == ZRES_OK);
return *r.out;
}
String ZRes_str(ZRes r) {
assert(r.which == ZRES_OK);
String res = r.out->bytes;
free(r.out);
return res;
}
static const char* errors[9] = {
"Version Error",
"Buffer Error",
"Memory Error",
"Data Error",
"Stream Error",
"errno",
NULL,
"Stream Ended",
"Need Dict",
};
char* ZRes_err(ZRes r) {
assert(r.which == ZRES_ERR);
// +6 because zlib errors are contiguous, starting at -6
char* x = (char*)errors[(r.err)+6];
return String_copy(&x);
}
// --- END ZRES HELPER ---
// this code is mostly from the zlib usage example: https://www.zlib.net/zlib_how.html
#define CHUNK 16384
#define min(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a < _b ? _a : _b; })
ZRes ZLib_inflate_c(ZLibZBytes b) {
int ret;
ZRes res;
unsigned have;
z_stream strm;
unsigned char in[CHUNK];
unsigned char out[CHUNK];
int offs = 0;
int len = b.len;
char* source = b.bytes;
ZLibZBytes* bytes = malloc(sizeof(ZLibZBytes));
bytes->bytes = NULL;
bytes->len = 0;
/* allocate inflate state */
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = 0;
strm.next_in = Z_NULL;
ret = inflateInit(&strm);
if (ret != Z_OK) goto err;
/* decompress until deflate stream ends or end of file */
do {
strm.avail_in = min(CHUNK, len-offs);
if (strm.avail_in <= 0) break;
memcpy(in, source+offs, strm.avail_in);
offs += strm.avail_in;
strm.next_in = in;
/* run inflate() on input until output buffer not full */
do {
strm.avail_out = CHUNK;
strm.next_out = out;
ret = inflate(&strm, Z_NO_FLUSH);
assert(ret != Z_STREAM_ERROR); /* state not clobbered */
switch (ret) {
case Z_NEED_DICT:
ret = Z_DATA_ERROR; /* and fall through */
case Z_DATA_ERROR:
case Z_MEM_ERROR:
(void)inflateEnd(&strm);
goto err;
}
have = CHUNK - strm.avail_out;
bytes->bytes = realloc(bytes->bytes, (bytes->len)+have);
memcpy((bytes->bytes)+(bytes->len), out, have);
bytes->len += have;
} while (strm.avail_out == 0);
/* done when inflate() says its done */
} while (ret != Z_STREAM_END);
/* clean up and return */
(void)inflateEnd(&strm);
if (ret == Z_STREAM_END) {
res.which = ZRES_OK;
res.out = bytes;
return res;
}
/* we didnt succeed, we fail */
ret = Z_DATA_ERROR;
err:
res.which = ZRES_ERR;
res.err = ret;
if (bytes->bytes) free(bytes->bytes);
free(bytes);
return res;
}
ZRes ZLib_deflate_c(String* s, int level) {
int ret, flush;
unsigned have;
z_stream strm;
ZRes res;
unsigned char in[CHUNK];
unsigned char out[CHUNK];
int offs = 0;
int len = strlen(*s);
ZLibZBytes* bytes = malloc(sizeof(ZLibZBytes));
bytes->bytes = NULL;
bytes->len = 0;
/* allocate deflate state */
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
ret = deflateInit(&strm, level);
if (ret != Z_OK) goto err;
/* compress until end of file */
do {
strm.avail_in = min(CHUNK, len-offs);
if (strm.avail_in <= 0) break;
memcpy(in, (*s)+offs, strm.avail_in);
offs += strm.avail_in;
flush = offs >= len-1 ? Z_FINISH : Z_NO_FLUSH;
strm.next_in = in;
/* run deflate() on input until output buffer not full, finish
compression if all of source has been read in */
do {
strm.avail_out = CHUNK;
strm.next_out = out;
ret = deflate(&strm, flush); /* no bad return value */
assert(ret != Z_STREAM_ERROR); /* state not clobbered */
have = CHUNK - strm.avail_out;
bytes->bytes = realloc(bytes->bytes, (bytes->len)+have);
memcpy((bytes->bytes)+(bytes->len), out, have);
bytes->len += have;
} while (strm.avail_out == 0);
assert(strm.avail_in == 0); /* all input will be used */
/* done when last data in file processed */
} while (flush != Z_FINISH);
assert(ret == Z_STREAM_END); /* stream will be complete */
/* clean up and return */
(void)deflateEnd(&strm);
res.which = ZRES_OK;
res.out = bytes;
return res;
err:
res.which = ZRES_ERR;
res.err = ret;
if (bytes->bytes) free(bytes->bytes);
free(bytes);
return res;
}