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
ppData()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
GetCompressionRate()87     function GetCompressionRate: Single;
88   public
89     constructor Create(CompressionLevel: TCompressionLevel; Dest: TStream);
90     destructor Destroy; override;
Read(var Buffer; Count: Longint)91     function Read(var Buffer; Count: Longint): Longint; override;
Write(const Buffer; Count: Longint)92     function Write(const Buffer; Count: Longint): Longint; override;
Seek(Offset: Longint; Origin: Word)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;
Read(var Buffer; Count: Longint)120     function Read(var Buffer; Count: Longint): Longint; override;
Write(const Buffer; Count: Longint)121     function Write(const Buffer; Count: Longint): Longint; override;
Seek(Offset: Longint; Origin: Word)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 
_malloc(Size: Integer)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
deflateInit_(var strm: TZStreamRec; level: Integer; version: PChar;241 function deflateInit_(var strm: TZStreamRec; level: Integer; version: PChar;
242   recsize: Integer): Integer; external;
deflate(var strm: TZStreamRec; flush: Integer)243 function deflate(var strm: TZStreamRec; flush: Integer): Integer; external;
deflateEnd(var strm: TZStreamRec)244 function deflateEnd(var strm: TZStreamRec): Integer; external;
245 
246 // inflate decompresses data
inflateInit_(var strm: TZStreamRec; version: PChar;247 function inflateInit_(var strm: TZStreamRec; version: PChar;
248   recsize: Integer): Integer; external;
inflate(var strm: TZStreamRec; flush: Integer)249 function inflate(var strm: TZStreamRec; flush: Integer): Integer; external;
inflateEnd(var strm: TZStreamRec)250 function inflateEnd(var strm: TZStreamRec): Integer; external;
inflateReset(var strm: TZStreamRec)251 function inflateReset(var strm: TZStreamRec): Integer; external;
252 
253 
zlibAllocMem(AppData: Pointer; Items, Size: Integer)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 
CCheck(code: Integer)272 function CCheck(code: Integer): Integer;
273 begin
274   Result := code;
275   if code < 0 then
276     raise ECompressionError.Create('error'); //!!
277 end;
278 
DCheck(code: Integer)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 
Readnull439 function TCompressionStream.Read(var Buffer; Count: Longint): Longint;
440 begin
441   raise ECompressionError.CreateRes(@sInvalidStreamOp);
442 end;
443 
Writenull444 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 
TCompressionStream.Seek(Offset: Longint; Origin: Word)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 
TCompressionStream.GetCompressionRate()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 
Readnull498 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 
Writenull522 function TDecompressionStream.Write(const Buffer; Count: Longint): Longint;
523 begin
524   raise EDecompressionError.CreateRes(@sInvalidStreamOp);
525 end;
526 
Seeknull527 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.
558