mirror of
https://github.com/torvalds/linux.git
synced 2026-09-23 05:04:02 +02:00
Implement the transparent compression codec ops for XPRESS (4K, 8K, 16K) and LZX (32K) algorithms. The xpress_scratch_size, lzx_scratch_size, xpress_decompress_chunk, and lzx_decompress_chunk wrappers provide unified interfaces and use per-call dynamic scratch state allocation (avoiding global mutexed singletons). Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
155 lines
4.2 KiB
C
155 lines
4.2 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" (WOF) files.
|
|
*
|
|
* This is a port of the upstream wimlib "xpress_decompress.c" which uses a
|
|
* subtable-based Huffman decode table format. The decode table and the
|
|
* codeword-length array share a union since the lengths are fully consumed
|
|
* before the table is written.
|
|
*
|
|
* Copyright (C) 2012-2016 Eric Biggers
|
|
*/
|
|
|
|
#include <linux/array_size.h>
|
|
|
|
#include "decompress_common.h"
|
|
#include "lib.h"
|
|
#include "../ntfs_codec.h"
|
|
|
|
#define XPRESS_NUM_CHARS 256
|
|
#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 11
|
|
|
|
/* Reusable heap-allocated memory for XPRESS decompression. The decode table
|
|
* and the codeword-length array alias each other in a union: all lengths are
|
|
* consumed into the working space before any decode-table entry is written.
|
|
*/
|
|
struct xpress_decompressor {
|
|
union {
|
|
DECODE_TABLE(decode_table, XPRESS_NUM_SYMBOLS, XPRESS_TABLEBITS,
|
|
XPRESS_MAX_CODEWORD_LEN);
|
|
u8 lens[XPRESS_NUM_SYMBOLS];
|
|
};
|
|
DECODE_TABLE_WORKING_SPACE(working_space, XPRESS_NUM_SYMBOLS,
|
|
XPRESS_MAX_CODEWORD_LEN);
|
|
} __aligned(DECODE_TABLE_ALIGNMENT);
|
|
|
|
int xpress_decompress(struct xpress_decompressor *d,
|
|
const void *compressed_data, size_t compressed_size,
|
|
void *uncompressed_data, size_t uncompressed_size)
|
|
{
|
|
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 (512 4-bit values packed into 256
|
|
* bytes).
|
|
*/
|
|
if (compressed_size < XPRESS_NUM_SYMBOLS / 2)
|
|
return -1;
|
|
for (i = 0; i < XPRESS_NUM_SYMBOLS / 2; i++) {
|
|
d->lens[2 * i + 0] = in_begin[i] & 0xf;
|
|
d->lens[2 * i + 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,
|
|
ARRAY_SIZE(d->decode_table)))
|
|
return -1;
|
|
|
|
/* 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 < XPRESS_NUM_CHARS) {
|
|
/* 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 (unlikely(lz_copy(length, offset, out_begin, out_next,
|
|
out_end, XPRESS_MIN_MATCH_LEN)))
|
|
return -1;
|
|
|
|
out_next += length;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
struct xpress_decompressor *xpress_allocate_decompressor(void)
|
|
{
|
|
return kmalloc_obj(struct xpress_decompressor, GFP_NOFS);
|
|
}
|
|
|
|
void xpress_free_decompressor(struct xpress_decompressor *d)
|
|
{
|
|
kfree(d);
|
|
}
|
|
|
|
static size_t xpress_scratch_size(u32 chunk_size)
|
|
{
|
|
return sizeof(struct xpress_decompressor);
|
|
}
|
|
|
|
static int xpress_decompress_chunk(void *scratch, const void *src,
|
|
size_t src_len, void *dst, size_t dst_len,
|
|
u32 chunk_size)
|
|
{
|
|
return xpress_decompress(scratch, src, src_len, dst, dst_len);
|
|
}
|
|
|
|
const struct ntfs_codec_ops ntfs_xpress4k_codec_ops = {
|
|
.id = NTFS_CODEC_XPRESS4K,
|
|
.name = "xpress4k",
|
|
.scratch_size = xpress_scratch_size,
|
|
.decompress_chunk = xpress_decompress_chunk,
|
|
};
|
|
|
|
const struct ntfs_codec_ops ntfs_xpress8k_codec_ops = {
|
|
.id = NTFS_CODEC_XPRESS8K,
|
|
.name = "xpress8k",
|
|
.scratch_size = xpress_scratch_size,
|
|
.decompress_chunk = xpress_decompress_chunk,
|
|
};
|
|
|
|
const struct ntfs_codec_ops ntfs_xpress16k_codec_ops = {
|
|
.id = NTFS_CODEC_XPRESS16K,
|
|
.name = "xpress16k",
|
|
.scratch_size = xpress_scratch_size,
|
|
.decompress_chunk = xpress_decompress_chunk,
|
|
};
|