1 {*******************************************************}
3 { Borland Delphi Supplemental Components }
4 { ZLIB Data Compression Interface Unit }
6 { Copyright (c) 1997,99 Borland Corporation }
8 {*******************************************************}
10 { Updated for zlib 1.2.x by Cosmin Truta <cosmint@cs.ubbcluj.ro> }
16 uses SysUtils, Classes;
19 TAlloc = function (AppData: Pointer; Items, Size: Integer): Pointer; cdecl;
20 TFree = procedure (AppData, Block: Pointer); cdecl;
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
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
32 msg: PChar; // last error message, NULL if no error
33 internal: Pointer; // not visible by applications
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
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
44 // Abstract ancestor class
45 TCustomZlibStream = class(TStream)
49 FOnProgress: TNotifyEvent;
51 FBuffer: array [Word] of Char;
53 procedure Progress(Sender: TObject); dynamic;
54 property OnProgress: TNotifyEvent read FOnProgress write FOnProgress;
55 constructor Create(Strm: TStream);
58 { TCompressionStream compresses data on the fly as data is written to it, and
59 stores the compressed data to another stream.
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.
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.
69 The Position property returns the number of uncompressed bytes of
70 data that have been written to the stream so far.
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
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.}
83 TCompressionLevel = (clNone, clFastest, clDefault, clMax);
85 TCompressionStream = class(TCustomZlibStream)
87 function GetCompressionRate: Single;
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;
98 { TDecompressionStream decompresses data on the fly as data is read from it.
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.
108 The Position property returns the number of bytes of uncompressed data that
109 have been read from the stream so far.
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.}
116 TDecompressionStream = class(TCustomZlibStream)
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;
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);
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);
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);
155 zlib_version = '1.2.5';
158 EZlibError = class(Exception);
159 ECompressionError = class(EZlibError);
160 EDecompressionError = class(EZlibError);
177 Z_STREAM_ERROR = (-2);
181 Z_VERSION_ERROR = (-6);
183 Z_NO_COMPRESSION = 0;
185 Z_BEST_COMPRESSION = 9;
186 Z_DEFAULT_COMPRESSION = (-1);
191 Z_DEFAULT_STRATEGY = 0;
212 procedure adler32; external;
213 procedure compressBound; external;
214 procedure crc32; external;
215 procedure deflateInit2_; external;
216 procedure deflateParams; external;
218 function _malloc(Size: Integer): Pointer; cdecl;
220 Result := AllocMem(Size);
223 procedure _free(Block: Pointer); cdecl;
228 procedure _memset(P: Pointer; B: Byte; count: Integer); cdecl;
230 FillChar(P^, count, B);
233 procedure _memcpy(dest, source: Pointer; count: Integer); cdecl;
235 Move(source^, dest^, count);
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;
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;
254 function zlibAllocMem(AppData: Pointer; Items, Size: Integer): Pointer; cdecl;
256 // GetMem(Result, Items*Size);
257 Result := AllocMem(Items * Size);
260 procedure zlibFreeMem(AppData, Block: Pointer); cdecl;
265 {function zlibCheck(code: Integer): Integer;
269 raise EZlibError.Create('error'); //!!
272 function CCheck(code: Integer): Integer;
276 raise ECompressionError.Create('error'); //!!
279 function DCheck(code: Integer): Integer;
283 raise EDecompressionError.Create('error'); //!!
286 procedure CompressBuf(const InBuf: Pointer; InBytes: Integer;
287 out OutBuf: Pointer; out OutBytes: Integer);
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);
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)));
304 while CCheck(deflate(strm, Z_FINISH)) <> Z_STREAM_END do
308 ReallocMem(OutBuf, OutBytes);
309 strm.next_out := PChar(Integer(OutBuf) + (Integer(strm.next_out) - Integer(P)));
310 strm.avail_out := 256;
313 CCheck(deflateEnd(strm));
315 ReallocMem(OutBuf, strm.total_out);
316 OutBytes := strm.total_out;
324 procedure DecompressBuf(const InBuf: Pointer; InBytes: Integer;
325 OutEstimate: Integer; out OutBuf: Pointer; out OutBytes: Integer);
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
338 OutBytes := OutEstimate;
339 GetMem(OutBuf, OutBytes);
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)));
347 while DCheck(inflate(strm, Z_NO_FLUSH)) <> Z_STREAM_END do
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;
356 DCheck(inflateEnd(strm));
358 ReallocMem(OutBuf, strm.total_out);
359 OutBytes := strm.total_out;
366 procedure DecompressToUserBuf(const InBuf: Pointer; InBytes: Integer;
367 const OutBuf: Pointer; BufSize: Integer);
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)));
380 if DCheck(inflate(strm, Z_FINISH)) <> Z_STREAM_END then
381 raise EZlibError.CreateRes(@sTargetBufferTooSmall);
383 DCheck(inflateEnd(strm));
389 constructor TCustomZLibStream.Create(Strm: TStream);
393 FStrmPos := Strm.Position;
394 FZRec.zalloc := zlibAllocMem;
395 FZRec.zfree := zlibFreeMem;
398 procedure TCustomZLibStream.Progress(Sender: TObject);
400 if Assigned(FOnProgress) then FOnProgress(Sender);
404 // TCompressionStream
406 constructor TCompressionStream.Create(CompressionLevel: TCompressionLevel;
409 Levels: array [TCompressionLevel] of ShortInt =
410 (Z_NO_COMPRESSION, Z_BEST_SPEED, Z_DEFAULT_COMPRESSION, Z_BEST_COMPRESSION);
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)));
418 destructor TCompressionStream.Destroy;
420 FZRec.next_in := nil;
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
427 FStrm.WriteBuffer(FBuffer, sizeof(FBuffer));
428 FZRec.next_out := FBuffer;
429 FZRec.avail_out := sizeof(FBuffer);
431 if FZRec.avail_out < sizeof(FBuffer) then
432 FStrm.WriteBuffer(FBuffer, sizeof(FBuffer) - FZRec.avail_out);
439 function TCompressionStream.Read(var Buffer; Count: Longint): Longint;
441 raise ECompressionError.CreateRes(@sInvalidStreamOp);
444 function TCompressionStream.Write(const Buffer; Count: Longint): Longint;
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
451 CCheck(deflate(FZRec, 0));
452 if FZRec.avail_out = 0 then
454 FStrm.WriteBuffer(FBuffer, sizeof(FBuffer));
455 FZRec.next_out := FBuffer;
456 FZRec.avail_out := sizeof(FBuffer);
457 FStrmPos := FStrm.Position;
464 function TCompressionStream.Seek(Offset: Longint; Origin: Word): Longint;
466 if (Offset = 0) and (Origin = soFromCurrent) then
467 Result := FZRec.total_in
469 raise ECompressionError.CreateRes(@sInvalidStreamOp);
472 function TCompressionStream.GetCompressionRate: Single;
474 if FZRec.total_in = 0 then
477 Result := (1.0 - (FZRec.total_out / FZRec.total_in)) * 100.0;
481 // TDecompressionStream
483 constructor TDecompressionStream.Create(Source: TStream);
485 inherited Create(Source);
486 FZRec.next_in := FBuffer;
488 DCheck(inflateInit_(FZRec, zlib_version, sizeof(FZRec)));
491 destructor TDecompressionStream.Destroy;
493 FStrm.Seek(-FZRec.avail_in, 1);
498 function TDecompressionStream.Read(var Buffer; Count: Longint): Longint;
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
505 if FZRec.avail_in = 0 then
507 FZRec.avail_in := FStrm.Read(FBuffer, sizeof(FBuffer));
508 if FZRec.avail_in = 0 then
510 Result := Count - FZRec.avail_out;
513 FZRec.next_in := FBuffer;
514 FStrmPos := FStrm.Position;
517 CCheck(inflate(FZRec, 0));
522 function TDecompressionStream.Write(const Buffer; Count: Longint): Longint;
524 raise EDecompressionError.CreateRes(@sInvalidStreamOp);
527 function TDecompressionStream.Seek(Offset: Longint; Origin: Word): Longint;
530 Buf: array [0..4095] of Char;
532 if (Offset = 0) and (Origin = soFromBeginning) then
534 DCheck(inflateReset(FZRec));
535 FZRec.next_in := FBuffer;
540 else if ( (Offset >= 0) and (Origin = soFromCurrent)) or
541 ( ((Offset - FZRec.total_out) > 0) and (Origin = soFromBeginning)) then
543 if Origin = soFromBeginning then Dec(Offset, FZRec.total_out);
546 for I := 1 to Offset div sizeof(Buf) do
547 ReadBuffer(Buf, sizeof(Buf));
548 ReadBuffer(Buf, Offset mod sizeof(Buf));
552 raise EDecompressionError.CreateRes(@sInvalidStreamOp);
553 Result := FZRec.total_out;