/* SPDX-License-Identifier: GPL-3.0-or-later */ /* * gzip.c * * Copyright (C) 2021 David Oberhollenzer */ #include #include #include #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); }