Componentes.Terceros.jcl/official/1.96/source/common/JclCompression.pas

780 lines
24 KiB
ObjectPascal

{**************************************************************************************************}
{ }
{ Project JEDI Code Library (JCL) }
{ }
{ The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); }
{ you may not use this file except in compliance with the License. You may obtain a copy of the }
{ License at http://www.mozilla.org/MPL/ }
{ }
{ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF }
{ ANY KIND, either express or implied. See the License for the specific language governing rights }
{ and limitations under the License. }
{ }
{ The Original Code is JclCompression.pas. }
{ }
{ The Initial Developer of the Original Code is Matthias Thoma. }
{ All Rights Reserved. }
{ }
{ Contributors: }
{ }
{**************************************************************************************************}
{ }
{ Alternatively, the contents of this file may be used under the terms of the GNU Lesser General }
{ Public License (the "LGPL License"), in which case the provisions of the LGPL License are }
{ applicable instead of those above. If you wish to allow use of your version of this file only }
{ under the terms of the LGPL License and not to allow others to use your version of this file }
{ under the MPL, indicate your decision by deleting the provisions above and replace them with the }
{ notice and other provisions required by the LGPL License. If you do not delete the provisions }
{ above, a recipient may use your version of this file under either the MPL or the LGPL License. }
{ }
{ For more information about the LGPL: }
{ http://www.gnu.org/copyleft/lesser.html }
{ }
{**************************************************************************************************}
{ }
{ This unit is still in alpha state. It is likely that it will change a lot. Suggestions are }
{ welcome. }
{ }
{**************************************************************************************************}
// Last modified: $Date: 2005/10/25 04:46:31 $
// For history see end of file
unit JclCompression;
interface
uses
{$IFDEF MSWINDOWS}
Windows,
{$ENDIF MSWINDOWS}
{$IFDEF UNIX}
Types,
{$ENDIF UNIX}
{$IFDEF HAS_UNIT_LIBC}
Libc,
{$ENDIF HAS_UNIT_LIBC}
SysUtils, Classes,
JclBase,
zlibh;
{**************************************************************************************************}
{
TJclCompressionStream
- -
----------------------- --------------------------
- -
TJclCompressStream TJclDecompressStream
- -
--------------------------------- ---------------------------------
- - - - - -
- - - - - -
TJclZLibCompressStream - TBZIP2CompressStram TJclZLibDecompressStream - TBZIP2DeCompressStream
- -
- TGZDecompressStream
TGZCompressStream
}
{**************************************************************************************************}
type
TJclCompressionStream = class(TStream)
private
FOnProgress: TNotifyEvent;
FBuffer: Pointer;
FBufferSize: Cardinal;
FStream: TStream;
protected
function SetBufferSize(Size: Cardinal): Cardinal; virtual;
procedure Progress(Sender: TObject); dynamic;
property OnProgress: TNotifyEvent read FOnProgress write FOnProgress;
public
constructor Create(Stream: 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;
procedure Reset; virtual;
end;
TJclCompressStream = class(TJclCompressionStream)
public
function Flush: Integer; dynamic; abstract;
constructor Create(Destination: TStream);
end;
TJclDecompressStream = class(TJclCompressionStream)
public
constructor Create(Source: TStream);
end;
// ZIP Support
TJclCompressionLevel = Integer;
TJclZLibCompressStream = class(TJclCompressStream)
private
FWindowBits: Integer;
FMemLevel: Integer;
FMethod: Integer;
FStrategy: Integer;
FDeflateInitialized: Boolean;
FCompressionLevel: Integer;
protected
ZLibRecord: TZStreamRec;
procedure SetCompressionLevel(Value: Integer);
procedure SetStrategy(Value: Integer);
procedure SetMemLevel(Value: Integer);
procedure SetMethod(Value: Integer);
procedure SetWindowBits(Value: Integer);
public
constructor Create(Destination: TStream; CompressionLevel: TJclCompressionLevel = -1);
destructor Destroy; override;
function Flush: Integer; override;
procedure Reset; override;
function Seek(Offset: Longint; Origin: Word): Longint; override;
function Write(const Buffer; Count: Longint): Longint; override;
property WindowBits: Integer read FWindowBits write SetWindowBits;
property MemLevel: Integer read FMemLevel write SetMemLevel;
property Method: Integer read FMethod write SetMethod;
property Strategy: Integer read FStrategy write SetStrategy;
property CompressionLevel: Integer read FCompressionLevel write SetCompressionLevel;
end;
TJclZLibDecompressStream = class(TJclDecompressStream)
private
FWindowBits: Integer;
FInflateInitialized: Boolean;
protected
ZLibRecord: TZStreamRec;
procedure SetWindowBits(Value: Integer);
public
constructor Create(Source: TStream; WindowBits: Integer = DEF_WBITS);
destructor Destroy; override;
function Read(var Buffer; Count: Longint): Longint; override;
function Seek(Offset: Longint; Origin: Word): Longint; override;
property WindowBits: Integer read FWindowBits write SetWindowBits;
end;
// GZIP Support
TJclGZIPCompressionStream = class(TJclCompressionStream)
end;
TJclGZIPDecompressionStream = class(TJclDecompressStream)
end;
// RAR Support
TJclRARCompressionStream = class(TJclCompressionStream)
end;
TJclRARDecompressionStream = class(TJclDecompressStream)
end;
// TAR Support
TJclTARCompressionStream = class(TJclCompressionStream)
end;
TJclTARDecompressionStream = class(TJclDecompressStream)
end;
// BZIP2 Support
(*
TJclBZIP2CompressStream = class(TJclCompressStream)
private
FDeflateInitialized: Boolean;
protected
BZLibRecord: TBZStream;
public
function Flush: Integer; override;
function Seek(Offset: Longint; Origin: Word): Longint; override;
function Write(const Buffer; Count: Longint): Longint; override;
constructor Create(Destination: TStream; CompressionLevel: TJclCompressionLevel = -1);
destructor Destroy; override;
end;
TJclBZIP2DecompressStream = class(TJclDecompressStream)
private
FInflateInitialized: Boolean;
protected
BZLibRecord: TBZStream;
public
function Read(var Buffer; Count: Longint): Longint; override;
function Seek(Offset: Longint; Origin: Word): Longint; override;
constructor Create(Source: TStream); overload;
destructor Destroy; override;
end;
*)
EJclCompressionError = class(EJclError);
implementation
uses
JclResources;
const
JclDefaultBufferSize = 131072; // 128k
//=== { TJclCompressionStream } ==============================================
constructor TJclCompressionStream.Create(Stream: TStream);
begin
inherited Create;
FBuffer := nil;
SetBufferSize(JclDefaultBufferSize);
end;
destructor TJclCompressionStream.Destroy;
begin
SetBufferSize(0);
inherited Destroy;
end;
function TJclCompressionStream.Read(var Buffer; Count: Longint): Longint;
begin
raise EJclCompressionError.CreateRes(@RsCompressionReadNotSupported);
end;
function TJclCompressionStream.Write(const Buffer; Count: Longint): Longint;
begin
raise EJclCompressionError.CreateRes(@RsCompressionWriteNotSupported);
end;
function TJclCompressionStream.Seek(Offset: Longint; Origin: Word): Longint;
begin
raise EJclCompressionError.CreateRes(@RsCompressionSeekNotSupported);
end;
procedure TJclCompressionStream.Reset;
begin
raise EJclCompressionError.CreateRes(@RsCompressionResetNotSupported);
end;
function TJclCompressionStream.SetBufferSize(Size: Cardinal): Cardinal;
begin
if FBuffer <> nil then
FreeMem(FBuffer, FBufferSize);
FBufferSize := Size;
if FBufferSize > 0 then
GetMem(FBuffer, FBufferSize)
else
FBuffer := nil;
Result := FBufferSize;
end;
procedure TJclCompressionStream.Progress(Sender: TObject);
begin
if Assigned(FOnProgress) then
FOnProgress(Sender);
end;
//=== { TJclCompressStream } =================================================
constructor TJclCompressStream.Create(Destination: TStream);
begin
inherited Create(Destination);
FStream := Destination;
end;
//=== { TJclDecompressStream } ===============================================
constructor TJclDecompressStream.Create(Source: TStream);
begin
inherited Create(Source);
FStream := Source;
end;
//=== { TJclZLibCompressionStream } ==========================================
{ Error checking helper }
function ZLibCheck(const ErrCode: Integer): Integer;
begin
Result := ErrCode;
if ErrCode < 0 then
case ErrCode of
Z_ERRNO:
raise EJclCompressionError.CreateRes(@RsCompressionZLibZErrNo);
Z_STREAM_ERROR:
raise EJclCompressionError.CreateRes(@RsCompressionZLibZStreamError);
Z_DATA_ERROR:
raise EJclCompressionError.CreateRes(@RsCompressionZLibZDataError);
Z_MEM_ERROR:
raise EJclCompressionError.CreateRes(@RsCompressionZLibZMemError);
Z_BUF_ERROR:
raise EJclCompressionError.CreateRes(@RsCompressionZLibZBufError);
Z_VERSION_ERROR:
raise EJclCompressionError.CreateRes(@RsCompressionZLibZVersionError);
else
raise EJclCompressionError.CreateRes(@RsCompressionZLibError);
end;
end;
constructor TJclZLibCompressStream.Create(Destination: TStream; CompressionLevel: TJclCompressionLevel);
begin
inherited Create(Destination);
Assert(FBuffer <> nil);
Assert(FBufferSize > 0);
// Initialize ZLib StreamRecord
with ZLibRecord do
begin
zalloc := nil; // Use build-in memory allocation functionality
zfree := nil;
next_in := nil;
avail_in := 0;
next_out := FBuffer;
avail_out := FBufferSize;
end;
FWindowBits := DEF_WBITS;
FMemLevel := DEF_MEM_LEVEL;
FMethod := Z_DEFLATED;
FStrategy := Z_DEFAULT_STRATEGY;
FCompressionLevel := CompressionLevel;
FDeflateInitialized := False;
end;
destructor TJclZLibCompressStream.Destroy;
begin
Flush;
if FDeflateInitialized then
begin
ZLibRecord.next_in := nil;
ZLibRecord.avail_in := 0;
ZLibRecord.avail_out := 0;
ZLibRecord.next_out := nil;
ZLibCheck(deflateEnd(ZLibRecord));
end;
inherited Destroy;
end;
function TJclZLibCompressStream.Write(const Buffer; Count: Longint): Longint;
begin
if not FDeflateInitialized then
begin
ZLibCheck(deflateInit2(ZLibRecord, FCompressionLevel, FMethod, FWindowBits, FMemLevel, FStrategy));
FDeflateInitialized := True;
end;
ZLibRecord.next_in := @Buffer;
ZLibRecord.avail_in := Count;
while ZLibRecord.avail_in > 0 do
begin
ZLibCheck(deflate(ZLibRecord, Z_NO_FLUSH));
if ZLibRecord.avail_out = 0 then // Output buffer empty. Write to stream and go on...
begin
FStream.WriteBuffer(FBuffer^, FBufferSize);
Progress(Self);
ZLibRecord.next_out := FBuffer;
ZLibRecord.avail_out := FBufferSize;
end;
end;
Result := Count;
end;
function TJclZLibCompressStream.Flush: Integer;
begin
Result := 0;
if FDeflateInitialized then
begin
ZLibRecord.next_in := nil;
ZLibRecord.avail_in := 0;
while (ZLibCheck(deflate(ZLibRecord, Z_FINISH)) <> Z_STREAM_END) and
(ZLibRecord.avail_out = 0) do
begin
FStream.WriteBuffer(FBuffer^, FBufferSize);
Progress(Self);
ZLibRecord.next_out := FBuffer;
ZLibRecord.avail_out := FBufferSize;
Inc(Result, FBufferSize);
end;
if ZLibRecord.avail_out < FBufferSize then
begin
FStream.WriteBuffer(FBuffer^, FBufferSize-ZLibRecord.avail_out);
Progress(Self);
Inc(Result, FBufferSize - ZLibRecord.avail_out);
ZLibRecord.next_out := FBuffer;
ZLibRecord.avail_out := FBufferSize;
end;
end;
end;
function TJclZLibCompressStream.Seek(Offset: Longint; Origin: Word): Longint;
begin
if (Offset = 0) and (Origin = soFromCurrent) then
Result := ZLibRecord.total_in
else
if (Offset = 0) and (Origin = soFromBeginning) and (ZLibRecord.total_in = 0) then
Result := 0
else
Result := inherited Seek(Offset, Origin);
end;
procedure TJclZLibCompressStream.SetWindowBits(Value: Integer);
begin
FWindowBits := Value;
end;
procedure TJclZLibCompressStream.SetMethod(Value: Integer);
begin
FMethod := Value;
end;
procedure TJclZLibCompressStream.SetStrategy(Value: Integer);
begin
FStrategy := Value;
if FDeflateInitialized then
ZLibCheck(deflateParams(ZLibRecord, FCompressionLevel, FStrategy));
end;
procedure TJclZLibCompressStream.SetMemLevel(Value: Integer);
begin
FMemLevel := Value;
end;
procedure TJclZLibCompressStream.SetCompressionLevel(Value: Integer);
begin
FCompressionLevel := Value;
if FDeflateInitialized then
ZLibCheck(deflateParams(ZLibRecord, FCompressionLevel, FStrategy));
end;
procedure TJclZLibCompressStream.Reset;
begin
if FDeflateInitialized then
begin
Flush;
ZLibCheck(deflateReset(ZLibRecord));
end;
end;
//=== { TJclZLibDecompressionStream } =======================================
constructor TJclZLibDecompressStream.Create(Source: TStream; WindowBits: Integer = DEF_WBITS);
begin
inherited Create(Source);
// Initialize ZLib StreamRecord
with ZLibRecord do
begin
zalloc := nil; // Use build-in memory allocation functionality
zfree := nil;
next_in := nil;
avail_in := 0;
next_out := FBuffer;
avail_out := FBufferSize;
end;
FInflateInitialized := False;
FWindowBits := WindowBits;
end;
destructor TJclZLibDecompressStream.Destroy;
begin
if FInflateInitialized then
begin
FStream.Seek(-ZLibRecord.avail_in, soFromCurrent);
ZLibCheck(inflateEnd(ZLibRecord));
end;
inherited Destroy;
end;
function TJclZLibDecompressStream.Read(var Buffer; Count: Longint): Longint;
begin
if not FInflateInitialized then
begin
ZLibCheck(InflateInit2(ZLibRecord, FWindowBits));
FInflateInitialized := True;
end;
ZLibRecord.next_out := @Buffer;
ZLibRecord.avail_out := Count;
while ZLibRecord.avail_out > 0 do // as long as we have data
begin
if ZLibRecord.avail_in = 0 then
begin
ZLibRecord.avail_in := FStream.Read(FBuffer^, FBufferSize);
if ZLibRecord.avail_in = 0 then
begin
Result := Count - Longint(ZLibRecord.avail_out);
Exit;
end;
ZLibRecord.next_in := FBuffer;
end;
if ZLibRecord.avail_in > 0 then
begin
ZLibCheck(inflate(ZLibRecord, Z_NO_FLUSH));
Progress(Self);
end;
end;
Result := Count;
end;
function TJclZLibDecompressStream.Seek(Offset: Longint; Origin: Word): Longint;
begin
if (Offset = 0) and (Origin = soFromCurrent) then
Result := ZLibRecord.total_out
else
Result := inherited Seek(Offset, Origin);
end;
procedure TJclZLibDecompressStream.SetWindowBits(Value: Integer);
begin
FWindowBits := Value;
end;
//=== { TJclBZLibCompressionStream } =========================================
(*
{ Error checking helper }
function BZIP2LibCheck(const ErrCode: Integer): Integer;
begin
Result := ErrCode;
if ErrCode < 0 then
begin
case ErrCode of
Z_ERRNO: raise EJclCompressionError.CreateRes(@RsCompressionZLibZErrNo);
Z_STREAM_ERROR: raise EJclCompressionError.CreateRes(@RsCompressionZLibZStreamError);
Z_DATA_ERROR: raise EJclCompressionError.CreateRes(@RsCompressionZLibZDataError);
Z_MEM_ERROR: raise EJclCompressionError.CreateRes(@RsCompressionZLibZMemError);
Z_BUF_ERROR: raise EJclCompressionError.CreateRes(@RsCompressionZLibZBufError);
Z_VERSION_ERROR: raise EJclCompressionError.CreateRes(@RsCompressionZLibZVersionError);
else
raise EJclCompressionError.CreateRes(@RsCompressionZLibError);
end;
end;
end;
constructor TJclBZIP2CompressStream.Create(Destination: TStream; CompressionLevel: TJclCompressionLevel);
begin
inherited Create(Destination);
Assert(FBuffer <> nil);
Assert(FBufferSize > 0);
// Initialize ZLib StreamRecord
with BZLibRecord do
begin
bzalloc := nil; // Use build-in memory allocation functionality
bzfree := nil;
next_in := nil;
avail_in := 0;
next_out := FBuffer;
avail_out := FBufferSize;
end;
FDeflateInitialized := False;
end;
destructor TJclBZIP2CompressStream.Destroy;
begin
Flush;
if FDeflateInitialized then
BZIP2LibCheck(BZ2_bzCompressEnd(@BZLibRecord));
inherited Destroy;
end;
function TJclBZIP2CompressStream.Write(const Buffer; Count: Longint): Longint;
begin
if not FDeflateInitialized then
begin
BZIP2LibCheck(BZ2_bzCompressInit(@BZLibRecord,9,0,0));
FDeflateInitialized := True;
end;
BZLibRecord.next_in := @Buffer;
BZLibRecord.avail_in := Count;
while BZLibRecord.avail_in > 0 do
begin
BZIP2LibCheck(BZ2_bzCompress(@BZLibRecord, BZ_RUN));
if BZLibRecord.avail_out = 0 then // Output buffer empty. Write to stream and go on...
begin
FStream.WriteBuffer(FBuffer^, FBufferSize);
Progress(Self);
BZLibRecord.next_out := FBuffer;
BZLibRecord.avail_out := FBufferSize;
end;
end;
Result := Count;
end;
function TJclBZIP2CompressStream.Flush: Integer;
begin
Result := 0;
if FDeflateInitialized then
begin
BZLibRecord.next_in := nil;
BZLibRecord.avail_in := 0;
while (BZIP2LibCheck(BZ2_bzCompress(@BZLibRecord, BZ_FLUSH)) <> Z_STREAM_END) and (BZLibRecord.avail_out = 0) do
begin
FStream.WriteBuffer(FBuffer^, FBufferSize);
Progress(Self);
BZLibRecord.next_out := FBuffer;
BZLibRecord.avail_out := FBufferSize;
Result := Result + FBufferSize;
end;
if BZLibRecord.avail_out < FBufferSize then
begin
FStream.WriteBuffer(FBuffer^, FBufferSize-BZLibRecord.avail_out);
Progress(Self);
Result := Result + FBufferSize-BZLibRecord.avail_out;
BZLibRecord.next_out := FBuffer;
BZLibRecord.avail_out := FBufferSize;
end;
end;
end;
function TJclBZIP2CompressStream.Seek(Offset: Longint; Origin: Word): Longint;
begin
if (Offset = 0) and (Origin = soFromCurrent) then
Result := BZLibRecord.total_in_lo32
else
if (Offset = 0) and (Origin = soFromBeginning) and (BZLibRecord.total_in_lo32 = 0) then
Result := 0
else
Result := inherited Seek(Offset, Origin);
end;
//=== { TJclZLibDecompressionStream } ========================================
constructor TJclBZIP2DecompressStream.Create(Source: TStream);
begin
inherited Create(Source);
// Initialize ZLib StreamRecord
with BZLibRecord do
begin
bzalloc := nil; // Use build-in memory allocation functionality
bzfree := nil;
opaque := nil;
next_in := nil;
state := nil;
avail_in := 0;
next_out := FBuffer;
avail_out := FBufferSize;
end;
FInflateInitialized := False;
end;
destructor TJclBZIP2DecompressStream.Destroy;
begin
if FInflateInitialized then
begin
FStream.Seek(-BZLibRecord.avail_in, soFromCurrent);
BZIP2LibCheck(BZ2_bzDecompressEnd(@BZLibRecord));
end;
inherited Destroy;
end;
function TJclBZIP2DecompressStream.Read(var Buffer; Count: Longint): Longint;
var
avail_out_ctr: Integer;
begin
if not FInflateInitialized then
begin
BZIP2LibCheck(BZ2_bzDecompressInit(@BZLibRecord,0,0));
FInflateInitialized := True;
end;
BZLibRecord.next_out := @Buffer;
BZLibRecord.avail_out := Count;
avail_out_ctr := Count;
while avail_out_ctr > 0 do // as long as we have data
begin
if BZLibRecord.avail_in = 0 then
begin
BZLibRecord.avail_in := FStream.Read(FBuffer^, FBufferSize);
if BZLibRecord.avail_in = 0 then
begin
Result := Count - avail_out_ctr;
Exit;
end;
BZLibRecord.next_in := FBuffer;
end;
if BZLibRecord.avail_in > 0 then
begin
BZIP2LibCheck(BZ2_bzDecompress(@BZLibRecord));
avail_out_ctr := Count - BZLibRecord.avail_out;
end
end;
Result := Count;
end;
function TJclBZIP2DecompressStream.Seek(Offset: Longint; Origin: Word): Longint;
begin
if (Offset = 0) and (Origin = soFromCurrent) then
Result := BZLibRecord.total_out_lo32
else
Result := inherited Seek(Offset, Origin);
end;
*)
// History:
// $Log: JclCompression.pas,v $
// Revision 1.9 2005/10/25 04:46:31 rrossmair
// - fix for issue #0003276 (as provided by reporter)
//
// Revision 1.8 2005/03/08 08:33:15 marquardt
// overhaul of exceptions and resourcestrings, minor style cleaning
//
// Revision 1.7 2005/02/27 14:55:25 marquardt
// changed overloaded constructors to constructor with default parameter (BCB friendly)
//
// Revision 1.6 2005/02/24 16:34:39 marquardt
// remove divider lines, add section lines (unfinished)
//
// Revision 1.5 2005/02/24 07:36:24 marquardt
// resolved the compiler warnings, style cleanup, removed code from JclContainerIntf.pas
//
// Revision 1.4 2004/11/17 03:24:43 mthoma
// Just noticed that I checked in the wrong version... this one is bugfixed and contains
// $date and $log
//
end.