1 module hio.zlib.impl; 2 3 import std.experimental.logger; 4 import std.string; 5 import std.stdio; 6 import std.algorithm; 7 import std.typecons; 8 9 import etc.c.zlib; 10 11 import nbuff: Nbuff, NbuffChunk, MutableNbuffChunk; 12 13 14 enum OBUFFSZ = 16*1024; 15 16 alias zInflateResult = Tuple!(ulong, "consumed", NbuffChunk, "result", int, "status"); 17 18 struct ZLib 19 { 20 private 21 { 22 z_stream _zstr; 23 uint _obufsz = 16*1024; 24 MutableNbuffChunk _outBuff; 25 int _state = Z_OK; 26 } 27 28 void zInit(uint obufsz) 29 { 30 immutable r = inflateInit2(&_zstr, 15+32); // allow autodetect 31 assert(r == Z_OK); 32 _obufsz = obufsz; 33 _outBuff = Nbuff.get(_obufsz); 34 _zstr.next_out = &_outBuff.data.ptr[0]; 35 _zstr.avail_out = _obufsz; 36 _state = Z_OK; 37 } 38 39 zInflateResult zInflate(NbuffChunk b) 40 { 41 // process data in b 42 // after call to inflate we can have 43 // 0) avail_in = 0 - everything processed (input empty) 44 // avail_out>= 0 - 45 // 46 // 1) avail_in > 0 - something consumed but... 47 // avail_out == 0 - no space in out buffer 48 // 49 // 2) 50 // avail_in == 0 51 // avail_out > 0 52 if (_state != Z_OK) 53 { 54 return zInflateResult(0, NbuffChunk(), _state); 55 } 56 _zstr.next_in = &b.data[0]; 57 _zstr.avail_in = cast(int)b.length; 58 int rc = inflate(&_zstr, 0); 59 switch(rc) 60 { 61 case Z_OK, Z_STREAM_END, Z_STREAM_ERROR, Z_DATA_ERROR: 62 _state = rc; 63 break; 64 default: 65 break; 66 } 67 // writefln("r=%d, avail_in=%d, avail_out=%d", rc, _zstr.avail_in, _zstr.avail_out); 68 // writefln("in progress = %d", b.length - _zstr.avail_in); 69 // writefln("out space = %d", _zstr.avail_out); 70 // writefln("data=%s", cast(string)_outBuff.data[0.._obufsz-_zstr.avail_out]); 71 if ( _zstr.avail_out == 0 || rc == Z_STREAM_END) 72 { 73 auto res = zInflateResult( 74 b.length - _zstr.avail_in, 75 NbuffChunk(_outBuff, min(_obufsz - _zstr.avail_out, _obufsz)), 76 rc); 77 _outBuff = Nbuff.get(_obufsz); 78 _zstr.avail_out = _obufsz; 79 _zstr.next_out = &_outBuff.data.ptr[0]; 80 return res; 81 } 82 else 83 { 84 return zInflateResult(b.length - _zstr.avail_in, NbuffChunk(), rc); 85 } 86 } 87 88 void zFlush() @trusted 89 { 90 inflateEnd(&_zstr); 91 } 92 } 93 94 95 unittest 96 { 97 import std.range; 98 info("testing zlib"); 99 immutable(ubyte)[] hello = 100 [ 101 0x1f, 0x8b, 0x08, 0x00, 0x94, 0x82, 0xc1, 0x5e, 0x00, 0x03, 0xcb, 0x48, 0xcd, 0xc9, 0xc9, 0xe7, 102 0x02, 0x00, 0x20, 0x30, 0x3a, 0x36, 0x06, 0x00, 0x00, 0x00 103 ]; 104 NbuffChunk b = NbuffChunk(hello); 105 ZLib zlib; 106 zlib.zInit(4); 107 int processed; 108 while(processed<b.length) 109 { 110 auto r = zlib.zInflate(b[processed..$]); 111 processed += r.consumed; 112 } 113 zlib.zFlush(); 114 115 immutable(ubyte)[] compressed = 116 [ 117 0x1F,0x8B,0x08,0x00, 0xF0,0xFD,0xC2,0x5E, 0x00,0x03,0x4B,0x4C, 0x4A,0x4E,0x49,0x4D, 118 0x4B,0xCF,0xC8,0xCC, 0xCA,0xCE,0xC9,0xCD, 0xCB,0x2F,0x28,0x2C, 0x2A,0x2E,0x29,0x2D, 119 0x2B,0xAF,0xA8,0xAC, 0xE2,0x72,0x74,0x72, 0x76,0x71,0x75,0x73, 0xF7,0xF0,0xF4,0xF2, 120 0xF6,0xF1,0xF5,0xF3, 0x0F,0x08,0x0C,0x0A, 0x0E,0x09,0x0D,0x0B, 0x8F,0x88,0x8C,0xE2, 121 0x4A,0xA4,0xA3,0x2E, 0x00,0x94,0xA7,0x42, 0x7C,0xA2,0x00,0x00, 0x00, 122 0xff,0xff,0xff // this is 3 garbage bytes 123 ]; 124 125 126 127 for(int s=1; s<=1024; s++) 128 for(int c=1; c<=64; c++) 129 { 130 Nbuff uncompressed; 131 zlib.zInit(s); 132 foreach(chunk; chunks(compressed, c)) 133 { 134 processed = 0; 135 b = NbuffChunk(chunk); 136 while(processed<b.length) 137 { 138 auto r = zlib.zInflate(b[processed..$]); 139 processed += r.consumed; 140 if (r.result.length) 141 { 142 uncompressed.append(r.result); 143 } 144 if (r.status == Z_STREAM_END) 145 { 146 break; 147 } 148 } 149 } 150 zlib.zFlush(); 151 assert(uncompressed.data.data == 152 "abcdefghijklmnopqrstuvwxyz\nABCDEFGHIJKLMNOPQRSTUVWXYZ\nabcdefghijklmnopqrstuvwxyz\n" ~ 153 "ABCDEFGHIJKLMNOPQRSTUVWXYZ\nabcdefghijklmnopqrstuvwxyz\nABCDEFGHIJKLMNOPQRSTUVWXYZ\n"); 154 //writefln("ok for %d/%d", s, c); 155 } 156 info("ok"); 157 }