xref: /plan9/sys/src/cmd/venti/srv/whack.c (revision 368c31ab13393dea083228fdd1c3445076f83a4b)
1*368c31abSDavid du Colombier #include "stdinc.h"
2*368c31abSDavid du Colombier #include "whack.h"
3*368c31abSDavid du Colombier 
4*368c31abSDavid du Colombier typedef struct Huff	Huff;
5*368c31abSDavid du Colombier int compressblocks = 1;
6*368c31abSDavid du Colombier 
7*368c31abSDavid du Colombier enum
8*368c31abSDavid du Colombier {
9*368c31abSDavid du Colombier 	MaxFastLen	= 9,
10*368c31abSDavid du Colombier 	BigLenCode	= 0x1f4,	/* minimum code for large lenth encoding */
11*368c31abSDavid du Colombier 	BigLenBits	= 9,
12*368c31abSDavid du Colombier 	BigLenBase	= 4,		/* starting items to encode for big lens */
13*368c31abSDavid du Colombier 
14*368c31abSDavid du Colombier 	MinOffBits	= 6,
15*368c31abSDavid du Colombier 	MaxOffBits	= MinOffBits + 8,
16*368c31abSDavid du Colombier 
17*368c31abSDavid du Colombier 	MaxLen		= 2051		/* max. length encodable in 24 bits */
18*368c31abSDavid du Colombier };
19*368c31abSDavid du Colombier 
20*368c31abSDavid du Colombier enum
21*368c31abSDavid du Colombier {
22*368c31abSDavid du Colombier 	StatBytes,
23*368c31abSDavid du Colombier 	StatOutBytes,
24*368c31abSDavid du Colombier 	StatLits,
25*368c31abSDavid du Colombier 	StatMatches,
26*368c31abSDavid du Colombier 	StatLitBits,
27*368c31abSDavid du Colombier 	StatOffBits,
28*368c31abSDavid du Colombier 	StatLenBits,
29*368c31abSDavid du Colombier 
30*368c31abSDavid du Colombier 	MaxStat
31*368c31abSDavid du Colombier };
32*368c31abSDavid du Colombier 
33*368c31abSDavid du Colombier struct Huff
34*368c31abSDavid du Colombier {
35*368c31abSDavid du Colombier 	short	bits;				/* length of the code */
36*368c31abSDavid du Colombier 	ulong	encode;				/* the code */
37*368c31abSDavid du Colombier };
38*368c31abSDavid du Colombier 
39*368c31abSDavid du Colombier static	Huff	lentab[MaxFastLen] =
40*368c31abSDavid du Colombier {
41*368c31abSDavid du Colombier 	{2,	0x2},		/* 10 */
42*368c31abSDavid du Colombier 	{3,	0x6},		/* 110 */
43*368c31abSDavid du Colombier 	{5,	0x1c},		/* 11100 */
44*368c31abSDavid du Colombier 	{5,	0x1d},		/* 11101 */
45*368c31abSDavid du Colombier 	{6,	0x3c},		/* 111100 */
46*368c31abSDavid du Colombier 	{7,	0x7a},		/* 1111010 */
47*368c31abSDavid du Colombier 	{7,	0x7b},		/* 1111011 */
48*368c31abSDavid du Colombier 	{8,	0xf8},		/* 11111000 */
49*368c31abSDavid du Colombier 	{8,	0xf9},		/* 11111001 */
50*368c31abSDavid du Colombier };
51*368c31abSDavid du Colombier 
52*368c31abSDavid du Colombier static int	thwmaxcheck;
53*368c31abSDavid du Colombier 
54*368c31abSDavid du Colombier void
whackinit(Whack * tw,int level)55*368c31abSDavid du Colombier whackinit(Whack *tw, int level)
56*368c31abSDavid du Colombier {
57*368c31abSDavid du Colombier 	thwmaxcheck = (1 << level);
58*368c31abSDavid du Colombier 	thwmaxcheck -= thwmaxcheck >> 2;
59*368c31abSDavid du Colombier 	if(thwmaxcheck < 2)
60*368c31abSDavid du Colombier 		thwmaxcheck = 2;
61*368c31abSDavid du Colombier 	else if(thwmaxcheck > 1024)
62*368c31abSDavid du Colombier 		thwmaxcheck = 1024;
63*368c31abSDavid du Colombier 	memset(tw, 0, sizeof *tw);
64*368c31abSDavid du Colombier 	tw->begin = 2 * WhackMaxOff;
65*368c31abSDavid du Colombier }
66*368c31abSDavid du Colombier 
67*368c31abSDavid du Colombier /*
68*368c31abSDavid du Colombier  * find a string in the dictionary
69*368c31abSDavid du Colombier  */
70*368c31abSDavid du Colombier static int
whackmatch(Whack * b,uchar ** ss,uchar * esrc,ulong h,ulong now)71*368c31abSDavid du Colombier whackmatch(Whack *b, uchar **ss, uchar *esrc, ulong h, ulong now)
72*368c31abSDavid du Colombier {
73*368c31abSDavid du Colombier 	ushort then, off, last;
74*368c31abSDavid du Colombier 	int bestoff, bestlen, check;
75*368c31abSDavid du Colombier 	uchar *s, *t;
76*368c31abSDavid du Colombier 
77*368c31abSDavid du Colombier 	s = *ss;
78*368c31abSDavid du Colombier 	if(esrc < s + MinMatch)
79*368c31abSDavid du Colombier 		return -1;
80*368c31abSDavid du Colombier 	if(s + MaxLen < esrc)
81*368c31abSDavid du Colombier 		esrc = s + MaxLen;
82*368c31abSDavid du Colombier 
83*368c31abSDavid du Colombier 	bestoff = 0;
84*368c31abSDavid du Colombier 	bestlen = 0;
85*368c31abSDavid du Colombier 	check = thwmaxcheck;
86*368c31abSDavid du Colombier 	last = 0;
87*368c31abSDavid du Colombier 	for(then = b->hash[h]; check-- > 0; then = b->next[then & (WhackMaxOff - 1)]){
88*368c31abSDavid du Colombier 		off = now - then;
89*368c31abSDavid du Colombier 		if(off <= last || off > WhackMaxOff)
90*368c31abSDavid du Colombier 			break;
91*368c31abSDavid du Colombier 
92*368c31abSDavid du Colombier 		/*
93*368c31abSDavid du Colombier 		 * don't need to check for the end because
94*368c31abSDavid du Colombier 		 * 1) s too close check above
95*368c31abSDavid du Colombier 		 */
96*368c31abSDavid du Colombier 		t = s - off;
97*368c31abSDavid du Colombier 		if(s[0] == t[0] && s[1] == t[1] && s[2] == t[2]){
98*368c31abSDavid du Colombier 			if(!bestlen || esrc - s > bestlen && s[bestlen] == t[bestlen]){
99*368c31abSDavid du Colombier 				t += 3;
100*368c31abSDavid du Colombier 				for(s += 3; s < esrc; s++){
101*368c31abSDavid du Colombier 					if(*s != *t)
102*368c31abSDavid du Colombier 						break;
103*368c31abSDavid du Colombier 					t++;
104*368c31abSDavid du Colombier 				}
105*368c31abSDavid du Colombier 				if(s - *ss > bestlen){
106*368c31abSDavid du Colombier 					bestlen = s - *ss;
107*368c31abSDavid du Colombier 					bestoff = off;
108*368c31abSDavid du Colombier 					if(bestlen > thwmaxcheck)
109*368c31abSDavid du Colombier 						break;
110*368c31abSDavid du Colombier 				}
111*368c31abSDavid du Colombier 			}
112*368c31abSDavid du Colombier 		}
113*368c31abSDavid du Colombier 		s = *ss;
114*368c31abSDavid du Colombier 		last = off;
115*368c31abSDavid du Colombier 	}
116*368c31abSDavid du Colombier 	*ss += bestlen;
117*368c31abSDavid du Colombier 	return bestoff;
118*368c31abSDavid du Colombier }
119*368c31abSDavid du Colombier 
120*368c31abSDavid du Colombier /*
121*368c31abSDavid du Colombier  * knuth vol. 3 multiplicative hashing
122*368c31abSDavid du Colombier  * each byte x chosen according to rules
123*368c31abSDavid du Colombier  * 1/4 < x < 3/10, 1/3 x < < 3/7, 4/7 < x < 2/3, 7/10 < x < 3/4
124*368c31abSDavid du Colombier  * with reasonable spread between the bytes & their complements
125*368c31abSDavid du Colombier  *
126*368c31abSDavid du Colombier  * the 3 byte value appears to be as almost good as the 4 byte value,
127*368c31abSDavid du Colombier  * and might be faster on some machines
128*368c31abSDavid du Colombier  */
129*368c31abSDavid du Colombier /*
130*368c31abSDavid du Colombier #define hashit(c)	((((ulong)(c) * 0x6b43a9) >> (24 - HashLog)) & HashMask)
131*368c31abSDavid du Colombier */
132*368c31abSDavid du Colombier #define hashit(c)	(((((ulong)(c) & 0xffffff) * 0x6b43a9b5) >> (32 - HashLog)) & HashMask)
133*368c31abSDavid du Colombier 
134*368c31abSDavid du Colombier /*
135*368c31abSDavid du Colombier  * lz77 compression with single lookup in a hash table for each block
136*368c31abSDavid du Colombier  */
137*368c31abSDavid du Colombier int
whack(Whack * w,uchar * dst,uchar * src,int n,ulong stats[WhackStats])138*368c31abSDavid du Colombier whack(Whack *w, uchar *dst, uchar *src, int n, ulong stats[WhackStats])
139*368c31abSDavid du Colombier {
140*368c31abSDavid du Colombier 	uchar *s, *ss, *sss, *esrc, *half, *wdst, *wdmax;
141*368c31abSDavid du Colombier 	ulong cont, code, wbits;
142*368c31abSDavid du Colombier 	ushort now;
143*368c31abSDavid du Colombier 	int toff, lithist, h, len, bits, use, wnbits, lits, matches, offbits, lenbits;
144*368c31abSDavid du Colombier 
145*368c31abSDavid du Colombier 	if(!compressblocks || n < MinMatch)
146*368c31abSDavid du Colombier 		return -1;
147*368c31abSDavid du Colombier 
148*368c31abSDavid du Colombier 	wdst = dst;
149*368c31abSDavid du Colombier 	wdmax = dst + n;
150*368c31abSDavid du Colombier 
151*368c31abSDavid du Colombier 	now = w->begin;
152*368c31abSDavid du Colombier 	s = src;
153*368c31abSDavid du Colombier 	w->data = s;
154*368c31abSDavid du Colombier 
155*368c31abSDavid du Colombier 	cont = (s[0] << 16) | (s[1] << 8) | s[2];
156*368c31abSDavid du Colombier 
157*368c31abSDavid du Colombier 	esrc = s + n;
158*368c31abSDavid du Colombier 	half = s + (n >> 1);
159*368c31abSDavid du Colombier 	wnbits = 0;
160*368c31abSDavid du Colombier 	wbits = 0;
161*368c31abSDavid du Colombier 	lits = 0;
162*368c31abSDavid du Colombier 	matches = 0;
163*368c31abSDavid du Colombier 	offbits = 0;
164*368c31abSDavid du Colombier 	lenbits = 0;
165*368c31abSDavid du Colombier 	lithist = ~0;
166*368c31abSDavid du Colombier 	while(s < esrc){
167*368c31abSDavid du Colombier 		h = hashit(cont);
168*368c31abSDavid du Colombier 
169*368c31abSDavid du Colombier 		sss = s;
170*368c31abSDavid du Colombier 		toff = whackmatch(w, &sss, esrc, h, now);
171*368c31abSDavid du Colombier 		ss = sss;
172*368c31abSDavid du Colombier 
173*368c31abSDavid du Colombier 		len = ss - s;
174*368c31abSDavid du Colombier 		for(; wnbits >= 8; wnbits -= 8){
175*368c31abSDavid du Colombier 			if(wdst >= wdmax){
176*368c31abSDavid du Colombier 				w->begin = now;
177*368c31abSDavid du Colombier 				return -1;
178*368c31abSDavid du Colombier 			}
179*368c31abSDavid du Colombier 			*wdst++ = wbits >> (wnbits - 8);
180*368c31abSDavid du Colombier 		}
181*368c31abSDavid du Colombier 		if(len < MinMatch){
182*368c31abSDavid du Colombier 			toff = *s;
183*368c31abSDavid du Colombier 			lithist = (lithist << 1) | toff < 32 | toff > 127;
184*368c31abSDavid du Colombier 			if(lithist & 0x1e){
185*368c31abSDavid du Colombier 				wbits = (wbits << 9) | toff;
186*368c31abSDavid du Colombier 				wnbits += 9;
187*368c31abSDavid du Colombier 			}else if(lithist & 1){
188*368c31abSDavid du Colombier 				toff = (toff + 64) & 0xff;
189*368c31abSDavid du Colombier 				if(toff < 96){
190*368c31abSDavid du Colombier 					wbits = (wbits << 10) | toff;
191*368c31abSDavid du Colombier 					wnbits += 10;
192*368c31abSDavid du Colombier 				}else{
193*368c31abSDavid du Colombier 					wbits = (wbits << 11) | toff;
194*368c31abSDavid du Colombier 					wnbits += 11;
195*368c31abSDavid du Colombier 				}
196*368c31abSDavid du Colombier 			}else{
197*368c31abSDavid du Colombier 				wbits = (wbits << 8) | toff;
198*368c31abSDavid du Colombier 				wnbits += 8;
199*368c31abSDavid du Colombier 			}
200*368c31abSDavid du Colombier 			lits++;
201*368c31abSDavid du Colombier 
202*368c31abSDavid du Colombier 			/*
203*368c31abSDavid du Colombier 			 * speed hack
204*368c31abSDavid du Colombier 			 * check for compression progress, bail if none achieved
205*368c31abSDavid du Colombier 			 */
206*368c31abSDavid du Colombier 			if(s > half){
207*368c31abSDavid du Colombier 				if(4 * (s - src) < 5 * lits){
208*368c31abSDavid du Colombier 					w->begin = now;
209*368c31abSDavid du Colombier 					return -1;
210*368c31abSDavid du Colombier 				}
211*368c31abSDavid du Colombier 				half = esrc;
212*368c31abSDavid du Colombier 			}
213*368c31abSDavid du Colombier 
214*368c31abSDavid du Colombier 			if(s + MinMatch <= esrc){
215*368c31abSDavid du Colombier 				w->next[now & (WhackMaxOff - 1)] = w->hash[h];
216*368c31abSDavid du Colombier 				w->hash[h] = now;
217*368c31abSDavid du Colombier 				if(s + MinMatch < esrc)
218*368c31abSDavid du Colombier 					cont = (cont << 8) | s[MinMatch];
219*368c31abSDavid du Colombier 			}
220*368c31abSDavid du Colombier 			now++;
221*368c31abSDavid du Colombier 			s++;
222*368c31abSDavid du Colombier 			continue;
223*368c31abSDavid du Colombier 		}
224*368c31abSDavid du Colombier 
225*368c31abSDavid du Colombier 		matches++;
226*368c31abSDavid du Colombier 
227*368c31abSDavid du Colombier 		/*
228*368c31abSDavid du Colombier 		 * length of match
229*368c31abSDavid du Colombier 		 */
230*368c31abSDavid du Colombier 		if(len > MaxLen){
231*368c31abSDavid du Colombier 			len = MaxLen;
232*368c31abSDavid du Colombier 			ss = s + len;
233*368c31abSDavid du Colombier 		}
234*368c31abSDavid du Colombier 		len -= MinMatch;
235*368c31abSDavid du Colombier 		if(len < MaxFastLen){
236*368c31abSDavid du Colombier 			bits = lentab[len].bits;
237*368c31abSDavid du Colombier 			wbits = (wbits << bits) | lentab[len].encode;
238*368c31abSDavid du Colombier 			wnbits += bits;
239*368c31abSDavid du Colombier 			lenbits += bits;
240*368c31abSDavid du Colombier 		}else{
241*368c31abSDavid du Colombier 			code = BigLenCode;
242*368c31abSDavid du Colombier 			bits = BigLenBits;
243*368c31abSDavid du Colombier 			use = BigLenBase;
244*368c31abSDavid du Colombier 			len -= MaxFastLen;
245*368c31abSDavid du Colombier 			while(len >= use){
246*368c31abSDavid du Colombier 				len -= use;
247*368c31abSDavid du Colombier 				code = (code + use) << 1;
248*368c31abSDavid du Colombier 				use <<= (bits & 1) ^ 1;
249*368c31abSDavid du Colombier 				bits++;
250*368c31abSDavid du Colombier 			}
251*368c31abSDavid du Colombier 
252*368c31abSDavid du Colombier 			wbits = (wbits << bits) | (code + len);
253*368c31abSDavid du Colombier 			wnbits += bits;
254*368c31abSDavid du Colombier 			lenbits += bits;
255*368c31abSDavid du Colombier 
256*368c31abSDavid du Colombier 			for(; wnbits >= 8; wnbits -= 8){
257*368c31abSDavid du Colombier 				if(wdst >= wdmax){
258*368c31abSDavid du Colombier 					w->begin = now;
259*368c31abSDavid du Colombier 					return -1;
260*368c31abSDavid du Colombier 				}
261*368c31abSDavid du Colombier 				*wdst++ = wbits >> (wnbits - 8);
262*368c31abSDavid du Colombier 			}
263*368c31abSDavid du Colombier 		}
264*368c31abSDavid du Colombier 
265*368c31abSDavid du Colombier 		/*
266*368c31abSDavid du Colombier 		 * offset in history
267*368c31abSDavid du Colombier 		 */
268*368c31abSDavid du Colombier 		toff--;
269*368c31abSDavid du Colombier 		for(bits = MinOffBits; toff >= (1 << bits); bits++)
270*368c31abSDavid du Colombier 			;
271*368c31abSDavid du Colombier 		if(bits < MaxOffBits-1){
272*368c31abSDavid du Colombier 			wbits = (wbits << 3) | (bits - MinOffBits);
273*368c31abSDavid du Colombier 			if(bits != MinOffBits)
274*368c31abSDavid du Colombier 				bits--;
275*368c31abSDavid du Colombier 			wnbits += bits + 3;
276*368c31abSDavid du Colombier 			offbits += bits + 3;
277*368c31abSDavid du Colombier 		}else{
278*368c31abSDavid du Colombier 			wbits = (wbits << 4) | 0xe | (bits - (MaxOffBits-1));
279*368c31abSDavid du Colombier 			bits--;
280*368c31abSDavid du Colombier 			wnbits += bits + 4;
281*368c31abSDavid du Colombier 			offbits += bits + 4;
282*368c31abSDavid du Colombier 		}
283*368c31abSDavid du Colombier 		wbits = (wbits << bits) | toff & ((1 << bits) - 1);
284*368c31abSDavid du Colombier 
285*368c31abSDavid du Colombier 		for(; s != ss; s++){
286*368c31abSDavid du Colombier 			if(s + MinMatch <= esrc){
287*368c31abSDavid du Colombier 				h = hashit(cont);
288*368c31abSDavid du Colombier 				w->next[now & (WhackMaxOff - 1)] = w->hash[h];
289*368c31abSDavid du Colombier 				w->hash[h] = now;
290*368c31abSDavid du Colombier 				if(s + MinMatch < esrc)
291*368c31abSDavid du Colombier 					cont = (cont << 8) | s[MinMatch];
292*368c31abSDavid du Colombier 			}
293*368c31abSDavid du Colombier 			now++;
294*368c31abSDavid du Colombier 		}
295*368c31abSDavid du Colombier 	}
296*368c31abSDavid du Colombier 
297*368c31abSDavid du Colombier 	w->begin = now;
298*368c31abSDavid du Colombier 
299*368c31abSDavid du Colombier 	stats[StatBytes] += esrc - src;
300*368c31abSDavid du Colombier 	stats[StatLits] += lits;
301*368c31abSDavid du Colombier 	stats[StatMatches] += matches;
302*368c31abSDavid du Colombier 	stats[StatLitBits] += (wdst - (dst + 2)) * 8 + wnbits - offbits - lenbits;
303*368c31abSDavid du Colombier 	stats[StatOffBits] += offbits;
304*368c31abSDavid du Colombier 	stats[StatLenBits] += lenbits;
305*368c31abSDavid du Colombier 
306*368c31abSDavid du Colombier 	if(wnbits & 7){
307*368c31abSDavid du Colombier 		wbits <<= 8 - (wnbits & 7);
308*368c31abSDavid du Colombier 		wnbits += 8 - (wnbits & 7);
309*368c31abSDavid du Colombier 	}
310*368c31abSDavid du Colombier 	for(; wnbits >= 8; wnbits -= 8){
311*368c31abSDavid du Colombier 		if(wdst >= wdmax)
312*368c31abSDavid du Colombier 			return -1;
313*368c31abSDavid du Colombier 		*wdst++ = wbits >> (wnbits - 8);
314*368c31abSDavid du Colombier 	}
315*368c31abSDavid du Colombier 
316*368c31abSDavid du Colombier 	stats[StatOutBytes] += wdst - dst;
317*368c31abSDavid du Colombier 
318*368c31abSDavid du Colombier 	return wdst - dst;
319*368c31abSDavid du Colombier }
320*368c31abSDavid du Colombier 
321*368c31abSDavid du Colombier int
whackblock(uchar * dst,uchar * src,int ssize)322*368c31abSDavid du Colombier whackblock(uchar *dst, uchar *src, int ssize)
323*368c31abSDavid du Colombier {
324*368c31abSDavid du Colombier 	Whack w;
325*368c31abSDavid du Colombier 	ulong stats[MaxStat];
326*368c31abSDavid du Colombier 	int r;
327*368c31abSDavid du Colombier 
328*368c31abSDavid du Colombier 	whackinit(&w, 6);
329*368c31abSDavid du Colombier 	r = whack(&w, dst, src, ssize, stats);
330*368c31abSDavid du Colombier 	return r;
331*368c31abSDavid du Colombier }
332