#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "thwack.h"
typedef struct HuffDec HuffDec;
struct HuffDec
{
ulong maxcode[MaxLen];
ulong last[MaxLen];
ulong decode[MaxLen];
};
static HuffDec lentab =
{
/* 0 1 2 3 4 5 6 */
{0, 0, 0x2, 0, 0xe, 0x1e, 0x3f},
{-1, 0+0, 0x2+1, -1, 0xe+2, 0x1e+5, 0x3f+6},
{
0,
1,
7, 3, 2,
4,
6, 5
},
};
static ulong bitget(Unthwack *ut, int nb);
static ulong iomegaget(Unthwack *ut);
void
unthwackinit(Unthwack *ut)
{
int i;
memset(ut, 0, sizeof *ut);
for(i = 0; i < EWinBlocks; i++)
ut->blocks[i].data = ut->data[i];
}
/*
* to speed up, inline bitget
*/
int
unthwack(Unthwack *ut, uchar *dst, int ndst, uchar *src, int nsrc, ulong seq)
{
UnthwBlock blocks[CompBlocks], *b, *eblocks;
uchar *s, *es, *d, *dmax;
ulong cmask, cseq, bseq, utbits, utnbits;
int off, len, bits, slot, tslot;
if(nsrc < 4 || nsrc > ThwMaxBlock || waserror())
return -1;
ut->src = src + 2;
ut->smax = src + nsrc;
/*
* find the correct slot for this block,
* the oldest block around. the encoder
* doesn't use a history at wraparound,
* so don't worry about that case.
*/
tslot = ut->slot;
for(;;){
slot = tslot - 1;
if(slot < 0)
slot += DWinBlocks;
if(ut->blocks[slot].seq <= seq)
break;
ut->blocks[slot] = ut->blocks[slot];
tslot = slot;
}
b = blocks;
ut->blocks[tslot].seq = seq;
ut->blocks[tslot].maxoff = 0;
*b = ut->blocks[tslot];
d = b->data;
dmax = d + ndst;
/*
* set up the history blocks
*/
cseq = seq - src[0];
cmask = src[1];
b++;
slot = tslot;
while(cseq != seq && b < blocks + CompBlocks){
slot--;
if(slot < 0)
slot += DWinBlocks;
if(slot == ut->slot)
break;
bseq = ut->blocks[slot].seq;
if(bseq == cseq){
*b = ut->blocks[slot];
b++;
if(cmask == 0){
cseq = seq;
break;
}
do{
bits = cmask & 1;
cseq--;
cmask >>= 1;
}while(!bits);
}
}
eblocks = b;
if(cseq != seq){
print("blocks not in decompression window: cseq=%d seq=%d cmask=%ux nb=%d\n", cseq, seq, cmask, eblocks - blocks);
error("unthwack bad window");
}
utnbits = 0;
utbits = 0;
while(ut->src < ut->smax || ut->nbits >= MinDecode){
while(utnbits < 9){
if(ut->src >= ut->smax)
error("unthwack eof");
utbits <<= 8;
utbits |= *ut->src++;
utnbits += 8;
}
utnbits -= 9;
off = (utbits >> utnbits) & ((1 << 9) - 1);
bits = off >> 5;
if(bits >= MaxOff){
*d++ = off;
blocks->maxoff++;
continue;
}
off &= (1 << 5) - 1;
if(bits){
bits--;
off |= 1 << 5;
}
bits += OffBase - 5;
off <<= bits;
while(utnbits < bits){
if(ut->src >= ut->smax)
error("unthwack eof");
utbits <<= 8;
utbits |= *ut->src++;
utnbits += 8;
}
utnbits -= bits;
off |= (utbits >> utnbits) & ((1 << bits) - 1);
off++;
len = 0;
bits = 0;
do{
len <<= 1;
if(utnbits < 1){
if(ut->src >= ut->smax)
error("unthwack eof");
utbits <<= 8;
utbits |= *ut->src++;
utnbits += 8;
}
utnbits--;
len |= (utbits >> utnbits) & 1;
bits++;
}while(len > lentab.maxcode[bits]);
len = lentab.decode[lentab.last[bits] - len];
if(len == MaxLen - 1){
ut->nbits = utnbits;
ut->bits = utbits;
len += iomegaget(ut) - 1;
utnbits = ut->nbits;
utbits = ut->bits;
}
len += MinMatch;
b = blocks;
while(off > b->maxoff){
off -= b->maxoff;
b++;
if(b >= eblocks)
error("unthwack offset");
}
if(d + len > dmax
|| b != blocks && len > off)
error("unthwack len");
s = b->data + b->maxoff - off;
es = s + len;
while(s < es)
*d++ = *s++;
blocks->maxoff += len;
}
len = d - blocks->data;
memmove(dst, blocks->data, len);
ut->blocks[tslot].maxoff = len;
ut->slot++;
if(ut->slot >= DWinBlocks)
ut->slot = 0;
poperror();
return len;
}
/*
* elias's omega code, modified
* for at least 3 bit transmission
*/
static ulong
iomegaget(Unthwack *ut)
{
ulong v;
int b;
v = bitget(ut, 3);
if((v & 0x4) == 0)
return v + 1;
for(;;){
b = bitget(ut, 1);
if(b == 0)
return v + 1;
if(v > 16)
break;
v--;
v = (b << v) | bitget(ut, v);
}
error("unthwack iomegaget");
return ~0;
}
static ulong
bitget(Unthwack *ut, int nb)
{
int c;
while(ut->nbits < nb){
if(ut->src >= ut->smax)
error("unthwack eof");
c = *ut->src++;
ut->bits <<= 8;
ut->bits |= c;
ut->nbits += 8;
}
ut->nbits -= nb;
return (ut->bits >> ut->nbits) & ((1 << nb) - 1);
}