2
This file is part of the Free Pascal run time library.
3
Copyright (c) 1999-2000 by the Free Pascal development team
5
Implementation of compression streams.
7
See the file COPYING.FPC, included in this distribution,
8
for details about the copyright.
10
This program is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
**********************************************************************}
20
{ ---------------------------------------------------------------------
21
For linux and freebsd it's also possible to use ZLib instead
22
of paszlib. You need to undefine 'usepaszlib'.
23
---------------------------------------------------------------------}
43
EZlibError = class(EStreamError);
44
ECompressionError = class(EZlibError);
45
EDecompressionError = class(EZlibError);
47
TCustomZlibStream = class(TOwnerStream)
50
FOnProgress: TNotifyEvent;
52
FBuffer: array [Word] of Byte;
54
procedure Progress(Sender: TObject); dynamic;
55
property OnProgress: TNotifyEvent read FOnProgress write FOnProgress;
57
constructor Create(Strm: TStream);
60
TCompressionLevel = (clNone, clFastest, clDefault, clMax);
62
TCompressionStream = class(TCustomZlibStream)
64
function GetCompressionRate: extended;
65
function CompressionCheck(code: Integer): Integer;
66
procedure CompressBuf(const InBuf: Pointer; InBytes: Integer;
67
var OutBuf: Pointer; var OutBytes: Integer);
69
constructor Create(CompressionLevel: TCompressionLevel; Dest: TStream; ASkipHeader : Boolean = False);
70
destructor Destroy; override;
71
function Read(var Buffer; Count: Longint): Longint; override;
72
function Write(const Buffer; Count: Longint): Longint; override;
73
function Seek(Offset: Longint; Origin: Word): Longint; override;
74
property CompressionRate: extended read GetCompressionRate;
78
TDecompressionStream = class(TCustomZlibStream)
80
function DecompressionCheck(code: Integer): Integer;
81
procedure DecompressBuf(const InBuf: Pointer; InBytes: Integer;
82
OutEstimate: Integer; var OutBuf: Pointer; var OutBytes: Integer);
84
constructor Create(ASource: TStream; ASkipHeader : Boolean = False);
85
destructor Destroy; override;
86
function Read(var Buffer; Count: Longint): Longint; override;
87
function Write(const Buffer; Count: Longint): Longint; override;
88
function Seek(Offset: Longint; Origin: Word): Longint; override;
92
TGZOpenMode = (gzOpenRead,gzOpenWrite);
94
TGZFileStream = Class(TStream)
96
FOpenMode : TGZOpenmode;
99
Constructor Create(FileName: String;FileMode: TGZOpenMode);
100
Destructor Destroy;override;
101
Function Read(Var Buffer; Count : longint): longint;override;
102
function Write(const Buffer; Count: Longint): Longint; override;
103
function Seek(Offset: Longint; Origin: Word): Longint; override;
110
ErrorStrings : array [0..6] of string =
111
('Unknown error %d','Z_ERRNO','Z_STREAM_ERROR',
112
'Z_DATA_ERROR','Z_MEM_ERROR','Z_BUF_ERROR','Z_VERSION_ERROR');
113
SCouldntOpenFile = 'Couldn''t open file : %s';
114
SReadOnlyStream = 'Decompression streams are read-only';
115
SWriteOnlyStream = 'Compression streams are write-only';
116
SSeekError = 'Compression stream seek error';
117
SInvalidSeek = 'Invalid Compression seek operation';
119
procedure TCompressionStream.CompressBuf(const InBuf: Pointer; InBytes: Integer;
120
var OutBuf: Pointer; var OutBytes: Integer);
125
FillChar(strm, sizeof(strm), 0);
126
OutBytes := ((InBytes + (InBytes div 10) + 12) + 255) and not 255;
127
OutBuf:=GetMem(OutBytes);
129
strm.next_in := InBuf;
130
strm.avail_in := InBytes;
131
strm.next_out := OutBuf;
132
strm.avail_out := OutBytes;
133
CompressionCheck(deflateInit(strm, Z_BEST_COMPRESSION));
135
while CompressionCheck(deflate(strm, Z_FINISH)) <> Z_STREAM_END do
139
ReallocMem(OutBuf,OutBytes);
140
strm.next_out := PByte(Integer(OutBuf) + (Integer(strm.next_out) - Integer(P)));
141
strm.avail_out := 256;
144
CompressionCheck(deflateEnd(strm));
146
ReallocMem(OutBuf,strm.total_out);
147
OutBytes := strm.total_out;
155
procedure TDecompressionStream.DecompressBuf(const InBuf: Pointer; InBytes: Integer;
156
OutEstimate: Integer; var OutBuf: Pointer; var OutBytes: Integer);
164
FillChar(strm, sizeof(strm), 0);
165
BufInc := (InBytes + 255) and not 255;
166
if OutEstimate = 0 then
169
OutBytes := OutEstimate;
170
OutBuf:=GetMem(OutBytes);
172
strm.next_in := InBuf;
173
strm.avail_in := InBytes;
174
strm.next_out := OutBuf;
175
strm.avail_out := OutBytes;
176
DecompressionCheck(inflateInit(strm));
178
while DecompressionCheck(inflate(strm, Z_FINISH)) <> Z_STREAM_END do
181
Inc(OutBytes, BufInc);
182
ReallocMem(OutBuf, OutBytes);
183
strm.next_out := PByte(Integer(OutBuf) + (Integer(strm.next_out) - Integer(P)));
184
strm.avail_out := BufInc;
187
DecompressionCheck(inflateEnd(strm));
189
ReallocMem(OutBuf, strm.total_out);
190
OutBytes := strm.total_out;
200
constructor TCustomZLibStream.Create(Strm: TStream);
202
inherited Create(Strm);
203
FStrmPos := Strm.Position;
206
procedure TCustomZLibStream.Progress(Sender: TObject);
208
if Assigned(FOnProgress) then FOnProgress(Sender);
212
// TCompressionStream
214
constructor TCompressionStream.Create(CompressionLevel: TCompressionLevel;
215
Dest: TStream; ASkipHeader : Boolean = False);
217
Levels: array [TCompressionLevel] of ShortInt =
218
(Z_NO_COMPRESSION, Z_BEST_SPEED, Z_DEFAULT_COMPRESSION, Z_BEST_COMPRESSION);
220
inherited Create(Dest);
221
FZRec.next_out := @FBuffer[0];
222
FZRec.avail_out := sizeof(FBuffer);
224
CompressionCheck(deflateInit2(FZRec, Levels[CompressionLevel],Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, 0))
226
CompressionCheck(deflateInit(FZRec, Levels[CompressionLevel]));
229
destructor TCompressionStream.Destroy;
231
FZRec.next_in := nil;
234
if Source.Position <> FStrmPos then Source.Position := FStrmPos;
235
while (CompressionCheck(deflate(FZRec, Z_FINISH)) <> Z_STREAM_END)
236
and (FZRec.avail_out = 0) do
238
Source.WriteBuffer(FBuffer, sizeof(FBuffer));
239
FZRec.next_out := @FBuffer[0];
240
FZRec.avail_out := sizeof(FBuffer);
242
if FZRec.avail_out < sizeof(FBuffer) then
243
Source.WriteBuffer(FBuffer, sizeof(FBuffer) - FZRec.avail_out);
250
function TCompressionStream.CompressionCheck(code: Integer): Integer;
255
raise ECompressionError.CreateFmt(Errorstrings[0],[Code])
257
raise ECompressionError.Create(ErrorStrings[Abs(Code)]);
261
function TCompressionStream.Read(var Buffer; Count: Longint): Longint;
263
raise ECompressionError.Create('Invalid stream operation');
267
function TCompressionStream.Write(const Buffer; Count: Longint): Longint;
269
FZRec.next_in := @Buffer;
270
FZRec.avail_in := Count;
271
if Source.Position <> FStrmPos then Source.Position := FStrmPos;
272
while (FZRec.avail_in > 0) do
274
CompressionCheck(deflate(FZRec, 0));
275
if FZRec.avail_out = 0 then
277
Source.WriteBuffer(FBuffer, sizeof(FBuffer));
278
FZRec.next_out := @FBuffer[0];
279
FZRec.avail_out := sizeof(FBuffer);
280
FStrmPos := Source.Position;
287
function TCompressionStream.Seek(Offset: Longint; Origin: Word): Longint;
289
if (Offset = 0) and (Origin = soFromCurrent) then
290
Result := FZRec.total_in
292
raise ECompressionError.Create(SInvalidSeek);
295
function TCompressionStream.GetCompressionRate: extended;
300
GetCompressionRate:=0.0
302
GetCompressionRate:=1.0E2*(1.0E0-(total_out/total_in));
307
// TDecompressionStream
309
constructor TDecompressionStream.Create(ASource: TStream; ASkipHeader : Boolean = False);
311
inherited Create(ASource);
312
FZRec.next_in := @FBuffer[0];
314
DeCompressionCheck(inflateInit2(FZRec,-MAX_WBITS))
316
DeCompressionCheck(inflateInit(FZRec));
319
destructor TDecompressionStream.Destroy;
321
if FZRec.avail_in <> 0 then
322
Source.Seek(-FZRec.avail_in, soFromCurrent);
327
function TDecompressionStream.DecompressionCheck(code: Integer): Integer;
332
raise EDecompressionError.CreateFmt(Errorstrings[0],[Code])
334
raise EDecompressionError.Create(ErrorStrings[Abs(Code)]);
337
function TDecompressionStream.Read(var Buffer; Count: Longint): Longint;
339
FZRec.next_out := @Buffer;
340
FZRec.avail_out := Count;
341
if Source.Position <> FStrmPos then Source.Position := FStrmPos;
342
while (FZRec.avail_out > 0) do
344
if FZRec.avail_in = 0 then
346
FZRec.avail_in := Source.Read(FBuffer, sizeof(FBuffer));
347
if FZRec.avail_in = 0 then
349
Result := Count - FZRec.avail_out;
352
FZRec.next_in := @FBuffer[0];
353
FStrmPos := Source.Position;
356
if DeCompressionCheck(inflate(FZRec, 0)) = Z_STREAM_END then
358
Result := Count - FZRec.avail_out;
365
function TDecompressionStream.Write(const Buffer; Count: Longint): Longint;
367
raise EDecompressionError.Create('Invalid stream operation');
371
function TDecompressionStream.Seek(Offset: Longint; Origin: Word): Longint;
374
Buf: array [0..4095] of Char;
376
if (Offset = 0) and (Origin = soFromBeginning) then
378
DecompressionCheck(inflateReset(FZRec));
379
FZRec.next_in := @FBuffer[0];
381
Source.Position := 0;
384
else if ( (Offset >= 0) and (Origin = soFromCurrent)) or
385
( ((Offset - FZRec.total_out) > 0) and (Origin = soFromBeginning)) then
387
if Origin = soFromBeginning then Dec(Offset, FZRec.total_out);
390
for I := 1 to Offset div sizeof(Buf) do
391
ReadBuffer(Buf, sizeof(Buf));
392
ReadBuffer(Buf, Offset mod sizeof(Buf));
396
raise EDecompressionError.Create(SInvalidSeek);
397
Result := FZRec.total_out;
402
Constructor TGZFileStream.Create(FileName: String;FileMode: TGZOpenMode);
404
Const OpenStrings : array[TGZOpenMode] of pchar = ('rb','wb');
408
FFile:=gzopen (PChar(FileName),Openstrings[FileMode]);
410
Raise ezlibError.CreateFmt (SCouldntOpenFIle,[FileName]);
413
Destructor TGZFileStream.Destroy;
419
Function TGZFileStream.Read(Var Buffer; Count : longint): longint;
421
If FOpenMode=gzOpenWrite then
422
Raise ezliberror.create(SWriteOnlyStream);
423
Result:=gzRead(FFile,@Buffer,Count);
426
function TGZFileStream.Write(const Buffer; Count: Longint): Longint;
428
If FOpenMode=gzOpenRead then
429
Raise EzlibError.Create(SReadonlyStream);
430
Result:=gzWrite(FFile,@Buffer,Count);
433
function TGZFileStream.Seek(Offset: Longint; Origin: Word): Longint;
435
Result:=gzseek(FFile,Offset,Origin);
437
Raise eZlibError.Create(SSeekError);