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