#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; }