Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

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