|  | // Written in the D programming language. | 
|  |  | 
|  | /** | 
|  | * Compress/decompress data using the $(HTTP www._zlib.net, _zlib library). | 
|  | * | 
|  | * Examples: | 
|  | * | 
|  | * If you have a small buffer you can use $(LREF compress) and | 
|  | * $(LREF uncompress) directly. | 
|  | * | 
|  | * ------- | 
|  | * import std.zlib; | 
|  | * | 
|  | * auto src = | 
|  | * "the quick brown fox jumps over the lazy dog\r | 
|  | *  the quick brown fox jumps over the lazy dog\r"; | 
|  | * | 
|  | * ubyte[] dst; | 
|  | * ubyte[] result; | 
|  | * | 
|  | * dst = compress(src); | 
|  | * result = cast(ubyte[]) uncompress(dst); | 
|  | * assert(result == src); | 
|  | * ------- | 
|  | * | 
|  | * When the data to be compressed doesn't fit in one buffer, use | 
|  | * $(LREF Compress) and $(LREF UnCompress). | 
|  | * | 
|  | * ------- | 
|  | * import std.zlib; | 
|  | * import std.stdio; | 
|  | * import std.conv : to; | 
|  | * import std.algorithm.iteration : map; | 
|  | * | 
|  | * UnCompress decmp = new UnCompress; | 
|  | * foreach (chunk; stdin.byChunk(4096).map!(x => decmp.uncompress(x))) | 
|  | * { | 
|  | *     chunk.to!string.write; | 
|  | * } | 
|  |  | 
|  | * ------- | 
|  | * | 
|  | * References: | 
|  | *  $(HTTP en.wikipedia.org/wiki/Zlib, Wikipedia) | 
|  | * | 
|  | * Copyright: Copyright Digital Mars 2000 - 2011. | 
|  | * License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). | 
|  | * Authors:   $(HTTP digitalmars.com, Walter Bright) | 
|  | * Source:    $(PHOBOSSRC std/_zlib.d) | 
|  | */ | 
|  | /*          Copyright Digital Mars 2000 - 2011. | 
|  | * Distributed under the Boost Software License, Version 1.0. | 
|  | *    (See accompanying file LICENSE_1_0.txt or copy at | 
|  | *          http://www.boost.org/LICENSE_1_0.txt) | 
|  | */ | 
|  | module std.zlib; | 
|  |  | 
|  | //debug=zlib;       // uncomment to turn on debugging printf's | 
|  |  | 
|  | import etc.c.zlib; | 
|  |  | 
|  | // Values for 'mode' | 
|  |  | 
|  | enum | 
|  | { | 
|  | Z_NO_FLUSH      = 0, | 
|  | Z_SYNC_FLUSH    = 2, | 
|  | Z_FULL_FLUSH    = 3, | 
|  | Z_FINISH        = 4, | 
|  | } | 
|  |  | 
|  | /************************************* | 
|  | * Errors throw a ZlibException. | 
|  | */ | 
|  |  | 
|  | class ZlibException : Exception | 
|  | { | 
|  | this(int errnum) | 
|  | {   string msg; | 
|  |  | 
|  | switch (errnum) | 
|  | { | 
|  | case Z_STREAM_END:      msg = "stream end"; break; | 
|  | case Z_NEED_DICT:       msg = "need dict"; break; | 
|  | case Z_ERRNO:           msg = "errno"; break; | 
|  | case Z_STREAM_ERROR:    msg = "stream error"; break; | 
|  | case Z_DATA_ERROR:      msg = "data error"; break; | 
|  | case Z_MEM_ERROR:       msg = "mem error"; break; | 
|  | case Z_BUF_ERROR:       msg = "buf error"; break; | 
|  | case Z_VERSION_ERROR:   msg = "version error"; break; | 
|  | default:                msg = "unknown error";  break; | 
|  | } | 
|  | super(msg); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * $(P Compute the Adler-32 checksum of a buffer's worth of data.) | 
|  | * | 
|  | * Params: | 
|  | *     adler = the starting checksum for the computation. Use 1 | 
|  | *             for a new checksum. Use the output of this function | 
|  | *             for a cumulative checksum. | 
|  | *     buf = buffer containing input data | 
|  | * | 
|  | * Returns: | 
|  | *     A $(D uint) checksum for the provided input data and starting checksum | 
|  | * | 
|  | * See_Also: | 
|  | *     $(LINK http://en.wikipedia.org/wiki/Adler-32) | 
|  | */ | 
|  |  | 
|  | uint adler32(uint adler, const(void)[] buf) | 
|  | { | 
|  | import std.range : chunks; | 
|  | foreach (chunk; (cast(ubyte[]) buf).chunks(0xFFFF0000)) | 
|  | { | 
|  | adler = etc.c.zlib.adler32(adler, chunk.ptr, cast(uint) chunk.length); | 
|  | } | 
|  | return adler; | 
|  | } | 
|  |  | 
|  | /// | 
|  | @system unittest | 
|  | { | 
|  | static ubyte[] data = [1,2,3,4,5,6,7,8,9,10]; | 
|  |  | 
|  | uint adler = adler32(0u, data); | 
|  | assert(adler == 0xdc0037); | 
|  | } | 
|  |  | 
|  | @system unittest | 
|  | { | 
|  | static string data = "test"; | 
|  |  | 
|  | uint adler = adler32(1, data); | 
|  | assert(adler == 0x045d01c1); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * $(P Compute the CRC32 checksum of a buffer's worth of data.) | 
|  | * | 
|  | * Params: | 
|  | *     crc = the starting checksum for the computation. Use 0 | 
|  | *             for a new checksum. Use the output of this function | 
|  | *             for a cumulative checksum. | 
|  | *     buf = buffer containing input data | 
|  | * | 
|  | * Returns: | 
|  | *     A $(D uint) checksum for the provided input data and starting checksum | 
|  | * | 
|  | * See_Also: | 
|  | *     $(LINK http://en.wikipedia.org/wiki/Cyclic_redundancy_check) | 
|  | */ | 
|  |  | 
|  | uint crc32(uint crc, const(void)[] buf) | 
|  | { | 
|  | import std.range : chunks; | 
|  | foreach (chunk; (cast(ubyte[]) buf).chunks(0xFFFF0000)) | 
|  | { | 
|  | crc = etc.c.zlib.crc32(crc, chunk.ptr, cast(uint) chunk.length); | 
|  | } | 
|  | return crc; | 
|  | } | 
|  |  | 
|  | @system unittest | 
|  | { | 
|  | static ubyte[] data = [1,2,3,4,5,6,7,8,9,10]; | 
|  |  | 
|  | uint crc; | 
|  |  | 
|  | debug(zlib) printf("D.zlib.crc32.unittest\n"); | 
|  | crc = crc32(0u, cast(void[]) data); | 
|  | debug(zlib) printf("crc = %x\n", crc); | 
|  | assert(crc == 0x2520577b); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * $(P Compress data) | 
|  | * | 
|  | * Params: | 
|  | *     srcbuf = buffer containing the data to compress | 
|  | *     level = compression level. Legal values are -1 .. 9, with -1 indicating | 
|  | *             the default level (6), 0 indicating no compression, 1 being the | 
|  | *             least compression and 9 being the most. | 
|  | * | 
|  | * Returns: | 
|  | *     the compressed data | 
|  | */ | 
|  |  | 
|  | ubyte[] compress(const(void)[] srcbuf, int level) | 
|  | in | 
|  | { | 
|  | assert(-1 <= level && level <= 9); | 
|  | } | 
|  | body | 
|  | { | 
|  | import core.memory : GC; | 
|  | auto destlen = srcbuf.length + ((srcbuf.length + 1023) / 1024) + 12; | 
|  | auto destbuf = new ubyte[destlen]; | 
|  | auto err = etc.c.zlib.compress2(destbuf.ptr, &destlen, cast(ubyte *) srcbuf.ptr, srcbuf.length, level); | 
|  | if (err) | 
|  | { | 
|  | GC.free(destbuf.ptr); | 
|  | throw new ZlibException(err); | 
|  | } | 
|  |  | 
|  | destbuf.length = destlen; | 
|  | return destbuf; | 
|  | } | 
|  |  | 
|  | /********************************************* | 
|  | * ditto | 
|  | */ | 
|  |  | 
|  | ubyte[] compress(const(void)[] srcbuf) | 
|  | { | 
|  | return compress(srcbuf, Z_DEFAULT_COMPRESSION); | 
|  | } | 
|  |  | 
|  | /********************************************* | 
|  | * Decompresses the data in srcbuf[]. | 
|  | * Params: | 
|  | *  srcbuf  = buffer containing the compressed data. | 
|  | *  destlen = size of the uncompressed data. | 
|  | *            It need not be accurate, but the decompression will be faster | 
|  | *            if the exact size is supplied. | 
|  | *  winbits = the base two logarithm of the maximum window size. | 
|  | * Returns: the decompressed data. | 
|  | */ | 
|  |  | 
|  | void[] uncompress(const(void)[] srcbuf, size_t destlen = 0u, int winbits = 15) | 
|  | { | 
|  | import std.conv : to; | 
|  | int err; | 
|  | ubyte[] destbuf; | 
|  |  | 
|  | if (!destlen) | 
|  | destlen = srcbuf.length * 2 + 1; | 
|  |  | 
|  | etc.c.zlib.z_stream zs; | 
|  | zs.next_in = cast(typeof(zs.next_in)) srcbuf.ptr; | 
|  | zs.avail_in = to!uint(srcbuf.length); | 
|  | err = etc.c.zlib.inflateInit2(&zs, winbits); | 
|  | if (err) | 
|  | { | 
|  | throw new ZlibException(err); | 
|  | } | 
|  |  | 
|  | size_t olddestlen = 0u; | 
|  |  | 
|  | loop: | 
|  | while (true) | 
|  | { | 
|  | destbuf.length = destlen; | 
|  | zs.next_out = cast(typeof(zs.next_out)) &destbuf[olddestlen]; | 
|  | zs.avail_out = to!uint(destlen - olddestlen); | 
|  | olddestlen = destlen; | 
|  |  | 
|  | err = etc.c.zlib.inflate(&zs, Z_NO_FLUSH); | 
|  | switch (err) | 
|  | { | 
|  | case Z_OK: | 
|  | destlen = destbuf.length * 2; | 
|  | continue loop; | 
|  |  | 
|  | case Z_STREAM_END: | 
|  | destbuf.length = zs.total_out; | 
|  | err = etc.c.zlib.inflateEnd(&zs); | 
|  | if (err != Z_OK) | 
|  | throw new ZlibException(err); | 
|  | return destbuf; | 
|  |  | 
|  | default: | 
|  | etc.c.zlib.inflateEnd(&zs); | 
|  | throw new ZlibException(err); | 
|  | } | 
|  | } | 
|  | assert(0); | 
|  | } | 
|  |  | 
|  | @system unittest | 
|  | { | 
|  | auto src = | 
|  | "the quick brown fox jumps over the lazy dog\r | 
|  | the quick brown fox jumps over the lazy dog\r | 
|  | "; | 
|  | ubyte[] dst; | 
|  | ubyte[] result; | 
|  |  | 
|  | //arrayPrint(src); | 
|  | dst = compress(src); | 
|  | //arrayPrint(dst); | 
|  | result = cast(ubyte[]) uncompress(dst); | 
|  | //arrayPrint(result); | 
|  | assert(result == src); | 
|  | } | 
|  |  | 
|  | @system unittest | 
|  | { | 
|  | ubyte[] src = new ubyte[1000000]; | 
|  | ubyte[] dst; | 
|  | ubyte[] result; | 
|  |  | 
|  | src[] = 0x80; | 
|  | dst = compress(src); | 
|  | assert(dst.length*2 + 1 < src.length); | 
|  | result = cast(ubyte[]) uncompress(dst); | 
|  | assert(result == src); | 
|  | } | 
|  |  | 
|  | /+ | 
|  | void arrayPrint(ubyte[] array) | 
|  | { | 
|  | //printf("array %p,%d\n", cast(void*) array, array.length); | 
|  | for (size_t i = 0; i < array.length; i++) | 
|  | { | 
|  | printf("%02x ", array[i]); | 
|  | if (((i + 1) & 15) == 0) | 
|  | printf("\n"); | 
|  | } | 
|  | printf("\n\n"); | 
|  | } | 
|  | +/ | 
|  |  | 
|  | /// the header format the compressed stream is wrapped in | 
|  | enum HeaderFormat { | 
|  | deflate, /// a standard zlib header | 
|  | gzip, /// a gzip file format header | 
|  | determineFromData /// used when decompressing. Try to automatically detect the stream format by looking at the data | 
|  | } | 
|  |  | 
|  | /********************************************* | 
|  | * Used when the data to be compressed is not all in one buffer. | 
|  | */ | 
|  |  | 
|  | class Compress | 
|  | { | 
|  | import std.conv : to; | 
|  |  | 
|  | private: | 
|  | z_stream zs; | 
|  | int level = Z_DEFAULT_COMPRESSION; | 
|  | int inited; | 
|  | immutable bool gzip; | 
|  |  | 
|  | void error(int err) | 
|  | { | 
|  | if (inited) | 
|  | {   deflateEnd(&zs); | 
|  | inited = 0; | 
|  | } | 
|  | throw new ZlibException(err); | 
|  | } | 
|  |  | 
|  | public: | 
|  |  | 
|  | /** | 
|  | * Constructor. | 
|  | * | 
|  | * Params: | 
|  | *    level = compression level. Legal values are 1 .. 9, with 1 being the least | 
|  | *            compression and 9 being the most. The default value is 6. | 
|  | *    header = sets the compression type to one of the options available | 
|  | *             in $(LREF HeaderFormat). Defaults to HeaderFormat.deflate. | 
|  | * | 
|  | * See_Also: | 
|  | *    $(LREF compress), $(LREF HeaderFormat) | 
|  | */ | 
|  | this(int level, HeaderFormat header = HeaderFormat.deflate) | 
|  | in | 
|  | { | 
|  | assert(1 <= level && level <= 9); | 
|  | } | 
|  | body | 
|  | { | 
|  | this.level = level; | 
|  | this.gzip = header == HeaderFormat.gzip; | 
|  | } | 
|  |  | 
|  | /// ditto | 
|  | this(HeaderFormat header = HeaderFormat.deflate) | 
|  | { | 
|  | this.gzip = header == HeaderFormat.gzip; | 
|  | } | 
|  |  | 
|  | ~this() | 
|  | {   int err; | 
|  |  | 
|  | if (inited) | 
|  | { | 
|  | inited = 0; | 
|  | deflateEnd(&zs); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Compress the data in buf and return the compressed data. | 
|  | * Params: | 
|  | *    buf = data to compress | 
|  | * | 
|  | * Returns: | 
|  | *    the compressed data. The buffers returned from successive calls to this should be concatenated together. | 
|  | * | 
|  | */ | 
|  | const(void)[] compress(const(void)[] buf) | 
|  | { | 
|  | import core.memory : GC; | 
|  | int err; | 
|  | ubyte[] destbuf; | 
|  |  | 
|  | if (buf.length == 0) | 
|  | return null; | 
|  |  | 
|  | if (!inited) | 
|  | { | 
|  | err = deflateInit2(&zs, level, Z_DEFLATED, 15 + (gzip ? 16 : 0), 8, Z_DEFAULT_STRATEGY); | 
|  | if (err) | 
|  | error(err); | 
|  | inited = 1; | 
|  | } | 
|  |  | 
|  | destbuf = new ubyte[zs.avail_in + buf.length]; | 
|  | zs.next_out = destbuf.ptr; | 
|  | zs.avail_out = to!uint(destbuf.length); | 
|  |  | 
|  | if (zs.avail_in) | 
|  | buf = zs.next_in[0 .. zs.avail_in] ~ cast(ubyte[]) buf; | 
|  |  | 
|  | zs.next_in = cast(typeof(zs.next_in)) buf.ptr; | 
|  | zs.avail_in = to!uint(buf.length); | 
|  |  | 
|  | err = deflate(&zs, Z_NO_FLUSH); | 
|  | if (err != Z_STREAM_END && err != Z_OK) | 
|  | { | 
|  | GC.free(destbuf.ptr); | 
|  | error(err); | 
|  | } | 
|  | destbuf.length = destbuf.length - zs.avail_out; | 
|  | return destbuf; | 
|  | } | 
|  |  | 
|  | /*** | 
|  | * Compress and return any remaining data. | 
|  | * The returned data should be appended to that returned by compress(). | 
|  | * Params: | 
|  | *  mode = one of the following: | 
|  | *          $(DL | 
|  | $(DT Z_SYNC_FLUSH ) | 
|  | $(DD Syncs up flushing to the next byte boundary. | 
|  | Used when more data is to be compressed later on.) | 
|  | $(DT Z_FULL_FLUSH ) | 
|  | $(DD Syncs up flushing to the next byte boundary. | 
|  | Used when more data is to be compressed later on, | 
|  | and the decompressor needs to be restartable at this | 
|  | point.) | 
|  | $(DT Z_FINISH) | 
|  | $(DD (default) Used when finished compressing the data. ) | 
|  | ) | 
|  | */ | 
|  | void[] flush(int mode = Z_FINISH) | 
|  | in | 
|  | { | 
|  | assert(mode == Z_FINISH || mode == Z_SYNC_FLUSH || mode == Z_FULL_FLUSH); | 
|  | } | 
|  | body | 
|  | { | 
|  | import core.memory : GC; | 
|  | ubyte[] destbuf; | 
|  | ubyte[512] tmpbuf = void; | 
|  | int err; | 
|  |  | 
|  | if (!inited) | 
|  | return null; | 
|  |  | 
|  | /* may be  zs.avail_out+<some constant> | 
|  | * zs.avail_out is set nonzero by deflate in previous compress() | 
|  | */ | 
|  | //tmpbuf = new void[zs.avail_out]; | 
|  | zs.next_out = tmpbuf.ptr; | 
|  | zs.avail_out = tmpbuf.length; | 
|  |  | 
|  | while ( (err = deflate(&zs, mode)) != Z_STREAM_END) | 
|  | { | 
|  | if (err == Z_OK) | 
|  | { | 
|  | if (zs.avail_out != 0 && mode != Z_FINISH) | 
|  | break; | 
|  | else if (zs.avail_out == 0) | 
|  | { | 
|  | destbuf ~= tmpbuf; | 
|  | zs.next_out = tmpbuf.ptr; | 
|  | zs.avail_out = tmpbuf.length; | 
|  | continue; | 
|  | } | 
|  | err = Z_BUF_ERROR; | 
|  | } | 
|  | GC.free(destbuf.ptr); | 
|  | error(err); | 
|  | } | 
|  | destbuf ~= tmpbuf[0 .. (tmpbuf.length - zs.avail_out)]; | 
|  |  | 
|  | if (mode == Z_FINISH) | 
|  | { | 
|  | err = deflateEnd(&zs); | 
|  | inited = 0; | 
|  | if (err) | 
|  | error(err); | 
|  | } | 
|  | return destbuf; | 
|  | } | 
|  | } | 
|  |  | 
|  | /****** | 
|  | * Used when the data to be decompressed is not all in one buffer. | 
|  | */ | 
|  |  | 
|  | class UnCompress | 
|  | { | 
|  | import std.conv : to; | 
|  |  | 
|  | private: | 
|  | z_stream zs; | 
|  | int inited; | 
|  | int done; | 
|  | size_t destbufsize; | 
|  |  | 
|  | HeaderFormat format; | 
|  |  | 
|  | void error(int err) | 
|  | { | 
|  | if (inited) | 
|  | {   inflateEnd(&zs); | 
|  | inited = 0; | 
|  | } | 
|  | throw new ZlibException(err); | 
|  | } | 
|  |  | 
|  | public: | 
|  |  | 
|  | /** | 
|  | * Construct. destbufsize is the same as for D.zlib.uncompress(). | 
|  | */ | 
|  | this(uint destbufsize) | 
|  | { | 
|  | this.destbufsize = destbufsize; | 
|  | } | 
|  |  | 
|  | /** ditto */ | 
|  | this(HeaderFormat format = HeaderFormat.determineFromData) | 
|  | { | 
|  | this.format = format; | 
|  | } | 
|  |  | 
|  | ~this() | 
|  | {   int err; | 
|  |  | 
|  | if (inited) | 
|  | { | 
|  | inited = 0; | 
|  | inflateEnd(&zs); | 
|  | } | 
|  | done = 1; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Decompress the data in buf and return the decompressed data. | 
|  | * The buffers returned from successive calls to this should be concatenated | 
|  | * together. | 
|  | */ | 
|  | const(void)[] uncompress(const(void)[] buf) | 
|  | in | 
|  | { | 
|  | assert(!done); | 
|  | } | 
|  | body | 
|  | { | 
|  | import core.memory : GC; | 
|  | int err; | 
|  | ubyte[] destbuf; | 
|  |  | 
|  | if (buf.length == 0) | 
|  | return null; | 
|  |  | 
|  | if (!inited) | 
|  | { | 
|  | int windowBits = 15; | 
|  | if (format == HeaderFormat.gzip) | 
|  | windowBits += 16; | 
|  | else if (format == HeaderFormat.determineFromData) | 
|  | windowBits += 32; | 
|  |  | 
|  | err = inflateInit2(&zs, windowBits); | 
|  | if (err) | 
|  | error(err); | 
|  | inited = 1; | 
|  | } | 
|  |  | 
|  | if (!destbufsize) | 
|  | destbufsize = to!uint(buf.length) * 2; | 
|  | destbuf = new ubyte[zs.avail_in * 2 + destbufsize]; | 
|  | zs.next_out = destbuf.ptr; | 
|  | zs.avail_out = to!uint(destbuf.length); | 
|  |  | 
|  | if (zs.avail_in) | 
|  | buf = zs.next_in[0 .. zs.avail_in] ~ cast(ubyte[]) buf; | 
|  |  | 
|  | zs.next_in = cast(ubyte*) buf.ptr; | 
|  | zs.avail_in = to!uint(buf.length); | 
|  |  | 
|  | err = inflate(&zs, Z_NO_FLUSH); | 
|  | if (err != Z_STREAM_END && err != Z_OK) | 
|  | { | 
|  | GC.free(destbuf.ptr); | 
|  | error(err); | 
|  | } | 
|  | destbuf.length = destbuf.length - zs.avail_out; | 
|  | return destbuf; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Decompress and return any remaining data. | 
|  | * The returned data should be appended to that returned by uncompress(). | 
|  | * The UnCompress object cannot be used further. | 
|  | */ | 
|  | void[] flush() | 
|  | in | 
|  | { | 
|  | assert(!done); | 
|  | } | 
|  | out | 
|  | { | 
|  | assert(done); | 
|  | } | 
|  | body | 
|  | { | 
|  | import core.memory : GC; | 
|  | ubyte[] extra; | 
|  | ubyte[] destbuf; | 
|  | int err; | 
|  |  | 
|  | done = 1; | 
|  | if (!inited) | 
|  | return null; | 
|  |  | 
|  | L1: | 
|  | destbuf = new ubyte[zs.avail_in * 2 + 100]; | 
|  | zs.next_out = destbuf.ptr; | 
|  | zs.avail_out = to!uint(destbuf.length); | 
|  |  | 
|  | err = etc.c.zlib.inflate(&zs, Z_NO_FLUSH); | 
|  | if (err == Z_OK && zs.avail_out == 0) | 
|  | { | 
|  | extra ~= destbuf; | 
|  | goto L1; | 
|  | } | 
|  | if (err != Z_STREAM_END) | 
|  | { | 
|  | GC.free(destbuf.ptr); | 
|  | if (err == Z_OK) | 
|  | err = Z_BUF_ERROR; | 
|  | error(err); | 
|  | } | 
|  | destbuf = destbuf.ptr[0 .. zs.next_out - destbuf.ptr]; | 
|  | err = etc.c.zlib.inflateEnd(&zs); | 
|  | inited = 0; | 
|  | if (err) | 
|  | error(err); | 
|  | if (extra.length) | 
|  | destbuf = extra ~ destbuf; | 
|  | return destbuf; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* ========================== unittest ========================= */ | 
|  |  | 
|  | import std.random; | 
|  | import std.stdio; | 
|  |  | 
|  | @system unittest // by Dave | 
|  | { | 
|  | debug(zlib) writeln("std.zlib.unittest"); | 
|  |  | 
|  | bool CompressThenUncompress (void[] src) | 
|  | { | 
|  | ubyte[] dst = std.zlib.compress(src); | 
|  | double ratio = (dst.length / cast(double) src.length); | 
|  | debug(zlib) writef("src.length: %1$d, dst: %2$d, Ratio = %3$f", src.length, dst.length, ratio); | 
|  | ubyte[] uncompressedBuf; | 
|  | uncompressedBuf = cast(ubyte[]) std.zlib.uncompress(dst); | 
|  | assert(src.length == uncompressedBuf.length); | 
|  | assert(src == uncompressedBuf); | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  |  | 
|  | // smallish buffers | 
|  | for (int idx = 0; idx < 25; idx++) | 
|  | { | 
|  | char[] buf = new char[uniform(0, 100)]; | 
|  |  | 
|  | // Alternate between more & less compressible | 
|  | foreach (ref char c; buf) | 
|  | c = cast(char) (' ' + (uniform(0, idx % 2 ? 91 : 2))); | 
|  |  | 
|  | if (CompressThenUncompress(buf)) | 
|  | { | 
|  | debug(zlib) writeln("; Success."); | 
|  | } | 
|  | else | 
|  | { | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | // larger buffers | 
|  | for (int idx = 0; idx < 25; idx++) | 
|  | { | 
|  | char[] buf = new char[uniform(0, 1000/*0000*/)]; | 
|  |  | 
|  | // Alternate between more & less compressible | 
|  | foreach (ref char c; buf) | 
|  | c = cast(char) (' ' + (uniform(0, idx % 2 ? 91 : 10))); | 
|  |  | 
|  | if (CompressThenUncompress(buf)) | 
|  | { | 
|  | debug(zlib) writefln("; Success."); | 
|  | } | 
|  | else | 
|  | { | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | debug(zlib) writefln("PASSED std.zlib.unittest"); | 
|  | } | 
|  |  | 
|  |  | 
|  | @system unittest // by Artem Rebrov | 
|  | { | 
|  | Compress cmp = new Compress; | 
|  | UnCompress decmp = new UnCompress; | 
|  |  | 
|  | const(void)[] input; | 
|  | input = "tesatdffadf"; | 
|  |  | 
|  | const(void)[] buf = cmp.compress(input); | 
|  | buf ~= cmp.flush(); | 
|  | const(void)[] output = decmp.uncompress(buf); | 
|  |  | 
|  | //writefln("input = '%s'", cast(char[]) input); | 
|  | //writefln("output = '%s'", cast(char[]) output); | 
|  | assert( output[] == input[] ); | 
|  | } | 
|  |  | 
|  | @system unittest | 
|  | { | 
|  | static assert(__traits(compiles, etc.c.zlib.gzclose(null)));        // bugzilla 15457 | 
|  | } |