From 082d35c32005b720f53af03a668e873c7381b6d7 Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Fri, 22 Oct 1999 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 1999-10-22 --- pc/mem.h | 4 +- port/cache.c | 8 ++ port/devsdp.c | 270 ++++++++++++++++++++++++++++++++++++++++-------- port/portdat.h | 2 +- port/thwack.c | 227 ++++++++++++++++++++++++++-------------- port/thwack.h | 14 +-- port/unthwack.c | 52 +++++++--- 7 files changed, 432 insertions(+), 145 deletions(-) diff --git a/pc/mem.h b/pc/mem.h index 68cba34ae6c9710b5d8b61a610496c3a6781598f..57d031980faa2a9e95702c9624e7feb7637dbec2 100644 --- a/pc/mem.h +++ b/pc/mem.h @@ -111,10 +111,10 @@ #define PTEVALID (1<<0) #define PTEWT (1<<3) #define PTEUNCACHED (1<<4) -#define PTEWRITE (1<<1) +#define PTEWRITE (1<<1) #define PTERONLY (0<<1) #define PTEKERNEL (0<<2) #define PTEUSER (1<<2) -#define PTESIZE (1<<7) +#define PTESIZE (1<<7) #define getpgcolor(a) 0 diff --git a/port/cache.c b/port/cache.c index 2754f11a1b2f8456ad5480330f4d5765d9e05bd1..d7d3cf3de0ffdda20c10ffefc26ade639a9e083b 100644 --- a/port/cache.c +++ b/port/cache.c @@ -396,6 +396,14 @@ cchain(uchar *buf, ulong offset, int len, Extent **tail) lock(&cache); e->bid = cache.pgno; cache.pgno += BY2PG; + /* wrap the counter; low bits are unused by pghash by checked by lookpage */ + if((cache.pgno & ~(BY2PG-1)) == 0){ + if(cache.pgno == BY2PG-1){ + print("cache wrapped\n"); + cache.pgno = 0; + }else + cache.pgno++; + } unlock(&cache); p->daddr = e->bid; diff --git a/port/devsdp.c b/port/devsdp.c index e50fccc699705a7ed02308cf57c5c3280925be4a..c4b363f3c8ac471c6ffabf5845053405783be06d 100644 --- a/port/devsdp.c +++ b/port/devsdp.c @@ -19,6 +19,7 @@ typedef struct OneWay OneWay; typedef struct Stats Stats; typedef struct AckPkt AckPkt; typedef struct Algorithm Algorithm; +typedef struct CipherRc4 CipherRc4; enum { @@ -70,6 +71,7 @@ struct Stats ulong inBadComp; ulong inBadAuth; ulong inBadSeq; + ulong inBadOther; }; struct OneWay @@ -141,9 +143,9 @@ struct Conv { char owner[NAMELEN]; /* protections */ int perm; - char *authname; - char *ciphername; - char *compname; + Algorithm *auth; + Algorithm *cipher; + Algorithm *comp; int drop; @@ -203,13 +205,30 @@ struct AckPkt uchar inBadComp[4]; uchar inBadAuth[4]; uchar inBadSeq[4]; + uchar inBadOther[4]; }; struct Algorithm { char *name; int keylen; // in bytes - void (*init)(Conv*, char* name, int keylen); + void (*init)(Conv*); +}; + +enum { + RC4forward = 10*1024*1024, // maximum skip forward + RC4back = 100*1024, // maximum look back +}; + +struct CipherRc4 +{ + ulong cseq; // current byte sequence number + RC4state current; + + int ovalid; // old is valid + ulong lgseq; // last good sequence + ulong oseq; // old byte sequence number + RC4state old; }; static Dirtab sdpdirtab[]={ @@ -279,23 +298,24 @@ static void convreader(void *a); static void convopenchan(Conv *c, char *path); static void convstats(Conv *c, int local, char *buf, int n); -static void setalg(Conv *c, char *name, Algorithm *tab); +static void setalg(Conv *c, char *name, Algorithm *tab, Algorithm **); static void setsecret(OneWay *cc, char *secret); -static void nullcipherinit(Conv*c, char *name, int keylen); -static void descipherinit(Conv*c, char *name, int keylen); -static void rc4cipherinit(Conv*c, char *name, int keylen); -static void nullauthinit(Conv*c, char *name, int keylen); -static void shaauthinit(Conv*c, char *name, int keylen); -static void md5authinit(Conv*c, char *name, int keylen); -static void nullcompinit(Conv*c, char *name, int keylen); -static void thwackcompinit(Conv*c, char *name, int keylen); +static void nullcipherinit(Conv*c); +static void descipherinit(Conv*c); +static void rc4cipherinit(Conv*c); +static void nullauthinit(Conv*c); +static void shaauthinit(Conv*c); +static void md5authinit(Conv*c); +static void nullcompinit(Conv*c); +static void thwackcompinit(Conv*c); static Algorithm cipheralg[] = { "null", 0, nullcipherinit, "des_56_cbc", 7, descipherinit, "rc4_128", 16, rc4cipherinit, + "rc4_256", 32, rc4cipherinit, nil, 0, nil, }; @@ -617,23 +637,31 @@ sdpwrite(Chan *ch, void *a, long n, vlong off) } else if(strcmp(arg0, "cipher") == 0) { if(cb->nf != 2) error("usage: cipher alg"); - setalg(c, cb->f[1], cipheralg); + setalg(c, cb->f[1], cipheralg, &c->cipher); } else if(strcmp(arg0, "auth") == 0) { if(cb->nf != 2) error("usage: auth alg"); - setalg(c, cb->f[1], authalg); + setalg(c, cb->f[1], authalg, &c->auth); } else if(strcmp(arg0, "comp") == 0) { if(cb->nf != 2) error("usage: comp alg"); - setalg(c, cb->f[1], compalg); + setalg(c, cb->f[1], compalg, &c->comp); } else if(strcmp(arg0, "insecret") == 0) { if(cb->nf != 2) error("usage: insecret secret"); setsecret(&c->in, cb->f[1]); + if(c->cipher) + c->cipher->init(c); + if(c->auth) + c->auth->init(c); } else if(strcmp(arg0, "outsecret") == 0) { if(cb->nf != 2) error("usage: outsecret secret"); setsecret(&c->out, cb->f[1]); + if(c->cipher) + c->cipher->init(c); + if(c->auth) + c->auth->init(c); } else error("unknown control request"); poperror(); @@ -934,7 +962,7 @@ print("convsetstate %s -> %s\n", convstatename[c->state], convstatename[state]); hnputl(c->out.secret, c->acceptid); hnputl(c->out.secret+4, c->dialid); } - md5authinit(c, "hmac_md5_96", 16); + setalg(c, "hmac_md5_96", authalg, &c->auth); break; case CLocalClose: assert(c->state == CAccept || c->state == COpen); @@ -961,9 +989,9 @@ print("CClosed -> ref = %d\n", c->ref); free(c->channame); c->channame = nil; } - c->ciphername = nil; - c->authname = nil; - c->compname = nil; + c->cipher = nil; + c->auth = nil; + c->comp = nil; strcpy(c->owner, "network"); c->perm = 0660; c->dialid = 0; @@ -1045,7 +1073,6 @@ convstats(Conv *c, int local, char *buf, int n) continue; p += snprint(p, ep-p, "outCompStats[%d]: %lud\n", i, stats->outCompStats[i]); } - p += snprint(p, ep-p, "outCompDataBytes: %lud\n", stats->outCompDataBytes); p += snprint(p, ep-p, "inPackets: %lud\n", stats->inPackets); p += snprint(p, ep-p, "inDataPackets: %lud\n", stats->inDataPackets); p += snprint(p, ep-p, "inDataBytes: %lud\n", stats->inDataBytes); @@ -1056,6 +1083,7 @@ convstats(Conv *c, int local, char *buf, int n) p += snprint(p, ep-p, "inBadComp: %lud\n", stats->inBadComp); p += snprint(p, ep-p, "inBadAuth: %lud\n", stats->inBadAuth); p += snprint(p, ep-p, "inBadSeq: %lud\n", stats->inBadSeq); + p += snprint(p, ep-p, "inBadOther: %lud\n", stats->inBadOther); USED(p); qunlock(c); } @@ -1090,6 +1118,7 @@ convack(Conv *c) hnputl(ack->inBadComp, s->inBadComp); hnputl(ack->inBadAuth, s->inBadAuth); hnputl(ack->inBadSeq, s->inBadSeq); + hnputl(ack->inBadOther, s->inBadOther); convoput(c, TControl, ControlAck, b); } @@ -1101,10 +1130,12 @@ conviput(Conv *c, Block *b, int control) int type, subtype; ulong seq, seqwrap; long seqdiff; + int pad; c->lstats.inPackets++; if(BLEN(b) < 4) { + c->lstats.inBadOther++; freeb(b); return nil; } @@ -1160,7 +1191,25 @@ print("bad auth\n"); b->wp -= c->in.authlen; } - // decrypt + if(c->in.cipher != 0) { + if(!(*c->in.cipher)(&c->in, b->rp, BLEN(b))) { +print("bad cipher\n"); + c->lstats.inBadOther++; + freeb(b); + return nil; + } + b->rp += c->in.cipherivlen; + if(c->in.cipherblklen > 1) { + pad = b->wp[-1]; + if(pad > BLEN(b)) { +print("pad too big\n"); + c->lstats.inBadOther++; + freeb(b); + return nil; + } + b->wp -= pad; + } + } // ok the packet is good if(seqdiff > 0) { @@ -1206,6 +1255,7 @@ print("missing packets: %ld-%ld\n", seq - SeqWindow - seqdiff+1, seq-SeqWindow); return b; } print("droping packet %d n=%ld\n", type, BLEN(b)); + c->lstats.inBadOther++; freeb(b); return nil; } @@ -1324,6 +1374,7 @@ convicontrol(Conv *c, int subtype, Block *b) { ulong cseq; AckPkt *ack; + int i; if(BLEN(b) < 4) return; @@ -1366,6 +1417,8 @@ print("ControlAck expected %ulx got %ulx\n", c->out.controlseq, cseq); c->rstats.outDataPackets = nhgetl(ack->outDataPackets); c->rstats.outDataBytes = nhgetl(ack->outDataBytes); c->rstats.outCompDataBytes = nhgetl(ack->outCompDataBytes); + for(i=0; irstats.outCompStats[i] = nhgetl(ack->outCompStats + 4*i); c->rstats.inPackets = nhgetl(ack->inPackets); c->rstats.inDataPackets = nhgetl(ack->inDataPackets); c->rstats.inDataBytes = nhgetl(ack->inDataBytes); @@ -1376,6 +1429,7 @@ print("ControlAck expected %ulx got %ulx\n", c->out.controlseq, cseq); c->rstats.inBadComp = nhgetl(ack->inBadComp); c->rstats.inBadAuth = nhgetl(ack->inBadAuth); c->rstats.inBadSeq = nhgetl(ack->inBadSeq); + c->rstats.inBadOther = nhgetl(ack->inBadOther); freeb(b); freeb(c->out.controlpkt); c->out.controlpkt = nil; @@ -1418,8 +1472,23 @@ convwriteblock(Conv *c, Block *b) static void convoput(Conv *c, int type, int subtype, Block *b) { - // try and compress + int pad; + c->lstats.outPackets++; + /* Make room for sdp trailer */ + if(c->out.cipherblklen > 1) + pad = c->out.cipherblklen - (BLEN(b) + c->out.cipherivlen) % c->out.cipherblklen; + else + pad = 0; + + b = padblock(b, -(pad+c->out.authlen)); + + if(pad) { + memset(b->wp, 0, pad-1); + b->wp[pad-1] = pad; + b->wp += pad; + } + /* Make space to fit sdp header */ b = padblock(b, 4 + c->out.cipherivlen); b->rp[0] = (type << 4) | subtype; @@ -1432,10 +1501,11 @@ convoput(Conv *c, int type, int subtype, Block *b) b->rp[2] = c->out.seq>>8; b->rp[3] = c->out.seq; - // encrypt + if(c->out.cipher) + (*c->out.cipher)(&c->out, b->rp+4, BLEN(b)-4); + // auth if(c->out.auth) { - b = padblock(b, -c->out.authlen); b->wp += c->out.authlen; (*c->out.auth)(&c->out, b->rp, BLEN(b)); } @@ -1710,7 +1780,7 @@ print("convreader exiting\n"); /* ciphers, authenticators, and compressors */ static void -setalg(Conv *c, char *name, Algorithm *alg) +setalg(Conv *c, char *name, Algorithm *alg, Algorithm **p) { for(; alg->name; alg++) if(strcmp(name, alg->name) == 0) @@ -1718,7 +1788,8 @@ setalg(Conv *c, char *name, Algorithm *alg) if(alg->name == nil) error("unknown algorithm"); - alg->init(c, alg->name, alg->keylen); + *p = alg; + alg->init(c); } static void @@ -1771,7 +1842,6 @@ setkey(uchar *key, int n, OneWay *ow, char *prefix) static void cipherfree(Conv *c) { - c->ciphername = nil; if(c->in.cipherstate) { free(c->in.cipherstate); c->in.cipherstate = nil; @@ -1781,12 +1851,15 @@ cipherfree(Conv *c) c->out.cipherstate = nil; } c->in.cipher = nil; + c->in.cipherblklen = 0; + c->out.cipherblklen = 0; + c->in.cipherivlen = 0; + c->out.cipherivlen = 0; } static void authfree(Conv *c) { - c->authname = nil; if(c->in.authstate) { free(c->in.authstate); c->in.authstate = nil; @@ -1796,12 +1869,13 @@ authfree(Conv *c) c->out.authstate = nil; } c->in.auth = nil; + c->in.authlen = 0; + c->out.authlen = 0; } static void compfree(Conv *c) { - c->compname = nil; if(c->in.compstate) { free(c->in.compstate); c->in.compstate = nil; @@ -1814,7 +1888,7 @@ compfree(Conv *c) } static void -nullcipherinit(Conv *c, char *, int) +nullcipherinit(Conv *c) { cipherfree(c); } @@ -1825,6 +1899,8 @@ desencrypt(OneWay *ow, uchar *p, int n) uchar *pp, *ip, *eip, *ep; DESstate *ds = ow->cipherstate; + if(n < 8 || (n & 0x7 != 0)) + return 0; ep = p + n; memmove(p, ds->ivec, 8); for(p += 8; p < ep; p += 8){ @@ -1845,6 +1921,8 @@ desdecrypt(OneWay *ow, uchar *p, int n) uchar *tp, *ip, *eip, *ep; DESstate *ds = ow->cipherstate; + if(n < 8 || (n & 0x7 != 0)) + return 0; ep = p + n; memmove(ds->ivec, p, 8); p += 8; @@ -1862,14 +1940,14 @@ desdecrypt(OneWay *ow, uchar *p, int n) } static void -descipherinit(Conv *c, char *name, int n) +descipherinit(Conv *c) { uchar key[8]; uchar ivec[8]; int i; + int n = c->cipher->keylen; cipherfree(c); - c->ciphername = name; if(n > sizeof(key)) n = sizeof(key); @@ -1896,20 +1974,126 @@ descipherinit(Conv *c, char *name, int n) setupDESstate(c->out.cipherstate, key, ivec); } +static int +rc4encrypt(OneWay *ow, uchar *p, int n) +{ + CipherRc4 *cr = ow->cipherstate; + + if(n < 4) + return 0; + + hnputl(p, cr->cseq); + p += 4; + n -= 4; + rc4(&cr->current, p, n); + cr->cseq += n; + return 1; +} + +static int +rc4decrypt(OneWay *ow, uchar *p, int n) +{ + CipherRc4 *cr = ow->cipherstate; + RC4state tmpstate; + ulong seq; + long d, dd; + + if(n < 4) + return 0; + + seq = nhgetl(p); + p += 4; + n -= 4; + d = seq-cr->cseq; + if(d == 0) { + rc4(&cr->current, p, n); + cr->cseq += n; + if(cr->ovalid) { + dd = cr->cseq - cr->lgseq; + if(dd > RC4back) + cr->ovalid = 0; + } + } else if(d > 0) { +print("missing packet: %uld %ld\n", seq, d); + // this link is hosed + if(d > RC4forward) + return 0; + cr->lgseq = seq; + if(!cr->ovalid) { + cr->ovalid = 1; + cr->oseq = cr->cseq; + memmove(&cr->old, &cr->current, sizeof(RC4state)); + } + rc4skip(&cr->current, d); + rc4(&cr->current, p, n); + cr->cseq = seq+n; + } else { +print("reordered packet: %uld %ld\n", seq, d); + dd = seq - cr->oseq; + if(!cr->ovalid || -d > RC4back || dd < 0) + return 0; + memmove(&tmpstate, &cr->old, sizeof(RC4state)); + rc4skip(&tmpstate, dd); + rc4(&tmpstate, p, n); + return 1; + } + + // move old state up + if(cr->ovalid) { + dd = cr->cseq - RC4back - cr->oseq; + if(dd > 0) { + rc4skip(&cr->old, dd); + cr->oseq += dd; + } + } + + return 1; +} + static void -rc4cipherinit(Conv *c, char *name, int keylen) +rc4cipherinit(Conv *c) { + uchar key[32]; + CipherRc4 *cr; + int n; + cipherfree(c); + + n = c->cipher->keylen; + if(n > sizeof(key)) + n = sizeof(key); + + /* in */ + memset(key, 0, sizeof(key)); + setkey(key, n, &c->in, "cipher"); + c->in.cipherblklen = 1; + c->in.cipherivlen = 4; + c->in.cipher = rc4decrypt; + cr = smalloc(sizeof(CipherRc4)); + memset(cr, 0, sizeof(*cr)); + setupRC4state(&cr->current, key, n); + c->in.cipherstate = cr; + + /* out */ + memset(key, 0, sizeof(key)); + setkey(key, n, &c->out, "cipher"); + c->out.cipherblklen = 1; + c->out.cipherivlen = 4; + c->out.cipher = rc4encrypt; + cr = smalloc(sizeof(CipherRc4)); + memset(cr, 0, sizeof(*cr)); + setupRC4state(&cr->current, key, n); + c->out.cipherstate = cr; } static void -nullauthinit(Conv *c, char *name, int keylen) +nullauthinit(Conv *c) { authfree(c); } static void -shaauthinit(Conv *c, char *name, int keylen) +shaauthinit(Conv *c) { authfree(c); } @@ -1957,12 +2141,13 @@ md5auth(OneWay *ow, uchar *t, int tlen) } static void -md5authinit(Conv *c, char *name, int keylen) +md5authinit(Conv *c) { - authfree(c); + int keylen; - c->authname = name; + authfree(c); + keylen = c->auth->keylen; if(keylen > 16) keylen = 16; @@ -1982,7 +2167,7 @@ md5authinit(Conv *c, char *name, int keylen) } static void -nullcompinit(Conv *c, char*, int) +nullcompinit(Conv *c) { compfree(c); } @@ -2001,7 +2186,7 @@ thwackcomp(Conv *c, int, ulong seq, Block **bp) b->rp[3] = c->in.seq; bb = allocb(BLEN(b)); - nn = thwack(c->out.compstate, bb->wp, b->rp, BLEN(b), seq); + nn = thwack(c->out.compstate, bb->wp, b->rp, BLEN(b), seq, c->lstats.outCompStats); if(nn < 0) { freeb(bb); *bp = b; @@ -2053,11 +2238,10 @@ print("unthwack failed: %r!\n"); } static void -thwackcompinit(Conv *c, char *name, int keylen) +thwackcompinit(Conv *c) { compfree(c); - c->compname = name; c->in.compstate = malloc(sizeof(Unthwack)); unthwackinit(c->in.compstate); c->out.compstate = malloc(sizeof(Thwack)); diff --git a/port/portdat.h b/port/portdat.h index ef3afe6cccafa4eaebe3d4ab3b79112622f28fd4..56aee143f5e2ca68eabda0d9a076b48fb309fd51 100644 --- a/port/portdat.h +++ b/port/portdat.h @@ -312,7 +312,7 @@ struct Swapalloc struct Image { Ref; - Chan *c; /* channl to text file */ + Chan *c; /* channel to text file */ Qid qid; /* Qid for page cache coherence */ Qid mqid; Chan *mchan; diff --git a/port/thwack.c b/port/thwack.c index 694870cc23b6b95277a3a381ee0b6d82fc0f8333..70f6495aaa392346d739456679a11feba4b8a707 100644 --- a/port/thwack.c +++ b/port/thwack.c @@ -1,5 +1,5 @@ #include "u.h" -#include "lib.h" +#include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" @@ -7,6 +7,23 @@ #include "thwack.h" typedef struct Huff Huff; + +enum +{ + StatBytes, + StatOutBytes, + StatLits, + StatMatches, + StatLitBits, + StatOffBits, + StatLenBits, + + StatProbe, + StatProbeMiss, + + MaxStat +}; + struct Huff { short bits; /* length of the code */ @@ -26,9 +43,6 @@ static Huff lentab[MaxFastLen] = {7, 0x79}, /* 1111001 */ }; -static void bitput(Thwack *tw, int c, int n); -static int iomegaput(Thwack *tw, ulong v); - void thwackinit(Thwack *tw) { @@ -81,7 +95,7 @@ thwackack(Thwack *tw, ulong seq, ulong mask) static int thwmatch(ThwBlock *b, ThwBlock *eblocks, uchar **ss, uchar *esrc, ulong h) { - int then, toff, w; + int then, toff, w, ok; uchar *s, *t; s = *ss; @@ -90,13 +104,14 @@ thwmatch(ThwBlock *b, ThwBlock *eblocks, uchar **ss, uchar *esrc, ulong h) toff = 0; for(; b < eblocks; b++){ - then = b->hash[h]; + then = b->hash[(h ^ b->seq) & HashMask]; toff += b->maxoff; w = (ushort)(then - b->begin); if(w >= b->maxoff) continue; + /* * don't need to check for the end because * 1) s too close check above @@ -105,8 +120,9 @@ thwmatch(ThwBlock *b, ThwBlock *eblocks, uchar **ss, uchar *esrc, ulong h) t = w + b->data; if(s[0] != t[0] || s[1] != t[1] || s[2] != t[2]) continue; - if(esrc - s > b->edata - t) - esrc = s + (b->edata - t); + ok = b->edata - t; + if(esrc - s > ok) + esrc = s + ok; t += 3; for(s += 3; s < esrc; s++){ @@ -129,27 +145,27 @@ thwmatch(ThwBlock *b, ThwBlock *eblocks, uchar **ss, uchar *esrc, ulong h) * the 3 byte value appears to be as almost good as the 4 byte value, * and might be faster on some machines */ -//#define hashit(c) (((((ulong)(c) & 0xffffff) * 0x6b43a9b5) >> (32 - HashLog)) & HashMask) -#define hashit(c) ((((ulong)(c) * 0x6b43a9) >> (24 - HashLog)) & HashMask) +/* +#define hashit(c) (((ulong)(c) * 0x6b43a9) >> (24 - HashLog)) +*/ +#define hashit(c) ((((ulong)(c) & 0xffffff) * 0x6b43a9b5) >> (32 - HashLog)) /* * lz77 compression with single lookup in a hash table for each block */ int -thwack(Thwack *tw, uchar *dst, uchar *src, int n, ulong seq) +thwack(Thwack *tw, uchar *dst, uchar *src, int n, ulong seq, ulong stats[ThwStats]) { ThwBlock *eblocks, *b, blocks[CompBlocks]; - uchar *s, *ss, *sss, *esrc, *half; - ulong cont, cseq, bseq, cmask, code; - int now, toff; - int h, m, slot, bits, use, totmatched; + uchar *s, *ss, *sss, *esrc, *half, *twdst, *twdmax; + ulong cont, cseq, bseq, cmask, code, twbits; + int now, toff, lithist, h, len, slot, bits, use, twnbits, lits, matches, offbits, lenbits; - if(n > ThwMaxBlock || n < MinMatch || waserror()) + if(n > ThwMaxBlock || n < MinMatch) return -1; - tw->dst = dst; - tw->dmax = dst + n; - tw->nbits = 0; + twdst = dst; + twdmax = dst + n; /* * add source to the coding window @@ -196,13 +212,20 @@ thwack(Thwack *tw, uchar *dst, uchar *src, int n, ulong seq) b++; } eblocks = b; - bitput(tw, ((seq - cseq) << MaxSeqMask) | cmask, 16); + *twdst++ = seq - cseq; + *twdst++ = cmask; - cont = (s[0] << 16) | (s[2] << 8) | s[2]; + cont = (s[0] << 16) | (s[1] << 8) | s[2]; - totmatched = 0; esrc = s + n; half = s + (n >> 1); + twnbits = 0; + twbits = 0; + lits = 0; + matches = 0; + offbits = 0; + lenbits = 0; + lithist = ~0; while(s < esrc){ h = hashit(cont); @@ -210,53 +233,102 @@ thwack(Thwack *tw, uchar *dst, uchar *src, int n, ulong seq) toff = thwmatch(blocks, eblocks, &sss, esrc, h); ss = sss; - m = ss - s; - if(m < MinMatch){ - bitput(tw, 0x100|*s, 9); - ss = s + 1; - }else{ - totmatched += m; - - toff--; - for(bits = OffBase; toff >= (1 << bits); bits++) - ; - if(bits >= MaxOff+OffBase) - error("thwack offset"); - bitput(tw, bits - OffBase, 4); - if(bits != OffBase) - bits--; - bitput(tw, toff & ((1 << bits) - 1), bits); - - m -= MinMatch; - if(m < MaxFastLen){ - bitput(tw, lentab[m].encode, lentab[m].bits); - }else{ - code = BigLenCode; - bits = BigLenBits; - use = BigLenBase; - m -= MaxFastLen; - while(m >= use){ - m -= use; - code = (code + use) << 1; - use <<= bits & 1; - bits++; + len = ss - s; + for(; twnbits >= 8; twnbits -= 8){ + if(twdst >= twdmax) + return -1; + *twdst++ = twbits >> (twnbits - 8); + } + if(len < MinMatch){ + toff = *s; + lithist = (lithist << 1) | toff < 32 | toff > 127; + if(lithist & 0x1e){ + twbits = (twbits << 9) | toff; + twnbits += 9; + }else if(lithist & 1){ + toff = (toff + 64) & 0xff; + if(toff < 96){ + twbits = (twbits << 10) | toff; + twnbits += 10; + }else{ + twbits = (twbits << 11) | toff; + twnbits += 11; } - bitput(tw, (code + m), bits); + }else{ + twbits = (twbits << 8) | toff; + twnbits += 8; + } + lits++; + blocks->maxoff++; + + /* + * speed hack + * check for compression progress, bail if none achieved + */ + if(s > half){ + if(4 * blocks->maxoff < 5 * lits) + return -1; + half = esrc; + } + + if(s + MinMatch <= esrc){ + blocks->hash[(h ^ blocks->seq) & HashMask] = now; + if(s + MinMatch < esrc) + cont = (cont << 8) | s[MinMatch]; } + now++; + s++; + continue; } - blocks->maxoff += ss - s; - /* - * speed hack - * check for compression progress, bail if none achieved - */ - if(s < half && ss >= half && totmatched * 10 < n) - error("thwack likely expanding"); + blocks->maxoff += len; + matches++; + + toff--; + for(bits = OffBase; toff >= (1 << bits); bits++) + ; + if(bits >= MaxOff+OffBase) + panic("thwack offset"); + twbits = (twbits << 4) | 0x8 | (bits - OffBase); + if(bits != OffBase) + bits--; + twbits = (twbits << bits) | toff & ((1 << bits) - 1); + twnbits += bits + 4; + offbits += bits + 4; + + len -= MinMatch; + if(len < MaxFastLen){ + bits = lentab[len].bits; + twbits = (twbits << bits) | lentab[len].encode; + twnbits += bits; + lenbits += bits; + }else{ + for(; twnbits >= 8; twnbits -= 8){ + if(twdst >= twdmax) + return -1; + *twdst++ = twbits >> (twnbits - 8); + } + code = BigLenCode; + bits = BigLenBits; + use = BigLenBase; + len -= MaxFastLen; + while(len >= use){ + len -= use; + code = (code + use) << 1; + use <<= bits & 1; + bits++; + } + if(bits > MaxLenDecode + BigLenBits) + panic("length too big"); + twbits = (twbits << bits) | (code + len); + twnbits += bits; + lenbits += bits; + } for(; s != ss; s++){ if(s + MinMatch <= esrc){ h = hashit(cont); - blocks->hash[h] = now; + blocks->hash[(h ^ blocks->seq) & HashMask] = now; if(s + MinMatch < esrc) cont = (cont << 8) | s[MinMatch]; } @@ -264,23 +336,28 @@ thwack(Thwack *tw, uchar *dst, uchar *src, int n, ulong seq) } } - if(tw->nbits) - bitput(tw, 0, 8 - tw->nbits); + stats[StatBytes] += blocks->maxoff; + stats[StatLits] += lits; + stats[StatMatches] += matches; + stats[StatLitBits] += (twdst - (dst + 2)) * 8 + twnbits - offbits - lenbits; + stats[StatOffBits] += offbits; + stats[StatLenBits] += lenbits; + + if(twnbits & 7){ + twbits <<= 8 - (twnbits & 7); + twnbits += 8 - (twnbits & 7); + } + for(; twnbits >= 8; twnbits -= 8){ + if(twdst >= twdmax) + return -1; + *twdst++ = twbits >> (twnbits - 8); + } + tw->slot++; if(tw->slot >= EWinBlocks) tw->slot = 0; - poperror(); - return tw->dst - dst; -} + stats[StatOutBytes] += twdst - dst; -static void -bitput(Thwack *tw, int c, int n) -{ - tw->bits = (tw->bits << n) | c; - for(tw->nbits += n; tw->nbits >= 8; tw->nbits -= 8){ - if(tw->dst >= tw->dmax) - error("thwack expanding"); - *tw->dst++ = tw->bits >> (tw->nbits - 8); - } + return twdst - dst; } diff --git a/port/thwack.h b/port/thwack.h index 255205837305000a7218aa298893421e8783f853..735b586d323433c323fea706b18c83232b858e7e 100644 --- a/port/thwack.h +++ b/port/thwack.h @@ -5,10 +5,11 @@ typedef struct UnthwBlock UnthwBlock; enum { + ThwStats = 8, ThwMaxBlock = 1600, /* max size of compressible block */ MinMatch = 3, /* shortest match possible */ - HashLog = 10, + HashLog = 12, HashSize = 1<= 8 */ MaxOffDecode = 4 + MaxOff + OffBase - 1, MaxLenDecode = 16, @@ -28,7 +29,7 @@ enum CompBlocks = 5, /* max blocks used to encode data */ MaxSeqMask = 8, /* number of bits in coding block mask */ - MaxSeqStart = 256, /* max offset of initial coding block */ + MaxSeqStart = 256 /* max offset of initial coding block */ }; struct ThwBlock @@ -44,11 +45,6 @@ struct ThwBlock struct Thwack { - ulong nbits; /* output bit buffer */ - ulong bits; - uchar *dst; /* output buffer */ - uchar *dmax; - int slot; /* next block to use */ ThwBlock blocks[EWinBlocks]; ushort hash[EWinBlocks][HashSize]; @@ -71,6 +67,6 @@ struct Unthwack void thwackinit(Thwack*); void unthwackinit(Unthwack*); -int thwack(Thwack*, uchar *dst, uchar *src, int nsrc, ulong seq); +int thwack(Thwack*, uchar *dst, uchar *src, int nsrc, ulong seq, ulong stats[ThwStats]); void thwackack(Thwack*, ulong seq, ulong mask); int unthwack(Unthwack*, uchar *dst, int ndst, uchar *src, int nsrc, ulong seq); diff --git a/port/unthwack.c b/port/unthwack.c index 6bf3d3be6b7886bc80f03f30ae0c59663b699736..651aad8c998d2c33e5b454fc3bfc54a017fb7834 100644 --- a/port/unthwack.c +++ b/port/unthwack.c @@ -1,5 +1,5 @@ #include "u.h" -#include "lib.h" +#include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" @@ -44,8 +44,8 @@ int unthwack(Unthwack *ut, uchar *dst, int ndst, uchar *src, int nsrc, ulong seq) { UnthwBlock blocks[CompBlocks], *b, *eblocks; - uchar *s, *es, *d, *dmax, *smax; - ulong cmask, cseq, bseq, utbits; + uchar *s, *es, *d, *dmax, *smax, lit; + ulong cmask, cseq, bseq, utbits, lithist; int off, len, bits, slot, tslot, use, code, utnbits, overbits; if(nsrc < 4 || nsrc > ThwMaxBlock) @@ -113,8 +113,9 @@ unthwack(Unthwack *ut, uchar *dst, int ndst, uchar *src, int nsrc, ulong seq) utnbits = 0; utbits = 0; overbits = 0; + lithist = ~0; while(src < smax || utnbits - overbits >= MinDecode){ - while(utnbits < MaxOffDecode + BigLenBits){ + while(utnbits <= 24){ utbits <<= 8; if(src < smax) utbits |= *src++; @@ -122,26 +123,47 @@ unthwack(Unthwack *ut, uchar *dst, int ndst, uchar *src, int nsrc, ulong seq) overbits += 8; utnbits += 8; } - utnbits -= 9; - off = (utbits >> utnbits) & ((1 << 9) - 1); /* * literal */ - bits = off >> 5; - if(bits >= MaxOff){ - *d++ = off; + if(((utbits >> (utnbits - 1)) & 1) == 0){ + if(lithist & 0xf){ + utnbits -= 9; + lit = (utbits >> utnbits) & 0xff; + lit &= 255; + }else{ + utnbits -= 8; + lit = (utbits >> utnbits) & 0x7f; + if(lit < 32){ + if(lit < 24){ + utnbits -= 2; + lit = (lit << 2) | ((utbits >> utnbits) & 3); + }else{ + utnbits -= 3; + lit = (lit << 3) | ((utbits >> utnbits) & 7); + } + lit = (lit - 64) & 0xff; + } + } + *d++ = lit; + lithist = (lithist << 1) | lit < 32 | lit > 127; blocks->maxoff++; continue; } - off &= (1 << 5) - 1; + + /* + * match; next 3 bits decode offset range + */ + utnbits -= 4; + bits = (utbits >> utnbits) & ((1 << 3) - 1); if(bits){ - bits--; - off |= 1 << 5; + bits += OffBase - 1; + off = 1 << bits; + }else{ + bits = OffBase; + off = 0; } - bits += OffBase - 5; - off <<= bits; - utnbits -= bits; off |= (utbits >> utnbits) & ((1 << bits) - 1); off++;