/*-
* Copyright (c) 2018 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Christos Zoulas.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/* Lzd - Educational decompressor for the lzip format
Copyright (C) 2013-2018 Antonio Diaz Diaz.
This program is free software. Redistribution and use in source and
binary forms, with or without modification, are permitted provided
that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
static bool
lz_st_is_char(int st) {
return st < 7;
}
static int
lz_st_get_char(int st) {
return lz_st_next[st];
}
static int
lz_st_get_match(int st) {
return st < 7 ? 7 : 10;
}
static int
lz_st_get_rep(int st) {
return st < 7 ? 8 : 11;
}
static int
lz_st_get_short_rep(int st) {
return st < 7 ? 9 : 11;
}
struct lz_len_model {
int choice1;
int choice2;
int bm_low[POS_STATES][LOW_SYMBOLS];
int bm_mid[POS_STATES][MID_SYMBOLS];
int bm_high[HIGH_SYMBOLS];
};
static uint32_t lz_crc[256];
static void
lz_crc_init(void)
{
for (unsigned i = 0; i < __arraycount(lz_crc); i++) {
unsigned c = i;
for (unsigned j = 0; j < 8; j++) {
if (c & 1)
c = 0xEDB88320U ^ (c >> 1);
else
c >>= 1;
}
lz_crc[i] = c;
}
}
static void
lz_bm_init(int *a, size_t l)
{
for (size_t i = 0; i < l; i++)
a[i] = BIT_MODEL_INIT;
}
#define LZ_BM_INIT(a) lz_bm_init(a, __arraycount(a))
#define LZ_BM_INIT2(a) do { \
size_t l = __arraycount(a[0]); \
for (size_t i = 0; i < __arraycount(a); i++) \
lz_bm_init(a[i], l); \
} while (0)
static bool
lz_decode_member(struct lz_decoder *lz)
{
int bm_literal[1 << LITERAL_CONTEXT_BITS][0x300];
int bm_match[LZ_STATES][POS_STATES];
int bm_rep[4][LZ_STATES];
int bm_len[LZ_STATES][POS_STATES];
int bm_dis_slot[LZ_STATES][1 << DIS_SLOT_BITS];
int bm_dis[MODELED_DISTANCES - DIS_MODEL_END + 1];
int bm_align[DIS_ALIGN_SIZE];
while (!feof(lz->fin) && !ferror(lz->fin)) {
const int pos_state = lz_get_data_position(lz) & POS_STATE_MASK;
// bit 1
if (lz_rd_decode_bit(rd, &bm_match[state][pos_state]) == 0) {
const uint8_t prev_byte = lz_peek(lz, 0);
const int literal_state =
prev_byte >> (8 - LITERAL_CONTEXT_BITS);
int *bm = bm_literal[literal_state];
if (lz_st_is_char(state))
lz_put(lz, lz_rd_decode_tree(rd, bm, 8));
else {
int peek = lz_peek(lz, rep[0]);
lz_put(lz, lz_rd_decode_matched(rd, bm, peek));
}
state = lz_st_get_char(state);
continue;
}
int len;
// bit 2
if (lz_rd_decode_bit(rd, &bm_rep[0][state]) != 0) {
// bit 3
if (lz_rd_decode_bit(rd, &bm_rep[1][state]) == 0) {
// bit 4
if (lz_rd_decode_bit(rd,
&bm_len[state][pos_state]) == 0)
{
state = lz_st_get_short_rep(state);
lz_put(lz, lz_peek(lz, rep[0]));
continue;
}
} else {
unsigned distance;
// bit 4
if (lz_rd_decode_bit(rd, &bm_rep[2][state])
== 0)
distance = rep[1];
else {
// bit 5
if (lz_rd_decode_bit(rd,
&bm_rep[3][state]) == 0)
distance = rep[2];
else {
distance = rep[3];
rep[3] = rep[2];
}
rep[2] = rep[1];
}
rep[1] = rep[0];
rep[0] = distance;
}
state = lz_st_get_rep(state);
len = MIN_MATCH_LEN +
lz_rd_decode_len(rd, &rep_len_model, pos_state);
} else {
rep[3] = rep[2]; rep[2] = rep[1]; rep[1] = rep[0];
len = MIN_MATCH_LEN +
lz_rd_decode_len(rd, &match_len_model, pos_state);
const int len_state =
MIN(len - MIN_MATCH_LEN, STATES - 1);
rep[0] = lz_rd_decode_tree(rd, bm_dis_slot[len_state],
DIS_SLOT_BITS);
if (rep[0] >= DIS_MODEL_START) {
const unsigned dis_slot = rep[0];
const int direct_bits = (dis_slot >> 1) - 1;
rep[0] = (2 | (dis_slot & 1)) << direct_bits;
if (dis_slot < DIS_MODEL_END)
rep[0] += lz_rd_decode_tree_reversed(rd,
&bm_dis[rep[0] - dis_slot],
direct_bits);
else {
rep[0] += lz_rd_decode(rd, direct_bits
- DIS_ALIGN_BITS) << DIS_ALIGN_BITS;
rep[0] += lz_rd_decode_tree_reversed(rd,
bm_align, DIS_ALIGN_BITS);
if (rep[0] == 0xFFFFFFFFU) {
lz_flush(lz);
return len == MIN_MATCH_LEN;
}
}
}
state = lz_st_get_match(state);
if (rep[0] >= lz->dict_size ||
(rep[0] >= lz->pos && !lz->wrapped)) {
lz_flush(lz);
return false;
}
}
for (int i = 0; i < len; i++)
lz_put(lz, lz_peek(lz, rep[0]));
}
lz_flush(lz);
return false;
}
/*
* 0-3 CRC32 of the uncompressed data
* 4-11 size of the uncompressed data
* 12-19 member size including header and trailer
*/
#define TRAILER_SIZE 20