egra: checkbox cosmetix
[iv.d.git] / vfs / inflate.d
blob79175c32beb0194c9827a0479399677b46cfd17c
1 /*
2 * tinf - tiny inflate library (inflate, gzip, zlib)
3 * version 1.00
5 * Copyright (c) 2003 by Joergen Ibsen / Jibz
6 * All Rights Reserved
8 * http://www.ibsensoftware.com/
10 * This software is provided 'as-is', without any express
11 * or implied warranty. In no event will the authors be
12 * held liable for any damages arising from the use of
13 * this software.
15 * Permission is granted to anyone to use this software
16 * for any purpose, including commercial applications,
17 * and to alter it and redistribute it freely, subject to
18 * the following restrictions:
20 * 1. The origin of this software must not be
21 * misrepresented; you must not claim that you
22 * wrote the original software. If you use this
23 * software in a product, an acknowledgment in
24 * the product documentation would be appreciated
25 * but is not required.
27 * 2. Altered source versions must be plainly marked
28 * as such, and must not be misrepresented as
29 * being the original software.
31 * 3. This notice may not be removed or altered from
32 * any source distribution.
35 * This D port was made by Ketmar // Invisible Vector
36 * ketmar@ketmar.no-ip.org
37 * the original code was heavily changed ang uglified (sorry!)
39 module iv.vfs.inflate /*is aliced*/;
40 import iv.alice;
43 ////////////////////////////////////////////////////////////////////////////////
45 * Adler-32 algorithm taken from the zlib source, which is
46 * Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler
48 /// Adler-32 checksum computer
49 struct Adler32 {
50 nothrow @safe @nogc:
51 private:
52 enum { BASE = 65521, NMAX = 5552 }
53 enum normS1S2 = `s1 %= BASE; s2 %= BASE;`;
55 template genByteProcessors(int count) {
56 static if (count > 0)
57 enum genByteProcessors = `s1 += *dta++;s2 += s1;`~genByteProcessors!(count-1);
58 else
59 enum genByteProcessors = "";
62 uint s1 = 1;
63 uint s2 = 0;
65 public:
66 /**
67 * Computes the Adler-32 checksum. We can do `Adler32(buf)`.
69 * Params:
70 * data = input data
72 * Returns:
73 * adler32 checksum
75 static uint opCall(T) (T[] data) {
76 Adler32 a32;
77 a32.doBuffer(data);
78 return a32.result;
81 /// reinitialize
82 void reset () {
83 s1 = 1;
84 s2 = 0;
87 /// get current Adler-32 sum
88 @property uint result () const pure { return ((s2<<16)|s1); }
90 /// process buffer
91 void doBuffer(T) (T[] data) @trusted {
92 if (data.length == 0) return;
93 usize len = data.length*data[0].sizeof; // length in bytes
94 const(ubyte)* dta = cast(const(ubyte)*)data.ptr;
95 foreach (immutable _; 0..len/NMAX) {
96 foreach (immutable _; 0..NMAX/16) { mixin(genByteProcessors!(16)); }
97 mixin(normS1S2);
99 len %= NMAX;
100 if (len) {
101 foreach (immutable _; 0..len) { mixin(genByteProcessors!(1)); }
102 mixin(normS1S2);
106 /// process one byte
107 void doByte(T) (T bt)
108 // sorry
109 if (is(T == char) || is(T == byte) || is(T == ubyte) ||
110 is(T == const char) || is(T == const byte) || is(T == const ubyte) ||
111 is(T == immutable char) || is(T == immutable byte) || is(T == immutable ubyte))
113 s1 += cast(ubyte)bt;
114 s2 += s1;
115 mixin(normS1S2);
121 * Computes the Adler-32 checksum.
123 * Params:
124 * data = input data
126 * Returns:
127 * adler32 checksum
129 alias adler32 = Adler32;
132 // -----------------------------------------------------------------------------
133 // internal data structures
135 private struct Tree {
136 ushort[16] table; // table of code length counts
137 ushort[288] trans; // code -> symbol translation table
139 @disable this (this); // disable copying
141 // given an array of code lengths, build a tree
142 void buildTree (const(ubyte)[] lengths) @trusted {
143 if (lengths.length < 1) throw new Exception("invalid lengths");
144 ushort[16] offs = void;
145 ushort sum = 0;
146 // clear code length count table
147 table[] = 0;
148 // scan symbol lengths, and sum code length counts
149 foreach (immutable l; lengths) {
150 if (l >= 16) throw new Exception("invalid lengths");
151 ++table.ptr[l];
153 table.ptr[0] = 0;
154 // compute offset table for distribution sort
155 foreach (immutable i, immutable n; table) {
156 offs.ptr[i] = sum;
157 sum += n;
159 // create code->symbol translation table (symbols sorted by code)
160 foreach (ushort i, immutable l; lengths) {
161 if (l) {
162 auto n = offs.ptr[l]++;
163 if (n >= 288) throw new Exception("invalid lengths");
164 trans.ptr[n] = i;
171 // -----------------------------------------------------------------------------
172 // stream of unpacked data
174 struct InfStream {
175 public:
176 alias ReadBufDg = int delegate (ubyte[] buf);
178 private:
179 // return number of bytes read, -1 on error, 0 on eof; can read less than requested
180 //ReadBufDg readBuf = null;
182 // state data
183 uint bytesLeft = void; // bytes to copy both for compressed and for uncompressed blocks
184 uint matchOfs = void; // match offset for inflated block
185 const(Tree)* lt = void; // dynamic length/symbol tree
186 const(Tree)* dt = void; // dynamic distance tree
187 bool doingFinalBlock = false; // stop on next processBlockHeader()
189 // current state
190 enum State {
191 ExpectZLibHeader, // expecting ZLib header
192 ExpectBlock, // expecting new block
193 RawBlock, // uncompressed block
194 CompressedBlock,
195 EOF, // readOneByte() returns false before block header
196 Dead, // some error occured, throw exception on any read
198 State state = State.ExpectZLibHeader;
199 Mode mode = Mode.ZLib;
201 // other data
202 ushort tag;
203 int bitcount;
205 // trees
206 Tree ltree; // dynamic length/symbol tree
207 Tree dtree; // dynamic distance tree
209 // adler-32
210 uint a32s1, a32s2;
211 uint nmaxLeft = Adler32.NMAX;
212 uint cura32;
214 // dictionary
215 ubyte[65536] dict = void;
216 uint dictEnd; // current dict free byte
218 ubyte[65536] rdbuf = void;
219 int rbpos, rbused;
220 bool rbeof;
222 // bt: arg
223 enum DictPutByteMixin = q{{
224 if (dictEnd == dict.length) {
225 import core.stdc.string : memmove;
226 // move dict data
227 memmove(dict.ptr, dict.ptr+dictEnd-32768, 32768);
228 dictEnd = 32768;
230 dict.ptr[dictEnd++] = bt;
231 if (mode == Mode.ZLib) {
232 a32s1 += bt;
233 a32s2 += a32s1;
234 if (--nmaxLeft == 0) {
235 nmaxLeft = Adler32.NMAX;
236 a32s1 %= Adler32.BASE;
237 a32s2 %= Adler32.BASE;
242 uint finishAdler32 () nothrow @safe @nogc {
243 pragma(inline, true);
244 a32s1 %= Adler32.BASE;
245 a32s2 %= Adler32.BASE;
246 return (a32s2<<16)|a32s1;
249 static template StrOr(string v, string def) {
250 static if (v.length == 0) enum StrOr = def; else enum StrOr = v;
253 enum ReadOneByteMixin(string dvar, string oneof="") = "{
254 //if (readBuf is null) rbeof = true;
255 if (rbeof) {
256 "~StrOr!(oneof, `throw new Exception("out of data for inflate");`)~"
258 if (rbpos >= rbused) {
259 rbpos = 0;
260 if ((rbused = readBuf(rdbuf[])) <= 0) {
261 rbeof = true;
262 if (rbused < 0) throw new Exception(`inflate read error`);
263 "~StrOr!(oneof, `throw new Exception("out of data for inflate");`)~"
266 //assert(rbpos < rbused);
267 "~dvar~" = rdbuf.ptr[rbpos++];
268 }\n";
270 private:
271 void setErrorState () nothrow @safe @nogc {
272 state = State.Dead;
273 lt = dt = null; // just in case
274 doingFinalBlock = false; // just in case too
277 void processZLibHeader (scope ReadBufDg readBuf) @trusted {
278 scope(failure) setErrorState();
279 ubyte cmf, flg;
280 // 7 bytes
281 // compression parameters
282 //if (!readOneByte(readBuf, cmf)) throw new Exception("out of input data");
283 mixin(ReadOneByteMixin!"cmf");
284 // flags
285 //if (!readOneByte(readBuf, flg)) throw new Exception("out of input data");
286 mixin(ReadOneByteMixin!"flg");
287 // check format
288 // check checksum
289 if ((256*cmf+flg)%31) throw new Exception("invalid zlib checksum");
290 // check method (only deflate allowed)
291 if ((cmf&0x0f) != 8) throw new Exception("invalid compression method");
292 // check window size
293 if ((cmf>>4) > 7) throw new Exception("invalid window size");
294 // there must be no preset dictionary
295 if (flg&0x20) throw new Exception("preset dictionaries are not supported");
296 // FYI: flg>>6 will give you compression level:
297 // 0 - compressor used fastest algorithm
298 // 1 - compressor used fast algorithm
299 // 2 - compressor used default algorithm
300 // 3 - compressor used maximum compression, slowest algorithm
301 // not that you can make any sane use of that info though...
302 // note that last 4 bytes is Adler32 checksum in big-endian format
303 // init Adler32 counters
304 a32s1 = 1;
305 a32s2 = 0;
306 nmaxLeft = Adler32.NMAX;
307 // ok, we can go now
308 state = State.ExpectBlock;
311 void finishZLibData (scope ReadBufDg readBuf) {
312 // read Adler32
313 scope(failure) setErrorState();
314 uint a32; // autoinit
315 ubyte bt = void;
316 foreach_reverse (immutable n; 0..4) {
317 //if (!readOneByte(readBuf, bt)) throw new Exception("out of input data");
318 mixin(ReadOneByteMixin!"bt");
319 a32 |= (cast(uint)bt)<<(n*8);
321 if (a32 != finishAdler32()) throw new Exception("invalid checksum");
324 // get one bit from source stream to `ubyte onebit`
325 enum GetBitMixin = q{{
326 if (!bitcount--) {
327 scope(failure) setErrorState();
328 //if (!readOneByte(readBuf, onebit)) throw new Exception("out of input data");
329 mixin(ReadOneByteMixin!"onebit");
330 tag = onebit;
331 bitcount = 7;
333 onebit = tag&0x01;
334 tag >>= 1;
337 // read a num bit value from a stream and add base
338 enum ReadBitsMixin(string dvar, string num, string base) = "{
339 uint val = 0;
340 ubyte onebit = void;
341 if ("~num~") {
342 immutable uint limit = 1<<"~num~";
343 for (uint mask = 1; mask < limit; mask <<= 1) {
344 //if (getBit(readBuf)) val += mask;
345 "~GetBitMixin~"
346 if (onebit) val += mask;
349 "~dvar~" = cast(typeof("~dvar~"))(val+"~base~");
350 }\n";
352 // given a data stream and a tree, decode a symbol
353 uint decodeSymbol (scope ReadBufDg readBuf, const(Tree*) t) {
354 scope(failure) setErrorState();
355 int cur, sum, len; // autoinit
356 ubyte onebit = void;
357 // get more bits while code value is above sum
358 do {
359 //cur = 2*cur+getBit(readBuf);
360 mixin(GetBitMixin);
361 cur = 2*cur+onebit;
362 ++len;
363 if (len >= 16) throw new Exception("invalid symbol");
364 ushort sl = t.table.ptr[len];
365 sum += sl;
366 cur -= sl;
367 } while (cur >= 0);
368 sum += cur;
369 if (sum < 0 || sum >= 288) throw new Exception("invalid symbol");
370 return t.trans.ptr[sum];
373 // given a data stream, decode dynamic trees from it
374 void decodeTrees (scope ReadBufDg readBuf) {
375 scope(failure) setErrorState();
376 // special ordering of code length codes
377 static immutable ubyte[19] clcidx = [16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15];
378 Tree codeTree;
379 ubyte[288+32] lengths;
380 uint hlit, hdist, hclen;
381 uint num, length;
382 // get 5 bits HLIT (257-286)
383 //hlit = readBits(readBuf, 5, 257);
384 mixin(ReadBitsMixin!("hlit", "5", "257"));
385 // get 5 bits HDIST (1-32)
386 //hdist = readBits(readBuf, 5, 1);
387 mixin(ReadBitsMixin!("hdist", "5", "1"));
388 if (hlit+hdist > 288+32) throw new Exception("invalid tree");
389 // get 4 bits HCLEN (4-19)
390 //hclen = readBits(readBuf, 4, 4);
391 mixin(ReadBitsMixin!("hclen", "4", "4"));
392 if (hclen > 19) throw new Exception("invalid tree");
393 lengths.ptr[0..19] = 0;
394 // read code lengths for code length alphabet
395 foreach (immutable i; 0..hclen) {
396 //lengths[clcidx[i]] = cast(ubyte)readBits(readBuf, 3, 0); // get 3 bits code length (0-7)
397 mixin(ReadBitsMixin!("lengths.ptr[clcidx.ptr[i]]", "3", "0")); // get 3 bits code length (0-7)
399 // build code length tree
400 codeTree.buildTree(lengths.ptr[0..19]);
401 // decode code lengths for the dynamic trees
402 for (num = 0; num < hlit+hdist; ) {
403 ubyte bt;
404 uint sym = decodeSymbol(readBuf, &codeTree);
405 switch (sym) {
406 case 16: // copy previous code length 3-6 times (read 2 bits)
407 if (num == 0) throw new Exception("invalid tree");
408 bt = lengths.ptr[num-1];
409 //length = readBits(readBuf, 2, 3);
410 mixin(ReadBitsMixin!("length", "2", "3"));
411 break;
412 case 17: // repeat code length 0 for 3-10 times (read 3 bits)
413 bt = 0;
414 //length = readBits(readBuf, 3, 3);
415 mixin(ReadBitsMixin!("length", "3", "3"));
416 break;
417 case 18: // repeat code length 0 for 11-138 times (read 7 bits)
418 bt = 0;
419 //length = readBits(readBuf, 7, 11);
420 mixin(ReadBitsMixin!("length", "7", "11"));
421 break;
422 default: // values 0-15 represent the actual code lengths
423 if (sym >= 19) throw new Exception("invalid tree symbol");
424 length = 1;
425 bt = cast(ubyte)sym;
426 break;
428 // fill it
429 if (num+length > 288+32) throw new Exception("invalid tree");
430 while (length-- > 0) lengths.ptr[num++] = bt;
432 // build dynamic trees
433 ltree.buildTree(lengths.ptr[0..hlit]);
434 dtree.buildTree(lengths.ptr[hlit..hlit+hdist]);
437 // can return zero-length slice (on state switch, for example)
438 ubyte[] processInflatedBlock (scope ReadBufDg readBuf, ubyte[] btdest) {
439 auto btleft = btdest.length;
440 ubyte* dest = btdest.ptr;
441 while (btleft > 0) {
442 if (bytesLeft > 0) {
443 // copying match; all checks already done
444 //--bytesLeft;
445 ubyte bt = dict.ptr[dictEnd-matchOfs];
446 mixin(DictPutByteMixin);
447 *dest++ = bt;
448 --bytesLeft;
449 --btleft;
450 } else {
451 uint sym = decodeSymbol(readBuf, lt);
452 if (sym == 256) {
453 // end of block, fix state
454 state = State.ExpectBlock;
455 //return null;
456 break;
458 if (sym < 256) {
459 // normal
460 ubyte bt = cast(ubyte)sym;
461 mixin(DictPutByteMixin);
462 *dest++ = bt;
463 --btleft;
464 } else {
465 scope(failure) setErrorState();
466 // copy
467 uint dist, length, offs;
468 sym -= 257;
469 // possibly get more bits from length code
470 if (sym >= 30) throw new Exception("invalid symbol");
471 //length = readBits(readBuf, lengthBits[sym], lengthBase[sym]);
472 mixin(ReadBitsMixin!("length", "lengthBits[sym]", "lengthBase[sym]"));
473 dist = decodeSymbol(readBuf, dt);
474 if (dist >= 30) throw new Exception("invalid distance");
475 // possibly get more bits from distance code
476 //offs = readBits(readBuf, distBits[dist], distBase[dist]);
477 mixin(ReadBitsMixin!("offs", "distBits[dist]", "distBase[dist]"));
478 if (offs > dictEnd) throw new Exception("invalid distance");
479 // copy match
480 bytesLeft = length;
481 matchOfs = offs;
482 //return false; // no byte read yet
486 return btdest[0..$-btleft];
489 // can return zero-length slice (on state switch, for example)
490 ubyte[] processUncompressedBlock (scope ReadBufDg readBuf, ubyte[] btdest) {
491 auto btleft = btdest.length;
492 ubyte* dest = btdest.ptr;
493 while (btleft > 0 && bytesLeft > 0) {
494 // copying
495 scope(failure) setErrorState();
496 //if (!readOneByte(readBuf, *dest)) throw new Exception("out of input data");
497 mixin(ReadOneByteMixin!"*dest");
498 ubyte bt = *dest++;
499 mixin(DictPutByteMixin);
500 --bytesLeft;
501 --btleft;
503 if (bytesLeft == 0) {
504 // end of block, fix state
505 state = State.ExpectBlock;
507 return btdest[0..$-btleft];
510 ushort readU16 (scope ReadBufDg readBuf) {
511 scope(failure) setErrorState();
512 ubyte b0 = void, b1 = void;
513 //if (!readOneByte(readBuf, b0)) throw new Exception("out of input data");
514 mixin(ReadOneByteMixin!"b0");
515 //if (!readOneByte(readBuf, b1)) throw new Exception("out of input data");
516 mixin(ReadOneByteMixin!"b1");
517 return cast(ushort)(b0|(b1<<8));
520 void processRawHeader (scope ReadBufDg readBuf) {
521 ushort length = readU16(readBuf);
522 ushort invlength = readU16(readBuf); // one's complement of length
523 // check length
524 if (length != cast(ushort)(~(invlength))) { setErrorState(); throw new Exception("invalid uncompressed block length"); }
525 bitcount = 0; // make sure we start next block on a byte boundary
526 bytesLeft = length;
527 state = State.RawBlock;
530 void processFixedHeader (scope ReadBufDg readBuf) nothrow @safe @nogc {
531 lt = &sltree;
532 dt = &sdtree;
533 bytesLeft = 0; // force reading of symbol (just in case)
534 state = State.CompressedBlock;
537 void processDynamicHeader (scope ReadBufDg readBuf) {
538 // decode trees from stream
539 decodeTrees(readBuf);
540 lt = &ltree;
541 dt = &dtree;
542 bytesLeft = 0; // force reading of symbol (just in case)
543 state = State.CompressedBlock;
546 // set state to State.EOF on correct EOF
547 void processBlockHeader (scope ReadBufDg readBuf) {
548 if (doingFinalBlock) {
549 if (mode == Mode.ZLib) finishZLibData(readBuf);
550 doingFinalBlock = false;
551 state = State.EOF;
552 return;
554 //doingFinalBlock = (getBit(readBuf) != 0); // final block flag
556 ubyte onebit = void;
557 mixin(GetBitMixin);
558 doingFinalBlock = (onebit != 0);
560 // read block type (2 bits) and fix state
561 ubyte btype = void;
562 mixin(ReadBitsMixin!("btype", "2", "0"));
563 switch (/*readBits(readBuf, 2, 0)*/btype) {
564 case 0: processRawHeader(readBuf); break; // uncompressed block
565 case 1: processFixedHeader(readBuf); break; // block with fixed huffman trees
566 case 2: processDynamicHeader(readBuf); break; // block with dynamic huffman trees
567 default: setErrorState(); throw new Exception("invalid input block type");
571 public:
572 /// stream format
573 enum Mode { ZLib, Deflate }
575 /// disable copying
576 @disable this (this);
579 * Initialize decompression stream.
581 * Params:
582 * dgb = byte reader;
583 * must either set bt to next byte and return true or return false on EOF;
584 * note that it will not be called anymore after EOF or error;
585 * can be null
586 * amode = stream format; either headerless 'deflate' stream or 'zlib' stream
588 * Throws:
589 * on error
591 this (Mode amode/*=Mode.ZLib*/) {
592 reinit(amode);
595 /// Ditto.
596 void reinit (Mode amode=Mode.ZLib) {
597 rbpos = rbused = 0;
598 rbeof = false;
599 mode = amode;
600 tag = 0;
601 bitcount = 0;
602 dictEnd = 0;
603 state = (amode == Mode.ZLib ? State.ExpectZLibHeader : State.ExpectBlock);
604 doingFinalBlock = false;
608 * Check stream header. Can be called after this() or reinit().
610 * Returns:
611 * nothing
613 * Throws:
614 * on error
616 void checkStreamHeader (scope ReadBufDg readBuf) {
617 if (mode == Mode.ZLib && state == State.ExpectZLibHeader) processZLibHeader(readBuf);
621 * Get bytes from stream.
623 * Returns:
624 * slice of `btdest`; can be zero-length
626 * Throws:
627 * Exception on error
629 ubyte[] getBytes (scope ReadBufDg readBuf, ubyte[] btdest) {
630 usize btpos = 0;
631 mainloop: while (btpos < btdest.length) {
632 //{ import core.stdc.stdio; printf("btpos=%u; btlen=%u; state=%u\n", cast(uint)btpos, cast(uint)btdest.length, cast(uint)state); }
633 final switch (state) {
634 case State.ExpectZLibHeader:
635 processZLibHeader(readBuf);
636 break;
637 case State.ExpectBlock:
638 processBlockHeader(readBuf);
639 break;
640 case State.RawBlock:
641 auto rd = processUncompressedBlock(readBuf, btdest[btpos..$]);
642 btpos += rd.length;
643 break;
644 case State.CompressedBlock:
645 auto rd = processInflatedBlock(readBuf, btdest[btpos..$]);
646 btpos += rd.length;
647 break;
648 case State.EOF: break mainloop;
649 case State.Dead: setErrorState(); throw new Exception("dead stream"); break;
652 //DONE: MOVE THIS TO READERS!
653 //dictPutByte(bt);
654 return btdest[0..btpos];
658 * Get another byte from stream.
660 * Returns:
661 * one decompressed byte
663 * Throws:
664 * Exception on EOF/error
666 ubyte getByte (scope ReadBufDg readBuf) {
667 ubyte res = void;
668 auto rd = getBytes(readBuf, (&res)[0..1]);
669 if (rd.length == 0) throw new Exception("no more data");
670 return res;
674 * Read bytes from stream. Almost similar to File.rawRead().
676 * Params:
677 * buf = destination buffer
679 * Returns:
680 * destination buffer or slice of destination buffer if EOF encountered
682 * Throws:
683 * on error
685 T[] rawRead(T) (scope ReadBufDg readBuf, T[] buf) {
686 auto len = buf.length*T.sizeof;
687 if (len == 0) return buf;
688 auto dst = cast(ubyte*)buf.ptr;
689 auto rd = getBytes(readBuf, dst[0..len]);
690 // check if the last 'dest' item is fully decompressed
691 static if (T.sizeof > 1) { if (rd.length%T.sizeof) { setErrorState(); throw new Exception("partial data"); } }
692 return buf[0..rd.length/T.sizeof];
695 @property const pure nothrow @safe @nogc {
696 bool eof () { return (state == State.EOF); }
697 bool invalid () { return (state == State.Dead); }
702 // -----------------------------------------------------------------------------
703 private:
704 // private global data
705 // build it using CTFE
707 // fixed length/symbol tree
708 static immutable Tree sltree = (){
709 Tree sl;
710 sl.table[7] = 24;
711 sl.table[8] = 152;
712 sl.table[9] = 112;
713 foreach (immutable i; 0..24) sl.trans[i] = cast(ushort)(256+i);
714 foreach (immutable ushort i; 0..144) sl.trans[24+i] = i;
715 foreach (immutable i; 0..8) sl.trans[24+144+i] = cast(ushort)(280+i);
716 foreach (immutable i; 0..112) sl.trans[24+144+8+i] = cast(ushort)(144+i);
717 return sl;
718 }();
720 // fixed distance tree
721 static immutable Tree sdtree = (){
722 Tree sd;
723 sd.table[5] = 32;
724 foreach (immutable ushort i; 0..32) sd.trans[i] = i;
725 return sd;
726 }();
728 void fillBits (ubyte[] bits, uint delta) @safe nothrow {
729 bits[0..delta] = 0;
730 foreach (immutable i; 0..30-delta) bits[i+delta] = cast(ubyte)(i/delta);
733 void fillBase (ushort[] base, immutable(ubyte[30]) bits, ushort first) @safe nothrow {
734 ushort sum = first;
735 foreach (immutable i; 0..30) {
736 base[i] = sum;
737 sum += 1<<bits[i];
741 // extra bits and base tables for length codes
742 static immutable ubyte[30] lengthBits = (){
743 ubyte[30] bits;
744 fillBits(bits, 4);
745 bits[28] = 0; // fix a special case
746 return bits;
747 }();
749 static immutable ushort[30] lengthBase = (){
750 ubyte[30] bits;
751 ushort[30] base;
752 fillBits(bits, 4);
753 fillBase(base, bits, 3);
754 base[28] = 258; // fix a special case
755 return base;
756 }();
758 // extra bits and base tables for distance codes
759 static immutable ubyte[30] distBits = (){
760 ubyte[30] bits;
761 fillBits(bits, 2);
762 return bits;
763 }();
765 static immutable ushort[30] distBase = (){
766 enum bits = distBits;
767 ushort[30] base;
768 fillBase(base, bits, 1);
769 return base;
770 }();
773 //InfStream ifs;