~kris/9p

9hist

082d35c32005b720f53af03a668e873c7381b6d7 — David du Colombier 26 years ago d2ab5ad
Plan 9 from Bell Labs 1999-10-22
7 files changed, 432 insertions(+), 145 deletions(-)

M pc/mem.h
M port/cache.c
M port/devsdp.c
M port/portdat.h
M port/thwack.c
M port/thwack.h
M port/unthwack.c
M pc/mem.h => pc/mem.h +2 -2
@@ 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

M port/cache.c => port/cache.c +8 -0
@@ 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;

M port/devsdp.c => port/devsdp.c +227 -43
@@ 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; i<NCompStats; i++)
			c->rstats.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));

M port/portdat.h => port/portdat.h +1 -1
@@ 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;

M port/thwack.c => port/thwack.c +152 -75
@@ 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;
}

M port/thwack.h => port/thwack.h +5 -9
@@ 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<<HashLog,
	HashMask	= HashSize - 1,



@@ 19,7 20,7 @@ enum
	MaxOff		= 8,
	OffBase		= 6,

	MinDecode	= 9,		/* minimum bits to decode a match or lit */
	MinDecode	= 8,		/* minimum bits to decode a match or lit; >= 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);

M port/unthwack.c => port/unthwack.c +37 -15
@@ 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++;