143 lines
3.9 KiB
C
143 lines
3.9 KiB
C
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||
|
/*
|
||
|
* xpress_decompress.c - A decompressor for the XPRESS compression format
|
||
|
* (Huffman variant), which can be used in "System Compressed" files. This is
|
||
|
* based on the code from wimlib.
|
||
|
*
|
||
|
* Copyright (C) 2015 Eric Biggers
|
||
|
*/
|
||
|
|
||
|
#include "decompress_common.h"
|
||
|
#include "lib.h"
|
||
|
|
||
|
#define XPRESS_NUM_SYMBOLS 512
|
||
|
#define XPRESS_MAX_CODEWORD_LEN 15
|
||
|
#define XPRESS_MIN_MATCH_LEN 3
|
||
|
|
||
|
/* This value is chosen for fast decompression. */
|
||
|
#define XPRESS_TABLEBITS 12
|
||
|
|
||
|
/* Reusable heap-allocated memory for XPRESS decompression */
|
||
|
struct xpress_decompressor {
|
||
|
|
||
|
/* The Huffman decoding table */
|
||
|
u16 decode_table[(1 << XPRESS_TABLEBITS) + 2 * XPRESS_NUM_SYMBOLS];
|
||
|
|
||
|
/* An array that maps symbols to codeword lengths */
|
||
|
u8 lens[XPRESS_NUM_SYMBOLS];
|
||
|
|
||
|
/* Temporary space for make_huffman_decode_table() */
|
||
|
u16 working_space[2 * (1 + XPRESS_MAX_CODEWORD_LEN) +
|
||
|
XPRESS_NUM_SYMBOLS];
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
* xpress_allocate_decompressor - Allocate an XPRESS decompressor
|
||
|
*
|
||
|
* Return the pointer to the decompressor on success, or return NULL and set
|
||
|
* errno on failure.
|
||
|
*/
|
||
|
struct xpress_decompressor *xpress_allocate_decompressor(void)
|
||
|
{
|
||
|
return kmalloc(sizeof(struct xpress_decompressor), GFP_NOFS);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* xpress_decompress - Decompress a buffer of XPRESS-compressed data
|
||
|
*
|
||
|
* @decompressor: A decompressor that was allocated with
|
||
|
* xpress_allocate_decompressor()
|
||
|
* @compressed_data: The buffer of data to decompress
|
||
|
* @compressed_size: Number of bytes of compressed data
|
||
|
* @uncompressed_data: The buffer in which to store the decompressed data
|
||
|
* @uncompressed_size: The number of bytes the data decompresses into
|
||
|
*
|
||
|
* Return 0 on success, or return -1 and set errno on failure.
|
||
|
*/
|
||
|
int xpress_decompress(struct xpress_decompressor *decompressor,
|
||
|
const void *compressed_data, size_t compressed_size,
|
||
|
void *uncompressed_data, size_t uncompressed_size)
|
||
|
{
|
||
|
struct xpress_decompressor *d = decompressor;
|
||
|
const u8 * const in_begin = compressed_data;
|
||
|
u8 * const out_begin = uncompressed_data;
|
||
|
u8 *out_next = out_begin;
|
||
|
u8 * const out_end = out_begin + uncompressed_size;
|
||
|
struct input_bitstream is;
|
||
|
u32 i;
|
||
|
|
||
|
/* Read the Huffman codeword lengths. */
|
||
|
if (compressed_size < XPRESS_NUM_SYMBOLS / 2)
|
||
|
goto invalid;
|
||
|
for (i = 0; i < XPRESS_NUM_SYMBOLS / 2; i++) {
|
||
|
d->lens[i*2 + 0] = in_begin[i] & 0xF;
|
||
|
d->lens[i*2 + 1] = in_begin[i] >> 4;
|
||
|
}
|
||
|
|
||
|
/* Build a decoding table for the Huffman code. */
|
||
|
if (make_huffman_decode_table(d->decode_table, XPRESS_NUM_SYMBOLS,
|
||
|
XPRESS_TABLEBITS, d->lens,
|
||
|
XPRESS_MAX_CODEWORD_LEN,
|
||
|
d->working_space))
|
||
|
goto invalid;
|
||
|
|
||
|
/* Decode the matches and literals. */
|
||
|
|
||
|
init_input_bitstream(&is, in_begin + XPRESS_NUM_SYMBOLS / 2,
|
||
|
compressed_size - XPRESS_NUM_SYMBOLS / 2);
|
||
|
|
||
|
while (out_next != out_end) {
|
||
|
u32 sym;
|
||
|
u32 log2_offset;
|
||
|
u32 length;
|
||
|
u32 offset;
|
||
|
|
||
|
sym = read_huffsym(&is, d->decode_table,
|
||
|
XPRESS_TABLEBITS, XPRESS_MAX_CODEWORD_LEN);
|
||
|
if (sym < 256) {
|
||
|
/* Literal */
|
||
|
*out_next++ = sym;
|
||
|
} else {
|
||
|
/* Match */
|
||
|
length = sym & 0xf;
|
||
|
log2_offset = (sym >> 4) & 0xf;
|
||
|
|
||
|
bitstream_ensure_bits(&is, 16);
|
||
|
|
||
|
offset = ((u32)1 << log2_offset) |
|
||
|
bitstream_pop_bits(&is, log2_offset);
|
||
|
|
||
|
if (length == 0xf) {
|
||
|
length += bitstream_read_byte(&is);
|
||
|
if (length == 0xf + 0xff)
|
||
|
length = bitstream_read_u16(&is);
|
||
|
}
|
||
|
length += XPRESS_MIN_MATCH_LEN;
|
||
|
|
||
|
if (offset > (size_t)(out_next - out_begin))
|
||
|
goto invalid;
|
||
|
|
||
|
if (length > (size_t)(out_end - out_next))
|
||
|
goto invalid;
|
||
|
|
||
|
out_next = lz_copy(out_next, length, offset, out_end,
|
||
|
XPRESS_MIN_MATCH_LEN);
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
|
||
|
invalid:
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* xpress_free_decompressor - Free an XPRESS decompressor
|
||
|
*
|
||
|
* @decompressor: A decompressor that was allocated with
|
||
|
* xpress_allocate_decompressor(), or NULL.
|
||
|
*/
|
||
|
void xpress_free_decompressor(struct xpress_decompressor *decompressor)
|
||
|
{
|
||
|
kfree(decompressor);
|
||
|
}
|