1 // Written in the D programming language.
4 * Compress/decompress data using the $(HTTP www._zlib.net, _zlib library).
8 * If you have a small buffer you can use $(LREF compress) and
9 * $(LREF uncompress) directly.
15 * "the quick brown fox jumps over the lazy dog\r
16 * the quick brown fox jumps over the lazy dog\r";
21 * dst = compress(src);
22 * result = cast(ubyte[]) uncompress(dst);
23 * assert(result == src);
26 * When the data to be compressed doesn't fit in one buffer, use
27 * $(LREF Compress) and $(LREF UnCompress).
32 * import std.conv : to;
33 * import std.algorithm.iteration : map;
35 * UnCompress decmp = new UnCompress;
36 * foreach (chunk; stdin.byChunk(4096).map!(x => decmp.uncompress(x)))
38 * chunk.to!string.write;
44 * $(HTTP en.wikipedia.org/wiki/Zlib, Wikipedia)
46 * Copyright: Copyright Digital Mars 2000 - 2011.
47 * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
48 * Authors: $(HTTP digitalmars.com, Walter Bright)
49 * Source: $(PHOBOSSRC std/_zlib.d)
51 /* Copyright Digital Mars 2000 - 2011.
52 * Distributed under the Boost Software License, Version 1.0.
53 * (See accompanying file LICENSE_1_0.txt or copy at
54 * http://www.boost.org/LICENSE_1_0.txt)
58 //debug=zlib; // uncomment to turn on debugging printf's
72 /*************************************
73 * Errors throw a ZlibException.
76 class ZlibException
: Exception
83 case Z_STREAM_END
: msg
= "stream end"; break;
84 case Z_NEED_DICT
: msg
= "need dict"; break;
85 case Z_ERRNO
: msg
= "errno"; break;
86 case Z_STREAM_ERROR
: msg
= "stream error"; break;
87 case Z_DATA_ERROR
: msg
= "data error"; break;
88 case Z_MEM_ERROR
: msg
= "mem error"; break;
89 case Z_BUF_ERROR
: msg
= "buf error"; break;
90 case Z_VERSION_ERROR
: msg
= "version error"; break;
91 default: msg
= "unknown error"; break;
98 * $(P Compute the Adler-32 checksum of a buffer's worth of data.)
101 * adler = the starting checksum for the computation. Use 1
102 * for a new checksum. Use the output of this function
103 * for a cumulative checksum.
104 * buf = buffer containing input data
107 * A $(D uint) checksum for the provided input data and starting checksum
110 * $(LINK http://en.wikipedia.org/wiki/Adler-32)
113 uint adler32(uint adler
, const(void)[] buf
)
115 import std
.range
: chunks
;
116 foreach (chunk
; (cast(ubyte[]) buf
).chunks(0xFFFF0000))
118 adler
= etc
.c
.zlib
.adler32(adler
, chunk
.ptr
, cast(uint) chunk
.length
);
126 static ubyte[] data
= [1,2,3,4,5,6,7,8,9,10];
128 uint adler
= adler32(0u, data
);
129 assert(adler
== 0xdc0037);
134 static string data
= "test";
136 uint adler
= adler32(1, data
);
137 assert(adler
== 0x045d01c1);
141 * $(P Compute the CRC32 checksum of a buffer's worth of data.)
144 * crc = the starting checksum for the computation. Use 0
145 * for a new checksum. Use the output of this function
146 * for a cumulative checksum.
147 * buf = buffer containing input data
150 * A $(D uint) checksum for the provided input data and starting checksum
153 * $(LINK http://en.wikipedia.org/wiki/Cyclic_redundancy_check)
156 uint crc32(uint crc
, const(void)[] buf
)
158 import std
.range
: chunks
;
159 foreach (chunk
; (cast(ubyte[]) buf
).chunks(0xFFFF0000))
161 crc
= etc
.c
.zlib
.crc32(crc
, chunk
.ptr
, cast(uint) chunk
.length
);
168 static ubyte[] data
= [1,2,3,4,5,6,7,8,9,10];
172 debug(zlib
) printf("D.zlib.crc32.unittest\n");
173 crc
= crc32(0u, cast(void[]) data
);
174 debug(zlib
) printf("crc = %x\n", crc
);
175 assert(crc
== 0x2520577b);
182 * srcbuf = buffer containing the data to compress
183 * level = compression level. Legal values are -1 .. 9, with -1 indicating
184 * the default level (6), 0 indicating no compression, 1 being the
185 * least compression and 9 being the most.
188 * the compressed data
191 ubyte[] compress(const(void)[] srcbuf
, int level
)
194 assert(-1 <= level
&& level
<= 9);
198 import core
.memory
: GC
;
199 auto destlen
= srcbuf
.length
+ ((srcbuf
.length
+ 1023) / 1024) + 12;
200 auto destbuf
= new ubyte[destlen
];
201 auto err
= etc
.c
.zlib
.compress2(destbuf
.ptr
, &destlen
, cast(ubyte *) srcbuf
.ptr
, srcbuf
.length
, level
);
204 GC
.free(destbuf
.ptr
);
205 throw new ZlibException(err
);
208 destbuf
.length
= destlen
;
212 /*********************************************
216 ubyte[] compress(const(void)[] srcbuf
)
218 return compress(srcbuf
, Z_DEFAULT_COMPRESSION
);
221 /*********************************************
222 * Decompresses the data in srcbuf[].
224 * srcbuf = buffer containing the compressed data.
225 * destlen = size of the uncompressed data.
226 * It need not be accurate, but the decompression will be faster
227 * if the exact size is supplied.
228 * winbits = the base two logarithm of the maximum window size.
229 * Returns: the decompressed data.
232 void[] uncompress(const(void)[] srcbuf
, size_t destlen
= 0u, int winbits
= 15)
234 import std
.conv
: to
;
239 destlen
= srcbuf
.length
* 2 + 1;
241 etc
.c
.zlib
.z_stream zs
;
242 zs
.next_in
= cast(typeof(zs
.next_in
)) srcbuf
.ptr
;
243 zs
.avail_in
= to
!uint(srcbuf
.length
);
244 err
= etc
.c
.zlib
.inflateInit2(&zs
, winbits
);
247 throw new ZlibException(err
);
250 size_t olddestlen
= 0u;
255 destbuf
.length
= destlen
;
256 zs
.next_out
= cast(typeof(zs
.next_out
)) &destbuf
[olddestlen
];
257 zs
.avail_out
= to
!uint(destlen
- olddestlen
);
258 olddestlen
= destlen
;
260 err
= etc
.c
.zlib
.inflate(&zs
, Z_NO_FLUSH
);
264 destlen
= destbuf
.length
* 2;
268 destbuf
.length
= zs
.total_out
;
269 err
= etc
.c
.zlib
.inflateEnd(&zs
);
271 throw new ZlibException(err
);
275 etc
.c
.zlib
.inflateEnd(&zs
);
276 throw new ZlibException(err
);
285 "the quick brown fox jumps over the lazy dog\r
286 the quick brown fox jumps over the lazy dog\r
294 result
= cast(ubyte[]) uncompress(dst
);
295 //arrayPrint(result);
296 assert(result
== src
);
301 ubyte[] src
= new ubyte[1000000];
307 assert(dst
.length
*2 + 1 < src
.length
);
308 result
= cast(ubyte[]) uncompress(dst
);
309 assert(result
== src
);
313 void arrayPrint(ubyte[] array)
315 //printf("array %p,%d\n", cast(void*) array, array.length);
316 for (size_t i = 0; i < array.length; i++)
318 printf("%02x ", array[i]);
319 if (((i + 1) & 15) == 0)
326 /// the header format the compressed stream is wrapped in
328 deflate
, /// a standard zlib header
329 gzip
, /// a gzip file format header
330 determineFromData
/// used when decompressing. Try to automatically detect the stream format by looking at the data
333 /*********************************************
334 * Used when the data to be compressed is not all in one buffer.
339 import std
.conv
: to
;
343 int level
= Z_DEFAULT_COMPRESSION
;
353 throw new ZlibException(err
);
362 * level = compression level. Legal values are 1 .. 9, with 1 being the least
363 * compression and 9 being the most. The default value is 6.
364 * header = sets the compression type to one of the options available
365 * in $(LREF HeaderFormat). Defaults to HeaderFormat.deflate.
368 * $(LREF compress), $(LREF HeaderFormat)
370 this(int level
, HeaderFormat header
= HeaderFormat
.deflate
)
373 assert(1 <= level
&& level
<= 9);
378 this.gzip
= header
== HeaderFormat
.gzip
;
382 this(HeaderFormat header
= HeaderFormat
.deflate
)
384 this.gzip
= header
== HeaderFormat
.gzip
;
398 * Compress the data in buf and return the compressed data.
400 * buf = data to compress
403 * the compressed data. The buffers returned from successive calls to this should be concatenated together.
406 const(void)[] compress(const(void)[] buf
)
408 import core
.memory
: GC
;
417 err
= deflateInit2(&zs
, level
, Z_DEFLATED
, 15 + (gzip ?
16 : 0), 8, Z_DEFAULT_STRATEGY
);
423 destbuf
= new ubyte[zs
.avail_in
+ buf
.length
];
424 zs
.next_out
= destbuf
.ptr
;
425 zs
.avail_out
= to
!uint(destbuf
.length
);
428 buf
= zs
.next_in
[0 .. zs
.avail_in
] ~ cast(ubyte[]) buf
;
430 zs
.next_in
= cast(typeof(zs
.next_in
)) buf
.ptr
;
431 zs
.avail_in
= to
!uint(buf
.length
);
433 err
= deflate(&zs
, Z_NO_FLUSH
);
434 if (err
!= Z_STREAM_END
&& err
!= Z_OK
)
436 GC
.free(destbuf
.ptr
);
439 destbuf
.length
= destbuf
.length
- zs
.avail_out
;
444 * Compress and return any remaining data.
445 * The returned data should be appended to that returned by compress().
447 * mode = one of the following:
450 $(DD Syncs up flushing to the next byte boundary.
451 Used when more data is to be compressed later on.)
453 $(DD Syncs up flushing to the next byte boundary.
454 Used when more data is to be compressed later on,
455 and the decompressor needs to be restartable at this
458 $(DD (default) Used when finished compressing the data. )
461 void[] flush(int mode
= Z_FINISH
)
464 assert(mode
== Z_FINISH || mode
== Z_SYNC_FLUSH || mode
== Z_FULL_FLUSH
);
468 import core
.memory
: GC
;
470 ubyte[512] tmpbuf
= void;
476 /* may be zs.avail_out+<some constant>
477 * zs.avail_out is set nonzero by deflate in previous compress()
479 //tmpbuf = new void[zs.avail_out];
480 zs
.next_out
= tmpbuf
.ptr
;
481 zs
.avail_out
= tmpbuf
.length
;
483 while ( (err
= deflate(&zs
, mode
)) != Z_STREAM_END
)
487 if (zs
.avail_out
!= 0 && mode
!= Z_FINISH
)
489 else if (zs
.avail_out
== 0)
492 zs
.next_out
= tmpbuf
.ptr
;
493 zs
.avail_out
= tmpbuf
.length
;
498 GC
.free(destbuf
.ptr
);
501 destbuf
~= tmpbuf
[0 .. (tmpbuf
.length
- zs
.avail_out
)];
503 if (mode
== Z_FINISH
)
505 err
= deflateEnd(&zs
);
515 * Used when the data to be decompressed is not all in one buffer.
520 import std
.conv
: to
;
536 throw new ZlibException(err
);
542 * Construct. destbufsize is the same as for D.zlib.uncompress().
544 this(uint destbufsize
)
546 this.destbufsize
= destbufsize
;
550 this(HeaderFormat format
= HeaderFormat
.determineFromData
)
552 this.format
= format
;
567 * Decompress the data in buf and return the decompressed data.
568 * The buffers returned from successive calls to this should be concatenated
571 const(void)[] uncompress(const(void)[] buf
)
578 import core
.memory
: GC
;
588 if (format
== HeaderFormat
.gzip
)
590 else if (format
== HeaderFormat
.determineFromData
)
593 err
= inflateInit2(&zs
, windowBits
);
600 destbufsize
= to
!uint(buf
.length
) * 2;
601 destbuf
= new ubyte[zs
.avail_in
* 2 + destbufsize
];
602 zs
.next_out
= destbuf
.ptr
;
603 zs
.avail_out
= to
!uint(destbuf
.length
);
606 buf
= zs
.next_in
[0 .. zs
.avail_in
] ~ cast(ubyte[]) buf
;
608 zs
.next_in
= cast(ubyte*) buf
.ptr
;
609 zs
.avail_in
= to
!uint(buf
.length
);
611 err
= inflate(&zs
, Z_NO_FLUSH
);
612 if (err
!= Z_STREAM_END
&& err
!= Z_OK
)
614 GC
.free(destbuf
.ptr
);
617 destbuf
.length
= destbuf
.length
- zs
.avail_out
;
622 * Decompress and return any remaining data.
623 * The returned data should be appended to that returned by uncompress().
624 * The UnCompress object cannot be used further.
637 import core
.memory
: GC
;
647 destbuf
= new ubyte[zs
.avail_in
* 2 + 100];
648 zs
.next_out
= destbuf
.ptr
;
649 zs
.avail_out
= to
!uint(destbuf
.length
);
651 err
= etc
.c
.zlib
.inflate(&zs
, Z_NO_FLUSH
);
652 if (err
== Z_OK
&& zs
.avail_out
== 0)
657 if (err
!= Z_STREAM_END
)
659 GC
.free(destbuf
.ptr
);
664 destbuf
= destbuf
.ptr
[0 .. zs
.next_out
- destbuf
.ptr
];
665 err
= etc
.c
.zlib
.inflateEnd(&zs
);
670 destbuf
= extra
~ destbuf
;
675 /* ========================== unittest ========================= */
680 @system unittest // by Dave
682 debug(zlib
) writeln("std.zlib.unittest");
684 bool CompressThenUncompress (void[] src
)
686 ubyte[] dst
= std
.zlib
.compress(src
);
687 double ratio
= (dst
.length
/ cast(double) src
.length
);
688 debug(zlib
) writef("src.length: %1$d, dst: %2$d, Ratio = %3$f", src
.length
, dst
.length
, ratio
);
689 ubyte[] uncompressedBuf
;
690 uncompressedBuf
= cast(ubyte[]) std
.zlib
.uncompress(dst
);
691 assert(src
.length
== uncompressedBuf
.length
);
692 assert(src
== uncompressedBuf
);
699 for (int idx
= 0; idx
< 25; idx
++)
701 char[] buf
= new char[uniform(0, 100)];
703 // Alternate between more & less compressible
704 foreach (ref char c
; buf
)
705 c
= cast(char) (' ' + (uniform(0, idx
% 2 ?
91 : 2)));
707 if (CompressThenUncompress(buf
))
709 debug(zlib
) writeln("; Success.");
718 for (int idx
= 0; idx
< 25; idx
++)
720 char[] buf
= new char[uniform(0, 1000/*0000*/)];
722 // Alternate between more & less compressible
723 foreach (ref char c
; buf
)
724 c
= cast(char) (' ' + (uniform(0, idx
% 2 ?
91 : 10)));
726 if (CompressThenUncompress(buf
))
728 debug(zlib
) writefln("; Success.");
736 debug(zlib
) writefln("PASSED std.zlib.unittest");
740 @system unittest // by Artem Rebrov
742 Compress
cmp = new Compress
;
743 UnCompress decmp
= new UnCompress
;
746 input
= "tesatdffadf";
748 const(void)[] buf
= cmp.compress(input
);
750 const(void)[] output
= decmp
.uncompress(buf
);
752 //writefln("input = '%s'", cast(char[]) input);
753 //writefln("output = '%s'", cast(char[]) output);
754 assert( output
[] == input
[] );
759 static assert(__traits(compiles
, etc
.c
.zlib
.gzclose(null))); // bugzilla 15457