162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * xpress_decompress.c - A decompressor for the XPRESS compression format 462306a36Sopenharmony_ci * (Huffman variant), which can be used in "System Compressed" files. This is 562306a36Sopenharmony_ci * based on the code from wimlib. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2015 Eric Biggers 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "decompress_common.h" 1162306a36Sopenharmony_ci#include "lib.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#define XPRESS_NUM_SYMBOLS 512 1462306a36Sopenharmony_ci#define XPRESS_MAX_CODEWORD_LEN 15 1562306a36Sopenharmony_ci#define XPRESS_MIN_MATCH_LEN 3 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* This value is chosen for fast decompression. */ 1862306a36Sopenharmony_ci#define XPRESS_TABLEBITS 12 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* Reusable heap-allocated memory for XPRESS decompression */ 2162306a36Sopenharmony_cistruct xpress_decompressor { 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci /* The Huffman decoding table */ 2462306a36Sopenharmony_ci u16 decode_table[(1 << XPRESS_TABLEBITS) + 2 * XPRESS_NUM_SYMBOLS]; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci /* An array that maps symbols to codeword lengths */ 2762306a36Sopenharmony_ci u8 lens[XPRESS_NUM_SYMBOLS]; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci /* Temporary space for make_huffman_decode_table() */ 3062306a36Sopenharmony_ci u16 working_space[2 * (1 + XPRESS_MAX_CODEWORD_LEN) + 3162306a36Sopenharmony_ci XPRESS_NUM_SYMBOLS]; 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* 3562306a36Sopenharmony_ci * xpress_allocate_decompressor - Allocate an XPRESS decompressor 3662306a36Sopenharmony_ci * 3762306a36Sopenharmony_ci * Return the pointer to the decompressor on success, or return NULL and set 3862306a36Sopenharmony_ci * errno on failure. 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_cistruct xpress_decompressor *xpress_allocate_decompressor(void) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci return kmalloc(sizeof(struct xpress_decompressor), GFP_NOFS); 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* 4662306a36Sopenharmony_ci * xpress_decompress - Decompress a buffer of XPRESS-compressed data 4762306a36Sopenharmony_ci * 4862306a36Sopenharmony_ci * @decompressor: A decompressor that was allocated with 4962306a36Sopenharmony_ci * xpress_allocate_decompressor() 5062306a36Sopenharmony_ci * @compressed_data: The buffer of data to decompress 5162306a36Sopenharmony_ci * @compressed_size: Number of bytes of compressed data 5262306a36Sopenharmony_ci * @uncompressed_data: The buffer in which to store the decompressed data 5362306a36Sopenharmony_ci * @uncompressed_size: The number of bytes the data decompresses into 5462306a36Sopenharmony_ci * 5562306a36Sopenharmony_ci * Return 0 on success, or return -1 and set errno on failure. 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_ciint xpress_decompress(struct xpress_decompressor *decompressor, 5862306a36Sopenharmony_ci const void *compressed_data, size_t compressed_size, 5962306a36Sopenharmony_ci void *uncompressed_data, size_t uncompressed_size) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci struct xpress_decompressor *d = decompressor; 6262306a36Sopenharmony_ci const u8 * const in_begin = compressed_data; 6362306a36Sopenharmony_ci u8 * const out_begin = uncompressed_data; 6462306a36Sopenharmony_ci u8 *out_next = out_begin; 6562306a36Sopenharmony_ci u8 * const out_end = out_begin + uncompressed_size; 6662306a36Sopenharmony_ci struct input_bitstream is; 6762306a36Sopenharmony_ci u32 i; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* Read the Huffman codeword lengths. */ 7062306a36Sopenharmony_ci if (compressed_size < XPRESS_NUM_SYMBOLS / 2) 7162306a36Sopenharmony_ci goto invalid; 7262306a36Sopenharmony_ci for (i = 0; i < XPRESS_NUM_SYMBOLS / 2; i++) { 7362306a36Sopenharmony_ci d->lens[i*2 + 0] = in_begin[i] & 0xF; 7462306a36Sopenharmony_ci d->lens[i*2 + 1] = in_begin[i] >> 4; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci /* Build a decoding table for the Huffman code. */ 7862306a36Sopenharmony_ci if (make_huffman_decode_table(d->decode_table, XPRESS_NUM_SYMBOLS, 7962306a36Sopenharmony_ci XPRESS_TABLEBITS, d->lens, 8062306a36Sopenharmony_ci XPRESS_MAX_CODEWORD_LEN, 8162306a36Sopenharmony_ci d->working_space)) 8262306a36Sopenharmony_ci goto invalid; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* Decode the matches and literals. */ 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci init_input_bitstream(&is, in_begin + XPRESS_NUM_SYMBOLS / 2, 8762306a36Sopenharmony_ci compressed_size - XPRESS_NUM_SYMBOLS / 2); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci while (out_next != out_end) { 9062306a36Sopenharmony_ci u32 sym; 9162306a36Sopenharmony_ci u32 log2_offset; 9262306a36Sopenharmony_ci u32 length; 9362306a36Sopenharmony_ci u32 offset; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci sym = read_huffsym(&is, d->decode_table, 9662306a36Sopenharmony_ci XPRESS_TABLEBITS, XPRESS_MAX_CODEWORD_LEN); 9762306a36Sopenharmony_ci if (sym < 256) { 9862306a36Sopenharmony_ci /* Literal */ 9962306a36Sopenharmony_ci *out_next++ = sym; 10062306a36Sopenharmony_ci } else { 10162306a36Sopenharmony_ci /* Match */ 10262306a36Sopenharmony_ci length = sym & 0xf; 10362306a36Sopenharmony_ci log2_offset = (sym >> 4) & 0xf; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci bitstream_ensure_bits(&is, 16); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci offset = ((u32)1 << log2_offset) | 10862306a36Sopenharmony_ci bitstream_pop_bits(&is, log2_offset); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (length == 0xf) { 11162306a36Sopenharmony_ci length += bitstream_read_byte(&is); 11262306a36Sopenharmony_ci if (length == 0xf + 0xff) 11362306a36Sopenharmony_ci length = bitstream_read_u16(&is); 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci length += XPRESS_MIN_MATCH_LEN; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (offset > (size_t)(out_next - out_begin)) 11862306a36Sopenharmony_ci goto invalid; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (length > (size_t)(out_end - out_next)) 12162306a36Sopenharmony_ci goto invalid; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci out_next = lz_copy(out_next, length, offset, out_end, 12462306a36Sopenharmony_ci XPRESS_MIN_MATCH_LEN); 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci return 0; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ciinvalid: 13062306a36Sopenharmony_ci return -1; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci/* 13462306a36Sopenharmony_ci * xpress_free_decompressor - Free an XPRESS decompressor 13562306a36Sopenharmony_ci * 13662306a36Sopenharmony_ci * @decompressor: A decompressor that was allocated with 13762306a36Sopenharmony_ci * xpress_allocate_decompressor(), or NULL. 13862306a36Sopenharmony_ci */ 13962306a36Sopenharmony_civoid xpress_free_decompressor(struct xpress_decompressor *decompressor) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci kfree(decompressor); 14262306a36Sopenharmony_ci} 143