Mercurial > projects > ldc
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 |