1 (* example.c -- usage example of the zlib compression library
   2  * Copyright (C) 1995-2003 Jean-loup Gailly.
   3  * For conditions of distribution and use, see copyright notice in zlib.h
   4  *
   5  * Pascal translation
   6  * Copyright (C) 1998 by Jacques Nomssi Nzali.
   7  * For conditions of distribution and use, see copyright notice in readme.txt
   8  *
   9  * Adaptation to the zlibpas interface
  10  * Copyright (C) 2003 by Cosmin Truta.
  11  * For conditions of distribution and use, see copyright notice in readme.txt
  12  *)
  13 
  14 program example;
  15 
  16 {$DEFINE TEST_COMPRESS}
  17 {DO NOT $DEFINE TEST_GZIO}
  18 {$DEFINE TEST_DEFLATE}
  19 {$DEFINE TEST_INFLATE}
  20 {$DEFINE TEST_FLUSH}
  21 {$DEFINE TEST_SYNC}
  22 {$DEFINE TEST_DICT}
  23 
  24 uses SysUtils, zlibpas;
  25 
  26 const TESTFILE = 'foo.gz';
  27 
  28 (* "hello world" would be more standard, but the repeated "hello"
  29  * stresses the compression code better, sorry...
  30  *)
  31 const hello: PChar = 'hello, hello!';
  32 
  33 const dictionary: PChar = 'hello';
  34 
  35 var dictId: LongInt; (* Adler32 value of the dictionary *)
  36 
  37 procedure CHECK_ERR(err: Integer; msg: String);
  38 begin
  39   if err <> Z_OK then
  40   begin
  41     WriteLn(msg, ' error: ', err);
  42     Halt(1);
  43   end;
  44 end;
  45 
  46 procedure EXIT_ERR(const msg: String);
  47 begin
  48   WriteLn('Error: ', msg);
  49   Halt(1);
  50 end;
  51 
  52 (* ===========================================================================
  53  * Test compress and uncompress
  54  *)
  55 {$IFDEF TEST_COMPRESS}
  56 procedure test_compress(compr: Pointer; comprLen: LongInt;
  57                         uncompr: Pointer; uncomprLen: LongInt);
  58 var err: Integer;
  59     len: LongInt;
  60 begin
  61   len := StrLen(hello)+1;
  62 
  63   err := compress(compr, comprLen, hello, len);
  64   CHECK_ERR(err, 'compress');
  65 
  66   StrCopy(PChar(uncompr), 'garbage');
  67 
  68   err := uncompress(uncompr, uncomprLen, compr, comprLen);
  69   CHECK_ERR(err, 'uncompress');
  70 
  71   if StrComp(PChar(uncompr), hello) <> 0 then
  72     EXIT_ERR('bad uncompress')
  73   else
  74     WriteLn('uncompress(): ', PChar(uncompr));
  75 end;
  76 {$ENDIF}
  77 
  78 (* ===========================================================================
  79  * Test read/write of .gz files
  80  *)
  81 {$IFDEF TEST_GZIO}
  82 procedure test_gzio(const fname: PChar; (* compressed file name *)
  83                     uncompr: Pointer;
  84                     uncomprLen: LongInt);
  85 var err: Integer;
  86     len: Integer;
  87     zfile: gzFile;
  88     pos: LongInt;
  89 begin
  90   len := StrLen(hello)+1;
  91 
  92   zfile := gzopen(fname, 'wb');
  93   if zfile = NIL then
  94   begin
  95     WriteLn('gzopen error');
  96     Halt(1);
  97   end;
  98   gzputc(zfile, 'h');
  99   if gzputs(zfile, 'ello') <> 4 then
 100   begin
 101     WriteLn('gzputs err: ', gzerror(zfile, err));
 102     Halt(1);
 103   end;
 104   {$IFDEF GZ_FORMAT_STRING}
 105   if gzprintf(zfile, ', %s!', 'hello') <> 8 then
 106   begin
 107     WriteLn('gzprintf err: ', gzerror(zfile, err));
 108     Halt(1);
 109   end;
 110   {$ELSE}
 111   if gzputs(zfile, ', hello!') <> 8 then
 112   begin
 113     WriteLn('gzputs err: ', gzerror(zfile, err));
 114     Halt(1);
 115   end;
 116   {$ENDIF}
 117   gzseek(zfile, 1, SEEK_CUR); (* add one zero byte *)
 118   gzclose(zfile);
 119 
 120   zfile := gzopen(fname, 'rb');
 121   if zfile = NIL then
 122   begin
 123     WriteLn('gzopen error');
 124     Halt(1);
 125   end;
 126 
 127   StrCopy(PChar(uncompr), 'garbage');
 128 
 129   if gzread(zfile, uncompr, uncomprLen) <> len then
 130   begin
 131     WriteLn('gzread err: ', gzerror(zfile, err));
 132     Halt(1);
 133   end;
 134   if StrComp(PChar(uncompr), hello) <> 0 then
 135   begin
 136     WriteLn('bad gzread: ', PChar(uncompr));
 137     Halt(1);
 138   end
 139   else
 140     WriteLn('gzread(): ', PChar(uncompr));
 141 
 142   pos := gzseek(zfile, -8, SEEK_CUR);
 143   if (pos <> 6) or (gztell(zfile) <> pos) then
 144   begin
 145     WriteLn('gzseek error, pos=', pos, ', gztell=', gztell(zfile));
 146     Halt(1);
 147   end;
 148 
 149   if gzgetc(zfile) <> ' ' then
 150   begin
 151     WriteLn('gzgetc error');
 152     Halt(1);
 153   end;
 154 
 155   if gzungetc(' ', zfile) <> ' ' then
 156   begin
 157     WriteLn('gzungetc error');
 158     Halt(1);
 159   end;
 160 
 161   gzgets(zfile, PChar(uncompr), uncomprLen);
 162   uncomprLen := StrLen(PChar(uncompr));
 163   if uncomprLen <> 7 then (* " hello!" *)
 164   begin
 165     WriteLn('gzgets err after gzseek: ', gzerror(zfile, err));
 166     Halt(1);
 167   end;
 168   if StrComp(PChar(uncompr), hello + 6) <> 0 then
 169   begin
 170     WriteLn('bad gzgets after gzseek');
 171     Halt(1);
 172   end
 173   else
 174     WriteLn('gzgets() after gzseek: ', PChar(uncompr));
 175 
 176   gzclose(zfile);
 177 end;
 178 {$ENDIF}
 179 
 180 (* ===========================================================================
 181  * Test deflate with small buffers
 182  *)
 183 {$IFDEF TEST_DEFLATE}
 184 procedure test_deflate(compr: Pointer; comprLen: LongInt);
 185 var c_stream: z_stream; (* compression stream *)
 186     err: Integer;
 187     len: LongInt;
 188 begin
 189   len := StrLen(hello)+1;
 190 
 191   c_stream.zalloc := NIL;
 192   c_stream.zfree := NIL;
 193   c_stream.opaque := NIL;
 194 
 195   err := deflateInit(c_stream, Z_DEFAULT_COMPRESSION);
 196   CHECK_ERR(err, 'deflateInit');
 197 
 198   c_stream.next_in := hello;
 199   c_stream.next_out := compr;
 200 
 201   while (c_stream.total_in <> len) and
 202         (c_stream.total_out < comprLen) do
 203   begin
 204     c_stream.avail_out := 1; { force small buffers }
 205     c_stream.avail_in := 1;
 206     err := deflate(c_stream, Z_NO_FLUSH);
 207     CHECK_ERR(err, 'deflate');
 208   end;
 209 
 210   (* Finish the stream, still forcing small buffers: *)
 211   while TRUE do
 212   begin
 213     c_stream.avail_out := 1;
 214     err := deflate(c_stream, Z_FINISH);
 215     if err = Z_STREAM_END then
 216       break;
 217     CHECK_ERR(err, 'deflate');
 218   end;
 219 
 220   err := deflateEnd(c_stream);
 221   CHECK_ERR(err, 'deflateEnd');
 222 end;
 223 {$ENDIF}
 224 
 225 (* ===========================================================================
 226  * Test inflate with small buffers
 227  *)
 228 {$IFDEF TEST_INFLATE}
 229 procedure test_inflate(compr: Pointer; comprLen : LongInt;
 230                        uncompr: Pointer; uncomprLen : LongInt);
 231 var err: Integer;
 232     d_stream: z_stream; (* decompression stream *)
 233 begin
 234   StrCopy(PChar(uncompr), 'garbage');
 235 
 236   d_stream.zalloc := NIL;
 237   d_stream.zfree := NIL;
 238   d_stream.opaque := NIL;
 239 
 240   d_stream.next_in := compr;
 241   d_stream.avail_in := 0;
 242   d_stream.next_out := uncompr;
 243 
 244   err := inflateInit(d_stream);
 245   CHECK_ERR(err, 'inflateInit');
 246 
 247   while (d_stream.total_out < uncomprLen) and
 248         (d_stream.total_in < comprLen) do
 249   begin
 250     d_stream.avail_out := 1; (* force small buffers *)
 251     d_stream.avail_in := 1;
 252     err := inflate(d_stream, Z_NO_FLUSH);
 253     if err = Z_STREAM_END then
 254       break;
 255     CHECK_ERR(err, 'inflate');
 256   end;
 257 
 258   err := inflateEnd(d_stream);
 259   CHECK_ERR(err, 'inflateEnd');
 260 
 261   if StrComp(PChar(uncompr), hello) <> 0 then
 262     EXIT_ERR('bad inflate')
 263   else
 264     WriteLn('inflate(): ', PChar(uncompr));
 265 end;
 266 {$ENDIF}
 267 
 268 (* ===========================================================================
 269  * Test deflate with large buffers and dynamic change of compression level
 270  *)
 271 {$IFDEF TEST_DEFLATE}
 272 procedure test_large_deflate(compr: Pointer; comprLen: LongInt;
 273                              uncompr: Pointer; uncomprLen: LongInt);
 274 var c_stream: z_stream; (* compression stream *)
 275     err: Integer;
 276 begin
 277   c_stream.zalloc := NIL;
 278   c_stream.zfree := NIL;
 279   c_stream.opaque := NIL;
 280 
 281   err := deflateInit(c_stream, Z_BEST_SPEED);
 282   CHECK_ERR(err, 'deflateInit');
 283 
 284   c_stream.next_out := compr;
 285   c_stream.avail_out := Integer(comprLen);
 286 
 287   (* At this point, uncompr is still mostly zeroes, so it should compress
 288    * very well:
 289    *)
 290   c_stream.next_in := uncompr;
 291   c_stream.avail_in := Integer(uncomprLen);
 292   err := deflate(c_stream, Z_NO_FLUSH);
 293   CHECK_ERR(err, 'deflate');
 294   if c_stream.avail_in <> 0 then
 295     EXIT_ERR('deflate not greedy');
 296 
 297   (* Feed in already compressed data and switch to no compression: *)
 298   deflateParams(c_stream, Z_NO_COMPRESSION, Z_DEFAULT_STRATEGY);
 299   c_stream.next_in := compr;
 300   c_stream.avail_in := Integer(comprLen div 2);
 301   err := deflate(c_stream, Z_NO_FLUSH);
 302   CHECK_ERR(err, 'deflate');
 303 
 304   (* Switch back to compressing mode: *)
 305   deflateParams(c_stream, Z_BEST_COMPRESSION, Z_FILTERED);
 306   c_stream.next_in := uncompr;
 307   c_stream.avail_in := Integer(uncomprLen);
 308   err := deflate(c_stream, Z_NO_FLUSH);
 309   CHECK_ERR(err, 'deflate');
 310 
 311   err := deflate(c_stream, Z_FINISH);
 312   if err <> Z_STREAM_END then
 313     EXIT_ERR('deflate should report Z_STREAM_END');
 314 
 315   err := deflateEnd(c_stream);
 316   CHECK_ERR(err, 'deflateEnd');
 317 end;
 318 {$ENDIF}
 319 
 320 (* ===========================================================================
 321  * Test inflate with large buffers
 322  *)
 323 {$IFDEF TEST_INFLATE}
 324 procedure test_large_inflate(compr: Pointer; comprLen: LongInt;
 325                              uncompr: Pointer; uncomprLen: LongInt);
 326 var err: Integer;
 327     d_stream: z_stream; (* decompression stream *)
 328 begin
 329   StrCopy(PChar(uncompr), 'garbage');
 330 
 331   d_stream.zalloc := NIL;
 332   d_stream.zfree := NIL;
 333   d_stream.opaque := NIL;
 334 
 335   d_stream.next_in := compr;
 336   d_stream.avail_in := Integer(comprLen);
 337 
 338   err := inflateInit(d_stream);
 339   CHECK_ERR(err, 'inflateInit');
 340 
 341   while TRUE do
 342   begin
 343     d_stream.next_out := uncompr;            (* discard the output *)
 344     d_stream.avail_out := Integer(uncomprLen);
 345     err := inflate(d_stream, Z_NO_FLUSH);
 346     if err = Z_STREAM_END then
 347       break;
 348     CHECK_ERR(err, 'large inflate');
 349   end;
 350 
 351   err := inflateEnd(d_stream);
 352   CHECK_ERR(err, 'inflateEnd');
 353 
 354   if d_stream.total_out <> 2 * uncomprLen + comprLen div 2 then
 355   begin
 356     WriteLn('bad large inflate: ', d_stream.total_out);
 357     Halt(1);
 358   end
 359   else
 360     WriteLn('large_inflate(): OK');
 361 end;
 362 {$ENDIF}
 363 
 364 (* ===========================================================================
 365  * Test deflate with full flush
 366  *)
 367 {$IFDEF TEST_FLUSH}
 368 procedure test_flush(compr: Pointer; var comprLen : LongInt);
 369 var c_stream: z_stream; (* compression stream *)
 370     err: Integer;
 371     len: Integer;
 372 begin
 373   len := StrLen(hello)+1;
 374 
 375   c_stream.zalloc := NIL;
 376   c_stream.zfree := NIL;
 377   c_stream.opaque := NIL;
 378 
 379   err := deflateInit(c_stream, Z_DEFAULT_COMPRESSION);
 380   CHECK_ERR(err, 'deflateInit');
 381 
 382   c_stream.next_in := hello;
 383   c_stream.next_out := compr;
 384   c_stream.avail_in := 3;
 385   c_stream.avail_out := Integer(comprLen);
 386   err := deflate(c_stream, Z_FULL_FLUSH);
 387   CHECK_ERR(err, 'deflate');
 388 
 389   Inc(PByteArray(compr)^[3]); (* force an error in first compressed block *)
 390   c_stream.avail_in := len - 3;
 391 
 392   err := deflate(c_stream, Z_FINISH);
 393   if err <> Z_STREAM_END then
 394     CHECK_ERR(err, 'deflate');
 395 
 396   err := deflateEnd(c_stream);
 397   CHECK_ERR(err, 'deflateEnd');
 398 
 399   comprLen := c_stream.total_out;
 400 end;
 401 {$ENDIF}
 402 
 403 (* ===========================================================================
 404  * Test inflateSync()
 405  *)
 406 {$IFDEF TEST_SYNC}
 407 procedure test_sync(compr: Pointer; comprLen: LongInt;
 408                     uncompr: Pointer; uncomprLen : LongInt);
 409 var err: Integer;
 410     d_stream: z_stream; (* decompression stream *)
 411 begin
 412   StrCopy(PChar(uncompr), 'garbage');
 413 
 414   d_stream.zalloc := NIL;
 415   d_stream.zfree := NIL;
 416   d_stream.opaque := NIL;
 417 
 418   d_stream.next_in := compr;
 419   d_stream.avail_in := 2; (* just read the zlib header *)
 420 
 421   err := inflateInit(d_stream);
 422   CHECK_ERR(err, 'inflateInit');
 423 
 424   d_stream.next_out := uncompr;
 425   d_stream.avail_out := Integer(uncomprLen);
 426 
 427   inflate(d_stream, Z_NO_FLUSH);
 428   CHECK_ERR(err, 'inflate');
 429 
 430   d_stream.avail_in := Integer(comprLen-2);   (* read all compressed data *)
 431   err := inflateSync(d_stream);               (* but skip the damaged part *)
 432   CHECK_ERR(err, 'inflateSync');
 433 
 434   err := inflate(d_stream, Z_FINISH);
 435   if err <> Z_DATA_ERROR then
 436     EXIT_ERR('inflate should report DATA_ERROR');
 437     (* Because of incorrect adler32 *)
 438 
 439   err := inflateEnd(d_stream);
 440   CHECK_ERR(err, 'inflateEnd');
 441 
 442   WriteLn('after inflateSync(): hel', PChar(uncompr));
 443 end;
 444 {$ENDIF}
 445 
 446 (* ===========================================================================
 447  * Test deflate with preset dictionary
 448  *)
 449 {$IFDEF TEST_DICT}
 450 procedure test_dict_deflate(compr: Pointer; comprLen: LongInt);
 451 var c_stream: z_stream; (* compression stream *)
 452     err: Integer;
 453 begin
 454   c_stream.zalloc := NIL;
 455   c_stream.zfree := NIL;
 456   c_stream.opaque := NIL;
 457 
 458   err := deflateInit(c_stream, Z_BEST_COMPRESSION);
 459   CHECK_ERR(err, 'deflateInit');
 460 
 461   err := deflateSetDictionary(c_stream, dictionary, StrLen(dictionary));
 462   CHECK_ERR(err, 'deflateSetDictionary');
 463 
 464   dictId := c_stream.adler;
 465   c_stream.next_out := compr;
 466   c_stream.avail_out := Integer(comprLen);
 467 
 468   c_stream.next_in := hello;
 469   c_stream.avail_in := StrLen(hello)+1;
 470 
 471   err := deflate(c_stream, Z_FINISH);
 472   if err <> Z_STREAM_END then
 473     EXIT_ERR('deflate should report Z_STREAM_END');
 474 
 475   err := deflateEnd(c_stream);
 476   CHECK_ERR(err, 'deflateEnd');
 477 end;
 478 {$ENDIF}
 479 
 480 (* ===========================================================================
 481  * Test inflate with a preset dictionary
 482  *)
 483 {$IFDEF TEST_DICT}
 484 procedure test_dict_inflate(compr: Pointer; comprLen: LongInt;
 485                             uncompr: Pointer; uncomprLen: LongInt);
 486 var err: Integer;
 487     d_stream: z_stream; (* decompression stream *)
 488 begin
 489   StrCopy(PChar(uncompr), 'garbage');
 490 
 491   d_stream.zalloc := NIL;
 492   d_stream.zfree := NIL;
 493   d_stream.opaque := NIL;
 494 
 495   d_stream.next_in := compr;
 496   d_stream.avail_in := Integer(comprLen);
 497 
 498   err := inflateInit(d_stream);
 499   CHECK_ERR(err, 'inflateInit');
 500 
 501   d_stream.next_out := uncompr;
 502   d_stream.avail_out := Integer(uncomprLen);
 503 
 504   while TRUE do
 505   begin
 506     err := inflate(d_stream, Z_NO_FLUSH);
 507     if err = Z_STREAM_END then
 508       break;
 509     if err = Z_NEED_DICT then
 510     begin
 511       if d_stream.adler <> dictId then
 512         EXIT_ERR('unexpected dictionary');
 513       err := inflateSetDictionary(d_stream, dictionary, StrLen(dictionary));
 514     end;
 515     CHECK_ERR(err, 'inflate with dict');
 516   end;
 517 
 518   err := inflateEnd(d_stream);
 519   CHECK_ERR(err, 'inflateEnd');
 520 
 521   if StrComp(PChar(uncompr), hello) <> 0 then
 522     EXIT_ERR('bad inflate with dict')
 523   else
 524     WriteLn('inflate with dictionary: ', PChar(uncompr));
 525 end;
 526 {$ENDIF}
 527 
 528 var compr, uncompr: Pointer;
 529     comprLen, uncomprLen: LongInt;
 530 
 531 begin
 532   if zlibVersion^ <> ZLIB_VERSION[1] then
 533     EXIT_ERR('Incompatible zlib version');
 534 
 535   WriteLn('zlib version: ', zlibVersion);
 536   WriteLn('zlib compile flags: ', Format('0x%x', [zlibCompileFlags]));
 537 
 538   comprLen := 10000 * SizeOf(Integer); (* don't overflow on MSDOS *)
 539   uncomprLen := comprLen;
 540   GetMem(compr, comprLen);
 541   GetMem(uncompr, uncomprLen);
 542   if (compr = NIL) or (uncompr = NIL) then
 543     EXIT_ERR('Out of memory');
 544   (* compr and uncompr are cleared to avoid reading uninitialized
 545    * data and to ensure that uncompr compresses well.
 546    *)
 547   FillChar(compr^, comprLen, 0);
 548   FillChar(uncompr^, uncomprLen, 0);
 549 
 550   {$IFDEF TEST_COMPRESS}
 551   WriteLn('** Testing compress');
 552   test_compress(compr, comprLen, uncompr, uncomprLen);
 553   {$ENDIF}
 554 
 555   {$IFDEF TEST_GZIO}
 556   WriteLn('** Testing gzio');
 557   if ParamCount >= 1 then
 558     test_gzio(ParamStr(1), uncompr, uncomprLen)
 559   else
 560     test_gzio(TESTFILE, uncompr, uncomprLen);
 561   {$ENDIF}
 562 
 563   {$IFDEF TEST_DEFLATE}
 564   WriteLn('** Testing deflate with small buffers');
 565   test_deflate(compr, comprLen);
 566   {$ENDIF}
 567   {$IFDEF TEST_INFLATE}
 568   WriteLn('** Testing inflate with small buffers');
 569   test_inflate(compr, comprLen, uncompr, uncomprLen);
 570   {$ENDIF}
 571 
 572   {$IFDEF TEST_DEFLATE}
 573   WriteLn('** Testing deflate with large buffers');
 574   test_large_deflate(compr, comprLen, uncompr, uncomprLen);
 575   {$ENDIF}
 576   {$IFDEF TEST_INFLATE}
 577   WriteLn('** Testing inflate with large buffers');
 578   test_large_inflate(compr, comprLen, uncompr, uncomprLen);
 579   {$ENDIF}
 580 
 581   {$IFDEF TEST_FLUSH}
 582   WriteLn('** Testing deflate with full flush');
 583   test_flush(compr, comprLen);
 584   {$ENDIF}
 585   {$IFDEF TEST_SYNC}
 586   WriteLn('** Testing inflateSync');
 587   test_sync(compr, comprLen, uncompr, uncomprLen);
 588   {$ENDIF}
 589   comprLen := uncomprLen;
 590 
 591   {$IFDEF TEST_DICT}
 592   WriteLn('** Testing deflate and inflate with preset dictionary');
 593   test_dict_deflate(compr, comprLen);
 594   test_dict_inflate(compr, comprLen, uncompr, uncomprLen);
 595   {$ENDIF}
 596 
 597   FreeMem(compr, comprLen);
 598   FreeMem(uncompr, uncomprLen);
 599 end.