261 lines
8.7 KiB
C
261 lines
8.7 KiB
C
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
|
│ │
|
|
│ This program is free software; you can redistribute it and/or modify │
|
|
│ it under the terms of the GNU General Public License as published by │
|
|
│ the Free Software Foundation; version 2 of the License. │
|
|
│ │
|
|
│ 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. See the GNU │
|
|
│ General Public License for more details. │
|
|
│ │
|
|
│ You should have received a copy of the GNU General Public License │
|
|
│ along with this program; if not, write to the Free Software │
|
|
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
|
|
│ 02110-1301 USA │
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
#include "libc/assert.h"
|
|
#include "libc/bits/bits.h"
|
|
#include "libc/intrin/repmovsb.h"
|
|
#include "libc/macros.h"
|
|
#include "libc/nexgen32e/kompressor.h"
|
|
#include "libc/str/str.h"
|
|
#include "libc/str/undeflate.h"
|
|
|
|
#define kDeflateCompressionTypeNone 0b00
|
|
#define kDeflateCompressionTypeFixedHuffman 0b01
|
|
#define kDeflateCompressionTypeDynamicHuffman 0b10
|
|
|
|
#define kDeflateCodeLengthCopyPrevious3To6Times 16
|
|
#define kDeflateCodeLengthRepeatZero3To10Times 17
|
|
#define kDeflateCodeLengthRepeatZero11To138Times 18
|
|
|
|
#define CONSUME(BITS) \
|
|
hold.word >>= BITS; \
|
|
hold.bits -= BITS
|
|
|
|
#define MOAR(BITS) \
|
|
while (hold.bits < BITS) { \
|
|
al = *ip++; \
|
|
hold.word |= (size_t)al << hold.bits; \
|
|
hold.bits += 8; \
|
|
}
|
|
|
|
struct DeflateHold {
|
|
size_t word;
|
|
size_t bits;
|
|
};
|
|
|
|
static const struct DeflateConsts {
|
|
uint16_t lenbase[32];
|
|
uint16_t distbase[32];
|
|
uint8_t lenbits[32];
|
|
uint8_t distbits[32];
|
|
uint8_t orders[19];
|
|
struct RlDecode lensrl[6];
|
|
} kDeflate = {
|
|
{3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27,
|
|
31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258},
|
|
{1, 2, 3, 4, 5, 7, 9, 13, 17, 25,
|
|
33, 49, 65, 97, 129, 193, 257, 385, 513, 769,
|
|
1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577},
|
|
{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2,
|
|
2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5},
|
|
{0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6,
|
|
6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13},
|
|
{16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15},
|
|
{{144, 8}, {112, 9}, {24, 7}, {8, 8}, {32, 5}, {0, 0}},
|
|
};
|
|
|
|
static uint32_t undeflatetree(struct DeflateState *ds, uint32_t *tree,
|
|
const uint8_t *lens, size_t symcount) {
|
|
size_t i, len;
|
|
uint32_t code, slot;
|
|
uint16_t codes[16], first[16], counts[16];
|
|
memset(counts, 0, sizeof(counts));
|
|
for (i = 0; i < symcount; i++) {
|
|
counts[lens[i]]++;
|
|
}
|
|
codes[0] = 0;
|
|
first[0] = 0;
|
|
counts[0] = 0;
|
|
for (i = 1; i < ARRAYLEN(codes); i++) {
|
|
codes[i] = (codes[i - 1] + counts[i - 1]) << 1;
|
|
first[i] = first[i - 1] + counts[i - 1];
|
|
}
|
|
assert(first[15] + counts[15] <= symcount);
|
|
for (i = 0; i < symcount; i++) {
|
|
if ((len = lens[i])) {
|
|
code = codes[len]++;
|
|
slot = first[len]++;
|
|
tree[slot] = code << (32 - len) | i << 4 | len;
|
|
}
|
|
}
|
|
return first[15];
|
|
}
|
|
|
|
static struct DeflateHold undeflatesymbol(struct DeflateHold hold,
|
|
const uint32_t *tree,
|
|
size_t treecount,
|
|
uint32_t *out_symbol) {
|
|
size_t left, right, m;
|
|
uint32_t search, key;
|
|
left = 0;
|
|
right = treecount;
|
|
search = bitreverse16(hold.word);
|
|
search <<= 16;
|
|
search |= 0xffff;
|
|
while (left < right) { /* TODO(jart): Make this O(1) like Zlib. */
|
|
m = (left + right) >> 1;
|
|
if (search < tree[m]) {
|
|
right = m;
|
|
} else {
|
|
left = m + 1;
|
|
}
|
|
}
|
|
key = tree[left - 1];
|
|
assert(!((search ^ key) >> (32 - (key & 0xf))));
|
|
*out_symbol = (key >> 4) & 0xfff;
|
|
CONSUME(key & 0xf);
|
|
return hold;
|
|
}
|
|
|
|
/**
|
|
* Decompresses raw DEFLATE data.
|
|
*
|
|
* This is 10x smaller and 10x slower than chromium zlib.
|
|
*
|
|
* @param output should be followed by a single guard page, and have
|
|
* 36kb of guard pages preceding it too
|
|
* @note h/t Phil Katz, David Huffman, Claude Shannon
|
|
*/
|
|
ssize_t undeflate(void *output, size_t outputsize, void *input,
|
|
size_t inputsize, struct DeflateState *ds) {
|
|
struct DeflateHold hold;
|
|
bool isfinalblock;
|
|
size_t i, nlit, ndist;
|
|
uint8_t *ip, *op, *si, b, al, last, compressiontype;
|
|
uint32_t j, l, len, sym, tlit, tdist, tlen, nlen;
|
|
|
|
op = output;
|
|
ip = input;
|
|
hold.word = 0;
|
|
hold.bits = 0;
|
|
isfinalblock = 0;
|
|
|
|
while (!isfinalblock) {
|
|
MOAR(3);
|
|
isfinalblock = hold.word & 0b1;
|
|
CONSUME(1);
|
|
compressiontype = hold.word & 0b11;
|
|
CONSUME(2);
|
|
|
|
switch (compressiontype) {
|
|
case kDeflateCompressionTypeNone:
|
|
CONSUME(hold.bits & 7);
|
|
MOAR(32);
|
|
len = hold.word & 0xffff;
|
|
nlen = (hold.word >> 16) & 0xffff;
|
|
assert(len == ~nlen);
|
|
CONSUME(32);
|
|
while (len--) {
|
|
if (hold.bits) {
|
|
*op++ = hold.word;
|
|
CONSUME(8);
|
|
} else {
|
|
*op++ = *ip++;
|
|
}
|
|
}
|
|
continue;
|
|
|
|
case kDeflateCompressionTypeFixedHuffman:
|
|
nlit = 288;
|
|
ndist = 32;
|
|
rldecode(ds->lens, kDeflate.lensrl);
|
|
break;
|
|
|
|
case kDeflateCompressionTypeDynamicHuffman:
|
|
MOAR(5 + 5 + 4);
|
|
nlit = (hold.word & 0b11111) + 257;
|
|
CONSUME(5);
|
|
ndist = (hold.word & 0b11111) + 1;
|
|
CONSUME(5);
|
|
nlen = (hold.word & 0b1111) + 4;
|
|
CONSUME(4);
|
|
for (i = 0; i < nlen; i++) {
|
|
MOAR(3);
|
|
ds->lenlens[kDeflate.orders[i]] = hold.word & 0b111;
|
|
CONSUME(3);
|
|
}
|
|
for (; i < ARRAYLEN(ds->lenlens); i++) {
|
|
ds->lenlens[kDeflate.orders[i]] = 0;
|
|
}
|
|
tlen =
|
|
undeflatetree(ds, ds->lencodes, ds->lenlens, ARRAYLEN(ds->lenlens));
|
|
i = 0;
|
|
last = 0;
|
|
while (i < nlit + ndist) {
|
|
MOAR(16);
|
|
hold = undeflatesymbol(hold, ds->lencodes, tlen, &sym);
|
|
b = 2;
|
|
j = 1;
|
|
switch (sym) {
|
|
case kDeflateCodeLengthRepeatZero11To138Times:
|
|
b += 4;
|
|
j += 8;
|
|
/* fallthrough */
|
|
case kDeflateCodeLengthRepeatZero3To10Times:
|
|
b += 1;
|
|
last = 0;
|
|
/* fallthrough */
|
|
case kDeflateCodeLengthCopyPrevious3To6Times:
|
|
MOAR(b);
|
|
j += (hold.word & ((1u << b) - 1u)) + 2u;
|
|
CONSUME(b);
|
|
break;
|
|
default:
|
|
last = sym;
|
|
break;
|
|
}
|
|
while (j--) {
|
|
ds->lens[i++] = last;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
tlit = undeflatetree(ds, ds->litcodes, ds->lens, nlit);
|
|
tdist = undeflatetree(ds, ds->distcodes, &ds->lens[nlit], ndist);
|
|
|
|
do {
|
|
MOAR(16);
|
|
hold = undeflatesymbol(hold, ds->litcodes, tlit, &sym);
|
|
if (sym < 256) {
|
|
*op++ = sym;
|
|
} else if (sym > 256) {
|
|
sym -= 257;
|
|
b = kDeflate.lenbits[sym];
|
|
MOAR(b);
|
|
l = kDeflate.lenbase[sym] + (hold.word & ((1u << b) - 1));
|
|
CONSUME(b);
|
|
MOAR(16);
|
|
hold = undeflatesymbol(hold, ds->distcodes, tdist, &sym);
|
|
b = kDeflate.distbits[sym];
|
|
MOAR(b);
|
|
/* max readback: 24577 + 2**13-1 = 32768 */
|
|
si = op - ((uint32_t)kDeflate.distbase[sym] +
|
|
(uint32_t)(hold.word & ((1u << b) - 1)));
|
|
CONSUME(b);
|
|
repmovsb(&op, &si, l);
|
|
}
|
|
} while (sym != 256);
|
|
}
|
|
|
|
return 0;
|
|
}
|