M pc/devarch.c => pc/devarch.c +1 -0
@@ 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", },
M port/devsdp.c => port/devsdp.c +202 -22
@@ 7,6 7,7 @@
#include "../port/error.h"
#include <libcrypt.h>
+#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;
A port/thwack.c => port/thwack.c +325 -0
@@ 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;
+}
A port/thwack.h => port/thwack.h +76 -0
@@ 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<<HashLog,
+ HashMask = HashSize - 1,
+
+ MaxLen = 8,
+ MaxOff = 8,
+ OffBase = 6,
+
+ MinDecode = 9, /* minimum bits to decode a match or lit */
+
+ EWinBlocks = 32, /* blocks held in encoder window */
+ DWinBlocks = 32, /* blocks held in decoder window */
+ 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 */
+};
+
+struct ThwBlock
+{
+ ulong seq; /* sequence number for this data */
+ uchar acked; /* ok to use this block; the decoder has it */
+ ushort begin; /* time of first byte in hash */
+ uchar *edata; /* last byte of valid data */
+ ushort maxoff; /* time of last valid hash entry */
+ ushort *hash;
+ uchar *data;
+};
+
+struct Thwack
+{
+ ulong nbits; /* output bit buffer */
+ ulong bits;
+ uchar *dst; /* output buffer */
+ uchar *dmax;
+
+ ulong slot; /* next block to use */
+ ThwBlock blocks[EWinBlocks];
+ ushort hash[EWinBlocks][HashSize];
+ uchar data[EWinBlocks][ThwMaxBlock];
+};
+
+struct UnthwBlock
+{
+ ulong seq; /* sequence number for this data */
+ ushort maxoff; /* valid data in each block */
+ uchar *data;
+};
+
+struct Unthwack
+{
+ ulong nbits; /* input bit buffer */
+ ulong bits;
+ uchar *src; /* input buffer */
+ uchar *smax;
+
+ int slot; /* next block to use */
+ UnthwBlock blocks[DWinBlocks];
+ uchar data[DWinBlocks][ThwMaxBlock];
+};
+
+void thwackinit(Thwack*);
+void unthwackinit(Unthwack*);
+int thwack(Thwack*, uchar *dst, uchar *src, int nsrc, ulong seq);
+void thwackack(Thwack*, ulong seq, ulong mask);
+int unthwack(Unthwack*, uchar *dst, int ndst, uchar *src, int nsrc, ulong seq);
A port/unthwack.c => port/unthwack.c +253 -0
@@ 0,0 1,253 @@
+#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);
+}