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.12';
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