blob: 5530bd07d003f43c444940d600c9b41cae07a809 [file] [log] [blame]
#include "stdinc.h"
#include "whack.h"
enum
{
DMaxFastLen = 7,
DBigLenCode = 0x3c, /* minimum code for large lenth encoding */
DBigLenBits = 6,
DBigLenBase = 1 /* starting items to encode for big lens */
};
static uchar lenval[1 << (DBigLenBits - 1)] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4,
5,
6,
255,
255
};
static uchar lenbits[] =
{
0, 0, 0,
2, 3, 5, 5,
};
static uchar offbits[16] =
{
5, 5, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 12, 13
};
static ushort offbase[16] =
{
0, 0x20,
0x40, 0x60,
0x80, 0xc0,
0x100, 0x180,
0x200, 0x300,
0x400, 0x600,
0x800, 0xc00,
0x1000,
0x2000
};
void
unwhackinit(Unwhack *uw)
{
uw->err[0] = '\0';
}
int
unwhack(Unwhack *uw, uchar *dst, int ndst, uchar *src, int nsrc)
{
uchar *s, *d, *dmax, *smax, lit;
ulong uwbits, lithist;
int i, off, len, bits, use, code, uwnbits, overbits;
d = dst;
dmax = d + ndst;
smax = src + nsrc;
uwnbits = 0;
uwbits = 0;
overbits = 0;
lithist = ~0;
while(src < smax || uwnbits - overbits >= MinDecode){
while(uwnbits <= 24){
uwbits <<= 8;
if(src < smax)
uwbits |= *src++;
else
overbits += 8;
uwnbits += 8;
}
/*
* literal
*/
len = lenval[(uwbits >> (uwnbits - 5)) & 0x1f];
if(len == 0){
if(lithist & 0xf){
uwnbits -= 9;
lit = (uwbits >> uwnbits) & 0xff;
lit &= 255;
}else{
uwnbits -= 8;
lit = (uwbits >> uwnbits) & 0x7f;
if(lit < 32){
if(lit < 24){
uwnbits -= 2;
lit = (lit << 2) | ((uwbits >> uwnbits) & 3);
}else{
uwnbits -= 3;
lit = (lit << 3) | ((uwbits >> uwnbits) & 7);
}
lit = (lit - 64) & 0xff;
}
}
if(d >= dmax){
snprint(uw->err, WhackErrLen, "too much output");
return -1;
}
*d++ = lit;
lithist = (lithist << 1) | (lit < 32) | (lit > 127);
continue;
}
/*
* length
*/
if(len < 255)
uwnbits -= lenbits[len];
else{
uwnbits -= DBigLenBits;
code = ((uwbits >> uwnbits) & ((1 << DBigLenBits) - 1)) - DBigLenCode;
len = DMaxFastLen;
use = DBigLenBase;
bits = (DBigLenBits & 1) ^ 1;
while(code >= use){
len += use;
code -= use;
code <<= 1;
uwnbits--;
if(uwnbits < 0){
snprint(uw->err, WhackErrLen, "len out of range");
return -1;
}
code |= (uwbits >> uwnbits) & 1;
use <<= bits;
bits ^= 1;
}
len += code;
while(uwnbits <= 24){
uwbits <<= 8;
if(src < smax)
uwbits |= *src++;
else
overbits += 8;
uwnbits += 8;
}
}
/*
* offset
*/
uwnbits -= 4;
bits = (uwbits >> uwnbits) & 0xf;
off = offbase[bits];
bits = offbits[bits];
uwnbits -= bits;
off |= (uwbits >> uwnbits) & ((1 << bits) - 1);
off++;
if(off > d - dst){
snprint(uw->err, WhackErrLen, "offset out of range: off=%d d=%ld len=%d nbits=%d", off, d - dst, len, uwnbits);
return -1;
}
if(d + len > dmax){
snprint(uw->err, WhackErrLen, "len out of range");
return -1;
}
s = d - off;
for(i = 0; i < len; i++)
d[i] = s[i];
d += len;
}
if(uwnbits < overbits){
snprint(uw->err, WhackErrLen, "compressed data overrun");
return -1;
}
len = d - dst;
return len;
}