diff options
Diffstat (limited to 'lib/sqfs/comp/xz.c')
-rw-r--r-- | lib/sqfs/comp/xz.c | 216 |
1 files changed, 55 insertions, 161 deletions
diff --git a/lib/sqfs/comp/xz.c b/lib/sqfs/comp/xz.c index d38aab6..4f67b1c 100644 --- a/lib/sqfs/comp/xz.c +++ b/lib/sqfs/comp/xz.c @@ -15,17 +15,6 @@ #include "internal.h" -typedef enum { - XZ_FILTER_X86 = 0x01, - XZ_FILTER_POWERPC = 0x02, - XZ_FILTER_IA64 = 0x04, - XZ_FILTER_ARM = 0x08, - XZ_FILTER_ARMTHUMB = 0x10, - XZ_FILTER_SPARC = 0x20, - - XZ_FILTER_ALL = 0x3F, -} XZ_FILTER_FLAG; - typedef struct { compressor_t base; size_t block_size; @@ -38,20 +27,15 @@ typedef struct { uint32_t flags; } xz_options_t; -static const struct { - const char *name; - lzma_vli filter; - int flag; -} xz_filters[] = { - { "x86", LZMA_FILTER_X86, XZ_FILTER_X86 }, - { "powerpc", LZMA_FILTER_POWERPC, XZ_FILTER_POWERPC }, - { "ia64", LZMA_FILTER_IA64, XZ_FILTER_IA64 }, - { "arm", LZMA_FILTER_ARM, XZ_FILTER_ARM }, - { "armthumb", LZMA_FILTER_ARMTHUMB, XZ_FILTER_ARMTHUMB }, - { "sparc", LZMA_FILTER_SPARC, XZ_FILTER_SPARC }, -}; - -#define XZ_NUM_FILTERS (sizeof(xz_filters) / sizeof(xz_filters[0])) +static bool is_dict_size_valid(size_t size) +{ + size_t x = size & (size - 1); + + if (x == 0) + return true; + + return size == (x | (x >> 1)); +} static int xz_write_options(compressor_t *base, int fd) { @@ -71,7 +55,6 @@ static int xz_read_options(compressor_t *base, int fd) { xz_compressor_t *xz = (xz_compressor_t *)base; xz_options_t opt; - uint32_t mask; if (generic_read_options(fd, &opt, sizeof(opt))) return -1; @@ -79,14 +62,12 @@ static int xz_read_options(compressor_t *base, int fd) opt.dict_size = le32toh(opt.dict_size); opt.flags = le32toh(opt.flags); - mask = opt.dict_size & (opt.dict_size - 1); - - if (mask != 0 && ((mask & (mask - 1)) != 0)) { + if (!is_dict_size_valid(opt.dict_size)) { fputs("Invalid lzma dictionary size.\n", stderr); return -1; } - if (opt.flags & ~XZ_FILTER_ALL) { + if (opt.flags & ~SQFS_COMP_FLAG_XZ_ALL) { fputs("Unknown BCJ filter used.\n", stderr); return -1; } @@ -141,11 +122,31 @@ static ssize_t compress(xz_compressor_t *xz, lzma_vli filter, return 0; } +static lzma_vli flag_to_vli(int flag) +{ + switch (flag) { + case SQFS_COMP_FLAG_XZ_X86: + return LZMA_FILTER_X86; + case SQFS_COMP_FLAG_XZ_POWERPC: + return LZMA_FILTER_POWERPC; + case SQFS_COMP_FLAG_XZ_IA64: + return LZMA_FILTER_IA64; + case SQFS_COMP_FLAG_XZ_ARM: + return LZMA_FILTER_ARM; + case SQFS_COMP_FLAG_XZ_ARMTHUMB: + return LZMA_FILTER_ARMTHUMB; + case SQFS_COMP_FLAG_XZ_SPARC: + return LZMA_FILTER_SPARC; + } + + return LZMA_VLI_UNKNOWN; +} + static ssize_t xz_comp_block(compressor_t *base, const uint8_t *in, size_t size, uint8_t *out, size_t outsize) { xz_compressor_t *xz = (xz_compressor_t *)base; - lzma_vli selected = LZMA_VLI_UNKNOWN; + lzma_vli filter, selected = LZMA_VLI_UNKNOWN; size_t i, smallest; ssize_t ret; @@ -155,17 +156,19 @@ static ssize_t xz_comp_block(compressor_t *base, const uint8_t *in, smallest = ret; - for (i = 0; i < XZ_NUM_FILTERS; ++i) { - if (!(xz->flags & xz_filters[i].flag)) + for (i = 0; i & SQFS_COMP_FLAG_XZ_ALL; i <<= 1) { + if ((xz->flags & i) == 0) continue; - ret = compress(xz, xz_filters[i].filter, in, size, out, outsize); + filter = flag_to_vli(i); + + ret = compress(xz, filter, in, size, out, outsize); if (ret < 0) return -1; if (ret > 0 && (smallest == 0 || (size_t)ret < smallest)) { smallest = ret; - selected = xz_filters[i].filter; + selected = filter; } } @@ -213,108 +216,22 @@ static void xz_destroy(compressor_t *base) free(base); } -static int process_options(char *options, size_t blocksize, - int *flags, uint64_t *dictsize) -{ - enum { - OPT_DICT = 0, - }; - char *const token[] = { - [OPT_DICT] = (char *)"dictsize", - NULL - }; - char *subopts, *value; - uint64_t mask; - size_t i; - int opt; - - subopts = options; - - while (*subopts != '\0') { - opt = getsubopt(&subopts, token, &value); - - switch (opt) { - case OPT_DICT: - if (value == NULL) - goto fail_value; - - for (i = 0; isdigit(value[i]); ++i) - ; - - if (i < 1 || i > 9) - goto fail_dict; - - *dictsize = atol(value); - - switch (value[i]) { - case '\0': - break; - case 'm': - case 'M': - *dictsize <<= 20; - break; - case 'k': - case 'K': - *dictsize <<= 10; - break; - case '%': - *dictsize = ((*dictsize) * blocksize) / 100; - break; - default: - goto fail_dict; - } - - if (*dictsize > 0x0FFFFFFFFUL) - goto fail_dict_ov; - - mask = *dictsize & (*dictsize - 1); - - if (mask != 0 && ((mask & (mask - 1)) != 0)) - goto fail_dict_pot; - break; - default: - for (i = 0; i < XZ_NUM_FILTERS; ++i) { - if (strcmp(value, xz_filters[i].name) == 0) { - *flags |= xz_filters[i].flag; - break; - } - } - if (i == XZ_NUM_FILTERS) - goto fail_opt; - break; - } - } - - return 0; -fail_dict_pot: - fputs("dictionary size must be either 2^n or 2^n + 2^(n-1)\n", stderr); - return -1; -fail_dict_ov: - fputs("dictionary size too large.\n", stderr); - return -1; -fail_dict: - fputs("dictionary size must be a number with the optional " - "suffix 'm','k' or '%'.\n", stderr); - return -1; -fail_opt: - fprintf(stderr, "Unknown option '%s'.\n", value); - return -1; -fail_value: - fprintf(stderr, "Missing value for '%s'.\n", token[opt]); - return -1; -} - -compressor_t *create_xz_compressor(bool compress, size_t block_size, - char *options) +compressor_t *create_xz_compressor(const compressor_config_t *cfg) { - uint64_t dictsize = block_size; xz_compressor_t *xz; compressor_t *base; - int flags = 0; - if (options != NULL) { - if (process_options(options, block_size, &flags, &dictsize)) - return NULL; + if (cfg->flags & ~(SQFS_COMP_FLAG_GENERIC_ALL | + SQFS_COMP_FLAG_XZ_ALL)) { + fputs("creating xz compressor: unknown compressor flags\n", + stderr); + return NULL; + } + + if (!is_dict_size_valid(cfg->opt.xz.dict_size)) { + fputs("creating xz compressor: invalid dictionary size\n", + stderr); + return NULL; } xz = calloc(1, sizeof(*xz)); @@ -324,37 +241,14 @@ compressor_t *create_xz_compressor(bool compress, size_t block_size, return NULL; } - xz->flags = flags; - xz->dict_size = dictsize; - xz->block_size = block_size; + xz->flags = cfg->flags; + xz->dict_size = cfg->opt.xz.dict_size; + xz->block_size = cfg->block_size; base->destroy = xz_destroy; - base->do_block = compress ? xz_comp_block : xz_uncomp_block; + base->do_block = (cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS) ? + xz_uncomp_block : xz_comp_block; base->write_options = xz_write_options; base->read_options = xz_read_options; base->create_copy = xz_create_copy; return base; } - -void compressor_xz_print_help(void) -{ - size_t i; - - fputs( -"Available options for xz compressor:\n" -"\n" -" dictsize=<value> Dictionary size. Either a value in bytes or a\n" -" percentage of the block size. Defaults to 100%.\n" -" The suffix '%' indicates a percentage. 'K' and 'M'\n" -" can also be used for kibi and mebi bytes\n" -" respecitively.\n" -"\n" -"In additon to the options, one or more bcj filters can be specified.\n" -"If multiple filters are provided, the one yielding the best compression\n" -"ratio will be used.\n" -"\n" -"The following filters are available:\n", - stdout); - - for (i = 0; i < XZ_NUM_FILTERS; ++i) - printf("\t%s\n", xz_filters[i].name); -} |