• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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