aboutsummaryrefslogtreecommitdiff
path: root/lib/xfrm
diff options
context:
space:
mode:
authorDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2022-12-13 09:15:19 +0100
committerDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2023-01-19 16:24:56 +0100
commit551dd3879c288a2b6b6fbaca5c09c04fbe994ff4 (patch)
treef3437139699edffd034168999854258f30c4023b /lib/xfrm
parent722ecf27eaf83685dfc6e92adc9d66f0107da5ea (diff)
Split stream compression out of libio
Move it to a separate libxfrm library, where it can be independently tested as well. The bulk of the new code is also mainly test cases for the compressors. Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
Diffstat (limited to 'lib/xfrm')
-rw-r--r--lib/xfrm/Makemodule.am27
-rw-r--r--lib/xfrm/bzip2.c151
-rw-r--r--lib/xfrm/compress.c102
-rw-r--r--lib/xfrm/gzip.c144
-rw-r--r--lib/xfrm/xz.c195
-rw-r--r--lib/xfrm/zstd.c136
6 files changed, 755 insertions, 0 deletions
diff --git a/lib/xfrm/Makemodule.am b/lib/xfrm/Makemodule.am
new file mode 100644
index 0000000..a344820
--- /dev/null
+++ b/lib/xfrm/Makemodule.am
@@ -0,0 +1,27 @@
+libxfrm_a_SOURCES = include/xfrm/stream.h include/xfrm/compress.h
+libxfrm_a_SOURCES += lib/xfrm/compress.c
+libxfrm_a_CFLAGS = $(AM_CFLAGS)
+
+if WITH_XZ
+libxfrm_a_SOURCES += lib/xfrm/xz.c
+libxfrm_a_CFLAGS += $(XZ_CFLAGS) -DWITH_XZ
+endif
+
+if WITH_BZIP2
+libxfrm_a_SOURCES += lib/xfrm/bzip2.c
+libxfrm_a_CFLAGS += $(BZIP2_CFLAGS) -DWITH_BZIP2
+endif
+
+if WITH_GZIP
+libxfrm_a_SOURCES += lib/xfrm/gzip.c
+libxfrm_a_CFLAGS += $(ZLIB_CFLAGS) -DWITH_GZIP
+endif
+
+if WITH_ZSTD
+if HAVE_ZSTD_STREAM
+libxfrm_a_SOURCES += lib/xfrm/zstd.c
+libxfrm_a_CFLAGS += $(ZSTD_CFLAGS) -DWITH_ZSTD
+endif
+endif
+
+noinst_LIBRARIES += libxfrm.a
diff --git a/lib/xfrm/bzip2.c b/lib/xfrm/bzip2.c
new file mode 100644
index 0000000..7e5807d
--- /dev/null
+++ b/lib/xfrm/bzip2.c
@@ -0,0 +1,151 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * bzip2.c
+ *
+ * Copyright (C) 2021 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <bzlib.h>
+
+#include "xfrm/stream.h"
+#include "xfrm/compress.h"
+
+typedef struct {
+ xfrm_stream_t base;
+
+ bz_stream strm;
+ bool compress;
+ bool initialized;
+
+ int level;
+ int work_factor;
+} xfrm_stream_bzip2_t;
+
+static const int bzlib_action[] = {
+ [XFRM_STREAM_FLUSH_NONE] = BZ_RUN,
+ [XFRM_STREAM_FLUSH_SYNC] = BZ_FLUSH,
+ [XFRM_STREAM_FLUSH_FULL] = BZ_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_bzip2_t *bzip2 = (xfrm_stream_bzip2_t *)stream;
+ sqfs_u32 diff;
+ int ret;
+
+ if (!bzip2->initialized) {
+ if (bzip2->compress) {
+ ret = BZ2_bzCompressInit(&bzip2->strm, bzip2->level, 0,
+ bzip2->work_factor);
+ } else {
+ ret = BZ2_bzDecompressInit(&bzip2->strm, 0, 0);
+ }
+
+ if (ret != BZ_OK)
+ return XFRM_STREAM_ERROR;
+
+ bzip2->initialized = true;
+ }
+
+ if (flush_mode < 0 || flush_mode >= XFRM_STREAM_FLUSH_COUNT)
+ flush_mode = XFRM_STREAM_FLUSH_NONE;
+
+ while (in_size > 0 && out_size > 0) {
+ bzip2->strm.next_in = (char *)in;
+ bzip2->strm.avail_in = in_size;
+
+ bzip2->strm.next_out = (char *)out;
+ bzip2->strm.avail_out = out_size;
+
+ if (bzip2->compress) {
+ ret = BZ2_bzCompress(&bzip2->strm,
+ bzlib_action[flush_mode]);
+ } else {
+ ret = BZ2_bzDecompress(&bzip2->strm);
+ }
+
+ if (ret == BZ_OUTBUFF_FULL)
+ return XFRM_STREAM_BUFFER_FULL;
+
+ if (ret < 0)
+ return XFRM_STREAM_ERROR;
+
+ diff = (in_size - bzip2->strm.avail_in);
+ in = (const char *)in + diff;
+ in_size -= diff;
+ *in_read += diff;
+
+ diff = (out_size - bzip2->strm.avail_out);
+ out = (char *)out + diff;
+ out_size -= diff;
+ *out_written += diff;
+
+ if (ret == BZ_STREAM_END) {
+ if (bzip2->compress) {
+ BZ2_bzCompressEnd(&bzip2->strm);
+ } else {
+ BZ2_bzDecompressEnd(&bzip2->strm);
+ }
+
+ bzip2->initialized = false;
+ return XFRM_STREAM_END;
+ }
+ }
+
+ return XFRM_STREAM_OK;
+}
+
+static void destroy(sqfs_object_t *obj)
+{
+ xfrm_stream_bzip2_t *bzip2 = (xfrm_stream_bzip2_t *)obj;
+
+ if (bzip2->initialized) {
+ if (bzip2->compress) {
+ BZ2_bzCompressEnd(&bzip2->strm);
+ } else {
+ BZ2_bzDecompressEnd(&bzip2->strm);
+ }
+ }
+
+ free(bzip2);
+}
+
+static xfrm_stream_t *stream_create(const compressor_config_t *cfg,
+ bool compress)
+{
+ xfrm_stream_bzip2_t *bzip2 = calloc(1, sizeof(*bzip2));
+ xfrm_stream_t *xfrm = (xfrm_stream_t *)bzip2;
+
+ if (bzip2 == NULL) {
+ perror("creating bzip2 stream compressor");
+ return NULL;
+ }
+
+ if (cfg == NULL) {
+ bzip2->level = COMP_BZIP2_DEFAULT_LEVEL;
+ bzip2->work_factor = COMP_BZIP2_DEFAULT_WORK_FACTOR;
+ } else {
+ bzip2->level = cfg->level;
+ bzip2->work_factor = cfg->opt.bzip2.work_factor;
+ }
+
+ bzip2->initialized = false;
+ bzip2->compress = compress;
+ xfrm->process_data = process_data;
+ sqfs_object_init(bzip2, destroy, NULL);
+ return xfrm;
+}
+
+xfrm_stream_t *compressor_stream_bzip2_create(const compressor_config_t *cfg)
+{
+ return stream_create(cfg, true);
+}
+
+xfrm_stream_t *decompressor_stream_bzip2_create(void)
+{
+ return stream_create(NULL, false);
+}
diff --git a/lib/xfrm/compress.c b/lib/xfrm/compress.c
new file mode 100644
index 0000000..fbd6987
--- /dev/null
+++ b/lib/xfrm/compress.c
@@ -0,0 +1,102 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * compress.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "xfrm/compress.h"
+#include "config.h"
+
+#include <string.h>
+
+static const struct {
+ int id;
+ const char *name;
+ const sqfs_u8 *magic;
+ size_t count;
+ xfrm_stream_t *(*mk_comp_stream)(const compressor_config_t *);
+ xfrm_stream_t *(*mk_decomp_stream)(void);
+} compressors[] = {
+#ifdef WITH_GZIP
+ { XFRM_COMPRESSOR_GZIP, "gzip", (const sqfs_u8 *)"\x1F\x8B\x08", 3,
+ compressor_stream_gzip_create, decompressor_stream_gzip_create },
+#endif
+#ifdef WITH_XZ
+ { XFRM_COMPRESSOR_XZ, "xz", (const sqfs_u8 *)("\xFD" "7zXZ"), 6,
+ compressor_stream_xz_create, decompressor_stream_xz_create },
+#endif
+#if defined(WITH_ZSTD) && defined(HAVE_ZSTD_STREAM)
+ { XFRM_COMPRESSOR_ZSTD, "zstd",
+ (const sqfs_u8 *)"\x28\xB5\x2F\xFD", 4,
+ compressor_stream_zstd_create, decompressor_stream_zstd_create },
+#endif
+#ifdef WITH_BZIP2
+ { XFRM_COMPRESSOR_BZIP2, "bzip2", (const sqfs_u8 *)"BZh", 3,
+ compressor_stream_bzip2_create, decompressor_stream_bzip2_create },
+#endif
+};
+
+int xfrm_compressor_id_from_name(const char *name)
+{
+ size_t i;
+
+ for (i = 0; i < sizeof(compressors) / sizeof(compressors[0]); ++i) {
+ if (strcmp(name, compressors[i].name) == 0)
+ return compressors[i].id;
+ }
+
+ return -1;
+}
+
+int xfrm_compressor_id_from_magic(const void *data, size_t count)
+{
+ size_t i;
+ int ret;
+
+ for (i = 0; i < sizeof(compressors) / sizeof(compressors[0]); ++i) {
+ if (compressors[i].count > count)
+ continue;
+
+ ret = memcmp(compressors[i].magic, data, compressors[i].count);
+ if (ret == 0)
+ return compressors[i].id;
+ }
+
+ return -1;
+}
+
+const char *xfrm_compressor_name_from_id(int id)
+{
+ size_t i;
+
+ for (i = 0; i < sizeof(compressors) / sizeof(compressors[0]); ++i) {
+ if (compressors[i].id == id)
+ return compressors[i].name;
+ }
+
+ return NULL;
+}
+
+xfrm_stream_t *compressor_stream_create(int id, const compressor_config_t *cfg)
+{
+ size_t i;
+
+ for (i = 0; i < sizeof(compressors) / sizeof(compressors[0]); ++i) {
+ if (compressors[i].id == id)
+ return compressors[i].mk_comp_stream(cfg);
+ }
+
+ return NULL;
+}
+
+xfrm_stream_t *decompressor_stream_create(int id)
+{
+ size_t i;
+
+ for (i = 0; i < sizeof(compressors) / sizeof(compressors[0]); ++i) {
+ if (compressors[i].id == id)
+ return compressors[i].mk_decomp_stream();
+ }
+
+ return NULL;
+}
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);
+}
diff --git a/lib/xfrm/xz.c b/lib/xfrm/xz.c
new file mode 100644
index 0000000..5adb6f3
--- /dev/null
+++ b/lib/xfrm/xz.c
@@ -0,0 +1,195 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * xz.c
+ *
+ * Copyright (C) 2021 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <lzma.h>
+
+#include "xfrm/stream.h"
+#include "xfrm/compress.h"
+
+typedef struct {
+ xfrm_stream_t base;
+
+ lzma_stream strm;
+
+ sqfs_u64 memlimit;
+ lzma_filter filters[3];
+ lzma_options_lzma opt;
+ lzma_vli vli_filter;
+ sqfs_u32 presets;
+
+ bool compress;
+ bool initialized;
+} xfrm_xz_t;
+
+static const lzma_action xzlib_action[] = {
+ [XFRM_STREAM_FLUSH_NONE] = LZMA_RUN,
+ [XFRM_STREAM_FLUSH_SYNC] = LZMA_FULL_FLUSH,
+ [XFRM_STREAM_FLUSH_FULL] = LZMA_FINISH,
+};
+
+static lzma_vli vli_filter_from_flags(int vli)
+{
+ switch (vli) {
+ case COMP_XZ_VLI_X86:
+ return LZMA_FILTER_X86;
+ case COMP_XZ_VLI_POWERPC:
+ return LZMA_FILTER_POWERPC;
+ case COMP_XZ_VLI_IA64:
+ return LZMA_FILTER_IA64;
+ case COMP_XZ_VLI_ARM:
+ return LZMA_FILTER_ARM;
+ case COMP_XZ_VLI_ARMTHUMB:
+ return LZMA_FILTER_ARMTHUMB;
+ case COMP_XZ_VLI_SPARC:
+ return LZMA_FILTER_SPARC;
+ default:
+ return LZMA_VLI_UNKNOWN;
+ }
+}
+
+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_xz_t *xz = (xfrm_xz_t *)stream;
+ lzma_ret ret_xz;
+ sqfs_u32 diff;
+
+ if (!xz->initialized) {
+ if (xz->compress) {
+ ret_xz = lzma_stream_encoder(&xz->strm, xz->filters,
+ LZMA_CHECK_CRC32);
+ } else {
+ ret_xz = lzma_stream_decoder(&xz->strm,
+ xz->memlimit, 0);
+ }
+
+ if (ret_xz != LZMA_OK)
+ return XFRM_STREAM_ERROR;
+
+ xz->initialized = true;
+ }
+
+ if (flush_mode < 0 || flush_mode >= XFRM_STREAM_FLUSH_COUNT)
+ flush_mode = XFRM_STREAM_FLUSH_NONE;
+
+ while (in_size > 0 && out_size > 0) {
+ xz->strm.next_in = in;
+ xz->strm.avail_in = in_size;
+
+ xz->strm.next_out = out;
+ xz->strm.avail_out = out_size;
+
+ ret_xz = lzma_code(&xz->strm, xzlib_action[flush_mode]);
+
+ if (ret_xz != LZMA_OK && ret_xz != LZMA_BUF_ERROR &&
+ ret_xz != LZMA_STREAM_END) {
+ return XFRM_STREAM_ERROR;
+ }
+
+ diff = in_size - xz->strm.avail_in;
+ in = (const char *)in + diff;
+ in_size -= diff;
+ *in_read += diff;
+
+ diff = out_size - xz->strm.avail_out;
+ out = (char *)out + diff;
+ out_size -= diff;
+ *out_written += diff;
+
+ if (ret_xz == LZMA_BUF_ERROR)
+ return XFRM_STREAM_BUFFER_FULL;
+
+ if (ret_xz == LZMA_STREAM_END) {
+ lzma_end(&xz->strm);
+ xz->initialized = false;
+ return XFRM_STREAM_END;
+ }
+ }
+
+ return XFRM_STREAM_OK;
+}
+
+static void destroy(sqfs_object_t *obj)
+{
+ xfrm_xz_t *xz = (xfrm_xz_t *)obj;
+
+ if (xz->initialized)
+ lzma_end(&xz->strm);
+
+ free(xz);
+}
+
+static xfrm_stream_t *create_stream(const compressor_config_t *cfg,
+ bool compress)
+{
+ xfrm_xz_t *xz = calloc(1, sizeof(*xz));
+ xfrm_stream_t *xfrm = (xfrm_stream_t *)xz;
+ int i = 0;
+
+ if (xz == NULL) {
+ perror("creating xz stream compressor");
+ return NULL;
+ }
+
+ xz->memlimit = 128 * 1024 * 1024;
+ xz->compress = compress;
+ xz->initialized = false;
+
+ if (compress) {
+ if (cfg == NULL) {
+ xz->presets = COMP_XZ_DEFAULT_LEVEL;
+ } else {
+ xz->presets = cfg->level;
+ if (cfg->flags & COMP_FLAG_XZ_EXTREME)
+ xz->presets |= LZMA_PRESET_EXTREME;
+ }
+
+ if (lzma_lzma_preset(&xz->opt, xz->presets))
+ goto fail_init;
+
+ if (cfg == NULL) {
+ xz->vli_filter = LZMA_VLI_UNKNOWN;
+ } else {
+ xz->vli_filter = vli_filter_from_flags(cfg->opt.xz.vli);
+ }
+
+ if (xz->vli_filter != LZMA_VLI_UNKNOWN) {
+ xz->filters[i].id = xz->vli_filter;
+ xz->filters[i].options = NULL;
+ ++i;
+ }
+
+ xz->filters[i].id = LZMA_FILTER_LZMA2;
+ xz->filters[i].options = &xz->opt;
+ ++i;
+
+ xz->filters[i].id = LZMA_VLI_UNKNOWN;
+ xz->filters[i].options = NULL;
+ ++i;
+ }
+
+ xfrm->process_data = process_data;
+ sqfs_object_init(xz, destroy, NULL);
+ return xfrm;
+fail_init:
+ fputs("error initializing XZ compressor\n", stderr);
+ free(xz);
+ return NULL;
+}
+
+xfrm_stream_t *compressor_stream_xz_create(const compressor_config_t *cfg)
+{
+ return create_stream(cfg, true);
+}
+
+xfrm_stream_t *decompressor_stream_xz_create(void)
+{
+ return create_stream(NULL, false);
+}
diff --git a/lib/xfrm/zstd.c b/lib/xfrm/zstd.c
new file mode 100644
index 0000000..70666c1
--- /dev/null
+++ b/lib/xfrm/zstd.c
@@ -0,0 +1,136 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * zstd.c
+ *
+ * Copyright (C) 2021 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <zstd.h>
+
+#include "xfrm/stream.h"
+#include "xfrm/compress.h"
+
+typedef struct {
+ xfrm_stream_t base;
+
+ ZSTD_CStream *cstrm;
+ ZSTD_DStream *dstrm;
+ bool compress;
+} xfrm_zstd_t;
+
+static const ZSTD_EndDirective zstd_action[] = {
+ [XFRM_STREAM_FLUSH_NONE] = ZSTD_e_continue,
+ [XFRM_STREAM_FLUSH_SYNC] = ZSTD_e_flush,
+ [XFRM_STREAM_FLUSH_FULL] = ZSTD_e_end,
+};
+
+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_zstd_t *zstd = (xfrm_zstd_t *)stream;
+ ZSTD_outBuffer out_desc;
+ ZSTD_inBuffer in_desc;
+ size_t ret;
+
+ if (flush_mode < 0 || flush_mode >= XFRM_STREAM_FLUSH_COUNT)
+ flush_mode = XFRM_STREAM_FLUSH_NONE;
+
+ while (in_size > 0 && out_size > 0) {
+ memset(&in_desc, 0, sizeof(in_desc));
+ in_desc.src = in;
+ in_desc.size = in_size;
+
+ memset(&out_desc, 0, sizeof(out_desc));
+ out_desc.dst = out;
+ out_desc.size = out_size;
+
+ if (zstd->compress) {
+ ret = ZSTD_compressStream2(zstd->cstrm, &out_desc,
+ &in_desc,
+ zstd_action[flush_mode]);
+ } else {
+ ret = ZSTD_decompressStream(zstd->dstrm, &out_desc,
+ &in_desc);
+ }
+
+ if (ZSTD_isError(ret))
+ return XFRM_STREAM_ERROR;
+
+ in = (const char *)in + in_desc.pos;
+ in_size -= in_desc.pos;
+ *in_read += in_desc.pos;
+
+ out = (char *)out + out_desc.pos;
+ out_size -= out_desc.pos;
+ *out_written += out_desc.pos;
+ }
+
+ if (flush_mode != XFRM_STREAM_FLUSH_NONE) {
+ if (in_size == 0)
+ return XFRM_STREAM_END;
+ }
+
+ if (in_size > 0 && out_size == 0)
+ return XFRM_STREAM_BUFFER_FULL;
+
+ return XFRM_STREAM_OK;
+}
+
+static void destroy(sqfs_object_t *obj)
+{
+ xfrm_zstd_t *zstd = (xfrm_zstd_t *)obj;
+
+ if (zstd->compress) {
+ ZSTD_freeCStream(zstd->cstrm);
+ } else {
+ ZSTD_freeDStream(zstd->dstrm);
+ }
+
+ free(zstd);
+}
+
+static xfrm_stream_t *stream_create(const compressor_config_t *cfg,
+ bool compress)
+{
+ xfrm_zstd_t *zstd = calloc(1, sizeof(*zstd));
+ xfrm_stream_t *strm = (xfrm_stream_t *)zstd;
+ (void)cfg;
+
+ if (zstd == NULL) {
+ perror("creating zstd stream compressor");
+ return NULL;
+ }
+
+ if (compress) {
+ zstd->cstrm = ZSTD_createCStream();
+ if (zstd->cstrm == NULL)
+ goto fail_strm;
+ } else {
+ zstd->dstrm = ZSTD_createDStream();
+ if (zstd->dstrm == NULL)
+ goto fail_strm;
+ }
+
+ zstd->compress = compress;
+ strm->process_data = process_data;
+ sqfs_object_init(zstd, destroy, NULL);
+ return strm;
+fail_strm:
+ fputs("error initializing zstd stream.\n", stderr);
+ free(zstd);
+ return NULL;
+}
+
+xfrm_stream_t *compressor_stream_zstd_create(const compressor_config_t *cfg)
+{
+ return stream_create(cfg, true);
+}
+
+xfrm_stream_t *decompressor_stream_zstd_create(void)
+{
+ return stream_create(NULL, false);
+}