xref: /plan9/sys/src/cmd/ip/ppp/thwack.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <ip.h>
4*9a747e4fSDavid du Colombier #include <auth.h>
57dd7cddfSDavid du Colombier #include "ppp.h"
67dd7cddfSDavid du Colombier #include "thwack.h"
77dd7cddfSDavid du Colombier 
87dd7cddfSDavid du Colombier typedef struct Huff	Huff;
97dd7cddfSDavid du Colombier 
107dd7cddfSDavid du Colombier enum
117dd7cddfSDavid du Colombier {
127dd7cddfSDavid du Colombier 	MaxFastLen	= 9,
137dd7cddfSDavid du Colombier 	BigLenCode	= 0x1f4,	/* minimum code for large lenth encoding */
147dd7cddfSDavid du Colombier 	BigLenBits	= 9,
157dd7cddfSDavid du Colombier 	BigLenBase	= 4		/* starting items to encode for big lens */
167dd7cddfSDavid du Colombier };
177dd7cddfSDavid du Colombier 
187dd7cddfSDavid du Colombier enum
197dd7cddfSDavid du Colombier {
207dd7cddfSDavid du Colombier 	StatBytes,
217dd7cddfSDavid du Colombier 	StatOutBytes,
227dd7cddfSDavid du Colombier 	StatLits,
237dd7cddfSDavid du Colombier 	StatMatches,
247dd7cddfSDavid du Colombier 	StatOffBits,
257dd7cddfSDavid du Colombier 	StatLenBits,
267dd7cddfSDavid du Colombier 
277dd7cddfSDavid du Colombier 	StatDelay,
287dd7cddfSDavid du Colombier 	StatHist,
297dd7cddfSDavid du Colombier 
307dd7cddfSDavid du Colombier 	MaxStat
317dd7cddfSDavid du Colombier };
327dd7cddfSDavid du Colombier 
337dd7cddfSDavid du Colombier struct Huff
347dd7cddfSDavid du Colombier {
357dd7cddfSDavid du Colombier 	short	bits;				/* length of the code */
367dd7cddfSDavid du Colombier 	ulong	encode;				/* the code */
377dd7cddfSDavid du Colombier };
387dd7cddfSDavid du Colombier 
397dd7cddfSDavid du Colombier static	Huff	lentab[MaxFastLen] =
407dd7cddfSDavid du Colombier {
417dd7cddfSDavid du Colombier 	{2,	0x2},		/* 10 */
427dd7cddfSDavid du Colombier 	{3,	0x6},		/* 110 */
437dd7cddfSDavid du Colombier 	{5,	0x1c},		/* 11100 */
447dd7cddfSDavid du Colombier 	{5,	0x1d},		/* 11101 */
457dd7cddfSDavid du Colombier 	{6,	0x3c},		/* 111100 */
467dd7cddfSDavid du Colombier 	{7,	0x7a},		/* 1111010 */
477dd7cddfSDavid du Colombier 	{7,	0x7b},		/* 1111011 */
487dd7cddfSDavid du Colombier 	{8,	0xf8},		/* 11111000 */
497dd7cddfSDavid du Colombier 	{8,	0xf9},		/* 11111001 */
507dd7cddfSDavid du Colombier };
517dd7cddfSDavid du Colombier 
527dd7cddfSDavid du Colombier void
thwackinit(Thwack * tw)537dd7cddfSDavid du Colombier thwackinit(Thwack *tw)
547dd7cddfSDavid du Colombier {
557dd7cddfSDavid du Colombier 	int i;
567dd7cddfSDavid du Colombier 
577dd7cddfSDavid du Colombier 	qlock(&tw->acklock);
587dd7cddfSDavid du Colombier 	tw->slot = 0;
597dd7cddfSDavid du Colombier 	memset(tw->hash, 0, sizeof(tw->hash));
607dd7cddfSDavid du Colombier 	memset(tw->blocks, 0, sizeof(tw->blocks));
617dd7cddfSDavid du Colombier 	for(i = 0; i < EWinBlocks; i++){
627dd7cddfSDavid du Colombier 		tw->blocks[i].hash = tw->hash[i];
637dd7cddfSDavid du Colombier 		if(tw->data[i] != nil){
647dd7cddfSDavid du Colombier 			freeb(tw->data[i]);
657dd7cddfSDavid du Colombier 			tw->data[i] = nil;
667dd7cddfSDavid du Colombier 		}
677dd7cddfSDavid du Colombier 	}
687dd7cddfSDavid du Colombier 	qunlock(&tw->acklock);
697dd7cddfSDavid du Colombier }
707dd7cddfSDavid du Colombier 
717dd7cddfSDavid du Colombier void
thwackcleanup(Thwack * tw)727dd7cddfSDavid du Colombier thwackcleanup(Thwack *tw)
737dd7cddfSDavid du Colombier {
747dd7cddfSDavid du Colombier 	int i;
757dd7cddfSDavid du Colombier 
767dd7cddfSDavid du Colombier 	qlock(&tw->acklock);
777dd7cddfSDavid du Colombier 	for(i = 0; i < EWinBlocks; i++){
787dd7cddfSDavid du Colombier 		if(tw->data[i] != nil){
797dd7cddfSDavid du Colombier 			freeb(tw->data[i]);
807dd7cddfSDavid du Colombier 			tw->data[i] = nil;
817dd7cddfSDavid du Colombier 		}
827dd7cddfSDavid du Colombier 	}
837dd7cddfSDavid du Colombier 	qunlock(&tw->acklock);
847dd7cddfSDavid du Colombier }
857dd7cddfSDavid du Colombier 
867dd7cddfSDavid du Colombier /*
877dd7cddfSDavid du Colombier  * acknowledgement for block seq & nearby preds
887dd7cddfSDavid du Colombier  */
897dd7cddfSDavid du Colombier void
thwackack(Thwack * tw,ulong seq,ulong mask)907dd7cddfSDavid du Colombier thwackack(Thwack *tw, ulong seq, ulong mask)
917dd7cddfSDavid du Colombier {
927dd7cddfSDavid du Colombier 	int slot, b;
937dd7cddfSDavid du Colombier 
947dd7cddfSDavid du Colombier 	qlock(&tw->acklock);
957dd7cddfSDavid du Colombier 	slot = tw->slot;
967dd7cddfSDavid du Colombier 	for(;;){
977dd7cddfSDavid du Colombier 		slot--;
987dd7cddfSDavid du Colombier 		if(slot < 0)
997dd7cddfSDavid du Colombier 			slot += EWinBlocks;
1007dd7cddfSDavid du Colombier 		if(slot == tw->slot)
1017dd7cddfSDavid du Colombier 			break;
1027dd7cddfSDavid du Colombier 		if(tw->blocks[slot].seq != seq)
1037dd7cddfSDavid du Colombier 			continue;
1047dd7cddfSDavid du Colombier 
1057dd7cddfSDavid du Colombier 		tw->blocks[slot].acked = 1;
1067dd7cddfSDavid du Colombier 
1077dd7cddfSDavid du Colombier 		if(mask == 0)
1087dd7cddfSDavid du Colombier 			break;
1097dd7cddfSDavid du Colombier 		do{
1107dd7cddfSDavid du Colombier 			b = mask & 1;
1117dd7cddfSDavid du Colombier 			seq--;
1127dd7cddfSDavid du Colombier 			mask >>= 1;
1137dd7cddfSDavid du Colombier 		}while(!b);
1147dd7cddfSDavid du Colombier 	}
1157dd7cddfSDavid du Colombier 	qunlock(&tw->acklock);
1167dd7cddfSDavid du Colombier }
1177dd7cddfSDavid du Colombier 
1187dd7cddfSDavid du Colombier /*
1197dd7cddfSDavid du Colombier  * find a string in the dictionary
1207dd7cddfSDavid du Colombier  */
1217dd7cddfSDavid du Colombier static int
thwmatch(ThwBlock * b,ThwBlock * eblocks,uchar ** ss,uchar * esrc,ulong h)1227dd7cddfSDavid du Colombier thwmatch(ThwBlock *b, ThwBlock *eblocks, uchar **ss, uchar *esrc, ulong h)
1237dd7cddfSDavid du Colombier {
1247dd7cddfSDavid du Colombier 	int then, toff, w, ok;
1257dd7cddfSDavid du Colombier 	uchar *s, *t;
1267dd7cddfSDavid du Colombier 
1277dd7cddfSDavid du Colombier 	s = *ss;
1287dd7cddfSDavid du Colombier 	if(esrc < s + MinMatch)
1297dd7cddfSDavid du Colombier 		return 0;
1307dd7cddfSDavid du Colombier 
1317dd7cddfSDavid du Colombier 	toff = 0;
1327dd7cddfSDavid du Colombier 	for(; b < eblocks; b++){
1337dd7cddfSDavid du Colombier 		then = b->hash[(h ^ b->seq) & HashMask];
1347dd7cddfSDavid du Colombier 		toff += b->maxoff;
1357dd7cddfSDavid du Colombier 		w = (ushort)(then - b->begin);
1367dd7cddfSDavid du Colombier 
1377dd7cddfSDavid du Colombier 		if(w >= b->maxoff)
1387dd7cddfSDavid du Colombier 			continue;
1397dd7cddfSDavid du Colombier 
1407dd7cddfSDavid du Colombier 
1417dd7cddfSDavid du Colombier 		/*
1427dd7cddfSDavid du Colombier 		 * don't need to check for the end because
1437dd7cddfSDavid du Colombier 		 * 1) s too close check above
1447dd7cddfSDavid du Colombier 		 * 2) entries too close not added to hash tables
1457dd7cddfSDavid du Colombier 		 */
1467dd7cddfSDavid du Colombier 		t = w + b->data;
1477dd7cddfSDavid du Colombier 		if(s[0] != t[0] || s[1] != t[1] || s[2] != t[2])
1487dd7cddfSDavid du Colombier 			continue;
1497dd7cddfSDavid du Colombier 		ok = b->edata - t;
1507dd7cddfSDavid du Colombier 		if(esrc - s > ok)
1517dd7cddfSDavid du Colombier 			esrc = s + ok;
1527dd7cddfSDavid du Colombier 
1537dd7cddfSDavid du Colombier 		t += 3;
1547dd7cddfSDavid du Colombier 		for(s += 3; s < esrc; s++){
1557dd7cddfSDavid du Colombier 			if(*s != *t)
1567dd7cddfSDavid du Colombier 				break;
1577dd7cddfSDavid du Colombier 			t++;
1587dd7cddfSDavid du Colombier 		}
1597dd7cddfSDavid du Colombier 		*ss = s;
1607dd7cddfSDavid du Colombier 		return toff - w;
1617dd7cddfSDavid du Colombier 	}
1627dd7cddfSDavid du Colombier 	return 0;
1637dd7cddfSDavid du Colombier }
1647dd7cddfSDavid du Colombier 
1657dd7cddfSDavid du Colombier /*
1667dd7cddfSDavid du Colombier  * knuth vol. 3 multiplicative hashing
1677dd7cddfSDavid du Colombier  * each byte x chosen according to rules
1687dd7cddfSDavid du Colombier  * 1/4 < x < 3/10, 1/3 x < < 3/7, 4/7 < x < 2/3, 7/10 < x < 3/4
1697dd7cddfSDavid du Colombier  * with reasonable spread between the bytes & their complements
1707dd7cddfSDavid du Colombier  *
1717dd7cddfSDavid du Colombier  * the 3 byte value appears to be as almost good as the 4 byte value,
1727dd7cddfSDavid du Colombier  * and might be faster on some machines
1737dd7cddfSDavid du Colombier  */
1747dd7cddfSDavid du Colombier /*
1757dd7cddfSDavid du Colombier #define hashit(c)	(((ulong)(c) * 0x6b43a9) >> (24 - HashLog))
1767dd7cddfSDavid du Colombier */
1777dd7cddfSDavid du Colombier #define hashit(c)	((((ulong)(c) & 0xffffff) * 0x6b43a9b5) >> (32 - HashLog))
1787dd7cddfSDavid du Colombier 
1797dd7cddfSDavid du Colombier /*
1807dd7cddfSDavid du Colombier  * lz77 compression with single lookup in a hash table for each block
1817dd7cddfSDavid du Colombier  */
1827dd7cddfSDavid du Colombier int
thwack(Thwack * tw,int mustadd,uchar * dst,int ndst,Block * bsrc,ulong seq,ulong stats[ThwStats])18359cc4ca5SDavid du Colombier thwack(Thwack *tw, int mustadd, uchar *dst, int ndst, Block *bsrc, ulong seq, ulong stats[ThwStats])
1847dd7cddfSDavid du Colombier {
1857dd7cddfSDavid du Colombier 	ThwBlock *eblocks, *b, blocks[CompBlocks];
1867dd7cddfSDavid du Colombier 	uchar *s, *ss, *sss, *esrc, *half, *twdst, *twdmax;
1877dd7cddfSDavid du Colombier 	ulong cont, cseq, bseq, cmask, code, twbits;
1887dd7cddfSDavid du Colombier 	int n, now, toff, lithist, h, len, slot, bits, use, twnbits, lits, matches, offbits, lenbits, nhist;
1897dd7cddfSDavid du Colombier 
1907dd7cddfSDavid du Colombier 	n = BLEN(bsrc);
1917dd7cddfSDavid du Colombier 	if(n > ThwMaxBlock || n < MinMatch)
1927dd7cddfSDavid du Colombier 		return -1;
1937dd7cddfSDavid du Colombier 
1947dd7cddfSDavid du Colombier 	twdst = dst;
19559cc4ca5SDavid du Colombier 	twdmax = dst + ndst;
1967dd7cddfSDavid du Colombier 
1977dd7cddfSDavid du Colombier 	/*
1987dd7cddfSDavid du Colombier 	 * add source to the coding window
1997dd7cddfSDavid du Colombier 	 * there is always enough space
2007dd7cddfSDavid du Colombier 	 */
2017dd7cddfSDavid du Colombier 	qlock(&tw->acklock);
2027dd7cddfSDavid du Colombier 	slot = tw->slot;
2037dd7cddfSDavid du Colombier 	b = &tw->blocks[slot];
2047dd7cddfSDavid du Colombier 	b->seq = seq;
2057dd7cddfSDavid du Colombier 	b->acked = 0;
2067dd7cddfSDavid du Colombier 	now = b->begin + b->maxoff;
2077dd7cddfSDavid du Colombier 	if(tw->data[slot] != nil){
2087dd7cddfSDavid du Colombier 		freeb(tw->data[slot]);
2097dd7cddfSDavid du Colombier 		tw->data[slot] = nil;
2107dd7cddfSDavid du Colombier 	}
2117dd7cddfSDavid du Colombier 	s = bsrc->rptr;
2127dd7cddfSDavid du Colombier 	b->data = s;
2137dd7cddfSDavid du Colombier 	b->edata = s + n;
2147dd7cddfSDavid du Colombier 	b->begin = now;
2157dd7cddfSDavid du Colombier 	b->maxoff = n;
2167dd7cddfSDavid du Colombier 
2177dd7cddfSDavid du Colombier 	/*
2187dd7cddfSDavid du Colombier 	 * set up the history blocks
2197dd7cddfSDavid du Colombier 	 */
2207dd7cddfSDavid du Colombier 	cseq = seq;
2217dd7cddfSDavid du Colombier 	cmask = 0;
2227dd7cddfSDavid du Colombier 	*blocks = *b;
2237dd7cddfSDavid du Colombier 	b = blocks;
2247dd7cddfSDavid du Colombier 	b->maxoff = 0;
2257dd7cddfSDavid du Colombier 	b++;
2267dd7cddfSDavid du Colombier 	nhist = 0;
2277dd7cddfSDavid du Colombier 	while(b < blocks + CompBlocks){
2287dd7cddfSDavid du Colombier 		slot--;
2297dd7cddfSDavid du Colombier 		if(slot < 0)
2307dd7cddfSDavid du Colombier 			slot += EWinBlocks;
2317dd7cddfSDavid du Colombier 		if(slot == tw->slot)
2327dd7cddfSDavid du Colombier 			break;
2337dd7cddfSDavid du Colombier 		if(tw->data[slot] == nil || !tw->blocks[slot].acked)
2347dd7cddfSDavid du Colombier 			continue;
2357dd7cddfSDavid du Colombier 		bseq = tw->blocks[slot].seq;
2367dd7cddfSDavid du Colombier 		if(cseq == seq){
2377dd7cddfSDavid du Colombier 			if(seq - bseq >= MaxSeqStart)
2387dd7cddfSDavid du Colombier 				break;
2397dd7cddfSDavid du Colombier 			cseq = bseq;
2407dd7cddfSDavid du Colombier 		}else if(cseq - bseq > MaxSeqMask)
2417dd7cddfSDavid du Colombier 			break;
2427dd7cddfSDavid du Colombier 		else
2437dd7cddfSDavid du Colombier 			cmask |= 1 << (cseq - bseq - 1);
2447dd7cddfSDavid du Colombier 		*b = tw->blocks[slot];
2457dd7cddfSDavid du Colombier 		nhist += b->maxoff;
2467dd7cddfSDavid du Colombier 		b++;
2477dd7cddfSDavid du Colombier 	}
2487dd7cddfSDavid du Colombier 	qunlock(&tw->acklock);
2497dd7cddfSDavid du Colombier 	eblocks = b;
2507dd7cddfSDavid du Colombier 	*twdst++ = seq - cseq;
2517dd7cddfSDavid du Colombier 	*twdst++ = cmask;
2527dd7cddfSDavid du Colombier 
2537dd7cddfSDavid du Colombier 	cont = (s[0] << 16) | (s[1] << 8) | s[2];
2547dd7cddfSDavid du Colombier 
2557dd7cddfSDavid du Colombier 	esrc = s + n;
2567dd7cddfSDavid du Colombier 	half = s + (n >> 1);
2577dd7cddfSDavid du Colombier 	twnbits = 0;
2587dd7cddfSDavid du Colombier 	twbits = 0;
2597dd7cddfSDavid du Colombier 	lits = 0;
2607dd7cddfSDavid du Colombier 	matches = 0;
2617dd7cddfSDavid du Colombier 	offbits = 0;
2627dd7cddfSDavid du Colombier 	lenbits = 0;
2637dd7cddfSDavid du Colombier 	lithist = ~0;
2647dd7cddfSDavid du Colombier 	while(s < esrc){
2657dd7cddfSDavid du Colombier 		h = hashit(cont);
2667dd7cddfSDavid du Colombier 
2677dd7cddfSDavid du Colombier 		sss = s;
2687dd7cddfSDavid du Colombier 		toff = thwmatch(blocks, eblocks, &sss, esrc, h);
2697dd7cddfSDavid du Colombier 		ss = sss;
2707dd7cddfSDavid du Colombier 
2717dd7cddfSDavid du Colombier 		len = ss - s;
2727dd7cddfSDavid du Colombier 		for(; twnbits >= 8; twnbits -= 8){
27359cc4ca5SDavid du Colombier 			if(twdst < twdmax)
2747dd7cddfSDavid du Colombier 				*twdst++ = twbits >> (twnbits - 8);
27559cc4ca5SDavid du Colombier 			else if(!mustadd)
27659cc4ca5SDavid du Colombier 				return -1;
2777dd7cddfSDavid du Colombier 		}
2787dd7cddfSDavid du Colombier 		if(len < MinMatch){
2797dd7cddfSDavid du Colombier 			toff = *s;
2807dd7cddfSDavid du Colombier 			lithist = (lithist << 1) | (toff < 32) | (toff > 127);
2817dd7cddfSDavid du Colombier 			if(lithist & 0x1e){
2827dd7cddfSDavid du Colombier 				twbits = (twbits << 9) | toff;
2837dd7cddfSDavid du Colombier 				twnbits += 9;
2847dd7cddfSDavid du Colombier 			}else if(lithist & 1){
2857dd7cddfSDavid du Colombier 				toff = (toff + 64) & 0xff;
2867dd7cddfSDavid du Colombier 				if(toff < 96){
2877dd7cddfSDavid du Colombier 					twbits = (twbits << 10) | toff;
2887dd7cddfSDavid du Colombier 					twnbits += 10;
2897dd7cddfSDavid du Colombier 				}else{
2907dd7cddfSDavid du Colombier 					twbits = (twbits << 11) | toff;
2917dd7cddfSDavid du Colombier 					twnbits += 11;
2927dd7cddfSDavid du Colombier 				}
2937dd7cddfSDavid du Colombier 			}else{
2947dd7cddfSDavid du Colombier 				twbits = (twbits << 8) | toff;
2957dd7cddfSDavid du Colombier 				twnbits += 8;
2967dd7cddfSDavid du Colombier 			}
2977dd7cddfSDavid du Colombier 			lits++;
2987dd7cddfSDavid du Colombier 			blocks->maxoff++;
2997dd7cddfSDavid du Colombier 
3007dd7cddfSDavid du Colombier 			/*
3017dd7cddfSDavid du Colombier 			 * speed hack
3027dd7cddfSDavid du Colombier 			 * check for compression progress, bail if none achieved
3037dd7cddfSDavid du Colombier 			 */
3047dd7cddfSDavid du Colombier 			if(s > half){
30559cc4ca5SDavid du Colombier 				if(!mustadd && 4 * blocks->maxoff < 5 * lits)
3067dd7cddfSDavid du Colombier 					return -1;
3077dd7cddfSDavid du Colombier 				half = esrc;
3087dd7cddfSDavid du Colombier 			}
3097dd7cddfSDavid du Colombier 
3107dd7cddfSDavid du Colombier 			if(s + MinMatch <= esrc){
3117dd7cddfSDavid du Colombier 				blocks->hash[(h ^ blocks->seq) & HashMask] = now;
3127dd7cddfSDavid du Colombier 				if(s + MinMatch < esrc)
3137dd7cddfSDavid du Colombier 					cont = (cont << 8) | s[MinMatch];
3147dd7cddfSDavid du Colombier 			}
3157dd7cddfSDavid du Colombier 			now++;
3167dd7cddfSDavid du Colombier 			s++;
3177dd7cddfSDavid du Colombier 			continue;
3187dd7cddfSDavid du Colombier 		}
3197dd7cddfSDavid du Colombier 
3207dd7cddfSDavid du Colombier 		blocks->maxoff += len;
3217dd7cddfSDavid du Colombier 		matches++;
3227dd7cddfSDavid du Colombier 
3237dd7cddfSDavid du Colombier 		/*
3247dd7cddfSDavid du Colombier 		 * length of match
3257dd7cddfSDavid du Colombier 		 */
3267dd7cddfSDavid du Colombier 		len -= MinMatch;
3277dd7cddfSDavid du Colombier 		if(len < MaxFastLen){
3287dd7cddfSDavid du Colombier 			bits = lentab[len].bits;
3297dd7cddfSDavid du Colombier 			twbits = (twbits << bits) | lentab[len].encode;
3307dd7cddfSDavid du Colombier 			twnbits += bits;
3317dd7cddfSDavid du Colombier 			lenbits += bits;
3327dd7cddfSDavid du Colombier 		}else{
3337dd7cddfSDavid du Colombier 			code = BigLenCode;
3347dd7cddfSDavid du Colombier 			bits = BigLenBits;
3357dd7cddfSDavid du Colombier 			use = BigLenBase;
3367dd7cddfSDavid du Colombier 			len -= MaxFastLen;
3377dd7cddfSDavid du Colombier 			while(len >= use){
3387dd7cddfSDavid du Colombier 				len -= use;
3397dd7cddfSDavid du Colombier 				code = (code + use) << 1;
3407dd7cddfSDavid du Colombier 				use <<= (bits & 1) ^ 1;
3417dd7cddfSDavid du Colombier 				bits++;
3427dd7cddfSDavid du Colombier 			}
3437dd7cddfSDavid du Colombier 			twbits = (twbits << bits) | (code + len);
3447dd7cddfSDavid du Colombier 			twnbits += bits;
3457dd7cddfSDavid du Colombier 			lenbits += bits;
3467dd7cddfSDavid du Colombier 
3477dd7cddfSDavid du Colombier 			for(; twnbits >= 8; twnbits -= 8){
34859cc4ca5SDavid du Colombier 				if(twdst < twdmax)
3497dd7cddfSDavid du Colombier 					*twdst++ = twbits >> (twnbits - 8);
35059cc4ca5SDavid du Colombier 				else if(!mustadd)
35159cc4ca5SDavid du Colombier 					return -1;
3527dd7cddfSDavid du Colombier 			}
3537dd7cddfSDavid du Colombier 		}
3547dd7cddfSDavid du Colombier 
3557dd7cddfSDavid du Colombier 		/*
3567dd7cddfSDavid du Colombier 		 * offset in history
3577dd7cddfSDavid du Colombier 		 */
3587dd7cddfSDavid du Colombier 		toff--;
3597dd7cddfSDavid du Colombier 		for(bits = OffBase; toff >= (1 << bits); bits++)
3607dd7cddfSDavid du Colombier 			;
3617dd7cddfSDavid du Colombier 		if(bits < MaxOff+OffBase-1){
3627dd7cddfSDavid du Colombier 			twbits = (twbits << 3) | (bits - OffBase);
3637dd7cddfSDavid du Colombier 			if(bits != OffBase)
3647dd7cddfSDavid du Colombier 				bits--;
3657dd7cddfSDavid du Colombier 			twnbits += bits + 3;
3667dd7cddfSDavid du Colombier 			offbits += bits + 3;
3677dd7cddfSDavid du Colombier 		}else{
3687dd7cddfSDavid du Colombier 			twbits = (twbits << 4) | 0xe | (bits - (MaxOff+OffBase-1));
3697dd7cddfSDavid du Colombier 			bits--;
3707dd7cddfSDavid du Colombier 			twnbits += bits + 4;
3717dd7cddfSDavid du Colombier 			offbits += bits + 4;
3727dd7cddfSDavid du Colombier 		}
3737dd7cddfSDavid du Colombier 		twbits = (twbits << bits) | toff & ((1 << bits) - 1);
3747dd7cddfSDavid du Colombier 
3757dd7cddfSDavid du Colombier 		for(; s != ss; s++){
3767dd7cddfSDavid du Colombier 			if(s + MinMatch <= esrc){
3777dd7cddfSDavid du Colombier 				h = hashit(cont);
3787dd7cddfSDavid du Colombier 				blocks->hash[(h ^ blocks->seq) & HashMask] = now;
3797dd7cddfSDavid du Colombier 				if(s + MinMatch < esrc)
3807dd7cddfSDavid du Colombier 					cont = (cont << 8) | s[MinMatch];
3817dd7cddfSDavid du Colombier 			}
3827dd7cddfSDavid du Colombier 			now++;
3837dd7cddfSDavid du Colombier 		}
3847dd7cddfSDavid du Colombier 	}
3857dd7cddfSDavid du Colombier 
3867dd7cddfSDavid du Colombier 	if(twnbits & 7){
3877dd7cddfSDavid du Colombier 		twbits <<= 8 - (twnbits & 7);
3887dd7cddfSDavid du Colombier 		twnbits += 8 - (twnbits & 7);
3897dd7cddfSDavid du Colombier 	}
3907dd7cddfSDavid du Colombier 	for(; twnbits >= 8; twnbits -= 8){
39159cc4ca5SDavid du Colombier 		if(twdst < twdmax)
3927dd7cddfSDavid du Colombier 			*twdst++ = twbits >> (twnbits - 8);
39359cc4ca5SDavid du Colombier 		else if(!mustadd)
39459cc4ca5SDavid du Colombier 			return -1;
3957dd7cddfSDavid du Colombier 	}
3967dd7cddfSDavid du Colombier 
397*9a747e4fSDavid du Colombier 	if(twdst >= twdmax && !mustadd)
398*9a747e4fSDavid du Colombier 		return -1;
399*9a747e4fSDavid du Colombier 
4007dd7cddfSDavid du Colombier 	qlock(&tw->acklock);
40159cc4ca5SDavid du Colombier 	tw->data[tw->slot] = bsrc;
4027dd7cddfSDavid du Colombier 	tw->slot++;
4037dd7cddfSDavid du Colombier 	if(tw->slot >= EWinBlocks)
4047dd7cddfSDavid du Colombier 		tw->slot = 0;
4057dd7cddfSDavid du Colombier 	qunlock(&tw->acklock);
4067dd7cddfSDavid du Colombier 
40759cc4ca5SDavid du Colombier 	if(twdst >= twdmax)
40859cc4ca5SDavid du Colombier 		return -1;
40959cc4ca5SDavid du Colombier 
4107dd7cddfSDavid du Colombier 	stats[StatBytes] += blocks->maxoff;
4117dd7cddfSDavid du Colombier 	stats[StatLits] += lits;
4127dd7cddfSDavid du Colombier 	stats[StatMatches] += matches;
4137dd7cddfSDavid du Colombier 	stats[StatOffBits] += offbits;
4147dd7cddfSDavid du Colombier 	stats[StatLenBits] += lenbits;
4157dd7cddfSDavid du Colombier 	stats[StatDelay] = stats[StatDelay]*7/8 + dst[0];
4167dd7cddfSDavid du Colombier 	stats[StatHist] = stats[StatHist]*7/8 + nhist;
4177dd7cddfSDavid du Colombier 	stats[StatOutBytes] += twdst - dst;
4187dd7cddfSDavid du Colombier 
4197dd7cddfSDavid du Colombier 	return twdst - dst;
4207dd7cddfSDavid du Colombier }
421