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