xref: /inferno-os/appl/lib/inflate.b (revision ce02cfb2169def0df6778d9be67d18545647bff4)
1# gzip-compatible decompression filter.
2
3implement Filter;
4
5include "sys.m";
6	sys:	Sys;
7include "filter.m";
8
9GZMAGIC1:	con byte 16r1f;
10GZMAGIC2:	con byte 16r8b;
11
12GZDEFLATE:	con byte 8;
13
14GZFTEXT:	con 1 << 0;		# file is text
15GZFHCRC:	con 1 << 1;		# crc of header included
16GZFEXTRA:	con 1 << 2;		# extra header included
17GZFNAME:	con 1 << 3;		# name of file included
18GZFCOMMENT:	con 1 << 4;		# header comment included
19GZFMASK:	con (1 << 5) - 1;	# mask of specified bits
20
21GZXBEST:	con byte 2;		# used maximum compression algorithm
22GZXFAST:	con byte 4;		# used fast algorithm little compression
23
24GZOSFAT:	con byte 0;		# FAT file system
25GZOSAMIGA:	con byte 1;		# Amiga
26GZOSVMS:	con byte 2;		# VMS or OpenVMS
27GZOSUNIX:	con byte 3;		# Unix
28GZOSVMCMS:	con byte 4;		# VM/CMS
29GZOSATARI:	con byte 5;		# Atari TOS
30GZOSHPFS:	con byte 6;		# HPFS file system
31GZOSMAC:	con byte 7;		# Macintosh
32GZOSZSYS:	con byte 8;		# Z-System
33GZOSCPM:	con byte 9;		# CP/M
34GZOSTOPS20:	con byte 10;		# TOPS-20
35GZOSNTFS:	con byte 11;		# NTFS file system
36GZOSQDOS:	con byte 12;		# QDOS
37GZOSACORN:	con byte 13;		# Acorn RISCOS
38GZOSUNK:	con byte 255;
39
40GZCRCPOLY:	con int 16redb88320;
41GZOSINFERNO:	con GZOSUNIX;
42
43# huffman code table
44Huff: adt
45{
46	bits:		int;		# length of the code
47	encode:		int;		# the code
48};
49
50# huffman decode table
51DeHuff: adt
52{
53	l1:		array of L1;	# the table
54	nb1:		int;		# no. of bits in first level
55	nb2:		int;		# no. of bits in second level
56};
57
58# first level of decode table
59L1: adt
60{
61	bits:		int;		# length of the code
62	decode:		int;		# the symbol
63	l2:		array of L2;
64};
65
66# second level
67L2: adt
68{
69	bits:		int;		# length of the code
70	decode:		int;		# the symbol
71};
72
73DeflateUnc:	con 0;			# uncompressed block
74DeflateFix:	con 1;			# fixed huffman codes
75DeflateDyn:	con 2;			# dynamic huffman codes
76DeflateErr:	con 3;			# reserved BTYPE (error)
77
78DeflateEob:	con 256;		# end of block code in lit/len book
79
80LenStart:	con 257;		# start of length codes in litlen
81LenEnd:		con 285;		# greatest valid length code
82Nlitlen:	con 288;		# number of litlen codes
83Noff:		con 30;			# number of offset codes
84Nclen:		con 19;			# number of codelen codes
85
86MaxHuffBits:	con 15;			# max bits in a huffman code
87RunlenBits:	con 7;			# max bits in a run-length huffman code
88MaxOff:		con 32*1024;		# max lempel-ziv distance
89
90Blocksize: con 32 * 1024;
91
92# tables from RFC 1951, section 3.2.5
93litlenbase := array[Noff] of
94{
95	3, 4, 5, 6, 7, 8, 9, 10, 11, 13,
96	15, 17, 19, 23, 27, 31, 35, 43, 51, 59,
97	67, 83, 99, 115, 131, 163, 195, 227, 258
98};
99
100litlenextra := array[Noff] of
101{
102	0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
103	2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4,
104	5, 5, 5, 5, 0
105};
106
107offbase := array[Noff] of
108{
109	1, 2, 3, 4, 5, 7, 9, 13, 17, 25,
110	33, 49, 65, 97, 129, 193, 257, 385, 513, 769,
111	1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577
112};
113
114offextra := array[Noff] of
115{
116	0,  0,  0,  0,  1,  1,  2,  2,  3,  3,
117	4,  4,  5,  5,  6,  6,  7,  7,  8,  8,
118	9,  9,  10, 10, 11, 11, 12, 12, 13, 13
119};
120
121# order of run-length codes
122clenorder := array[Nclen] of
123{
124	16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
125};
126
127# fixed huffman tables
128litlentab: array of Huff;
129offtab: array of Huff;
130
131# their decoding table counterparts
132litlendec: ref DeHuff;
133offdec: ref DeHuff;
134
135revtab: array of byte;	# bit reversal for endian swap of huffman codes
136mask: array of int;		# for masking low-order n bits of an int
137
138Hnone, Hgzip, Hzlib: con iota;  # State.headers
139State: adt {
140	ibuf, obuf: array of byte;
141	c: chan of ref Rq;
142	rc: chan of int;
143	in: int;		# next byte to consume from input buffer
144	ein: int;		# valid bytes in input buffer
145	out: int;		# valid bytes in output buffer
146	hist: array of byte;	# history buffer for lempel-ziv backward references
147	usehist: int;		# == 1 if 'hist' is valid
148	crctab: array of int;
149	crc, tot: int;		# for gzip trailer
150	sum:	big;		# for zlib trailer
151
152	reg: int;		# 24-bit shift register
153	nbits: int;		# number of valid bits in reg
154	svreg: int;		# save reg for efficient ungets
155	svn: int;		# number of bits gotten in last call to getn()
156	# reg bits are consumed from right to left
157	# so low-order byte of reg came first in the input stream
158	headers: int;
159};
160
161
162init()
163{
164	sys = load Sys Sys->PATH;
165
166	# byte reverse table
167	revtab = array[256] of byte;
168	for(i := 0; i < 256; i++){
169		revtab[i] = byte 0;
170		for(j := 0; j < 8; j++) {
171			if(i & (1 << j))
172				revtab[i] |= byte 16r80 >> j;
173		}
174	}
175
176	# bit-masking table
177	mask = array[MaxHuffBits+1] of int;
178	for(i = 0; i <= MaxHuffBits; i++)
179		mask[i] = (1 << i) - 1;
180
181	litlentab = array[Nlitlen] of Huff;
182
183	# static litlen bit lengths
184	for(i = 0; i < 144; i++)
185		litlentab[i].bits = 8;
186	for(i = 144; i < 256; i++)
187		litlentab[i].bits = 9;
188	for(i = 256; i < 280; i++)
189		litlentab[i].bits = 7;
190	for(i = 280; i < Nlitlen; i++)
191		litlentab[i].bits = 8;
192
193	bitcount := array[MaxHuffBits+1] of { * => 0 };
194	bitcount[8] += 144 - 0;
195	bitcount[9] += 256 - 144;
196	bitcount[7] += 280 - 256;
197	bitcount[8] += Nlitlen - 280;
198
199	hufftabinit(litlentab, Nlitlen, bitcount, 9);
200	litlendec = decodeinit(litlentab, Nlitlen, 9, 0);
201
202	offtab = array[Noff] of Huff;
203
204	# static offset bit lengths
205	for(i = 0; i < Noff; i++)
206		offtab[i].bits = 5;
207
208	for(i = 0; i < 5; i++)
209		bitcount[i] = 0;
210	bitcount[5] = Noff;
211
212	hufftabinit(offtab, Noff, bitcount, 5);
213	offdec = decodeinit(offtab, Noff, 5, 0);
214}
215
216start(params: string): chan of ref Rq
217{
218	s := ref State;
219	s.c = chan of ref Rq;
220	s.rc = chan of int;
221	s.ibuf = array[Blocksize] of byte;
222	s.obuf = array[Blocksize] of byte;
223	s.in = 0;
224	s.ein = 0;
225	s.out = 0;
226	s.usehist = 0;
227	s.reg = 0;
228	s.nbits = 0;
229	s.crc = 0;
230	s.tot = 0;
231	s.sum = big 1;
232	s.hist = array[Blocksize] of byte;
233	s.headers = Hnone;
234	if(params != nil) {
235		if(params[0] == 'h')
236			s.headers = Hgzip;
237		if(params[0] == 'z')
238			s.headers = Hzlib;
239	}
240	if (s.headers == Hgzip)
241		s.crctab = mkcrctab(GZCRCPOLY);
242	spawn inflate(s);
243	return s.c;
244}
245
246inflate(s: ref State)
247{
248	s.c <-= ref Rq.Start(sys->pctl(0, nil));
249	header(s);
250
251	for(;;) {
252		bfinal := getn(s, 1, 0);
253		btype := getn(s, 2, 0);
254		case(btype) {
255		DeflateUnc =>
256			flushbits(s);
257			unclen := getb(s);
258			unclen |= getb(s) << 8;
259			nlen := getb(s);
260			nlen |= getb(s) << 8;
261			if(unclen != (~nlen & 16rFFFF))
262				fatal(s, "corrupted data");
263			for(; unclen > 0; unclen--) {
264				# inline putb(s, getb(s));
265				b := byte getb(s);
266				if(s.out >= MaxOff)
267					flushout(s);
268				s.obuf[s.out++] = b;
269			}
270		DeflateFix =>
271			decodeblock(s, litlendec, offdec);
272		DeflateDyn =>
273			dynhuff(s);
274		DeflateErr =>
275			fatal(s, "bad block type");
276		}
277		if(bfinal) {
278			if(s.out) {
279				outblock(s);
280				s.c <- = ref Rq.Result(s.obuf[0:s.out], s.rc);
281				flag := <- s.rc;
282				if (flag == -1)
283					exit;
284			}
285			flushbits(s);
286			footer(s);
287			s.c <-= ref Rq.Finished(s.ibuf[s.in - s.nbits/8:s.ein]);
288			exit;
289		}
290	}
291}
292
293headergzip(s: ref State)
294{
295	if(byte getb(s) != GZMAGIC1 || byte getb(s) != GZMAGIC2)
296		fatal(s, "not a gzip file");
297
298	if(byte getb(s) != GZDEFLATE)
299		fatal(s, "not compressed with deflate");
300
301	flags := getb(s);
302	if(flags & ~GZFMASK)
303		fatal(s, "reserved flag bits set");
304
305	# read modification time (ignored)
306	mtime := getb(s);
307	mtime |= (getb(s) << 8);
308	mtime |= (getb(s) << 16);
309	mtime |= (getb(s) << 24);
310	s.c <-= ref Rq.Info("mtime " + string mtime);
311	getb(s);	# xfl
312	getb(s);	# os
313
314	# skip optional "extra field"
315	if(flags & GZFEXTRA) {
316		skip := getb(s);
317		skip |= getb(s) << 8;
318		while (skip-- > 0)
319			getb(s);
320	}
321
322	# read optional filename (ignored)
323	file: string;
324	if(flags & GZFNAME){
325		n := 0;
326		while(c := getb(s))
327			file[n++] = c;
328		s.c <-= ref Rq.Info("file " + file);
329	}
330
331	# skip optional comment
332	if(flags & GZFCOMMENT) {
333		while(getb(s))
334			;
335	}
336
337	# skip optional CRC16 field
338	if(flags & GZFHCRC) {
339		getb(s);
340		getb(s);
341	}
342}
343
344headerzlib(s: ref State)
345{
346	Fdict:		con 1<<5;
347	CMshift:	con 8;
348	CMmask:		con (1<<4)-1;
349	CMdeflate:	con 8;
350
351	h := 0;
352	h |= getb(s)<<8;
353	h |= getb(s);
354	if(h % 31 != 0)
355		fatal(s, "invalid zlib header");
356	if(h&Fdict)
357		fatal(s, "preset dictionary not supported");
358	if(((h>>CMshift)&CMmask) != CMdeflate)
359		fatal(s, "zlib compression method not deflate");
360}
361
362header(s: ref State)
363{
364	case s.headers {
365	Hgzip =>	headergzip(s);
366	Hzlib =>	headerzlib(s);
367	}
368}
369
370footergzip(s: ref State)
371{
372	fcrc := getword(s);
373	if(s.crc != fcrc)
374		fatal(s, sys->sprint("crc mismatch: computed %ux, expected %ux", s.crc, fcrc));
375	ftot := getword(s);
376	if(s.tot != ftot)
377		fatal(s, sys->sprint("byte count mismatch: computed %d, expected %d", s.tot, ftot));
378}
379
380footerzlib(s: ref State)
381{
382	sum := big 0;
383	sum = (sum<<8)|big getb(s);
384	sum = (sum<<8)|big getb(s);
385	sum = (sum<<8)|big getb(s);
386	sum = (sum<<8)|big getb(s);
387	if(sum != s.sum)
388		fatal(s, sys->sprint("adler32 mismatch: computed %bux, expected %bux", s.sum, sum));
389}
390
391footer(s: ref State)
392{
393	case s.headers {
394	Hgzip =>	footergzip(s);
395	Hzlib =>	footerzlib(s);
396	}
397}
398
399getword(s: ref State): int
400{
401	n := 0;
402	for(i := 0; i < 4; i++)
403		n |= getb(s) << (8 * i);
404	return n;
405}
406
407#
408# uncompress a block using given huffman decoding tables
409#
410decodeblock(s: ref State, litlendec, offdec: ref DeHuff)
411{
412	b: byte;
413
414	for(;;) {
415		sym := decodesym(s, litlendec);
416		if(sym < DeflateEob) {		# literal byte
417			# inline putb(s, byte sym);
418			b = byte sym;
419			if(s.out >= MaxOff)
420				flushout(s);
421			s.obuf[s.out++] = b;
422		} else if(sym == DeflateEob) {	# End-of-block
423			break;
424		} else {			# lempel-ziv <length, distance>
425			if(sym > LenEnd)
426				fatal(s, "symbol too long");
427			xbits := litlenextra[sym - LenStart];
428			xtra := 0;
429			if(xbits)
430				xtra = getn(s, xbits, 0);
431			length := litlenbase[sym - LenStart] + xtra;
432
433			sym = decodesym(s, offdec);
434			if(sym >= Noff)
435				fatal(s, "symbol too long");
436			xbits = offextra[sym];
437			if(xbits)
438				xtra = getn(s, xbits, 0);
439			else
440				xtra = 0;
441			dist := offbase[sym] + xtra;
442			if(dist > s.out && s.usehist == 0)
443				fatal(s, "corrupted data");
444			for(i := 0; i < length; i++) {
445				# inline putb(lzbyte(dist));
446				ix := s.out - dist;
447				if(dist <= s.out)
448					b = s.obuf[ix];
449				else
450					b = s.hist[MaxOff + ix];
451				if(s.out >= MaxOff)
452					flushout(s);
453				s.obuf[s.out++] = b;
454			}
455		}
456	}
457}
458
459#
460# decode next symbol in input stream using given huffman decoding table
461#
462decodesym(s: ref State, dec: ref DeHuff): int
463{
464	code, bits, n: int;
465
466	l1 := dec.l1;
467	nb1 := dec.nb1;
468	nb2 := dec.nb2;
469
470	code = getn(s, nb1, 1);
471	l2 := l1[code].l2;
472	if(l2 == nil) {		# first level table has answer
473		bits = l1[code].bits;
474		if(bits == 0)
475			fatal(s, "corrupt data");
476		if(nb1 > bits) {
477			# inline ungetn(nb1 - bits);
478			n = nb1 - bits;
479			s.reg = s.svreg >> (s.svn - n);
480			s.nbits += n;
481		}
482		return l1[code].decode;
483	}
484	# must advance to second-level table
485	code = getn(s, nb2, 1);
486	bits = l2[code].bits;
487	if(bits == 0)
488		fatal(s, "corrupt data");
489	if(nb1 + nb2 > bits) {
490		# inline ungetn(nb1 + nb2 - bits);
491		n = nb1 + nb2 - bits;
492		s.reg = s.svreg >> (s.svn - n);
493		s.nbits += n;
494	}
495	return l2[code].decode;
496}
497
498#
499# uncompress a block that was encoded with dynamic huffman codes
500# RFC 1951, section 3.2.7
501#
502dynhuff(s: ref State)
503{
504	hlit := getn(s, 5, 0) + 257;
505	hdist := getn(s, 5, 0) + 1;
506	hclen := getn(s, 4, 0) + 4;
507	if(hlit > Nlitlen || hlit < 257 || hdist > Noff)
508		fatal(s, "corrupt data");
509
510	runlentab := array[Nclen] of { * => Huff(0, 0) };
511	count := array[RunlenBits+1] of { * => 0 };
512	for(i := 0; i < hclen; i++) {
513		nb := getn(s, 3, 0);
514		if(nb) {
515			runlentab[clenorder[i]].bits = nb;
516			count[nb]++;
517		}
518	}
519	hufftabinit(runlentab, Nclen, count, RunlenBits);
520	runlendec := decodeinit(runlentab, Nclen, RunlenBits, 0);
521	if(runlendec == nil)
522		fatal(s, "corrupt data");
523
524	lengths := decodelen(s, runlendec, hlit+hdist);
525	if(lengths == nil)
526		fatal(s, "corrupt length table");
527
528	dlitlendec := decodedyn(s, lengths[0:hlit], hlit, 9);
529	doffdec := decodedyn(s, lengths[hlit:], hdist, 5);
530	decodeblock(s, dlitlendec, doffdec);
531}
532
533#
534# return the decoded combined length table for literal and distance alphabets
535#
536decodelen(s: ref State, runlendec: ref DeHuff, nlen: int): array of int
537{
538	lengths := array[nlen] of int;
539	for(n := 0; n < nlen;) {
540		nb := decodesym(s, runlendec);
541		nr := 1;
542		case nb {
543		0 to 15 =>
544			;
545		16 =>
546			nr = getn(s, 2, 0) + 3;
547			if(n == 0)
548				return nil;
549			nb = lengths[n-1];
550		17 =>
551			nr = getn(s, 3, 0) + 3;
552			nb = 0;
553		18 =>
554			nr = getn(s, 7, 0) + 11;
555			nb = 0;
556		* =>
557			return nil;
558		}
559		if(n+nr > nlen)
560			return nil;
561		while(--nr >= 0)
562			lengths[n++] = nb;
563	}
564	return lengths;
565}
566
567#
568# (1) read a dynamic huffman code from the input stream
569# (2) decode it using the run-length huffman code
570# (3) return the decode table for the dynamic huffman code
571#
572decodedyn(s: ref State, lengths: array of int, nlen, nb1: int): ref DeHuff
573{
574	hufftab := array[nlen] of Huff;
575	count := array[MaxHuffBits+1] of { * => 0 };
576
577	maxnb := 0;
578	for(n := 0; n < nlen; n++) {
579		c := lengths[n];
580		if(c) {
581			hufftab[n].bits = c;
582			count[c]++;
583			if(c > maxnb)
584				maxnb = c;
585		}else
586			hufftab[n].bits = 0;
587		hufftab[n].encode = 0;
588	}
589	hufftabinit(hufftab, nlen, count, maxnb);
590	nb2 := 0;
591	if(maxnb > nb1)
592		nb2 = maxnb - nb1;
593	d := decodeinit(hufftab, nlen, nb1, nb2);
594	if (d == nil)
595		fatal(s, "decodeinit failed");
596	return d;
597}
598
599#
600# RFC 1951, section 3.2.2
601#
602hufftabinit(tab: array of Huff, n: int, bitcount: array of int, nbits: int)
603{
604	nc := array[MaxHuffBits+1] of int;
605
606	code := 0;
607	for(bits := 1; bits <= nbits; bits++) {
608		code = (code + bitcount[bits-1]) << 1;
609		nc[bits] = code;
610	}
611
612	for(i := 0; i < n; i++) {
613		bits = tab[i].bits;
614		# differences from Deflate module:
615		#  (1) leave huffman code right-justified in encode
616		#  (2) don't reverse it
617		if(bits != 0)
618			tab[i].encode = nc[bits]++;
619	}
620}
621
622#
623# convert 'array of Huff' produced by hufftabinit()
624# into 2-level lookup table for decoding
625#
626# nb1(nb2): number of bits handled by first(second)-level table
627#
628decodeinit(tab: array of Huff, n, nb1, nb2: int): ref DeHuff
629{
630	i, j, k, d: int;
631
632	dehuff := ref DeHuff(array[1<<nb1] of { * => L1(0, 0, nil) }, nb1, nb2);
633	l1 := dehuff.l1;
634	for(i = 0; i < n; i++) {
635		bits := tab[i].bits;
636		if(bits == 0)
637			continue;
638		l1x := tab[i].encode;
639		if(l1x >= (1 << bits))
640			return nil;
641		if(bits <= nb1) {
642			d = nb1 - bits;
643			l1x <<= d;
644			k = l1x + mask[d];
645			for(j = l1x; j <= k; j++) {
646				l1[j].decode = i;
647				l1[j].bits = bits;
648			}
649			continue;
650		}
651		# advance to second-level table
652		d = bits - nb1;
653		l2x := l1x & mask[d];
654		l1x >>= d;
655		if(l1[l1x].l2 == nil)
656			l1[l1x].l2 = array[1<<nb2] of { * => L2(0, 0) };
657		l2 := l1[l1x].l2;
658		d = (nb1 + nb2) - bits;
659		l2x <<= d;
660		k = l2x + mask[d];
661		for(j = l2x; j <= k; j++) {
662			l2[j].decode = i;
663			l2[j].bits = bits;
664		}
665	}
666
667	return dehuff;
668}
669
670#
671# get next byte from reg
672# assumptions:
673#  (1) flushbits() has been called
674#  (2) ungetn() won't be called after a getb()
675#
676getb(s: ref State): int
677{
678	if(s.nbits < 8)
679		need(s, 8);
680	b := byte s.reg;
681	s.reg >>= 8;
682	s.nbits -= 8;
683	return int b;
684}
685
686#
687# get next n bits from reg; if r != 0, reverse the bits
688#
689getn(s: ref State, n, r: int): int
690{
691	if(s.nbits < n)
692		need(s, n);
693	s.svreg = s.reg;
694	s.svn = n;
695	i := s.reg & mask[n];
696	s.reg >>= n;
697	s.nbits -= n;
698	if(r) {
699		if(n <= 8) {
700			i = int revtab[i];
701			i >>= 8 - n;
702		} else {
703			i = ((int revtab[i & 16rff]) << 8)
704				| (int revtab[i >> 8]);
705			i >>= 16 - n;
706		}
707	}
708	return i;
709}
710
711#
712# ensure that at least n bits are available in reg
713#
714need(s: ref State, n: int)
715{
716	while(s.nbits < n) {
717		if(s.in >= s.ein) {
718			s.c <-= ref Rq.Fill(s.ibuf, s.rc);
719			s.ein = <- s.rc;
720			if (s.ein < 0)
721				exit;
722			if (s.ein == 0)
723				fatal(s, "premature end of stream");
724			s.in = 0;
725		}
726		s.reg = ((int s.ibuf[s.in++]) << s.nbits) | s.reg;
727		s.nbits += 8;
728	}
729}
730
731#
732# if partial byte consumed from reg, dispose of remaining bits
733#
734flushbits(s: ref State)
735{
736	drek := s.nbits % 8;
737	if(drek) {
738		s.reg >>= drek;
739		s.nbits -= drek;
740	}
741}
742
743#
744# output buffer is full, so flush it
745#
746flushout(s: ref State)
747{
748	outblock(s);
749	s.c <-= ref Rq.Result(s.obuf[0:s.out], s.rc);
750	flag := <- s.rc;
751	if (flag == -1)
752		exit;
753	buf := s.hist;
754	s.hist = s.obuf;
755	s.usehist = 1;
756	s.obuf = buf;
757	s.out = 0;
758}
759
760mkcrctab(poly: int): array of int
761{
762	crctab := array[256] of int;
763	for(i := 0; i < 256; i++){
764		crc := i;
765		for(j := 0; j < 8; j++){
766			c := crc & 1;
767			crc = (crc >> 1) & 16r7fffffff;
768			if(c)
769				crc ^= poly;
770		}
771		crctab[i] = crc;
772	}
773	return crctab;
774}
775
776outblockgzip(s: ref State)
777{
778	buf := s.obuf;
779	n := s.out;
780	crc := s.crc;
781	crc ^= int 16rffffffff;
782	for(i := 0; i < n; i++)
783		crc = s.crctab[int(byte crc ^ buf[i])] ^ ((crc >> 8) & 16r00ffffff);
784	s.crc = crc ^ int 16rffffffff;
785	s.tot += n;
786}
787
788outblockzlib(s: ref State)
789{
790	ZLADLERBASE:	con big 65521;
791
792	buf := s.obuf;
793	n := s.out;
794
795	s1 := s.sum & big 16rffff;
796	s2 := (s.sum>>16) & big 16rffff;
797
798	for(i := 0; i < n; i++) {
799		s1 = (s1 + big buf[i]) % ZLADLERBASE;
800		s2 = (s2 + s1) % ZLADLERBASE;
801	}
802	s.sum = (s2<<16) + s1;
803}
804
805outblock(s: ref State)
806{
807	case s.headers {
808	Hgzip =>	outblockgzip(s);
809	Hzlib =>	outblockzlib(s);
810	}
811}
812
813#
814# irrecoverable error; invariably denotes data corruption
815#
816fatal(s: ref State, e: string)
817{
818	s.c <-= ref Rq.Error(e);
819	exit;
820}
821