aboutsummaryrefslogtreecommitdiff
path: root/lib/xfrm/gzip.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/xfrm/gzip.c')
-rw-r--r--lib/xfrm/gzip.c144
1 files changed, 144 insertions, 0 deletions
diff --git a/lib/xfrm/gzip.c b/lib/xfrm/gzip.c
new file mode 100644
index 0000000..67224f7
--- /dev/null
+++ b/lib/xfrm/gzip.c
@@ -0,0 +1,144 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * gzip.c
+ *
+ * Copyright (C) 2021 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <zlib.h>
+
+#include "xfrm/stream.h"
+#include "xfrm/compress.h"
+
+typedef struct {
+ xfrm_stream_t base;
+
+ z_stream strm;
+ bool compress;
+} xfrm_stream_gzip_t;
+
+static const int zlib_action[] = {
+ [XFRM_STREAM_FLUSH_NONE] = Z_NO_FLUSH,
+ [XFRM_STREAM_FLUSH_SYNC] = Z_SYNC_FLUSH,
+ [XFRM_STREAM_FLUSH_FULL] = Z_FINISH,
+};
+
+static int process_data(xfrm_stream_t *stream, const void *in,
+ sqfs_u32 in_size, void *out, sqfs_u32 out_size,
+ sqfs_u32 *in_read, sqfs_u32 *out_written,
+ int flush_mode)
+{
+ xfrm_stream_gzip_t *gzip = (xfrm_stream_gzip_t *)stream;
+ sqfs_u32 diff;
+ int ret;
+
+ if (flush_mode < 0 || flush_mode >= XFRM_STREAM_FLUSH_COUNT)
+ flush_mode = XFRM_STREAM_FLUSH_NONE;
+
+ while (in_size > 0 && out_size > 0) {
+ gzip->strm.next_in = (void *)in;
+ gzip->strm.avail_in = in_size;
+
+ gzip->strm.next_out = out;
+ gzip->strm.avail_out = out_size;
+
+ if (gzip->compress) {
+ ret = deflate(&gzip->strm, zlib_action[flush_mode]);
+ } else {
+ ret = inflate(&gzip->strm, zlib_action[flush_mode]);
+ }
+
+ if (ret == Z_STREAM_ERROR)
+ return XFRM_STREAM_ERROR;
+
+ diff = in_size - gzip->strm.avail_in;
+ in = (const char *)in + diff;
+ in_size -= diff;
+ *in_read += diff;
+
+ diff = out_size - gzip->strm.avail_out;
+ out = (char *)out + diff;
+ out_size -= diff;
+ *out_written += diff;
+
+ if (ret == Z_STREAM_END) {
+ if (gzip->compress) {
+ ret = deflateReset(&gzip->strm);
+ } else {
+ ret = inflateReset(&gzip->strm);
+ }
+
+ if (ret != Z_OK)
+ return XFRM_STREAM_ERROR;
+
+ return XFRM_STREAM_END;
+ }
+
+ if (ret == Z_BUF_ERROR)
+ return XFRM_STREAM_BUFFER_FULL;
+ }
+
+ return XFRM_STREAM_OK;
+}
+
+static void destroy(sqfs_object_t *obj)
+{
+ xfrm_stream_gzip_t *gzip = (xfrm_stream_gzip_t *)obj;
+
+ if (gzip->compress) {
+ deflateEnd(&gzip->strm);
+ } else {
+ inflateEnd(&gzip->strm);
+ }
+ free(gzip);
+}
+
+static xfrm_stream_t *create_stream(const compressor_config_t *cfg,
+ bool compress)
+{
+ xfrm_stream_gzip_t *gzip = calloc(1, sizeof(*gzip));
+ xfrm_stream_t *xfrm = (xfrm_stream_t *)gzip;
+ int ret;
+
+ if (gzip == NULL) {
+ perror("creating gzip stream compressor");
+ return NULL;
+ }
+
+ if (compress) {
+ int level = COMP_GZIP_DEFAULT_LEVEL;
+ int wnd = COMP_GZIP_DEFAULT_WINDOW;
+
+ if (cfg != NULL) {
+ level = cfg->level;
+ wnd = cfg->opt.gzip.window_size;
+ }
+
+ ret = deflateInit2(&gzip->strm, level, Z_DEFLATED,
+ wnd + 16, 8, Z_DEFAULT_STRATEGY);
+ } else {
+ ret = inflateInit2(&gzip->strm, 16 + 15);
+ }
+
+ if (ret != Z_OK) {
+ fputs("internal error creating gzip compressor.\n", stderr);
+ free(gzip);
+ return NULL;
+ }
+
+ gzip->compress = compress;
+ xfrm->process_data = process_data;
+ sqfs_object_init(xfrm, destroy, NULL);
+ return xfrm;
+}
+
+xfrm_stream_t *compressor_stream_gzip_create(const compressor_config_t *cfg)
+{
+ return create_stream(cfg, true);
+}
+
+xfrm_stream_t *decompressor_stream_gzip_create(void)
+{
+ return create_stream(NULL, false);
+}