From 53194aa2ac589a8b570439c80592fc830655c31f Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Sun, 27 Jan 2019 00:43:05 +0100 Subject: [PATCH] Add lzma compressor Signed-off-by: David Oberhollenzer --- Makefile.am | 8 ++ include/pkgformat.h | 1 + main/cmd/pack/pack.c | 4 + main/compressors/lzma.c | 169 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 182 insertions(+) create mode 100644 main/compressors/lzma.c diff --git a/Makefile.am b/Makefile.am index 9575e02..8a842c4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -48,3 +48,11 @@ pkg_SOURCES += main/compressors/zlib.c pkg_CFLAGS += $(ZLIB_CFLAGS) pkg_LDADD += $(ZLIB_LIBS) endif + +# lzma compressor +if WITH_LZMA +pkg_SOURCES += main/compressors/lzma.c + +pkg_CFLAGS += $(XZ_CFLAGS) +pkg_LDADD += $(XZ_LIBS) +endif diff --git a/include/pkgformat.h b/include/pkgformat.h index 804877b..bdeadbb 100644 --- a/include/pkgformat.h +++ b/include/pkgformat.h @@ -15,6 +15,7 @@ typedef enum { typedef enum { PKG_COMPRESSION_NONE = 0, PKG_COMPRESSION_ZLIB = 1, + PKG_COMPRESSION_LZMA = 2, } PKG_COMPRESSION; typedef struct { diff --git a/main/cmd/pack/pack.c b/main/cmd/pack/pack.c index 5bc5583..a2a55fd 100644 --- a/main/cmd/pack/pack.c +++ b/main/cmd/pack/pack.c @@ -14,6 +14,10 @@ static compressor_t *get_default_compressor(void) { compressor_t *cmp; + cmp = compressor_by_id(PKG_COMPRESSION_LZMA); + if (cmp != NULL) + return cmp; + cmp = compressor_by_id(PKG_COMPRESSION_ZLIB); if (cmp != NULL) return cmp; diff --git a/main/compressors/lzma.c b/main/compressors/lzma.c new file mode 100644 index 0000000..42c3d6e --- /dev/null +++ b/main/compressors/lzma.c @@ -0,0 +1,169 @@ +#include +#include +#include +#include +#include + +#include "compressor.h" + +#define CHUNK_SIZE 16384 + +typedef struct { + compressor_stream_t base; + lzma_stream strm; + lzma_action action; + uint8_t chunk[CHUNK_SIZE]; + int used; + bool eof; + bool error; +} lzma_stream_t; + +static ssize_t lzma_write(compressor_stream_t *base, + const uint8_t *in, size_t size) +{ + lzma_stream_t *lzma = (lzma_stream_t *)base; + + if (size > (size_t)(CHUNK_SIZE - lzma->used)) + size = CHUNK_SIZE - lzma->used; + + if (size == 0) + return 0; + + memcpy(lzma->chunk + lzma->used, in, size); + lzma->used += size; + return size; +} + +static ssize_t lzma_read(compressor_stream_t *base, + uint8_t *out, size_t size) +{ + lzma_stream_t *lzma = (lzma_stream_t *)base; + int have, total = 0, processed; + + if (lzma->error) + return -1; + if (lzma->eof) + return 0; + + lzma->strm.avail_in = lzma->used; + lzma->strm.next_in = lzma->chunk; + + do { + lzma->strm.avail_out = size; + lzma->strm.next_out = out; + + switch (lzma_code(&lzma->strm, lzma->action)) { + case LZMA_STREAM_END: + lzma->eof = true; + break; + case LZMA_BUF_ERROR: + total += (size - lzma->strm.avail_out); + goto out_remove; + case LZMA_OK: + break; + default: + lzma->error = true; + return -1; + } + + have = size - lzma->strm.avail_out; + + out += have; + size -= have; + total += have; + } while (lzma->strm.avail_out == 0 && !lzma->eof); +out_remove: + processed = lzma->used - lzma->strm.avail_in; + + if (processed < lzma->used) { + memmove(lzma->chunk, lzma->chunk + processed, + lzma->used - processed); + } + + lzma->used -= processed; + return total; +} + +static void lzma_flush(compressor_stream_t *base) +{ + lzma_stream_t *lzma = (lzma_stream_t *)base; + + lzma->action = LZMA_FINISH; +} + +static void lzma_destroy(compressor_stream_t *base) +{ + lzma_stream_t *lzma = (lzma_stream_t *)base; + + lzma_end(&lzma->strm); + free(lzma); +} + +static compressor_stream_t *create_stream(bool compress) +{ + lzma_stream_t *lzma = calloc(1, sizeof(*lzma)); + compressor_stream_t *base; + lzma_filter filters[5]; + int ret; + + if (lzma == NULL) { + perror("creating lzma stream"); + return NULL; + } + + lzma->action = LZMA_RUN; + + base = (compressor_stream_t *)lzma; + base->write = lzma_write; + base->read = lzma_read; + base->flush = lzma_flush; + base->destroy = lzma_destroy; + + if (compress) { + lzma_options_lzma opt_lzma2; + + if (lzma_lzma_preset(&opt_lzma2, LZMA_PRESET_DEFAULT)) + goto fail; + + filters[0].id = LZMA_FILTER_LZMA2; + filters[0].options = &opt_lzma2; + + filters[1].id = LZMA_VLI_UNKNOWN; + filters[1].options = NULL; + + ret = lzma_stream_encoder(&lzma->strm, filters, + LZMA_CHECK_CRC32); + } else { + ret = lzma_stream_decoder(&lzma->strm, UINT64_MAX, 0); + } + + if (ret != LZMA_OK) + goto fail; + + return base; +fail: + fputs("internal error creating lzma stream\n", stderr); + free(lzma); + return NULL; +} + +static compressor_stream_t *lzma_compress(compressor_t *cmp) +{ + (void)cmp; + return create_stream(true); +} + +static compressor_stream_t *lzma_uncompress(compressor_t *cmp) +{ + (void)cmp; + return create_stream(false); +} + +static compressor_t lzma = { + .name = "lzma", + .id = PKG_COMPRESSION_LZMA, + .compression_stream = lzma_compress, + .uncompression_stream = lzma_uncompress, +}; + +REGISTER_COMPRESSOR(lzma)