aboutsummaryrefslogtreecommitdiff
path: root/libphobos/src/std/zlib.d
diff options
context:
space:
mode:
Diffstat (limited to 'libphobos/src/std/zlib.d')
-rw-r--r--libphobos/src/std/zlib.d760
1 files changed, 760 insertions, 0 deletions
diff --git a/libphobos/src/std/zlib.d b/libphobos/src/std/zlib.d
new file mode 100644
index 0000000..e6cce24
--- /dev/null
+++ b/libphobos/src/std/zlib.d
@@ -0,0 +1,760 @@
+// 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
+}