1 {*******************************************************}
   2 {                                                       }
   3 {       Borland Delphi Supplemental Components          }
   4 {       ZLIB Data Compression Interface Unit            }
   5 {                                                       }
   6 {       Copyright (c) 1997,99 Borland Corporation       }
   7 {                                                       }
   8 {*******************************************************}
   9 
  10 { Updated for zlib 1.2.x by Cosmin Truta <cosmint@cs.ubbcluj.ro> }
  11 
  12 unit ZLib;
  13 
  14 interface
  15 
  16 uses SysUtils, Classes;
  17 
  18 type
  19   TAlloc = function (AppData: Pointer; Items, Size: Integer): Pointer; cdecl;
  20   TFree = procedure (AppData, Block: Pointer); cdecl;
  21 
  22   // Internal structure.  Ignore.
  23   TZStreamRec = packed record
  24     next_in: PChar;       // next input byte
  25     avail_in: Integer;    // number of bytes available at next_in
  26     total_in: Longint;    // total nb of input bytes read so far
  27 
  28     next_out: PChar;      // next output byte should be put here
  29     avail_out: Integer;   // remaining free space at next_out
  30     total_out: Longint;   // total nb of bytes output so far
  31 
  32     msg: PChar;           // last error message, NULL if no error
  33     internal: Pointer;    // not visible by applications
  34 
  35     zalloc: TAlloc;       // used to allocate the internal state
  36     zfree: TFree;         // used to free the internal state
  37     AppData: Pointer;     // private data object passed to zalloc and zfree
  38 
  39     data_type: Integer;   // best guess about the data type: ascii or binary
  40     adler: Longint;       // adler32 value of the uncompressed data
  41     reserved: Longint;    // reserved for future use
  42   end;
  43 
  44   // Abstract ancestor class
  45   TCustomZlibStream = class(TStream)
  46   private
  47     FStrm: TStream;
  48     FStrmPos: Integer;
  49     FOnProgress: TNotifyEvent;
  50     FZRec: TZStreamRec;
  51     FBuffer: array [Word] of Char;
  52   protected
  53     procedure Progress(Sender: TObject); dynamic;
  54     property OnProgress: TNotifyEvent read FOnProgress write FOnProgress;
  55     constructor Create(Strm: TStream);
  56   end;
  57 
  58 { TCompressionStream compresses data on the fly as data is written to it, and
  59   stores the compressed data to another stream.
  60 
  61   TCompressionStream is write-only and strictly sequential. Reading from the
  62   stream will raise an exception. Using Seek to move the stream pointer
  63   will raise an exception.
  64 
  65   Output data is cached internally, written to the output stream only when
  66   the internal output buffer is full.  All pending output data is flushed
  67   when the stream is destroyed.
  68 
  69   The Position property returns the number of uncompressed bytes of
  70   data that have been written to the stream so far.
  71 
  72   CompressionRate returns the on-the-fly percentage by which the original
  73   data has been compressed:  (1 - (CompressedBytes / UncompressedBytes)) * 100
  74   If raw data size = 100 and compressed data size = 25, the CompressionRate
  75   is 75%
  76 
  77   The OnProgress event is called each time the output buffer is filled and
  78   written to the output stream.  This is useful for updating a progress
  79   indicator when you are writing a large chunk of data to the compression
  80   stream in a single call.}
  81 
  82 
  83   TCompressionLevel = (clNone, clFastest, clDefault, clMax);
  84 
  85   TCompressionStream = class(TCustomZlibStream)
  86   private
  87     function GetCompressionRate: Single;
  88   public
  89     constructor Create(CompressionLevel: TCompressionLevel; Dest: TStream);
  90     destructor Destroy; override;
  91     function Read(var Buffer; Count: Longint): Longint; override;
  92     function Write(const Buffer; Count: Longint): Longint; override;
  93     function Seek(Offset: Longint; Origin: Word): Longint; override;
  94     property CompressionRate: Single read GetCompressionRate;
  95     property OnProgress;
  96   end;
  97 
  98 { TDecompressionStream decompresses data on the fly as data is read from it.
  99 
 100   Compressed data comes from a separate source stream.  TDecompressionStream
 101   is read-only and unidirectional; you can seek forward in the stream, but not
 102   backwards.  The special case of setting the stream position to zero is
 103   allowed.  Seeking forward decompresses data until the requested position in
 104   the uncompressed data has been reached.  Seeking backwards, seeking relative
 105   to the end of the stream, requesting the size of the stream, and writing to
 106   the stream will raise an exception.
 107 
 108   The Position property returns the number of bytes of uncompressed data that
 109   have been read from the stream so far.
 110 
 111   The OnProgress event is called each time the internal input buffer of
 112   compressed data is exhausted and the next block is read from the input stream.
 113   This is useful for updating a progress indicator when you are reading a
 114   large chunk of data from the decompression stream in a single call.}
 115 
 116   TDecompressionStream = class(TCustomZlibStream)
 117   public
 118     constructor Create(Source: TStream);
 119     destructor Destroy; override;
 120     function Read(var Buffer; Count: Longint): Longint; override;
 121     function Write(const Buffer; Count: Longint): Longint; override;
 122     function Seek(Offset: Longint; Origin: Word): Longint; override;
 123     property OnProgress;
 124   end;
 125 
 126 
 127 
 128 { CompressBuf compresses data, buffer to buffer, in one call.
 129    In: InBuf = ptr to compressed data
 130        InBytes = number of bytes in InBuf
 131   Out: OutBuf = ptr to newly allocated buffer containing decompressed data
 132        OutBytes = number of bytes in OutBuf   }
 133 procedure CompressBuf(const InBuf: Pointer; InBytes: Integer;
 134                       out OutBuf: Pointer; out OutBytes: Integer);
 135 
 136 
 137 { DecompressBuf decompresses data, buffer to buffer, in one call.
 138    In: InBuf = ptr to compressed data
 139        InBytes = number of bytes in InBuf
 140        OutEstimate = zero, or est. size of the decompressed data
 141   Out: OutBuf = ptr to newly allocated buffer containing decompressed data
 142        OutBytes = number of bytes in OutBuf   }
 143 procedure DecompressBuf(const InBuf: Pointer; InBytes: Integer;
 144  OutEstimate: Integer; out OutBuf: Pointer; out OutBytes: Integer);
 145 
 146 { DecompressToUserBuf decompresses data, buffer to buffer, in one call.
 147    In: InBuf = ptr to compressed data
 148        InBytes = number of bytes in InBuf
 149   Out: OutBuf = ptr to user-allocated buffer to contain decompressed data
 150        BufSize = number of bytes in OutBuf   }
 151 procedure DecompressToUserBuf(const InBuf: Pointer; InBytes: Integer;
 152   const OutBuf: Pointer; BufSize: Integer);
 153 
 154 const
 155   zlib_version = '1.2.8';
 156 
 157 type
 158   EZlibError = class(Exception);
 159   ECompressionError = class(EZlibError);
 160   EDecompressionError = class(EZlibError);
 161 
 162 implementation
 163 
 164 uses ZLibConst;
 165 
 166 const
 167   Z_NO_FLUSH      = 0;
 168   Z_PARTIAL_FLUSH = 1;
 169   Z_SYNC_FLUSH    = 2;
 170   Z_FULL_FLUSH    = 3;
 171   Z_FINISH        = 4;
 172 
 173   Z_OK            = 0;
 174   Z_STREAM_END    = 1;
 175   Z_NEED_DICT     = 2;
 176   Z_ERRNO         = (-1);
 177   Z_STREAM_ERROR  = (-2);
 178   Z_DATA_ERROR    = (-3);
 179   Z_MEM_ERROR     = (-4);
 180   Z_BUF_ERROR     = (-5);
 181   Z_VERSION_ERROR = (-6);
 182 
 183   Z_NO_COMPRESSION       =   0;
 184   Z_BEST_SPEED           =   1;
 185   Z_BEST_COMPRESSION     =   9;
 186   Z_DEFAULT_COMPRESSION  = (-1);
 187 
 188   Z_FILTERED            = 1;
 189   Z_HUFFMAN_ONLY        = 2;
 190   Z_RLE                 = 3;
 191   Z_DEFAULT_STRATEGY    = 0;
 192 
 193   Z_BINARY   = 0;
 194   Z_ASCII    = 1;
 195   Z_UNKNOWN  = 2;
 196 
 197   Z_DEFLATED = 8;
 198 
 199 
 200 {$L adler32.obj}
 201 {$L compress.obj}
 202 {$L crc32.obj}
 203 {$L deflate.obj}
 204 {$L infback.obj}
 205 {$L inffast.obj}
 206 {$L inflate.obj}
 207 {$L inftrees.obj}
 208 {$L trees.obj}
 209 {$L uncompr.obj}
 210 {$L zutil.obj}
 211 
 212 procedure adler32; external;
 213 procedure compressBound; external;
 214 procedure crc32; external;
 215 procedure deflateInit2_; external;
 216 procedure deflateParams; external;
 217 
 218 function _malloc(Size: Integer): Pointer; cdecl;
 219 begin
 220   Result := AllocMem(Size);
 221 end;
 222 
 223 procedure _free(Block: Pointer); cdecl;
 224 begin
 225   FreeMem(Block);
 226 end;
 227 
 228 procedure _memset(P: Pointer; B: Byte; count: Integer); cdecl;
 229 begin
 230   FillChar(P^, count, B);
 231 end;
 232 
 233 procedure _memcpy(dest, source: Pointer; count: Integer); cdecl;
 234 begin
 235   Move(source^, dest^, count);
 236 end;
 237 
 238 
 239 
 240 // deflate compresses data
 241 function deflateInit_(var strm: TZStreamRec; level: Integer; version: PChar;
 242   recsize: Integer): Integer; external;
 243 function deflate(var strm: TZStreamRec; flush: Integer): Integer; external;
 244 function deflateEnd(var strm: TZStreamRec): Integer; external;
 245 
 246 // inflate decompresses data
 247 function inflateInit_(var strm: TZStreamRec; version: PChar;
 248   recsize: Integer): Integer; external;
 249 function inflate(var strm: TZStreamRec; flush: Integer): Integer; external;
 250 function inflateEnd(var strm: TZStreamRec): Integer; external;
 251 function inflateReset(var strm: TZStreamRec): Integer; external;
 252 
 253 
 254 function zlibAllocMem(AppData: Pointer; Items, Size: Integer): Pointer; cdecl;
 255 begin
 256 //  GetMem(Result, Items*Size);
 257   Result := AllocMem(Items * Size);
 258 end;
 259 
 260 procedure zlibFreeMem(AppData, Block: Pointer); cdecl;
 261 begin
 262   FreeMem(Block);
 263 end;
 264 
 265 {function zlibCheck(code: Integer): Integer;
 266 begin
 267   Result := code;
 268   if code < 0 then
 269     raise EZlibError.Create('error');    //!!
 270 end;}
 271 
 272 function CCheck(code: Integer): Integer;
 273 begin
 274   Result := code;
 275   if code < 0 then
 276     raise ECompressionError.Create('error'); //!!
 277 end;
 278 
 279 function DCheck(code: Integer): Integer;
 280 begin
 281   Result := code;
 282   if code < 0 then
 283     raise EDecompressionError.Create('error');  //!!
 284 end;
 285 
 286 procedure CompressBuf(const InBuf: Pointer; InBytes: Integer;
 287                       out OutBuf: Pointer; out OutBytes: Integer);
 288 var
 289   strm: TZStreamRec;
 290   P: Pointer;
 291 begin
 292   FillChar(strm, sizeof(strm), 0);
 293   strm.zalloc := zlibAllocMem;
 294   strm.zfree := zlibFreeMem;
 295   OutBytes := ((InBytes + (InBytes div 10) + 12) + 255) and not 255;
 296   GetMem(OutBuf, OutBytes);
 297   try
 298     strm.next_in := InBuf;
 299     strm.avail_in := InBytes;
 300     strm.next_out := OutBuf;
 301     strm.avail_out := OutBytes;
 302     CCheck(deflateInit_(strm, Z_BEST_COMPRESSION, zlib_version, sizeof(strm)));
 303     try
 304       while CCheck(deflate(strm, Z_FINISH)) <> Z_STREAM_END do
 305       begin
 306         P := OutBuf;
 307         Inc(OutBytes, 256);
 308         ReallocMem(OutBuf, OutBytes);
 309         strm.next_out := PChar(Integer(OutBuf) + (Integer(strm.next_out) - Integer(P)));
 310         strm.avail_out := 256;
 311       end;
 312     finally
 313       CCheck(deflateEnd(strm));
 314     end;
 315     ReallocMem(OutBuf, strm.total_out);
 316     OutBytes := strm.total_out;
 317   except
 318     FreeMem(OutBuf);
 319     raise
 320   end;
 321 end;
 322 
 323 
 324 procedure DecompressBuf(const InBuf: Pointer; InBytes: Integer;
 325   OutEstimate: Integer; out OutBuf: Pointer; out OutBytes: Integer);
 326 var
 327   strm: TZStreamRec;
 328   P: Pointer;
 329   BufInc: Integer;
 330 begin
 331   FillChar(strm, sizeof(strm), 0);
 332   strm.zalloc := zlibAllocMem;
 333   strm.zfree := zlibFreeMem;
 334   BufInc := (InBytes + 255) and not 255;
 335   if OutEstimate = 0 then
 336     OutBytes := BufInc
 337   else
 338     OutBytes := OutEstimate;
 339   GetMem(OutBuf, OutBytes);
 340   try
 341     strm.next_in := InBuf;
 342     strm.avail_in := InBytes;
 343     strm.next_out := OutBuf;
 344     strm.avail_out := OutBytes;
 345     DCheck(inflateInit_(strm, zlib_version, sizeof(strm)));
 346     try
 347       while DCheck(inflate(strm, Z_NO_FLUSH)) <> Z_STREAM_END do
 348       begin
 349         P := OutBuf;
 350         Inc(OutBytes, BufInc);
 351         ReallocMem(OutBuf, OutBytes);
 352         strm.next_out := PChar(Integer(OutBuf) + (Integer(strm.next_out) - Integer(P)));
 353         strm.avail_out := BufInc;
 354       end;
 355     finally
 356       DCheck(inflateEnd(strm));
 357     end;
 358     ReallocMem(OutBuf, strm.total_out);
 359     OutBytes := strm.total_out;
 360   except
 361     FreeMem(OutBuf);
 362     raise
 363   end;
 364 end;
 365 
 366 procedure DecompressToUserBuf(const InBuf: Pointer; InBytes: Integer;
 367   const OutBuf: Pointer; BufSize: Integer);
 368 var
 369   strm: TZStreamRec;
 370 begin
 371   FillChar(strm, sizeof(strm), 0);
 372   strm.zalloc := zlibAllocMem;
 373   strm.zfree := zlibFreeMem;
 374   strm.next_in := InBuf;
 375   strm.avail_in := InBytes;
 376   strm.next_out := OutBuf;
 377   strm.avail_out := BufSize;
 378   DCheck(inflateInit_(strm, zlib_version, sizeof(strm)));
 379   try
 380     if DCheck(inflate(strm, Z_FINISH)) <> Z_STREAM_END then
 381       raise EZlibError.CreateRes(@sTargetBufferTooSmall);
 382   finally
 383     DCheck(inflateEnd(strm));
 384   end;
 385 end;
 386 
 387 // TCustomZlibStream
 388 
 389 constructor TCustomZLibStream.Create(Strm: TStream);
 390 begin
 391   inherited Create;
 392   FStrm := Strm;
 393   FStrmPos := Strm.Position;
 394   FZRec.zalloc := zlibAllocMem;
 395   FZRec.zfree := zlibFreeMem;
 396 end;
 397 
 398 procedure TCustomZLibStream.Progress(Sender: TObject);
 399 begin
 400   if Assigned(FOnProgress) then FOnProgress(Sender);
 401 end;
 402 
 403 
 404 // TCompressionStream
 405 
 406 constructor TCompressionStream.Create(CompressionLevel: TCompressionLevel;
 407   Dest: TStream);
 408 const
 409   Levels: array [TCompressionLevel] of ShortInt =
 410     (Z_NO_COMPRESSION, Z_BEST_SPEED, Z_DEFAULT_COMPRESSION, Z_BEST_COMPRESSION);
 411 begin
 412   inherited Create(Dest);
 413   FZRec.next_out := FBuffer;
 414   FZRec.avail_out := sizeof(FBuffer);
 415   CCheck(deflateInit_(FZRec, Levels[CompressionLevel], zlib_version, sizeof(FZRec)));
 416 end;
 417 
 418 destructor TCompressionStream.Destroy;
 419 begin
 420   FZRec.next_in := nil;
 421   FZRec.avail_in := 0;
 422   try
 423     if FStrm.Position <> FStrmPos then FStrm.Position := FStrmPos;
 424     while (CCheck(deflate(FZRec, Z_FINISH)) <> Z_STREAM_END)
 425       and (FZRec.avail_out = 0) do
 426     begin
 427       FStrm.WriteBuffer(FBuffer, sizeof(FBuffer));
 428       FZRec.next_out := FBuffer;
 429       FZRec.avail_out := sizeof(FBuffer);
 430     end;
 431     if FZRec.avail_out < sizeof(FBuffer) then
 432       FStrm.WriteBuffer(FBuffer, sizeof(FBuffer) - FZRec.avail_out);
 433   finally
 434     deflateEnd(FZRec);
 435   end;
 436   inherited Destroy;
 437 end;
 438 
 439 function TCompressionStream.Read(var Buffer; Count: Longint): Longint;
 440 begin
 441   raise ECompressionError.CreateRes(@sInvalidStreamOp);
 442 end;
 443 
 444 function TCompressionStream.Write(const Buffer; Count: Longint): Longint;
 445 begin
 446   FZRec.next_in := @Buffer;
 447   FZRec.avail_in := Count;
 448   if FStrm.Position <> FStrmPos then FStrm.Position := FStrmPos;
 449   while (FZRec.avail_in > 0) do
 450   begin
 451     CCheck(deflate(FZRec, 0));
 452     if FZRec.avail_out = 0 then
 453     begin
 454       FStrm.WriteBuffer(FBuffer, sizeof(FBuffer));
 455       FZRec.next_out := FBuffer;
 456       FZRec.avail_out := sizeof(FBuffer);
 457       FStrmPos := FStrm.Position;
 458       Progress(Self);
 459     end;
 460   end;
 461   Result := Count;
 462 end;
 463 
 464 function TCompressionStream.Seek(Offset: Longint; Origin: Word): Longint;
 465 begin
 466   if (Offset = 0) and (Origin = soFromCurrent) then
 467     Result := FZRec.total_in
 468   else
 469     raise ECompressionError.CreateRes(@sInvalidStreamOp);
 470 end;
 471 
 472 function TCompressionStream.GetCompressionRate: Single;
 473 begin
 474   if FZRec.total_in = 0 then
 475     Result := 0
 476   else
 477     Result := (1.0 - (FZRec.total_out / FZRec.total_in)) * 100.0;
 478 end;
 479 
 480 
 481 // TDecompressionStream
 482 
 483 constructor TDecompressionStream.Create(Source: TStream);
 484 begin
 485   inherited Create(Source);
 486   FZRec.next_in := FBuffer;
 487   FZRec.avail_in := 0;
 488   DCheck(inflateInit_(FZRec, zlib_version, sizeof(FZRec)));
 489 end;
 490 
 491 destructor TDecompressionStream.Destroy;
 492 begin
 493   FStrm.Seek(-FZRec.avail_in, 1);
 494   inflateEnd(FZRec);
 495   inherited Destroy;
 496 end;
 497 
 498 function TDecompressionStream.Read(var Buffer; Count: Longint): Longint;
 499 begin
 500   FZRec.next_out := @Buffer;
 501   FZRec.avail_out := Count;
 502   if FStrm.Position <> FStrmPos then FStrm.Position := FStrmPos;
 503   while (FZRec.avail_out > 0) do
 504   begin
 505     if FZRec.avail_in = 0 then
 506     begin
 507       FZRec.avail_in := FStrm.Read(FBuffer, sizeof(FBuffer));
 508       if FZRec.avail_in = 0 then
 509       begin
 510         Result := Count - FZRec.avail_out;
 511         Exit;
 512       end;
 513       FZRec.next_in := FBuffer;
 514       FStrmPos := FStrm.Position;
 515       Progress(Self);
 516     end;
 517     CCheck(inflate(FZRec, 0));
 518   end;
 519   Result := Count;
 520 end;
 521 
 522 function TDecompressionStream.Write(const Buffer; Count: Longint): Longint;
 523 begin
 524   raise EDecompressionError.CreateRes(@sInvalidStreamOp);
 525 end;
 526 
 527 function TDecompressionStream.Seek(Offset: Longint; Origin: Word): Longint;
 528 var
 529   I: Integer;
 530   Buf: array [0..4095] of Char;
 531 begin
 532   if (Offset = 0) and (Origin = soFromBeginning) then
 533   begin
 534     DCheck(inflateReset(FZRec));
 535     FZRec.next_in := FBuffer;
 536     FZRec.avail_in := 0;
 537     FStrm.Position := 0;
 538     FStrmPos := 0;
 539   end
 540   else if ( (Offset >= 0) and (Origin = soFromCurrent)) or
 541           ( ((Offset - FZRec.total_out) > 0) and (Origin = soFromBeginning)) then
 542   begin
 543     if Origin = soFromBeginning then Dec(Offset, FZRec.total_out);
 544     if Offset > 0 then
 545     begin
 546       for I := 1 to Offset div sizeof(Buf) do
 547         ReadBuffer(Buf, sizeof(Buf));
 548       ReadBuffer(Buf, Offset mod sizeof(Buf));
 549     end;
 550   end
 551   else
 552     raise EDecompressionError.CreateRes(@sInvalidStreamOp);
 553   Result := FZRec.total_out;
 554 end;
 555 
 556 
 557 end.