comparison lphobos/std/zlib.d @ 473:373489eeaf90

Applied downs' lphobos update
author Tomas Lindquist Olsen <tomas.l.olsen@gmail.com>
date Mon, 04 Aug 2008 19:28:49 +0200
parents
children
comparison
equal deleted inserted replaced
472:15c804b6ce77 473:373489eeaf90
1 /**
2 * Compress/decompress data using the $(LINK2 http://www._zlib.net, zlib library).
3 *
4 * References:
5 * $(LINK2 http://en.wikipedia.org/wiki/Zlib, Wikipedia)
6 * License:
7 * Public Domain
8 *
9 * Macros:
10 * WIKI = Phobos/StdZlib
11 */
12
13 /* NOTE: This file has been patched from the original DMD distribution to
14 work with the GDC compiler.
15
16 Modified by David Friedman, February 2007
17 */
18
19 module std.zlib;
20
21 //debug=zlib; // uncomment to turn on debugging printf's
22
23 private import etc.c.zlib, std.stdint;
24
25 // Values for 'mode'
26
27 enum
28 {
29 Z_NO_FLUSH = 0,
30 Z_SYNC_FLUSH = 2,
31 Z_FULL_FLUSH = 3,
32 Z_FINISH = 4,
33 }
34
35 /*************************************
36 * Errors throw a ZlibException.
37 */
38
39 class ZlibException : Exception
40 {
41 this(int errnum)
42 { char[] msg;
43
44 switch (errnum)
45 {
46 case Z_STREAM_END: msg = "stream end"; break;
47 case Z_NEED_DICT: msg = "need dict"; break;
48 case Z_ERRNO: msg = "errno"; break;
49 case Z_STREAM_ERROR: msg = "stream error"; break;
50 case Z_DATA_ERROR: msg = "data error"; break;
51 case Z_MEM_ERROR: msg = "mem error"; break;
52 case Z_BUF_ERROR: msg = "buf error"; break;
53 case Z_VERSION_ERROR: msg = "version error"; break;
54 default: msg = "unknown error"; break;
55 }
56 super(msg);
57 }
58 }
59
60 /**************************************************
61 * Compute the Adler32 checksum of the data in buf[]. adler is the starting
62 * value when computing a cumulative checksum.
63 */
64
65 uint adler32(uint adler, void[] buf)
66 {
67 return etc.c.zlib.adler32(adler, cast(ubyte *)buf, buf.length);
68 }
69
70 unittest
71 {
72 static ubyte[] data = [1,2,3,4,5,6,7,8,9,10];
73
74 uint adler;
75
76 debug(zlib) printf("D.zlib.adler32.unittest\n");
77 adler = adler32(0u, cast(void[])data);
78 debug(zlib) printf("adler = %x\n", adler);
79 assert(adler == 0xdc0037);
80 }
81
82 /*********************************
83 * Compute the CRC32 checksum of the data in buf[]. crc is the starting value
84 * when computing a cumulative checksum.
85 */
86
87 uint crc32(uint crc, void[] buf)
88 {
89 return etc.c.zlib.crc32(crc, cast(ubyte *)buf, buf.length);
90 }
91
92 unittest
93 {
94 static ubyte[] data = [1,2,3,4,5,6,7,8,9,10];
95
96 uint crc;
97
98 debug(zlib) printf("D.zlib.crc32.unittest\n");
99 crc = crc32(0u, cast(void[])data);
100 debug(zlib) printf("crc = %x\n", crc);
101 assert(crc == 0x2520577b);
102 }
103
104 /*********************************************
105 * Compresses the data in srcbuf[] using compression _level level.
106 * The default value
107 * for level is 6, legal values are 1..9, with 1 being the least compression
108 * and 9 being the most.
109 * Returns the compressed data.
110 */
111
112 void[] compress(void[] srcbuf, int level)
113 in
114 {
115 assert(-1 <= level && level <= 9);
116 }
117 body
118 {
119 int err;
120 ubyte[] destbuf;
121 Culong_t destlen;
122
123 destlen = srcbuf.length + ((srcbuf.length + 1023) / 1024) + 12;
124 destbuf = new ubyte[destlen];
125 err = etc.c.zlib.compress2(destbuf.ptr, &destlen, cast(ubyte *)srcbuf, srcbuf.length, level);
126 if (err)
127 { delete destbuf;
128 throw new ZlibException(err);
129 }
130
131 destbuf.length = destlen;
132 return destbuf;
133 }
134
135 /*********************************************
136 * ditto
137 */
138
139 void[] compress(void[] buf)
140 {
141 return compress(buf, Z_DEFAULT_COMPRESSION);
142 }
143
144 /*********************************************
145 * Decompresses the data in srcbuf[].
146 * Params: destlen = size of the uncompressed data.
147 * It need not be accurate, but the decompression will be faster if the exact
148 * size is supplied.
149 * Returns: the decompressed data.
150 */
151
152 void[] uncompress(void[] srcbuf, size_t destlen = 0u, int winbits = 15)
153 {
154 int err;
155 ubyte[] destbuf;
156
157 if (!destlen)
158 destlen = srcbuf.length * 2 + 1;
159
160 while (1)
161 {
162 etc.c.zlib.z_stream zs;
163
164 destbuf = new ubyte[destlen];
165
166 zs.next_in = cast(ubyte*) srcbuf;
167 zs.avail_in = srcbuf.length;
168
169 zs.next_out = destbuf.ptr;
170 zs.avail_out = destlen;
171
172 err = etc.c.zlib.inflateInit2(&zs, winbits);
173 if (err)
174 { delete destbuf;
175 throw new ZlibException(err);
176 }
177 err = etc.c.zlib.inflate(&zs, Z_NO_FLUSH);
178 switch (err)
179 {
180 case Z_OK:
181 etc.c.zlib.inflateEnd(&zs);
182 destlen = destbuf.length * 2;
183 continue;
184
185 case Z_STREAM_END:
186 destbuf.length = zs.total_out;
187 err = etc.c.zlib.inflateEnd(&zs);
188 if (err != Z_OK)
189 goto Lerr;
190 return destbuf;
191
192 default:
193 etc.c.zlib.inflateEnd(&zs);
194 Lerr:
195 delete destbuf;
196 throw new ZlibException(err);
197 }
198 }
199 assert(0);
200 }
201
202 unittest
203 {
204 ubyte[] src = cast(ubyte[])
205 "the quick brown fox jumps over the lazy dog\r
206 the quick brown fox jumps over the lazy dog\r
207 ";
208 ubyte[] dst;
209 ubyte[] result;
210
211 //arrayPrint(src);
212 dst = cast(ubyte[])compress(cast(void[])src);
213 //arrayPrint(dst);
214 result = cast(ubyte[])uncompress(cast(void[])dst);
215 //arrayPrint(result);
216 assert(result == src);
217 }
218
219 /+
220 void arrayPrint(ubyte[] array)
221 {
222 //printf("array %p,%d\n", (void*)array, array.length);
223 for (int i = 0; i < array.length; i++)
224 {
225 printf("%02x ", array[i]);
226 if (((i + 1) & 15) == 0)
227 printf("\n");
228 }
229 printf("\n\n");
230 }
231 +/
232
233 /*********************************************
234 * Used when the data to be compressed is not all in one buffer.
235 */
236
237 class Compress
238 {
239 private:
240 z_stream zs;
241 int level = Z_DEFAULT_COMPRESSION;
242 int inited;
243
244 void error(int err)
245 {
246 if (inited)
247 { deflateEnd(&zs);
248 inited = 0;
249 }
250 throw new ZlibException(err);
251 }
252
253 public:
254
255 /**
256 * Construct. level is the same as for D.zlib.compress().
257 */
258 this(int level)
259 in
260 {
261 assert(1 <= level && level <= 9);
262 }
263 body
264 {
265 this.level = level;
266 }
267
268 /// ditto
269 this()
270 {
271 }
272
273 ~this()
274 { int err;
275
276 if (inited)
277 {
278 inited = 0;
279 err = deflateEnd(&zs);
280 if (err)
281 error(err);
282 }
283 }
284
285 /**
286 * Compress the data in buf and return the compressed data.
287 * The buffers
288 * returned from successive calls to this should be concatenated together.
289 */
290 void[] compress(void[] buf)
291 { int err;
292 ubyte[] destbuf;
293
294 if (buf.length == 0)
295 return null;
296
297 if (!inited)
298 {
299 err = deflateInit(&zs, level);
300 if (err)
301 error(err);
302 inited = 1;
303 }
304
305 destbuf = new ubyte[zs.avail_in + buf.length];
306 zs.next_out = destbuf.ptr;
307 zs.avail_out = destbuf.length;
308
309 if (zs.avail_in)
310 buf = cast(void[])zs.next_in[0 .. zs.avail_in] ~ buf;
311
312 zs.next_in = cast(ubyte*) buf.ptr;
313 zs.avail_in = buf.length;
314
315 err = deflate(&zs, Z_NO_FLUSH);
316 if (err != Z_STREAM_END && err != Z_OK)
317 { delete destbuf;
318 error(err);
319 }
320 destbuf.length = destbuf.length - zs.avail_out;
321 return destbuf;
322 }
323
324 /***
325 * Compress and return any remaining data.
326 * The returned data should be appended to that returned by compress().
327 * Params:
328 * mode = one of the following:
329 * $(DL
330 $(DT Z_SYNC_FLUSH )
331 $(DD Syncs up flushing to the next byte boundary.
332 Used when more data is to be compressed later on.)
333 $(DT Z_FULL_FLUSH )
334 $(DD Syncs up flushing to the next byte boundary.
335 Used when more data is to be compressed later on,
336 and the decompressor needs to be restartable at this
337 point.)
338 $(DT Z_FINISH)
339 $(DD (default) Used when finished compressing the data. )
340 )
341 */
342 void[] flush(int mode = Z_FINISH)
343 in
344 {
345 assert(mode == Z_FINISH || mode == Z_SYNC_FLUSH || mode == Z_FULL_FLUSH);
346 }
347 body
348 {
349 void[] destbuf;
350 ubyte[512] tmpbuf = void;
351 int err;
352
353 if (!inited)
354 return null;
355
356 /* may be zs.avail_out+<some constant>
357 * zs.avail_out is set nonzero by deflate in previous compress()
358 */
359 //tmpbuf = new void[zs.avail_out];
360 zs.next_out = tmpbuf.ptr;
361 zs.avail_out = tmpbuf.length;
362
363 while( (err = deflate(&zs, mode)) != Z_STREAM_END)
364 {
365 if (err == Z_OK)
366 {
367 if (zs.avail_out != 0 && mode != Z_FINISH)
368 break;
369 else if(zs.avail_out == 0)
370 {
371 destbuf ~= tmpbuf;
372 zs.next_out = tmpbuf.ptr;
373 zs.avail_out = tmpbuf.length;
374 continue;
375 }
376 err = Z_BUF_ERROR;
377 }
378 delete destbuf;
379 error(err);
380 }
381 destbuf ~= tmpbuf[0 .. (tmpbuf.length - zs.avail_out)];
382
383 if (mode == Z_FINISH)
384 {
385 err = deflateEnd(&zs);
386 inited = 0;
387 if (err)
388 error(err);
389 }
390 return destbuf;
391 }
392 }
393
394 /******
395 * Used when the data to be decompressed is not all in one buffer.
396 */
397
398 class UnCompress
399 {
400 private:
401 z_stream zs;
402 int inited;
403 int done;
404 uint destbufsize;
405
406 void error(int err)
407 {
408 if (inited)
409 { inflateEnd(&zs);
410 inited = 0;
411 }
412 throw new ZlibException(err);
413 }
414
415 public:
416
417 /**
418 * Construct. destbufsize is the same as for D.zlib.uncompress().
419 */
420 this(uint destbufsize)
421 {
422 this.destbufsize = destbufsize;
423 }
424
425 /** ditto */
426 this()
427 {
428 }
429
430 ~this()
431 { int err;
432
433 if (inited)
434 {
435 inited = 0;
436 err = inflateEnd(&zs);
437 if (err)
438 error(err);
439 }
440 done = 1;
441 }
442
443 /**
444 * Decompress the data in buf and return the decompressed data.
445 * The buffers returned from successive calls to this should be concatenated
446 * together.
447 */
448 void[] uncompress(void[] buf)
449 in
450 {
451 assert(!done);
452 }
453 body
454 { int err;
455 ubyte[] destbuf;
456
457 if (buf.length == 0)
458 return null;
459
460 if (!inited)
461 {
462 err = inflateInit(&zs);
463 if (err)
464 error(err);
465 inited = 1;
466 }
467
468 if (!destbufsize)
469 destbufsize = buf.length * 2;
470 destbuf = new ubyte[zs.avail_in * 2 + destbufsize];
471 zs.next_out = destbuf.ptr;
472 zs.avail_out = destbuf.length;
473
474 if (zs.avail_in)
475 buf = cast(void[])zs.next_in[0 .. zs.avail_in] ~ buf;
476
477 zs.next_in = cast(ubyte*) buf;
478 zs.avail_in = buf.length;
479
480 err = inflate(&zs, Z_NO_FLUSH);
481 if (err != Z_STREAM_END && err != Z_OK)
482 { delete destbuf;
483 error(err);
484 }
485 destbuf.length = destbuf.length - zs.avail_out;
486 return destbuf;
487 }
488
489 /**
490 * Decompress and return any remaining data.
491 * The returned data should be appended to that returned by uncompress().
492 * The UnCompress object cannot be used further.
493 */
494 void[] flush()
495 in
496 {
497 assert(!done);
498 }
499 out
500 {
501 assert(done);
502 }
503 body
504 {
505 ubyte[] extra;
506 ubyte[] destbuf;
507 int err;
508
509 done = 1;
510 if (!inited)
511 return null;
512
513 L1:
514 destbuf = new ubyte[zs.avail_in * 2 + 100];
515 zs.next_out = destbuf.ptr;
516 zs.avail_out = destbuf.length;
517
518 err = etc.c.zlib.inflate(&zs, Z_NO_FLUSH);
519 if (err == Z_OK && zs.avail_out == 0)
520 {
521 extra ~= destbuf;
522 goto L1;
523 }
524 if (err != Z_STREAM_END)
525 {
526 delete destbuf;
527 if (err == Z_OK)
528 err = Z_BUF_ERROR;
529 error(err);
530 }
531 destbuf = destbuf.ptr[0 .. zs.next_out - destbuf.ptr];
532 err = etc.c.zlib.inflateEnd(&zs);
533 inited = 0;
534 if (err)
535 error(err);
536 if (extra.length)
537 destbuf = extra ~ destbuf;
538 return destbuf;
539 }
540 }
541
542 /* ========================== unittest ========================= */
543
544 private import std.stdio;
545 private import std.random;
546
547 unittest // by Dave
548 {
549 debug(zlib) printf("std.zlib.unittest\n");
550
551 bool CompressThenUncompress (ubyte[] src)
552 {
553 try {
554 ubyte[] dst = cast(ubyte[])std.zlib.compress(cast(void[])src);
555 double ratio = (dst.length / cast(double)src.length);
556 debug(zlib) writef("src.length: ", src.length, ", dst: ", dst.length, ", Ratio = ", ratio);
557 ubyte[] uncompressedBuf;
558 uncompressedBuf = cast(ubyte[])std.zlib.uncompress(cast(void[])dst);
559 assert(src.length == uncompressedBuf.length);
560 assert(src == uncompressedBuf);
561 }
562 catch {
563 debug(zlib) writefln(" ... Exception thrown when src.length = ", src.length, ".");
564 return false;
565 }
566 return true;
567 }
568
569
570 // smallish buffers
571 for(int idx = 0; idx < 25; idx++) {
572 char[] buf = new char[rand() % 100];
573
574 // Alternate between more & less compressible
575 foreach(inout char c; buf) c = ' ' + (rand() % (idx % 2 ? 91 : 2));
576
577 if(CompressThenUncompress(cast(ubyte[])buf)) {
578 debug(zlib) printf("; Success.\n");
579 } else {
580 return;
581 }
582 }
583
584 // larger buffers
585 for(int idx = 0; idx < 25; idx++) {
586 char[] buf = new char[rand() % 1000/*0000*/];
587
588 // Alternate between more & less compressible
589 foreach(inout char c; buf) c = ' ' + (rand() % (idx % 2 ? 91 : 10));
590
591 if(CompressThenUncompress(cast(ubyte[])buf)) {
592 debug(zlib) printf("; Success.\n");
593 } else {
594 return;
595 }
596 }
597
598 debug(zlib) printf("PASSED std.zlib.unittest\n");
599 }
600
601
602 unittest // by Artem Rebrov
603 {
604 Compress cmp = new Compress;
605 UnCompress decmp = new UnCompress;
606
607 void[] input;
608 input = "tesatdffadf";
609
610 void[] buf = cmp.compress(input);
611 buf ~= cmp.flush();
612 void[] output = decmp.uncompress(buf);
613
614 //writefln("input = '%s'", cast(char[])input);
615 //writefln("output = '%s'", cast(char[])output);
616 assert( output[] == input[] );
617 }
618