Initial revision
[silc.git] / lib / zlib / contrib / delphi2 / zlib.pas
1 {*******************************************************}
2 {                                                       }
3 {       Delphi Supplemental Components                  }
4 {       ZLIB Data Compression Interface Unit            }
5 {                                                       }
6 {       Copyright (c) 1997 Borland International        }
7 {                                                       }
8 {*******************************************************}
9
10 { Modified for zlib 1.1.3 by Davide Moretti <dave@rimini.com }
11
12 unit zlib;
13
14 interface
15
16 uses Sysutils, Classes;
17
18 type
19   TAlloc = function (AppData: Pointer; Items, Size: Integer): Pointer;
20   TFree = procedure (AppData, Block: Pointer);
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: Integer;    // 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: Integer;   // 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: Integer;       // adler32 value of the uncompressed data
41     reserved: Integer;    // 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 const
147   zlib_version = '1.1.3';
148
149 type
150   EZlibError = class(Exception);
151   ECompressionError = class(EZlibError);
152   EDecompressionError = class(EZlibError);
153
154 function adler32(adler: Integer; buf: PChar; len: Integer): Integer;
155
156 implementation
157
158 const
159   Z_NO_FLUSH      = 0;
160   Z_PARTIAL_FLUSH = 1;
161   Z_SYNC_FLUSH    = 2;
162   Z_FULL_FLUSH    = 3;
163   Z_FINISH        = 4;
164
165   Z_OK            = 0;
166   Z_STREAM_END    = 1;
167   Z_NEED_DICT     = 2;
168   Z_ERRNO         = (-1);
169   Z_STREAM_ERROR  = (-2);
170   Z_DATA_ERROR    = (-3);
171   Z_MEM_ERROR     = (-4);
172   Z_BUF_ERROR     = (-5);
173   Z_VERSION_ERROR = (-6);
174
175   Z_NO_COMPRESSION       =   0;
176   Z_BEST_SPEED           =   1;
177   Z_BEST_COMPRESSION     =   9;
178   Z_DEFAULT_COMPRESSION  = (-1);
179
180   Z_FILTERED            = 1;
181   Z_HUFFMAN_ONLY        = 2;
182   Z_DEFAULT_STRATEGY    = 0;
183
184   Z_BINARY   = 0;
185   Z_ASCII    = 1;
186   Z_UNKNOWN  = 2;
187
188   Z_DEFLATED = 8;
189
190   _z_errmsg: array[0..9] of PChar = (
191     'need dictionary',      // Z_NEED_DICT      (2)
192     'stream end',           // Z_STREAM_END     (1)
193     '',                     // Z_OK             (0)
194     'file error',           // Z_ERRNO          (-1)
195     'stream error',         // Z_STREAM_ERROR   (-2)
196     'data error',           // Z_DATA_ERROR     (-3)
197     'insufficient memory',  // Z_MEM_ERROR      (-4)
198     'buffer error',         // Z_BUF_ERROR      (-5)
199     'incompatible version', // Z_VERSION_ERROR  (-6)
200     ''
201   );
202
203 {$L deflate.obj}
204 {$L inflate.obj}
205 {$L inftrees.obj}
206 {$L trees.obj}
207 {$L adler32.obj}
208 {$L infblock.obj}
209 {$L infcodes.obj}
210 {$L infutil.obj}
211 {$L inffast.obj}
212
213 procedure _tr_init; external;
214 procedure _tr_tally; external;
215 procedure _tr_flush_block; external;
216 procedure _tr_align; external;
217 procedure _tr_stored_block; external;
218 function adler32; external;
219 procedure inflate_blocks_new; external;
220 procedure inflate_blocks; external;
221 procedure inflate_blocks_reset; external;
222 procedure inflate_blocks_free; external;
223 procedure inflate_set_dictionary; external;
224 procedure inflate_trees_bits; external;
225 procedure inflate_trees_dynamic; external;
226 procedure inflate_trees_fixed; external;
227 procedure inflate_codes_new; external;
228 procedure inflate_codes; external;
229 procedure inflate_codes_free; external;
230 procedure _inflate_mask; external;
231 procedure inflate_flush; external;
232 procedure inflate_fast; external;
233
234 procedure _memset(P: Pointer; B: Byte; count: Integer);cdecl;
235 begin
236   FillChar(P^, count, B);
237 end;
238
239 procedure _memcpy(dest, source: Pointer; count: Integer);cdecl;
240 begin
241   Move(source^, dest^, count);
242 end;
243
244
245
246 // deflate compresses data
247 function deflateInit_(var strm: TZStreamRec; level: Integer; version: PChar;
248   recsize: Integer): Integer; external;
249 function deflate(var strm: TZStreamRec; flush: Integer): Integer; external;
250 function deflateEnd(var strm: TZStreamRec): Integer; external;
251
252 // inflate decompresses data
253 function inflateInit_(var strm: TZStreamRec; version: PChar;
254   recsize: Integer): Integer; external;
255 function inflate(var strm: TZStreamRec; flush: Integer): Integer; external;
256 function inflateEnd(var strm: TZStreamRec): Integer; external;
257 function inflateReset(var strm: TZStreamRec): Integer; external;
258
259
260 function zcalloc(AppData: Pointer; Items, Size: Integer): Pointer;
261 begin
262   GetMem(Result, Items*Size);
263 end;
264
265 procedure zcfree(AppData, Block: Pointer);
266 begin
267   FreeMem(Block);
268 end;
269
270 function zlibCheck(code: Integer): Integer;
271 begin
272   Result := code;
273   if code < 0 then
274     raise EZlibError.Create('error');    //!!
275 end;
276
277 function CCheck(code: Integer): Integer;
278 begin
279   Result := code;
280   if code < 0 then
281     raise ECompressionError.Create('error'); //!!
282 end;
283
284 function DCheck(code: Integer): Integer;
285 begin
286   Result := code;
287   if code < 0 then
288     raise EDecompressionError.Create('error');  //!!
289 end;
290
291 procedure CompressBuf(const InBuf: Pointer; InBytes: Integer;
292                       out OutBuf: Pointer; out OutBytes: Integer);
293 var
294   strm: TZStreamRec;
295   P: Pointer;
296 begin
297   FillChar(strm, sizeof(strm), 0);
298   OutBytes := ((InBytes + (InBytes div 10) + 12) + 255) and not 255;
299   GetMem(OutBuf, OutBytes);
300   try
301     strm.next_in := InBuf;
302     strm.avail_in := InBytes;
303     strm.next_out := OutBuf;
304     strm.avail_out := OutBytes;
305     CCheck(deflateInit_(strm, Z_BEST_COMPRESSION, zlib_version, sizeof(strm)));
306     try
307       while CCheck(deflate(strm, Z_FINISH)) <> Z_STREAM_END do
308       begin
309         P := OutBuf;
310         Inc(OutBytes, 256);
311         ReallocMem(OutBuf, OutBytes);
312         strm.next_out := PChar(Integer(OutBuf) + (Integer(strm.next_out) - Integer(P)));
313         strm.avail_out := 256;
314       end;
315     finally
316       CCheck(deflateEnd(strm));
317     end;
318     ReallocMem(OutBuf, strm.total_out);
319     OutBytes := strm.total_out;
320   except
321     FreeMem(OutBuf);
322     raise
323   end;
324 end;
325
326
327 procedure DecompressBuf(const InBuf: Pointer; InBytes: Integer;
328   OutEstimate: Integer; out OutBuf: Pointer; out OutBytes: Integer);
329 var
330   strm: TZStreamRec;
331   P: Pointer;
332   BufInc: Integer;
333 begin
334   FillChar(strm, sizeof(strm), 0);
335   BufInc := (InBytes + 255) and not 255;
336   if OutEstimate = 0 then
337     OutBytes := BufInc
338   else
339     OutBytes := OutEstimate;
340   GetMem(OutBuf, OutBytes);
341   try
342     strm.next_in := InBuf;
343     strm.avail_in := InBytes;
344     strm.next_out := OutBuf;
345     strm.avail_out := OutBytes;
346     DCheck(inflateInit_(strm, zlib_version, sizeof(strm)));
347     try
348       while DCheck(inflate(strm, Z_FINISH)) <> Z_STREAM_END do
349       begin
350         P := OutBuf;
351         Inc(OutBytes, BufInc);
352         ReallocMem(OutBuf, OutBytes);
353         strm.next_out := PChar(Integer(OutBuf) + (Integer(strm.next_out) - Integer(P)));
354         strm.avail_out := BufInc;
355       end;
356     finally
357       DCheck(inflateEnd(strm));
358     end;
359     ReallocMem(OutBuf, strm.total_out);
360     OutBytes := strm.total_out;
361   except
362     FreeMem(OutBuf);
363     raise
364   end;
365 end;
366
367
368 // TCustomZlibStream
369
370 constructor TCustomZLibStream.Create(Strm: TStream);
371 begin
372   inherited Create;
373   FStrm := Strm;
374   FStrmPos := Strm.Position;
375 end;
376
377 procedure TCustomZLibStream.Progress(Sender: TObject);
378 begin
379   if Assigned(FOnProgress) then FOnProgress(Sender);
380 end;
381
382
383 // TCompressionStream
384
385 constructor TCompressionStream.Create(CompressionLevel: TCompressionLevel;
386   Dest: TStream);
387 const
388   Levels: array [TCompressionLevel] of ShortInt =
389     (Z_NO_COMPRESSION, Z_BEST_SPEED, Z_DEFAULT_COMPRESSION, Z_BEST_COMPRESSION);
390 begin
391   inherited Create(Dest);
392   FZRec.next_out := FBuffer;
393   FZRec.avail_out := sizeof(FBuffer);
394   CCheck(deflateInit_(FZRec, Levels[CompressionLevel], zlib_version, sizeof(FZRec)));
395 end;
396
397 destructor TCompressionStream.Destroy;
398 begin
399   FZRec.next_in := nil;
400   FZRec.avail_in := 0;
401   try
402     if FStrm.Position <> FStrmPos then FStrm.Position := FStrmPos;
403     while (CCheck(deflate(FZRec, Z_FINISH)) <> Z_STREAM_END)
404       and (FZRec.avail_out = 0) do
405     begin
406       FStrm.WriteBuffer(FBuffer, sizeof(FBuffer));
407       FZRec.next_out := FBuffer;
408       FZRec.avail_out := sizeof(FBuffer);
409     end;
410     if FZRec.avail_out < sizeof(FBuffer) then
411       FStrm.WriteBuffer(FBuffer, sizeof(FBuffer) - FZRec.avail_out);
412   finally
413     deflateEnd(FZRec);
414   end;
415   inherited Destroy;
416 end;
417
418 function TCompressionStream.Read(var Buffer; Count: Longint): Longint;
419 begin
420   raise ECompressionError.Create('Invalid stream operation');
421 end;
422
423 function TCompressionStream.Write(const Buffer; Count: Longint): Longint;
424 begin
425   FZRec.next_in := @Buffer;
426   FZRec.avail_in := Count;
427   if FStrm.Position <> FStrmPos then FStrm.Position := FStrmPos;
428   while (FZRec.avail_in > 0) do
429   begin
430     CCheck(deflate(FZRec, 0));
431     if FZRec.avail_out = 0 then
432     begin
433       FStrm.WriteBuffer(FBuffer, sizeof(FBuffer));
434       FZRec.next_out := FBuffer;
435       FZRec.avail_out := sizeof(FBuffer);
436       FStrmPos := FStrm.Position;
437       Progress(Self);
438     end;
439   end;
440   Result := Count;
441 end;
442
443 function TCompressionStream.Seek(Offset: Longint; Origin: Word): Longint;
444 begin
445   if (Offset = 0) and (Origin = soFromCurrent) then
446     Result := FZRec.total_in
447   else
448     raise ECompressionError.Create('Invalid stream operation');
449 end;
450
451 function TCompressionStream.GetCompressionRate: Single;
452 begin
453   if FZRec.total_in = 0 then
454     Result := 0
455   else
456     Result := (1.0 - (FZRec.total_out / FZRec.total_in)) * 100.0;
457 end;
458
459
460 // TDecompressionStream
461
462 constructor TDecompressionStream.Create(Source: TStream);
463 begin
464   inherited Create(Source);
465   FZRec.next_in := FBuffer;
466   FZRec.avail_in := 0;
467   DCheck(inflateInit_(FZRec, zlib_version, sizeof(FZRec)));
468 end;
469
470 destructor TDecompressionStream.Destroy;
471 begin
472   inflateEnd(FZRec);
473   inherited Destroy;
474 end;
475
476 function TDecompressionStream.Read(var Buffer; Count: Longint): Longint;
477 begin
478   FZRec.next_out := @Buffer;
479   FZRec.avail_out := Count;
480   if FStrm.Position <> FStrmPos then FStrm.Position := FStrmPos;
481   while (FZRec.avail_out > 0) do
482   begin
483     if FZRec.avail_in = 0 then
484     begin
485       FZRec.avail_in := FStrm.Read(FBuffer, sizeof(FBuffer));
486       if FZRec.avail_in = 0 then
487         begin
488           Result := Count - FZRec.avail_out;
489           Exit;
490         end;
491       FZRec.next_in := FBuffer;
492       FStrmPos := FStrm.Position;
493       Progress(Self);
494     end;
495     DCheck(inflate(FZRec, 0));
496   end;
497   Result := Count;
498 end;
499
500 function TDecompressionStream.Write(const Buffer; Count: Longint): Longint;
501 begin
502   raise EDecompressionError.Create('Invalid stream operation');
503 end;
504
505 function TDecompressionStream.Seek(Offset: Longint; Origin: Word): Longint;
506 var
507   I: Integer;
508   Buf: array [0..4095] of Char;
509 begin
510   if (Offset = 0) and (Origin = soFromBeginning) then
511   begin
512     DCheck(inflateReset(FZRec));
513     FZRec.next_in := FBuffer;
514     FZRec.avail_in := 0;
515     FStrm.Position := 0;
516     FStrmPos := 0;
517   end
518   else if ( (Offset >= 0) and (Origin = soFromCurrent)) or
519           ( ((Offset - FZRec.total_out) > 0) and (Origin = soFromBeginning)) then
520   begin
521     if Origin = soFromBeginning then Dec(Offset, FZRec.total_out);
522     if Offset > 0 then
523     begin
524       for I := 1 to Offset div sizeof(Buf) do
525         ReadBuffer(Buf, sizeof(Buf));
526       ReadBuffer(Buf, Offset mod sizeof(Buf));
527     end;
528   end
529   else
530     raise EDecompressionError.Create('Invalid stream operation');
531   Result := FZRec.total_out;
532 end;
533
534 end.