From 50f18502fc15527f7b2bbc8933d9085db8ba8046 Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Fri, 1 Oct 1999 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 1999-10-01 --- pc/devarch.c | 1 + port/devsdp.c | 224 +++++++++++++++++++++++++++++---- port/thwack.c | 325 ++++++++++++++++++++++++++++++++++++++++++++++++ port/thwack.h | 76 +++++++++++ port/unthwack.c | 253 +++++++++++++++++++++++++++++++++++++ 5 files changed, 857 insertions(+), 22 deletions(-) create mode 100644 port/thwack.c create mode 100644 port/thwack.h create mode 100644 port/unthwack.c diff --git a/pc/devarch.c b/pc/devarch.c index aea6f0ba6c3a9836be0008490418dc4c88e3ac3f..de92d21a6e14e9b472c231c2be80f4a195186132 100644 --- a/pc/devarch.c +++ b/pc/devarch.c @@ -409,6 +409,7 @@ static X86type x86intel[] = { 6, 1, 16, "PentiumPro", },/* determined by trial and error */ { 6, 3, 16, "PentiumII", }, { 6, 5, 16, "PentiumII/Xeon", }, + { 6, 7, 16, "PentiumIII/Xeon", }, { 3, -1, 32, "386", }, /* family defaults */ { 4, -1, 22, "486", }, diff --git a/port/devsdp.c b/port/devsdp.c index 56a8a078f527551c7605014533ec51259bf1ef65..e3d258b389d921d100300bcd0103adfa2f9dce6d 100644 --- a/port/devsdp.c +++ b/port/devsdp.c @@ -7,6 +7,7 @@ #include "../port/error.h" #include +#include "../port/thwack.h" /* * sdp - secure datagram protocol @@ -18,6 +19,7 @@ typedef struct OneWay OneWay; typedef struct Stats Stats; typedef struct ConnectPkt ConnectPkt; typedef struct AckPkt AckPkt; +typedef struct Algorithm Algorithm; enum { @@ -39,9 +41,11 @@ enum Maxconv= 256, // power of 2 Nfs= 4, // number of file systems - MaxRetries= 4, + MaxRetries= 8, KeepAlive = 60, // keep alive in seconds KeyLength= 32, + SeqMax = (1<<24), + SeqWindow = 32, }; #define TYPE(x) ((x).path & 0xff) @@ -115,7 +119,8 @@ struct Conv { Stats lstats; Stats rstats; - + + ulong lastrecv; // time last packet was received ulong timeout; int retries; @@ -133,6 +138,9 @@ struct Conv { int perm; uchar masterkey[KeyLength]; + char *authname; + char *ciphername; + char *compname; int drop; @@ -193,6 +201,12 @@ struct AckPkt uchar inBadSeq[4]; }; +struct Algorithm +{ + char *name; + int keylen; // in bytes + void (*init)(Conv*, char* name, int keylen); +}; static Dirtab sdpdirtab[]={ "log", {Qlog}, 0, 0666, @@ -208,6 +222,31 @@ static Dirtab convdirtab[]={ "rstats", {Qrstats}, 0, 0444, }; +#ifdef XXX +static Algorithm cipheralg[] = +{ + "null", 0, nullcipherinit, + "des_56_cbc", 7, descipherinit, + "rc4_128", 16, rc4cipherinit, + nil, 0, nil, +}; + +static Algorithm authalg[] = +{ + "null", 0, nullauthinit, + "hmac_sha_96", 16, shaauthinit, + "hmac_md5_96", 16, md5authinit, + nil, 0, nil, +}; + +static Algorithm compalg[] = +{ + "null", 0, nullcompinit, + "thwack", 0, thwackcompinit, + nil, 0, nil, +}; +#endif + static int m2p[] = { [OREAD] 4, [OWRITE] 2, @@ -572,7 +611,6 @@ sdpwrite(Chan *ch, void *a, long n, vlong off) error(p); return n; case Qcontrol: -print("writecontrol %ld\n", n); writecontrol(sdp->conv[CONV(ch->qid)], a, n, 0); return n; case Qdata: @@ -660,6 +698,7 @@ sdpclone(Sdp *sdp) c = malloc(sizeof(Conv)); if(c == nil) error(Enomem); + memset(c, 0, sizeof(Conv)); qlock(c); c->sdp = sdp; c->id = pp - sdp->conv; @@ -681,6 +720,11 @@ sdpclone(Sdp *sdp) c->ref++; c->state = CInit; + c->in.window = ~0; + c->in.compstate = malloc(sizeof(Unthwack)); + unthwackinit(c->in.compstate); + c->out.compstate = malloc(sizeof(Thwack)); + thwackinit(c->out.compstate); strncpy(c->owner, up->user, sizeof(c->owner)); c->perm = 0660; qunlock(c); @@ -719,7 +763,7 @@ convtimer(Conv *c, ulong sec) { Block *b; - if(c->timeout == 0 || c->timeout > sec) + if(c->timeout > sec) return; qlock(c); if(waserror()) { @@ -740,10 +784,27 @@ convtimer(Conv *c, ulong sec) if(b != nil) { if(convretry(c, 1)) convoput(c, TControl, copyblock(b, blocklen(b))); - } else { - c->timeout = 0; + break; + } + + c->timeout = c->lastrecv + KeepAlive; + if(c->timeout > sec) + break; + // keepalive - randomly spaced between KeepAlive and 2*KeepAlive + if(c->timeout + KeepAlive > sec && nrand(c->lastrecv + 2*KeepAlive - sec) > 0) + break; +print("sending keep alive: %ld\n", sec - c->lastrecv); + // can not use writecontrol + b = allocb(4); + c->out.controlseq++; + hnputl(b->wp, c->out.controlseq); + b->wp += 4; + c->out.controlpkt = b; + convretryinit(c); + if(!waserror()) { + convoput(c, TControl, copyblock(b, blocklen(b))); + poperror(); } - // keepalive break; case CLocalClose: if(convretry(c, 0)) @@ -751,7 +812,7 @@ convtimer(Conv *c, ulong sec) break; case CRemoteClose: case CClosed: - c->timeout = 0; + c->timeout = ~0; break; } poperror(); @@ -858,16 +919,30 @@ print("CClosed -> ref = %d\n", c->ref); free(c->channame); c->channame = nil; } - strcpy(c->owner, "network"); + if(c->ciphername) { + free(c->ciphername); + c->ciphername = nil; + } + if(c->authname) { + free(c->authname); + c->authname = nil; + } + if(c->compname) { + free(c->compname); + c->compname = nil; + } + strcpy(c->owner, "network"); c->perm = 0660; c->dialid = 0; c->acceptid = 0; - c->timeout = 0; + c->timeout = ~0; c->retries = 0; c->drop = 0; memset(c->masterkey, 0, sizeof(c->masterkey)); onewaycleanup(&c->in); onewaycleanup(&c->out); + memset(&c->lstats, 0, sizeof(Stats)); + memset(&c->rstats, 0, sizeof(Stats)); break; } c->state = state; @@ -979,9 +1054,12 @@ convack(Conv *c) static Block * conviput(Conv *c, Block *b, int control) { - int type; - ulong seq, cseq; + int type, n; + ulong seq, seqwrap, cseq; + long seqdiff; AckPkt *ack; + ulong mseq, mask; + Block *bb; c->lstats.inPackets++; @@ -999,12 +1077,58 @@ conviput(Conv *c, Block *b, int control) seq = (b->rp[1]<<16) + (b->rp[2]<<8) + b->rp[3]; b->rp += 4; - USED(seq); + seqwrap = c->in.seqwrap; + seqdiff = seq - c->in.seq; + if(seqdiff < -(SeqMax*3/4)) { + seqwrap++; + seqdiff += SeqMax; + } else if(seqdiff > SeqMax*3/4) { + seqwrap--; + seqdiff -= SeqMax; + } + + if(seqdiff <= 0) { + if(seqdiff <= -SeqWindow) { +print("old sequence number: %ld (%ld %ld)\n", seq, c->in.seqwrap, seqdiff); + c->lstats.inBadSeq++; + freeb(b); + return nil; + } + + if(c->in.window & (1<<-seqdiff)) { +print("dup sequence number: %ld (%ld %ld)\n", seq, c->in.seqwrap, seqdiff); + c->lstats.inDup++; + freeb(b); + return nil; + } + + c->lstats.inReorder++; + } + + // ok the sequence number looks ok if(0) print("coniput seq=%ulx\n", seq); // auth // decrypt // ok the packet is good + if(seqdiff > 0) { + while(seqdiff > 0 && c->in.window != 0) { + if((c->in.window & (1<<(SeqWindow-1))) == 0) { +print("missing packet: %ld\n", seq - seqdiff); + c->lstats.inMissing++; + } + c->in.window <<= 1; + seqdiff--; + } + if(seqdiff > 0) { +print("missing packets: %ld-%ld\n", seq - SeqWindow - seqdiff+1, seq-SeqWindow); + c->lstats.inMissing += seqdiff; + } + c->in.seq = seq; + c->in.seqwrap = seqwrap; + c->in.window |= 1; + } + c->lastrecv = TK2SEC(m->ticks); switch(type) { case TControl: @@ -1059,6 +1183,7 @@ print("ControlAck expected %ulx got %ulx\n", c->out.controlseq, cseq); freeb(b); freeb(c->out.controlpkt); c->out.controlpkt = nil; + c->timeout = c->lastrecv + KeepAlive; wakeup(&c->out.controlready); return nil; case TData: @@ -1068,6 +1193,35 @@ print("ControlAck expected %ulx got %ulx\n", c->out.controlseq, cseq); if(control) break; return b; + case TThwackU: + c->lstats.inDataPackets++; + c->lstats.inCompDataBytes += BLEN(b); + mask = b->rp[0]; + mseq = (b->rp[1]<<16) | (b->rp[2]<<8) | b->rp[3]; + b->rp += 4; + thwackack(c->out.compstate, mseq, mask); + c->lstats.inDataBytes += BLEN(b); + if(control) + break; + return b; + case TThwackC: + c->lstats.inDataPackets++; + c->lstats.inCompDataBytes += BLEN(b); + bb = b; + b = allocb(ThwMaxBlock); + n = unthwack(c->in.compstate, b->wp, ThwMaxBlock, bb->rp, BLEN(bb), seq); + freeb(bb); + if(n < 0) + break; + b->wp += n; + mask = b->rp[0]; + mseq = (b->rp[1]<<16) | (b->rp[2]<<8) | b->rp[3]; + thwackack(c->out.compstate, mseq, mask); + b->rp += 4; + c->lstats.inDataBytes += BLEN(b); + if(control) + break; + return b; } print("droping packet %d n=%ld\n", type, BLEN(b)); freeb(b); @@ -1112,7 +1266,6 @@ print("conviput2: %s: %d %uld %uld\n", convstatename[c->state], con->op, dialid, goto Reset; } - switch(con->op) { case ConOpenRequest: switch(c->state) { @@ -1189,7 +1342,7 @@ static void convwriteblock(Conv *c, Block *b) { // simulated errors - if(c->drop && c->drop > nrand(c->drop)) + if(c->drop && nrand(c->drop) == 0) return; if(waserror()) { @@ -1388,12 +1541,9 @@ writecontrol(Conv *c, void *p, int n, int wait) b->wp += 4+n; c->out.controlpkt = b; convretryinit(c); -print("send %ld size=%ld\n", c->out.controlseq, BLEN(b)); convoput(c, TControl, copyblock(b, blocklen(b))); - if(wait) { -print("writecontrol wait!\n"); + if(wait) writewait(c); - } poperror(); qunlock(c); qunlock(&c->out.controllk); @@ -1424,7 +1574,9 @@ readdata(Conv *c, int n) static long writedata(Conv *c, Block *b) { - int n; + int n, nn; + ulong seq; + Block *bb; qlock(c); if(waserror()) { @@ -1440,8 +1592,36 @@ writedata(Conv *c, Block *b) n = BLEN(b); c->lstats.outDataPackets++; c->lstats.outDataBytes += n; - c->lstats.outCompDataBytes += n; - convoput(c, TData, b); + + if(0) { + c->lstats.outCompDataBytes += n; + convoput(c, TData, b); + poperror(); + qunlock(c); + return n; + } + b = padblock(b, 4); + b->rp[0] = (c->in.window>>1) & 0xff; + b->rp[1] = c->in.seq>>16; + b->rp[2] = c->in.seq>>8; + b->rp[3] = c->in.seq; + + // must generate same value as convoput + seq = (c->out.seq + 1) & (SeqMax-1); + + bb = allocb(BLEN(b)); + nn = thwack(c->out.compstate, bb->wp, b->rp, BLEN(b), seq); + if(nn < 0) { + c->lstats.outCompDataBytes += BLEN(b); + convoput(c, TThwackU, b); + freeb(bb); + } else { + c->lstats.outCompDataBytes += nn; + bb->wp += nn; + convoput(c, TThwackC, bb); + freeb(b); + } + poperror(); qunlock(c); return n; diff --git a/port/thwack.c b/port/thwack.c new file mode 100644 index 0000000000000000000000000000000000000000..f2e34912d80339a316802af57a5fe7ed01ef3af4 --- /dev/null +++ b/port/thwack.c @@ -0,0 +1,325 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +#include "thwack.h" + +typedef struct Huff Huff; +struct Huff +{ + short bits; /* length of the code */ + ulong encode; /* the code */ +}; + +static Huff lentab[MaxLen] = +{ + {1, 0x0}, /* 0 */ + {2, 0x2}, /* 10 */ + {4, 0xc}, /* 1100 */ + {4, 0xd}, /* 1101 */ + {5, 0x1e}, /* 11110 */ + {6, 0x3e}, /* 111110 */ + {6, 0x3f}, /* 111111 */ + {4, 0xe}, /* 1110 */ +}; + +static void bitput(Thwack *tw, int c, int n); +static int iomegaput(Thwack *tw, ulong v); + +void +thwackinit(Thwack *tw) +{ + int i; + + memset(tw, 0, sizeof *tw); + for(i = 0; i < EWinBlocks; i++){ + tw->blocks[i].data = tw->data[i]; + tw->blocks[i].edata = tw->blocks[i].data; + tw->blocks[i].hash = tw->hash[i]; + tw->blocks[i].acked = 0; + } +} + +/* + * acknowledgement for block seq & nearby preds + */ +void +thwackack(Thwack *tw, ulong seq, ulong mask) +{ + int slot, b; + + slot = tw->slot; + for(;;){ + for(;;){ + slot--; + if(slot < 0) + slot += EWinBlocks; + if(slot == tw->slot) + return; + if(tw->blocks[slot].seq == seq){ + tw->blocks[slot].acked = 1; + break; + } + } + + if(mask == 0) + break; + do{ + b = mask & 1; + seq--; + mask >>= 1; + }while(!b); + } +} + +/* + * find a string in the dictionary + */ +static int +thwmatch(ThwBlock *b, ThwBlock *eblocks, uchar **ss, uchar *esrc, ulong h) +{ + int then, toff, w; + uchar *s, *t; + + s = *ss; + if(esrc < s + MinMatch) + return 0; + + toff = 0; + for(; b < eblocks; b++){ + then = b->hash[h]; + 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 + * 2) entries too close not added to hash tables + */ + 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); + + t += 3; + for(s += 3; s < esrc; s++){ + if(*s != *t) + break; + t++; + } + *ss = s; + return toff - w; + } + return 0; +} + +/* + * knuth vol. 3 multiplicative hashing + * each byte x chosen according to rules + * 1/4 < x < 3/10, 1/3 x < < 3/7, 4/7 < x < 2/3, 7/10 < x < 3/4 + * with reasonable spread between the bytes & their complements + * + * 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) + +/* + * lz77 compression with single lookup in a hash table for each block + */ +int +thwack(Thwack *tw, uchar *dst, uchar *src, int n, ulong seq) +{ + ThwBlock *eblocks, *b, blocks[CompBlocks]; + uchar *s, *ss, *sss, *esrc, *half; + ulong cont, cseq, bseq, cmask; + int now, toff; + int h, m, slot, bits, totmatched; + + if(n > ThwMaxBlock || n < MinMatch || waserror()) + return -1; + + tw->dst = dst; + tw->dmax = dst + n; + tw->nbits = 0; + + /* + * add source to the coding window + * there is always enough space + */ + slot = tw->slot; + b = &tw->blocks[slot]; + b->seq = seq; + b->acked = 0; + now = b->begin + b->maxoff; + s = b->data; + memmove(s, src, n); + b->edata = s + n; + b->begin = now; + b->maxoff = n; + + /* + * set up the history blocks + */ + cseq = seq; + cmask = 0; + *blocks = *b; + b = blocks; + b->maxoff = 0; + b++; + while(b < blocks + CompBlocks){ + slot--; + if(slot < 0) + slot += EWinBlocks; + if(slot == tw->slot) + break; + if(!tw->blocks[slot].acked) + continue; + bseq = tw->blocks[slot].seq; + if(cseq == seq){ + if(seq - bseq >= MaxSeqStart) + break; + cseq = bseq; + }else if(cseq - bseq > MaxSeqMask) + break; + else + cmask |= 1 << (cseq - bseq - 1); + *b = tw->blocks[slot]; + b++; + } + eblocks = b; + bitput(tw, ((seq - cseq) << MaxSeqMask) | cmask, 16); + + cont = (s[0] << 16) | (s[2] << 8) | s[2]; + + totmatched = 0; + esrc = s + n; + half = s + (n >> 1); + while(s < esrc){ + h = hashit(cont); + + sss = s; + 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 < MaxLen-1){ + bitput(tw, lentab[m].encode, lentab[m].bits); + }else{ + bitput(tw, lentab[MaxLen-1].encode, lentab[MaxLen-1].bits); + iomegaput(tw, m - (MaxLen - 2)); + } + } + 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"); + + for(; s != ss; s++){ + if(s + MinMatch <= esrc){ + h = hashit(cont); + blocks->hash[h] = now; + if(s + MinMatch < esrc) + cont = (cont << 8) | s[MinMatch]; + } + now++; + } + } + + if(tw->nbits) + bitput(tw, 0, 8 - tw->nbits); + tw->slot++; + if(tw->slot >= EWinBlocks) + tw->slot = 0; + + poperror(); + return tw->dst - 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); + } +} + +/* + * elias's omega code, modified + * for at least 3 bit transmission + */ +static Huff omegatab[16] = +{ + {0, 0}, /* 1 */ + {0, 0}, + {0, 0}, + {0, 0}, + {3, 0x4}, /* 5 */ + {3, 0x5}, + {3, 0x6}, + {3, 0x7}, + {7, 0x48}, + {7, 0x49}, /* 10 */ + {7, 0x4a}, + {7, 0x4b}, + {7, 0x4c}, + {7, 0x4d}, + {7, 0x4e}, /* 15 */ + {7, 0x4f}, +}; + +static int +iomegaput(Thwack *tw, ulong v) +{ + int b, bb; + + v--; + if(v < 4){ + bitput(tw, v, 3); + return 3; + } + if(v < 16){ + b = omegatab[v].bits + 1; + bitput(tw, omegatab[v].encode << 1, b); + return b; + } + + for(bb = 5; v >= (1 << bb); bb++) + ; + if(bb >= 16) + error("thwack omegaput"); + b = bb + omegatab[bb].bits + 1; + bitput(tw, (omegatab[bb].encode << (bb + 1)) | (v << 1), b); + return b; +} diff --git a/port/thwack.h b/port/thwack.h new file mode 100644 index 0000000000000000000000000000000000000000..02443548ab915fcdf1e6ee1397c2f3ed96dae4ab --- /dev/null +++ b/port/thwack.h @@ -0,0 +1,76 @@ +typedef struct Thwack Thwack; +typedef struct Unthwack Unthwack; +typedef struct ThwBlock ThwBlock; +typedef struct UnthwBlock UnthwBlock; + +enum +{ + ThwMaxBlock = 1600, /* max size of compressible block */ + + MinMatch = 3, /* shortest match possible */ + HashLog = 10, + HashSize = 1<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); +}