123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454 |
- {
- $Id$
- This file is part of the Free Pascal run time library.
- Copyright (c) 1999-2000 by the Free Pascal development team
- Implementation of compression streams.
- See the file COPYING.FPC, included in this distribution,
- for details about the copyright.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- **********************************************************************}
- {$mode objfpc}
- unit zstream;
- { ---------------------------------------------------------------------
- For linux and freebsd it's also possible to use ZLib instead
- of paszlib. You need to undefine 'usepaszlib'.
- ---------------------------------------------------------------------}
- {$define usepaszlib}
- interface
- uses
- Sysutils, Classes
- {$ifdef usepaszlib}
- ,paszlib
- {$else}
- ,zlib
- {$endif}
- ;
- {$H+}
- type
- // Error reporting.
- EZlibError = class(EStreamError);
- ECompressionError = class(EZlibError);
- EDecompressionError = class(EZlibError);
- TCustomZlibStream = class(TStream)
- private
- FStrm: TStream;
- FStrmPos: Integer;
- FOnProgress: TNotifyEvent;
- FZRec: TZStream;
- FBuffer: array [Word] of Byte;
- protected
- procedure Progress(Sender: TObject); dynamic;
- property OnProgress: TNotifyEvent read FOnProgress write FOnProgress;
- public
- constructor Create(Strm: TStream);
- end;
- TCompressionLevel = (clNone, clFastest, clDefault, clMax);
- TCompressionStream = class(TCustomZlibStream)
- private
- function GetCompressionRate: extended;
- function CompressionCheck(code: Integer): Integer;
- procedure CompressBuf(const InBuf: Pointer; InBytes: Integer;
- var OutBuf: Pointer; var OutBytes: Integer);
- public
- constructor Create(CompressionLevel: TCompressionLevel; Dest: TStream);
- destructor Destroy; override;
- function Read(var Buffer; Count: Longint): Longint; override;
- function Write(const Buffer; Count: Longint): Longint; override;
- function Seek(Offset: Longint; Origin: Word): Longint; override;
- property CompressionRate: extended read GetCompressionRate;
- property OnProgress;
- end;
- TDecompressionStream = class(TCustomZlibStream)
- private
- function DecompressionCheck(code: Integer): Integer;
- procedure DecompressBuf(const InBuf: Pointer; InBytes: Integer;
- OutEstimate: Integer; var OutBuf: Pointer; var OutBytes: Integer);
- public
- constructor Create(Source: TStream);
- destructor Destroy; override;
- function Read(var Buffer; Count: Longint): Longint; override;
- function Write(const Buffer; Count: Longint): Longint; override;
- function Seek(Offset: Longint; Origin: Word): Longint; override;
- property OnProgress;
- end;
- TGZOpenMode = (gzOpenRead,gzOpenWrite);
- TGZFileStream = Class(TStream)
- Private
- FOpenMode : TGZOpenmode;
- FFIle : gzfile;
- Public
- Constructor Create(FileName: String;FileMode: TGZOpenMode);
- Destructor Destroy;override;
- Function Read(Var Buffer; Count : longint): longint;override;
- function Write(const Buffer; Count: Longint): Longint; override;
- function Seek(Offset: Longint; Origin: Word): Longint; override;
- end;
- implementation
- Const
- ErrorStrings : array [0..6] of string =
- ('Unknown error %d','Z_ERRNO','Z_STREAM_ERROR',
- 'Z_DATA_ERROR','Z_MEM_ERROR','Z_BUF_ERROR','Z_VERSION_ERROR');
- SCouldntOpenFile = 'Couldn''t open file : %s';
- SReadOnlyStream = 'Decompression streams are read-only';
- SWriteOnlyStream = 'Compression streams are write-only';
- SSeekError = 'Compression stream seek error';
- SInvalidSeek = 'Invalid Compression seek operation';
- function zlibAllocMem(opaque:pointer; items:uInt; size:uInt):pointer;{$ifndef usepaszlib}cdecl;{$endif}
- begin
- Result:=GetMem(Items*Size);
- end;
- procedure zlibFreeMem(opaque:pointer; address:pointer);{$ifndef usepaszlib}cdecl;{$endif}
- begin
- FreeMem(address);
- end;
- procedure TCompressionStream.CompressBuf(const InBuf: Pointer; InBytes: Integer;
- var OutBuf: Pointer; var OutBytes: Integer);
- var
- strm: TZStream;
- P: Pointer;
- begin
- FillChar(strm, sizeof(strm), 0);
- strm.zalloc := @zlibAllocMem;
- strm.zfree := @zlibFreeMem;
- OutBytes := ((InBytes + (InBytes div 10) + 12) + 255) and not 255;
- OutBuf:=GetMem(OutBytes);
- try
- strm.next_in := InBuf;
- strm.avail_in := InBytes;
- strm.next_out := OutBuf;
- strm.avail_out := OutBytes;
- CompressionCheck(deflateInit_(strm, Z_BEST_COMPRESSION, ZLIB_VERSION, sizeof(strm)));
- try
- while CompressionCheck(deflate(strm, Z_FINISH)) <> Z_STREAM_END do
- begin
- P := OutBuf;
- Inc(OutBytes, 256);
- ReallocMem(OutBuf,OutBytes);
- strm.next_out := PByte(Integer(OutBuf) + (Integer(strm.next_out) - Integer(P)));
- strm.avail_out := 256;
- end;
- finally
- CompressionCheck(deflateEnd(strm));
- end;
- ReallocMem(OutBuf,strm.total_out);
- OutBytes := strm.total_out;
- except
- FreeMem(OutBuf);
- raise;
- end;
- end;
- procedure TDecompressionStream.DecompressBuf(const InBuf: Pointer; InBytes: Integer;
- OutEstimate: Integer; var OutBuf: Pointer; var OutBytes: Integer);
- var
- strm: TZStream;
- P: Pointer;
- BufInc: Integer;
- Type
- PByte = ^Byte;
- begin
- FillChar(strm, sizeof(strm), 0);
- strm.zalloc := @zlibAllocMem;
- strm.zfree := @zlibFreeMem;
- BufInc := (InBytes + 255) and not 255;
- if OutEstimate = 0 then
- OutBytes := BufInc
- else
- OutBytes := OutEstimate;
- OutBuf:=GetMem(OutBytes);
- try
- strm.next_in := InBuf;
- strm.avail_in := InBytes;
- strm.next_out := OutBuf;
- strm.avail_out := OutBytes;
- DecompressionCheck(inflateInit_(strm, ZLIB_VERSION, sizeof(strm)));
- try
- while DecompressionCheck(inflate(strm, Z_FINISH)) <> Z_STREAM_END do
- begin
- P := OutBuf;
- Inc(OutBytes, BufInc);
- ReallocMem(OutBuf, OutBytes);
- strm.next_out := PByte(Integer(OutBuf) + (Integer(strm.next_out) - Integer(P)));
- strm.avail_out := BufInc;
- end;
- finally
- DecompressionCheck(inflateEnd(strm));
- end;
- ReallocMem(OutBuf, strm.total_out);
- OutBytes := strm.total_out;
- except
- FreeMem(OutBuf);
- raise;
- end;
- end;
- // TCustomZlibStream
- constructor TCustomZLibStream.Create(Strm: TStream);
- begin
- inherited Create;
- FStrm := Strm;
- FStrmPos := Strm.Position;
- FZRec.zalloc := @zlibAllocMem;
- FZRec.zfree := @zlibFreeMem;
- end;
- procedure TCustomZLibStream.Progress(Sender: TObject);
- begin
- if Assigned(FOnProgress) then FOnProgress(Sender);
- end;
- // TCompressionStream
- constructor TCompressionStream.Create(CompressionLevel: TCompressionLevel;
- Dest: TStream);
- const
- Levels: array [TCompressionLevel] of ShortInt =
- (Z_NO_COMPRESSION, Z_BEST_SPEED, Z_DEFAULT_COMPRESSION, Z_BEST_COMPRESSION);
- begin
- inherited Create(Dest);
- FZRec.next_out := @FBuffer;
- FZRec.avail_out := sizeof(FBuffer);
- CompressionCheck(deflateInit_(FZRec, Levels[CompressionLevel], ZLIB_VERSION, sizeof(FZRec)));
- end;
- destructor TCompressionStream.Destroy;
- begin
- FZRec.next_in := nil;
- FZRec.avail_in := 0;
- try
- if FStrm.Position <> FStrmPos then FStrm.Position := FStrmPos;
- while (CompressionCheck(deflate(FZRec, Z_FINISH)) <> Z_STREAM_END)
- and (FZRec.avail_out = 0) do
- begin
- FStrm.WriteBuffer(FBuffer, sizeof(FBuffer));
- FZRec.next_out := @FBuffer;
- FZRec.avail_out := sizeof(FBuffer);
- end;
- if FZRec.avail_out < sizeof(FBuffer) then
- FStrm.WriteBuffer(FBuffer, sizeof(FBuffer) - FZRec.avail_out);
- finally
- deflateEnd(FZRec);
- end;
- inherited Destroy;
- end;
- function TCompressionStream.CompressionCheck(code: Integer): Integer;
- begin
- Result := code;
- if (code < 0) then
- if code < -6 then
- raise ECompressionError.CreateFmt(Errorstrings[0],[Code])
- else
- raise ECompressionError.Create(ErrorStrings[Abs(Code)]);
- end;
- function TCompressionStream.Read(var Buffer; Count: Longint): Longint;
- begin
- raise ECompressionError.Create('Invalid stream operation');
- result:=0;
- end;
- function TCompressionStream.Write(const Buffer; Count: Longint): Longint;
- begin
- FZRec.next_in := @Buffer;
- FZRec.avail_in := Count;
- if FStrm.Position <> FStrmPos then FStrm.Position := FStrmPos;
- while (FZRec.avail_in > 0) do
- begin
- CompressionCheck(deflate(FZRec, 0));
- if FZRec.avail_out = 0 then
- begin
- FStrm.WriteBuffer(FBuffer, sizeof(FBuffer));
- FZRec.next_out := @FBuffer;
- FZRec.avail_out := sizeof(FBuffer);
- FStrmPos := FStrm.Position;
- Progress(Self);
- end;
- end;
- Result := Count;
- end;
- function TCompressionStream.Seek(Offset: Longint; Origin: Word): Longint;
- begin
- if (Offset = 0) and (Origin = soFromCurrent) then
- Result := FZRec.total_in
- else
- raise ECompressionError.Create(SInvalidSeek);
- end;
- function TCompressionStream.GetCompressionRate: extended;
- begin
- Result:=0.0;
- { With FZrec do
- if total_in = 0 then
- GetCompressionRate:=0.0
- else
- GetCompressionRate:=1.0E2*(1.0E0-(total_out/total_in));
- }
- end;
- // TDecompressionStream
- constructor TDecompressionStream.Create(Source: TStream);
- begin
- inherited Create(Source);
- FZRec.next_in := @FBuffer;
- DecompressionCheck(inflateInit_(FZRec, ZLIB_VERSION, sizeof(FZRec)));
- end;
- destructor TDecompressionStream.Destroy;
- begin
- inflateEnd(FZRec);
- inherited Destroy;
- end;
- function TDecompressionStream.DecompressionCheck(code: Integer): Integer;
- begin
- Result := code;
- If Code<0 then
- if code < -6 then
- raise EDecompressionError.CreateFmt(Errorstrings[0],[Code])
- else
- raise EDecompressionError.Create(ErrorStrings[Abs(Code)]);
- end;
- function TDecompressionStream.Read(var Buffer; Count: Longint): Longint;
- begin
- FZRec.next_out := @Buffer;
- FZRec.avail_out := Count;
- if FStrm.Position <> FStrmPos then FStrm.Position := FStrmPos;
- while (FZRec.avail_out > 0) do
- begin
- if FZRec.avail_in = 0 then
- begin
- FZRec.avail_in := FStrm.Read(FBuffer, sizeof(FBuffer));
- if FZRec.avail_in = 0 then
- begin
- Result := Count - FZRec.avail_out;
- Exit;
- end;
- FZRec.next_in := @FBuffer;
- FStrmPos := FStrm.Position;
- Progress(Self);
- end;
- DeCompressionCheck(inflate(FZRec, 0));
- end;
- Result := Count;
- end;
- function TDecompressionStream.Write(const Buffer; Count: Longint): Longint;
- begin
- raise EDecompressionError.Create('Invalid stream operation');
- result:=0;
- end;
- function TDecompressionStream.Seek(Offset: Longint; Origin: Word): Longint;
- var
- I: Integer;
- Buf: array [0..4095] of Char;
- begin
- if (Offset = 0) and (Origin = soFromBeginning) then
- begin
- DecompressionCheck(inflateReset(FZRec));
- FZRec.next_in := @FBuffer;
- FZRec.avail_in := 0;
- FStrm.Position := 0;
- FStrmPos := 0;
- end
- else if ( (Offset >= 0) and (Origin = soFromCurrent)) or
- ( ((Offset - FZRec.total_out) > 0) and (Origin = soFromBeginning)) then
- begin
- if Origin = soFromBeginning then Dec(Offset, FZRec.total_out);
- if Offset > 0 then
- begin
- for I := 1 to Offset div sizeof(Buf) do
- ReadBuffer(Buf, sizeof(Buf));
- ReadBuffer(Buf, Offset mod sizeof(Buf));
- end;
- end
- else
- raise EDecompressionError.Create(SInvalidSeek);
- Result := FZRec.total_out;
- end;
- // TGZFileStream
- Constructor TGZFileStream.Create(FileName: String;FileMode: TGZOpenMode);
- Const OpenStrings : array[TGZOpenMode] of pchar = ('rb','wb');
- begin
- FOpenMode:=FileMode;
- FFile:=gzopen (PChar(FileName),Openstrings[FileMode]);
- If FFile=Nil then
- Raise ezlibError.CreateFmt (SCouldntOpenFIle,[FileName]);
- end;
- Destructor TGZFileStream.Destroy;
- begin
- gzclose(FFile);
- Inherited Destroy;
- end;
- Function TGZFileStream.Read(Var Buffer; Count : longint): longint;
- begin
- If FOpenMode=gzOpenWrite then
- Raise ezliberror.create(SWriteOnlyStream);
- Result:=gzRead(FFile,@Buffer,Count);
- end;
- function TGZFileStream.Write(const Buffer; Count: Longint): Longint;
- begin
- If FOpenMode=gzOpenRead then
- Raise EzlibError.Create(SReadonlyStream);
- Result:=gzWrite(FFile,@Buffer,Count);
- end;
- function TGZFileStream.Seek(Offset: Longint; Origin: Word): Longint;
- begin
- Result:=gzseek(FFile,Offset,Origin);
- If Result=-1 then
- Raise eZlibError.Create(SSeekError);
- end;
- end.
- {
- $Log$
- Revision 1.5 2002-09-07 15:15:26 peter
- * old logs removed and tabs fixed
- }
|