xref: /linux/fs/ntfs/lib/xpress_decompress.c (revision 67f8bc848ee31831336bd478e57d2f993551902e)
1f39cd3f7SHyunchul Lee // SPDX-License-Identifier: GPL-2.0-or-later
2f39cd3f7SHyunchul Lee /*
3f39cd3f7SHyunchul Lee  * xpress_decompress.c - A decompressor for the XPRESS compression format
4f39cd3f7SHyunchul Lee  * (Huffman variant), which can be used in "System Compressed" (WOF) files.
5f39cd3f7SHyunchul Lee  *
6f39cd3f7SHyunchul Lee  * This is a port of the upstream wimlib "xpress_decompress.c" which uses a
7f39cd3f7SHyunchul Lee  * subtable-based Huffman decode table format.  The decode table and the
8f39cd3f7SHyunchul Lee  * codeword-length array share a union since the lengths are fully consumed
9f39cd3f7SHyunchul Lee  * before the table is written.
10f39cd3f7SHyunchul Lee  *
11f39cd3f7SHyunchul Lee  * Copyright (C) 2012-2016 Eric Biggers
12f39cd3f7SHyunchul Lee  */
13f39cd3f7SHyunchul Lee 
14f39cd3f7SHyunchul Lee #include <linux/array_size.h>
15f39cd3f7SHyunchul Lee 
16f39cd3f7SHyunchul Lee #include "decompress_common.h"
17f39cd3f7SHyunchul Lee #include "lib.h"
18*7ddb3fefSHyunchul Lee #include "../ntfs_codec.h"
19f39cd3f7SHyunchul Lee 
20f39cd3f7SHyunchul Lee #define XPRESS_NUM_CHARS	256
21f39cd3f7SHyunchul Lee #define XPRESS_NUM_SYMBOLS	512
22f39cd3f7SHyunchul Lee #define XPRESS_MAX_CODEWORD_LEN	15
23f39cd3f7SHyunchul Lee #define XPRESS_MIN_MATCH_LEN	3
24f39cd3f7SHyunchul Lee 
25f39cd3f7SHyunchul Lee /* This value is chosen for fast decompression. */
26f39cd3f7SHyunchul Lee #define XPRESS_TABLEBITS	11
27f39cd3f7SHyunchul Lee 
28f39cd3f7SHyunchul Lee /* Reusable heap-allocated memory for XPRESS decompression.  The decode table
29f39cd3f7SHyunchul Lee  * and the codeword-length array alias each other in a union: all lengths are
30f39cd3f7SHyunchul Lee  * consumed into the working space before any decode-table entry is written.
31f39cd3f7SHyunchul Lee  */
32f39cd3f7SHyunchul Lee struct xpress_decompressor {
33f39cd3f7SHyunchul Lee 	union {
34f39cd3f7SHyunchul Lee 		DECODE_TABLE(decode_table, XPRESS_NUM_SYMBOLS, XPRESS_TABLEBITS,
35f39cd3f7SHyunchul Lee 			     XPRESS_MAX_CODEWORD_LEN);
36f39cd3f7SHyunchul Lee 		u8 lens[XPRESS_NUM_SYMBOLS];
37f39cd3f7SHyunchul Lee 	};
38f39cd3f7SHyunchul Lee 	DECODE_TABLE_WORKING_SPACE(working_space, XPRESS_NUM_SYMBOLS,
39f39cd3f7SHyunchul Lee 				   XPRESS_MAX_CODEWORD_LEN);
40f39cd3f7SHyunchul Lee } __aligned(DECODE_TABLE_ALIGNMENT);
41f39cd3f7SHyunchul Lee 
42f39cd3f7SHyunchul Lee int xpress_decompress(struct xpress_decompressor *d,
43f39cd3f7SHyunchul Lee 		      const void *compressed_data, size_t compressed_size,
44f39cd3f7SHyunchul Lee 		      void *uncompressed_data, size_t uncompressed_size)
45f39cd3f7SHyunchul Lee {
46f39cd3f7SHyunchul Lee 	const u8 *const in_begin = compressed_data;
47f39cd3f7SHyunchul Lee 	u8 *const out_begin = uncompressed_data;
48f39cd3f7SHyunchul Lee 	u8 *out_next = out_begin;
49f39cd3f7SHyunchul Lee 	u8 *const out_end = out_begin + uncompressed_size;
50f39cd3f7SHyunchul Lee 	struct input_bitstream is;
51f39cd3f7SHyunchul Lee 	u32 i;
52f39cd3f7SHyunchul Lee 
53f39cd3f7SHyunchul Lee 	/* Read the Huffman codeword lengths (512 4-bit values packed into 256
54f39cd3f7SHyunchul Lee 	 * bytes).
55f39cd3f7SHyunchul Lee 	 */
56f39cd3f7SHyunchul Lee 	if (compressed_size < XPRESS_NUM_SYMBOLS / 2)
57f39cd3f7SHyunchul Lee 		return -1;
58f39cd3f7SHyunchul Lee 	for (i = 0; i < XPRESS_NUM_SYMBOLS / 2; i++) {
59f39cd3f7SHyunchul Lee 		d->lens[2 * i + 0] = in_begin[i] & 0xf;
60f39cd3f7SHyunchul Lee 		d->lens[2 * i + 1] = in_begin[i] >> 4;
61f39cd3f7SHyunchul Lee 	}
62f39cd3f7SHyunchul Lee 
63f39cd3f7SHyunchul Lee 	/* Build a decoding table for the Huffman code. */
64f39cd3f7SHyunchul Lee 	if (make_huffman_decode_table(d->decode_table, XPRESS_NUM_SYMBOLS,
65f39cd3f7SHyunchul Lee 				      XPRESS_TABLEBITS, d->lens,
66f39cd3f7SHyunchul Lee 				      XPRESS_MAX_CODEWORD_LEN,
67f39cd3f7SHyunchul Lee 				      d->working_space,
68f39cd3f7SHyunchul Lee 				      ARRAY_SIZE(d->decode_table)))
69f39cd3f7SHyunchul Lee 		return -1;
70f39cd3f7SHyunchul Lee 
71f39cd3f7SHyunchul Lee 	/* Decode the matches and literals. */
72f39cd3f7SHyunchul Lee 	init_input_bitstream(&is, in_begin + XPRESS_NUM_SYMBOLS / 2,
73f39cd3f7SHyunchul Lee 			     compressed_size - XPRESS_NUM_SYMBOLS / 2);
74f39cd3f7SHyunchul Lee 
75f39cd3f7SHyunchul Lee 	while (out_next != out_end) {
76f39cd3f7SHyunchul Lee 		u32 sym;
77f39cd3f7SHyunchul Lee 		u32 log2_offset;
78f39cd3f7SHyunchul Lee 		u32 length;
79f39cd3f7SHyunchul Lee 		u32 offset;
80f39cd3f7SHyunchul Lee 
81f39cd3f7SHyunchul Lee 		sym = read_huffsym(&is, d->decode_table, XPRESS_TABLEBITS,
82f39cd3f7SHyunchul Lee 				   XPRESS_MAX_CODEWORD_LEN);
83f39cd3f7SHyunchul Lee 		if (sym < XPRESS_NUM_CHARS) {
84f39cd3f7SHyunchul Lee 			/* Literal */
85f39cd3f7SHyunchul Lee 			*out_next++ = sym;
86f39cd3f7SHyunchul Lee 		} else {
87f39cd3f7SHyunchul Lee 			/* Match */
88f39cd3f7SHyunchul Lee 			length = sym & 0xf;
89f39cd3f7SHyunchul Lee 			log2_offset = (sym >> 4) & 0xf;
90f39cd3f7SHyunchul Lee 
91f39cd3f7SHyunchul Lee 			bitstream_ensure_bits(&is, 16);
92f39cd3f7SHyunchul Lee 
93f39cd3f7SHyunchul Lee 			offset = ((u32)1 << log2_offset) |
94f39cd3f7SHyunchul Lee 				 bitstream_pop_bits(&is, log2_offset);
95f39cd3f7SHyunchul Lee 
96f39cd3f7SHyunchul Lee 			if (length == 0xf) {
97f39cd3f7SHyunchul Lee 				length += bitstream_read_byte(&is);
98f39cd3f7SHyunchul Lee 				if (length == 0xf + 0xff)
99f39cd3f7SHyunchul Lee 					length = bitstream_read_u16(&is);
100f39cd3f7SHyunchul Lee 			}
101f39cd3f7SHyunchul Lee 			length += XPRESS_MIN_MATCH_LEN;
102f39cd3f7SHyunchul Lee 
103f39cd3f7SHyunchul Lee 			if (unlikely(lz_copy(length, offset, out_begin, out_next,
104f39cd3f7SHyunchul Lee 					     out_end, XPRESS_MIN_MATCH_LEN)))
105f39cd3f7SHyunchul Lee 				return -1;
106f39cd3f7SHyunchul Lee 
107f39cd3f7SHyunchul Lee 			out_next += length;
108f39cd3f7SHyunchul Lee 		}
109f39cd3f7SHyunchul Lee 	}
110f39cd3f7SHyunchul Lee 	return 0;
111f39cd3f7SHyunchul Lee }
112f39cd3f7SHyunchul Lee 
113f39cd3f7SHyunchul Lee struct xpress_decompressor *xpress_allocate_decompressor(void)
114f39cd3f7SHyunchul Lee {
115f39cd3f7SHyunchul Lee 	return kmalloc_obj(struct xpress_decompressor, GFP_NOFS);
116f39cd3f7SHyunchul Lee }
117f39cd3f7SHyunchul Lee 
118f39cd3f7SHyunchul Lee void xpress_free_decompressor(struct xpress_decompressor *d)
119f39cd3f7SHyunchul Lee {
120f39cd3f7SHyunchul Lee 	kfree(d);
121f39cd3f7SHyunchul Lee }
122*7ddb3fefSHyunchul Lee 
123*7ddb3fefSHyunchul Lee static size_t xpress_scratch_size(u32 chunk_size)
124*7ddb3fefSHyunchul Lee {
125*7ddb3fefSHyunchul Lee 	return sizeof(struct xpress_decompressor);
126*7ddb3fefSHyunchul Lee }
127*7ddb3fefSHyunchul Lee 
128*7ddb3fefSHyunchul Lee static int xpress_decompress_chunk(void *scratch, const void *src,
129*7ddb3fefSHyunchul Lee 				   size_t src_len, void *dst, size_t dst_len,
130*7ddb3fefSHyunchul Lee 				   u32 chunk_size)
131*7ddb3fefSHyunchul Lee {
132*7ddb3fefSHyunchul Lee 	return xpress_decompress(scratch, src, src_len, dst, dst_len);
133*7ddb3fefSHyunchul Lee }
134*7ddb3fefSHyunchul Lee 
135*7ddb3fefSHyunchul Lee const struct ntfs_codec_ops ntfs_xpress4k_codec_ops = {
136*7ddb3fefSHyunchul Lee 	.id = NTFS_CODEC_XPRESS4K,
137*7ddb3fefSHyunchul Lee 	.name = "xpress4k",
138*7ddb3fefSHyunchul Lee 	.scratch_size = xpress_scratch_size,
139*7ddb3fefSHyunchul Lee 	.decompress_chunk = xpress_decompress_chunk,
140*7ddb3fefSHyunchul Lee };
141*7ddb3fefSHyunchul Lee 
142*7ddb3fefSHyunchul Lee const struct ntfs_codec_ops ntfs_xpress8k_codec_ops = {
143*7ddb3fefSHyunchul Lee 	.id = NTFS_CODEC_XPRESS8K,
144*7ddb3fefSHyunchul Lee 	.name = "xpress8k",
145*7ddb3fefSHyunchul Lee 	.scratch_size = xpress_scratch_size,
146*7ddb3fefSHyunchul Lee 	.decompress_chunk = xpress_decompress_chunk,
147*7ddb3fefSHyunchul Lee };
148*7ddb3fefSHyunchul Lee 
149*7ddb3fefSHyunchul Lee const struct ntfs_codec_ops ntfs_xpress16k_codec_ops = {
150*7ddb3fefSHyunchul Lee 	.id = NTFS_CODEC_XPRESS16K,
151*7ddb3fefSHyunchul Lee 	.name = "xpress16k",
152*7ddb3fefSHyunchul Lee 	.scratch_size = xpress_scratch_size,
153*7ddb3fefSHyunchul Lee 	.decompress_chunk = xpress_decompress_chunk,
154*7ddb3fefSHyunchul Lee };
155