unit ABSCompression;

interface

{$I ABSVer.inc}

{$DEFINE ZLIB}
{$DEFINE PPMD}
{$DEFINE BZIP}


uses SysUtils,Classes,Windows,

// AbsoluteDatabase units

    {$IFDEF DEBUG_LOG}
     ABSDebug,
    {$ENDIF}

     ABSTypes,
     ABSMemory,
     ABSConst,
     ABSExcept
{$IFDEF ZLIB}
     ,ABSZlib
{$ENDIF}
{$IFDEF BZIP}
    ,ABSBzip2
{$ENDIF}
{$IFDEF PPMD}
    ,ABSPPM
{$ENDIF}
;

type

 TABSCompressionAlgorithm = (acaNone,acaZLIB,acaBZIP,acaPPM);
 // SQL Names of CompressionAlgorithm
const ABSCompressionAlgorithmNames:array[0..3] of string = ('NONE', 'ZLIB','BZIP','PPM');

type
 TABSCompressionMode = Byte; // 0-9
 TABSCompressionLevel = (aclNone,aclFastest,aclNormal,aclMaximum); // 0,1,5,9

 TABSCompression = packed record
  CompressionAlgorithm: TABSCompressionAlgorithm;
  CompressionMode:      TABSCompressionMode;
  CompressionLevel:     TABSCompressionLevel;
 end;

var
 // block sizes for stream classes, LoadFromStream / SaveToStream
 DefaultTemporaryBlockSize: Integer = 100 * 1024;
 // size of maximum temporary stream that stores in memory
 DefaultTemporaryLimit: Integer = 1024 * 1024;
 DefaultMemoryBlockSize: Integer = 100 * 1024; // for memory stream
 DefaultFileBlockSize: Integer = 100 * 1024; // for memory stream
 DefaultBLOBBlockSize: Integer = 100 * 1024; // for BLOB stream
 BlockSizeForFastest: Integer = 512 * 1024; // 0.5 Mb for fastest modes
 BlockSizeForNormal: Integer = 1024 * 1024; // 1.0 Mb for normal modes
 BlockSizeForMax: Integer = 1536 * 1024; // 1.5 Mb for max modes

 AsbCompressionLastError: String;

type
 // Events
 TABSNoCancelProgressEvent = procedure(
                                        Sender:       TObject;
                                        PercentDone:  Double
                             		) of object;


////////////////////////////////////////////////////////////////////////////////
//
// TABSStream
//
////////////////////////////////////////////////////////////////////////////////


  TABSStream = class (TStream)
  private
    FCSect:             TRTLCriticalSection;
    FBlockSize:         Integer;
    FOnProgress:        TABSNoCancelProgressEvent; // progress for bulk operations
    FModified:   Boolean;
   protected
    // on progress
    procedure DoOnProgress(Progress: Double);
   public
    // lock
    procedure Lock; virtual;
    // unlock
    procedure Unlock; virtual;
    constructor Create;
    procedure SaveToStream(Stream: TABSStream);
    procedure LoadFromStreamWithPosition(
                    Stream:       TABSStream;
                    FromPosition: Int64;
                    StreamSize:   Int64
                    );
    procedure LoadFromStream(Stream: TABSStream);
    procedure LoadFromFile(const FileName: string);
    procedure SaveToFile(const FileName: string);
   public
    property BlockSize: Integer read FBlockSize write FBlockSize;
    // Progress Event
    property OnProgress: TABSNoCancelProgressEvent read FOnProgress write FOnProgress;
    property Modified: Boolean read FModified write FModified;
  end; // TABSStream


////////////////////////////////////////////////////////////////////////////////
//
// TABSMemoryStream
//
////////////////////////////////////////////////////////////////////////////////


  TABSMemoryStream = class (TABSStream)
   private
    FBuffer:        TAbsPByte;
    FBufferSize:    Integer;
    FAllocatedBufferSize: Integer;
    FPosition:      Integer;
   protected
    // sets new size of the stream
    procedure InternalSetSize(const NewSize: Int64);
    // seek
    function InternalSeek(NewPosition: Integer): Integer;
    // sets new size of the stream
    procedure SetSize(NewSize: Longint);
    {$IFDEF D6H}
      overload;
    {$ENDIF}
      override;
    {$IFDEF D6H}
    procedure SetSize(const NewSize: Int64); overload; override;
    {$ENDIF}
   public
    function Read(var Buffer; Count: Longint): Longint; override;
    function Write(const Buffer; Count: Longint): Longint; override;
    function Seek(Offset: Longint; Origin: Word): Longint;
    {$IFDEF D6H}
            overload;
    {$ENDIF}
      override;
    {$IFDEF D6H}
    function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; overload; override;
    {$ENDIF}
    constructor Create(Buffer: TAbsPByte = nil; DefaultAllocatedSize: Integer = 0);
    destructor Destroy; override;
   public
    property Buffer: TAbsPByte read FBuffer;
  end; // TABSStream


////////////////////////////////////////////////////////////////////////////////
//
// TABSFileStream
//
////////////////////////////////////////////////////////////////////////////////


  TABSFileStream = class (TABSStream)
   private
    FHandle:      Integer;
    FFileName:    string;
    FMode:        Word;
    FIsTemporary: Boolean;
   protected
    function InternalFileCreate(const FileName: string): Integer;
    // sets new size of the stream
    procedure InternalSetSize(const NewSize: Int64);
    // sets new size of the stream
    procedure SetSize(NewSize: Longint);
    {$IFDEF D6H}
      overload;
    {$ENDIF}
      override;
    {$IFDEF D6H}
    // sets new size of the stream
    procedure SetSize(const NewSize: Int64); overload; override;
    {$ENDIF}
   public
    function Read(var Buffer; Count: Longint): Longint; override;
    function Write(const Buffer; Count: Longint): Longint; override;
    function Seek(Offset: Longint; Origin: Word): Longint;
    {$IFDEF D6H}
            overload;
    {$ENDIF}
      override;
    {$IFDEF D6H}
    function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; overload; override;
    {$ENDIF}
    constructor Create(const FileName: string; Mode: Word; IsTemporary: Boolean = False);
    destructor Destroy; override;
   public
    property Handle: Integer read FHandle;
    property FileName: string read FFileName;
    property Mode: Word read FMode;
    property IsTemporary: Boolean read FIsTemporary;
  end; // TABSStream


////////////////////////////////////////////////////////////////////////////////
//
// TABSTemporaryStream
//
////////////////////////////////////////////////////////////////////////////////


  TABSTemporaryStream = class (TABSStream)
   private
    FMemoryLimit:        Integer;
    FMemoryStream:       TABSMemoryStream;
    FFileStream:         TABSFileStream;
    FFileName:           string;
    FInMemory:           Boolean;
    FDisableTempFiles:   Boolean;
   protected
    // sets new size of the stream
    procedure InternalSetSize(const NewSize: Int64);
    // sets new size of the stream
    procedure SetSize(NewSize: Longint);
    {$IFDEF D6H}
      overload;
    {$ENDIF}
      override;
    {$IFDEF D6H}
    // sets new size of the stream
    procedure SetSize(const NewSize: Int64); overload; override;
    {$ENDIF}
   public
    function Read(var Buffer; Count: Longint): Longint; override;
    function Write(const Buffer; Count: Longint): Longint; override;
    function Seek(Offset: Longint; Origin: Word): Longint;
    {$IFDEF D6H}
            overload;
    {$ENDIF}
      override;
    {$IFDEF D6H}
    function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; overload; override;
    {$ENDIF}
    constructor Create(DisableTempFiles: Boolean);
    destructor Destroy; override;
   public
    property FileStream: TABSFileStream read FFileStream;
    property MemoryStream: TABSMemoryStream read FMemoryStream;
    property FileName: string read FFileName;
    property InMemory: Boolean read FInMemory;
    property MemoryLimit: Integer read FMemoryLimit write FMemoryLimit;
  end; // TABSTemporaryStream


////////////////////////////////////////////////////////////////////////////////
//
// TABSCompressedBLOBStream
//
////////////////////////////////////////////////////////////////////////////////


  // AbsoluteDatabase BLOB stream with optional compression
  // when compression algorithm <> acaNone Write allowed
  // only ot the end of stream
  TABSCompressedBLOBStream = class (TABSStream)
   private
    FRepair:                Boolean;
    FHeaders:               TABSCompressedStreamBlockHeadersArray;
    FUncompressedSize:      Int64;
    FCompressedSize:        Int64;
    FStartPosition:         Int64;
    FCurrentHeader:         Integer;
    FPosition:              Int64;
    FCompressionMode:       TABSCompressionMode;
    FCompressionAlgorithm:  TABSCompressionAlgorithm;
    FCompressionRate:       Double;
    FCompressedStream:      TStream; // internal stream for storing compressed data
    FBLOBDescriptor:        TABSBLOBDescriptor;
   private
    // returns block size for creating a compressed blob stream with specified compression level
    function InternalGetBlockSize(CompressionMode: Byte): Integer;
    // calculates rate
    procedure CalculateRate;
    // create
    procedure InternalCreate(ToCreate: Boolean);
    // load all headers
    procedure LoadHeaders;
    // prepares buffer for writing (compresses, fills header structure, calculates crc)
    procedure PrepareBufferForWriting(
                                      InBuf:        TAbsPByte;
                                      InSize:       Integer;
                                      var OutBuf:   TAbsPByte;
                                      var Header:   TABSCompressedStreamBlockHeader
                                     );
    // load block from file, decompress it and checks crc
    procedure LoadBlock(
                        CurHeader:  Int64;
                        var OutBuf: TAbsPByte
                       );
    procedure InternalIncreaseSize(NewSize: Int64);
    procedure InternalDecreaseSize(NewSize: Int64);
    procedure InternalSetSize(NewSize: Int64);
    // internal seek
    function InternalSeek(NewPosition: Int64): Int64;
   protected
    // sets new size of the stream
    procedure SetSize(NewSize: Longint);
    {$IFDEF D6H}
      overload;
    {$ENDIF}
      override;
    {$IFDEF D6H}
    procedure SetSize(const NewSize: Int64); overload; override;
    {$ENDIF}
    // gets compressed size
    function GetCompressedSize: Int64;
    // returns compression rate (100.0 if there is no compression)
    function GetCompressionRate: Double;
   public
    // Create
    constructor Create(
						           Stream:                TStream;
                       BLOBDescriptor:        TABSBLOBDescriptor;
                       ToCreate:              Boolean = false;
                       ToRepair:              Boolean = false
                      );
    // Destroy
    destructor Destroy; override;

    function Read(var Buffer; Count: Longint): Longint; override;
   private
    // write beyond EOF
    procedure InternalWriteBeyondEOF;
    // write block
    procedure InternalWriteBlock(InBuf: TAbsPByte; InSize: Integer);
    // write prepare
    procedure InternalWritePrepare(Count, Result: Integer);
   public
    function Write(const Buffer; Count: Longint): Longint; override;
    function Seek(Offset: Longint; Origin: Word): Longint;
    {$IFDEF D6H}
     overload;
    {$ENDIF}
     override;
    {$IFDEF D6H}
    function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; overload; override;
    {$ENDIF}

   public
    property CompressedStream: TStream read FCompressedStream;
    // compression rate
    property CompressionRate: Double read GetCompressionRate;
    // compression algorithm
    property CompressionAlgorithm: TABSCompressionAlgorithm read FCompressionAlgorithm;
    // compression mode
    property CompressionMode: Byte read FCompressionMode;
    // compressed size
    property CompressedSize: Int64 read GetCompressedSize;
    property BLOBDescriptor: TABSBLOBDescriptor read FBLOBDescriptor;
  end; // TABSCompressedBLOBStream


 //------------------------------------------------------------------------------
 // compresses buffer
 // returns true if successful
 // outBuf - pointer to compressed data
 // outSize - size of compressed data
 //------------------------------------------------------------------------------
 function ABSInternalCompressBuffer(
                          CompressionAlgorithm:   TABSCompressionAlgorithm;
                          CompressionMode:        Byte;
                          InBuf:                  TAbsPByte;
                          InSize:                 Integer;
                          out OutBuf:             TAbsPByte;
                          out OutSize:            Integer
                          ): Boolean;

 // decompresse buffer
 // Outsize must be set to uncompressed size
 // return true if successful
 // OutBuf - pointer to compressed data
 // OutSize - size of compressed data
 function ABSInternalDecompressBuffer(
                          CompressionAlgorithm:   TABSCompressionAlgorithm;
                          InBuf:                  TAbsPByte;
                          InSize:                 Integer;
                          out OutBuf:             TAbsPByte;
                          out OutSize:            Integer
                          ): Boolean;

 function CRC32(CRC: LongWord; Data: Pointer; DataSize: LongWord): LongWord;
 function GetTempFileName: string;
 procedure SaveDataToStream(var Data; DataSize: Integer; Stream: TStream; ErrorCode: Integer);
 procedure LoadDataFromStream(var Data; DataSize: Integer; Stream: TStream; ErrorCode: Integer);
 procedure SetStreamPosition(Stream: TStream; NewPosition: Int64; ErrorCode: Integer);

 function GetCompressionAlgorithm(Name: string): TABSCompressionAlgorithm;

 function ABSCompressionGetLastError: String;

implementation


procedure memset(P: Pointer; B: Byte; count: Integer); cdecl;
begin
  FillChar(P^, count, B);
end;

procedure memcpy(dest, source: Pointer; count: Integer); cdecl;
begin
  Move(source^, dest^, count);
end;


function aa_malloc(count : integer) : TAbsPByte;cdecl;
begin
 result := AllocMem(count);
end;

procedure aa_free(buffer : TAbsPByte);cdecl;
begin
 FreeMem(buffer);
end;


////////////////////////////////////////////////////////////////////////////////
//
// TABSStream
//
////////////////////////////////////////////////////////////////////////////////


//------------------------------------------------------------------------------
// on progress
//------------------------------------------------------------------------------
procedure TABSStream.DoOnProgress(Progress: Double);
begin
  if Assigned(FOnProgress) then
    FOnProgress(Self,Progress);
end; // on progress


//------------------------------------------------------------------------------
// lock
//------------------------------------------------------------------------------
procedure TABSStream.Lock;
begin
 EnterCriticalSection(FCSect);
end; // Lock


//------------------------------------------------------------------------------
// unlock
//------------------------------------------------------------------------------
procedure TABSStream.Unlock;
begin
  LeaveCriticalSection(FCSect);
end; // Unlock


//------------------------------------------------------------------------------
// Create
//------------------------------------------------------------------------------
constructor TABSStream.Create;
begin
 FBlockSize := DefaultMemoryBlockSize;
 FModified := False;
end; // Create


//------------------------------------------------------------------------------
// save all data to another stream
//------------------------------------------------------------------------------
procedure TABSStream.SaveToStream(Stream: TABSStream);
var OutBytes,OldPos,OldPos1,InSize:	Int64;
    OutSize:					              Integer;
    Buf:            	              TAbsPByte;
    FProgress:      	              Extended;
    FProgressMax:   	              Extended;
    ReadBytes,WriteBytes:           Integer;
    Pos:                            Int64;
begin
 if (FBlockSize = 0) then
  raise EABSException.Create(10418,ErrorLZeroBlockSizeIsNotAllowed);
 OldPos := Position;
 OldPos1 := Stream.Position;
 Position := 0;
 OutBytes := 0;
 DoOnProgress(0);
 InSize := Size;
 Buf := AllocMem(FBlockSize);
 while OutBytes < InSize do
  begin
   if (InSize - OutBytes > FBlockSize) then
    OutSize := FBlockSize
   else
    OutSize := Size - OutBytes;

   Pos := Self.Position;
   ReadBytes := Self.Read(Buf^,OutSize);
   if (ReadBytes <> OutSize) then
    raise EABSException.Create(10146,ErrorLCannotReadFromStream,
      [Pos,Self.Size,OutSize,ReadBytes]);

   Pos := Stream.Position;
   WriteBytes := Stream.Write(Buf^,OutSize);
   if (WriteBytes <> OutSize) then
    raise EABSException.Create(10147,ErrorLCannotWriteToStream,
      [Pos,Stream.Size,OutSize,WriteBytes]);

   Inc(OutBytes,OutSize);
   FProgressMax := Size;
   FProgress := OutBytes;
   DoOnProgress(FProgress/FProgressMax*100.0);
  end;
 FreeMem(Buf);
 Position := OldPos;
 Stream.Position := OldPos1;
 DoOnProgress(100.0);
end; // SaveToStream


//------------------------------------------------------------------------------
// load all data from another stream
//------------------------------------------------------------------------------
procedure TABSStream.LoadFromStream(Stream: TABSStream);
begin
 LoadFromStreamWithPosition(Stream,0,Stream.Size);
end; // LoadFromStream


//------------------------------------------------------------------------------
// load from stream
//------------------------------------------------------------------------------
procedure TABSStream.LoadFromStreamWithPosition(
                    Stream:       TABSStream;
                    FromPosition: Int64;
                    StreamSize:   Int64
                    );
var OldPos,OldPos1:	                Int64;
    OutSize:					              Integer;
    Buf:            	              TAbsPByte;
    FProgress:      	              Extended;
    FProgressMax:   	              Extended;
    ReadBytes,WriteBytes:           Integer;
    Pos:                            Int64;
begin
 if (FBlockSize = 0) then
  raise EABSException.Create(10419,ErrorLZeroBlockSizeIsNotAllowed);
 OldPos := Position;
 OldPos1 := Stream.Position;
 Stream.Position := FromPosition;
 Size := 0;
 Position := 0;
 DoOnProgress(0);
 Buf := AllocMem(FBlockSize);
 while (Stream.Position < FromPosition + StreamSize) do
  begin
   if ((FromPosition + StreamSize) - Stream.Position > FBlockSize) then
    OutSize := FBlockSize
   else
    OutSize := (FromPosition + StreamSize) - Stream.Position;

   Pos := Stream.Position;
   ReadBytes := Stream.Read(Buf^,OutSize);
   if (ReadBytes <> OutSize) then
    raise EABSException.Create(10148,ErrorLCannotReadFromStream,
      [Pos,Stream.Size,OutSize,ReadBytes]);

   Pos := Self.Position;
   WriteBytes := Self.Write(Buf^,OutSize);
   if (WriteBytes <> OutSize) then
    raise EABSException.Create(10149,ErrorLCannotWriteToStream,
      [Pos,Self.Size,OutSize,WriteBytes]);

   FProgressMax := Stream.Size;
   FProgress := Stream.Position;
   DoOnProgress(FProgress/FProgressMax*100.0);
  end;
 FreeMem(buf);
 Position := OldPos;
 Stream.Position := OldPos1;
 DoOnProgress(100.0);
end;


//------------------------------------------------------------------------------
// load all data from file
//------------------------------------------------------------------------------
procedure TABSStream.LoadFromFile(const FileName: string);
var
  Stream: TABSStream;
begin
  Stream := TABSFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
  try
    LoadFromStream(Stream);
  finally
    Stream.Free;
  end;
end; // LoadFromFile


//------------------------------------------------------------------------------
// save all data to file
//------------------------------------------------------------------------------
procedure TABSStream.SaveToFile(const FileName: string);
var
  Stream: TABSStream;
begin
  Stream := TABSFileStream.Create(FileName, fmCreate);
  try
    SaveToStream(Stream);
  finally
    Stream.Free;
  end;
end; // SaveToFile


////////////////////////////////////////////////////////////////////////////////
//
// TABSMemoryStream
//
////////////////////////////////////////////////////////////////////////////////


//------------------------------------------------------------------------------
// sets new size of the stream
//------------------------------------------------------------------------------
procedure TABSMemoryStream.InternalSetSize(const NewSize: Int64);
begin
 if (NewSize <= 0) then
  begin
   FBufferSize := 0;
   FAllocatedBufferSize := 0;
   if (FBuffer <> nil) then
     MemoryManager.FreeAndNillMem(FBuffer);
  end
 else
 if (FAllocatedBufferSize = 0) then
  begin
   FBuffer := MemoryManager.GetMem(NewSize);
   FBufferSize := NewSize;
   FAllocatedBufferSize := NewSize;
  end
 else
  begin
   FBufferSize := NewSize;
   if (FBufferSize > FAllocatedBufferSize) then
     begin
       FAllocatedBufferSize := FBufferSize * 2;
       MemoryManager.ReallocMem(FBuffer,FAllocatedBufferSize);
     end;
  end;
 if (FPosition > FBufferSize) then
  FPosition := FBufferSize; 
end; // InternalSetSize


//------------------------------------------------------------------------------
// seek
//------------------------------------------------------------------------------
function TABSMemoryStream.InternalSeek(NewPosition: Integer): Integer;
begin
 FPosition := NewPosition;
 result := FPosition;
end; // InternalSeek


//------------------------------------------------------------------------------
// sets new size of the stream
//------------------------------------------------------------------------------
procedure TABSMemoryStream.SetSize(NewSize: Longint);
begin
 InternalSetSize(NewSize);
end; // SetSize


{$IFDEF D6H}
//------------------------------------------------------------------------------
// sets new size of the stream
//------------------------------------------------------------------------------
procedure TABSMemoryStream.SetSize(const NewSize: Int64);
begin
 InternalSetSize(NewSize);
end; // SetSize
{$ENDIF}


//------------------------------------------------------------------------------
// set size of compressed stream
//------------------------------------------------------------------------------
function TABSMemoryStream.Read(var Buffer; Count: Longint): Longint;
var NewCount: Integer;
begin
 Result := 0;
 if ((FPosition < FBufferSize) and (Count > 0)) then
  begin
   // count more than size of the buffer minus position
   if (Count > FBufferSize - FPosition) then
    NewCount := FBufferSize - FPosition
   else
    NewCount := Count;
   Move(TAbsPByte(FBuffer + FPosition)^,Buffer,NewCount);
   Result := NewCount;
   Inc(FPosition,NewCount);
  end;
end; // Read


//------------------------------------------------------------------------------
// set size of compressed stream
//------------------------------------------------------------------------------
function TABSMemoryStream.Write(const Buffer; Count: Longint): Longint;
begin
 if (FBufferSize < FPosition + Count) then
  InternalSetSize(FPosition + Count);
 Result := Count;
 System.Move(Buffer,TAbsPByte(FBuffer + FPosition)^,Count);
 Inc(FPosition,Count);
end; // Write


//------------------------------------------------------------------------------
// set size of compressed stream
//------------------------------------------------------------------------------
function TABSMemoryStream.Seek(Offset: Longint; Origin: Word): Longint;
var NewPosition: Integer;
begin
 NewPosition := FPosition;
 case (Origin) of
  soFromBeginning:
    NewPosition := Offset;
  soFromCurrent:
    NewPosition := Integer(FPosition) + Offset;
  soFromEnd:
    NewPosition := Integer(FBufferSize) + Offset;
 end;
 Result := InternalSeek(NewPosition);
end; // Seek


{$IFDEF D6H}
//------------------------------------------------------------------------------
// set size of compressed stream
//------------------------------------------------------------------------------
function TABSMemoryStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
var NewPosition: Integer;
begin
 NewPosition := 0;
 case (Origin) of
  soBeginning:
    NewPosition := Offset;
  soCurrent:
    NewPosition := FPosition + Offset;
  soEnd:
    NewPosition := FBufferSize + Offset;
 end;
 Result := InternalSeek(NewPosition);
end; // Seek
{$ENDIF}


//------------------------------------------------------------------------------
// set size of compressed stream
//------------------------------------------------------------------------------
constructor TABSMemoryStream.Create(Buffer: TAbsPByte = nil; DefaultAllocatedSize: Integer = 0);
begin
 FBuffer := nil;
 FBufferSize := 0;
 FAllocatedBufferSize := 0;
 if (Buffer <> nil) then
  begin
   FBuffer := Buffer;
   FBufferSize := MemoryManager.GetMemoryBufferSize(Buffer);
   FAllocatedBufferSize := FBufferSize;
  end
 else
   if (DefaultAllocatedSize <> 0) then
     begin
       FBuffer := MemoryManager.GetMem(DefaultAllocatedSize);
       FAllocatedBufferSize := DefaultAllocatedSize;
     end;
 FPosition := 0;
 inherited Create;
end; // Create


//------------------------------------------------------------------------------
// set size of compressed stream
//------------------------------------------------------------------------------
destructor TABSMemoryStream.Destroy;
begin
 InternalSetSize(0);
 inherited;
end; // Destroy


////////////////////////////////////////////////////////////////////////////////
//
// TABSFileStream
//
////////////////////////////////////////////////////////////////////////////////


//------------------------------------------------------------------------------
// InternalFileCreate
//------------------------------------------------------------------------------
function TABSFileStream.InternalFileCreate(const FileName: string): Integer;
var
  FlagsAndAttributes: DWORD;
begin
  if IsTemporary then
    FlagsAndAttributes := FILE_ATTRIBUTE_TEMPORARY or FILE_FLAG_DELETE_ON_CLOSE
  else
    FlagsAndAttributes := FILE_ATTRIBUTE_NORMAL;

   //Result := FileCreate(FileName);
  Result := Integer(CreateFile(PChar(FileName), GENERIC_READ or GENERIC_WRITE,
                               0, nil, CREATE_ALWAYS, FlagsAndAttributes,
                               0));
end;//InternalFileCreate

//------------------------------------------------------------------------------
// sets new size of the stream
//------------------------------------------------------------------------------
procedure TABSFileStream.InternalSetSize(const NewSize: Int64);
var OldPos: Int64;
begin
 OldPos := Position;
 Position := NewSize;
 Win32Check(SetEndOfFile(FHandle));
 if (OldPos > NewSize) then
  Position := NewSize
 else
  Position := OldPos;
end; // InternalSetSize


//------------------------------------------------------------------------------
// sets new size of the stream
//------------------------------------------------------------------------------
procedure TABSFileStream.SetSize(NewSize: Longint);
begin
 InternalSetSize(NewSize);
end; // SetSize


{$IFDEF D6H}
//------------------------------------------------------------------------------
// sets new size of the stream
//------------------------------------------------------------------------------
procedure TABSFileStream.SetSize(const NewSize: Int64);
begin
 InternalSetSize(NewSize);
end; // SetSize
{$ENDIF}


//------------------------------------------------------------------------------
// read
//------------------------------------------------------------------------------
function TABSFileStream.Read(var Buffer; Count: Longint): Longint;
begin
 Result := FileRead(FHandle, Buffer, Count);
 if (Result = -1) then
  Result := 0;
end; // SetSize


//------------------------------------------------------------------------------
// write
//------------------------------------------------------------------------------
function TABSFileStream.Write(const Buffer; Count: Longint): Longint;
begin
 Result := FileWrite(FHandle, Buffer, Count);
 if (Result = -1) then
  Result := 0;
end; // SetSize


//------------------------------------------------------------------------------
// seek
//------------------------------------------------------------------------------
function TABSFileStream.Seek(Offset: Longint; Origin: Word): Longint;
begin
 Result := FileSeek(FHandle, Offset, Origin);
end; // SetSize


{$IFDEF D6H}
//------------------------------------------------------------------------------
// seek
//------------------------------------------------------------------------------
function TABSFileStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
begin
 Result := FileSeek(FHandle, Offset, Ord(Origin));
end; // Seek
{$ENDIF}


//------------------------------------------------------------------------------
// Create
//------------------------------------------------------------------------------
constructor TABSFileStream.Create(const FileName: string; Mode: Word; IsTemporary: Boolean = False);
begin
 FBlockSize := DefaultFileBlockSize;
 FMode := Mode;
 FFileName := FileName;
 FIsTemporary := IsTemporary; 
 if (Mode = fmCreate) then
  begin
   //FHandle := FileCreate(FileName);
     FHandle := InternalFileCreate(FileName);
   if (FHandle < 0) then
    raise EABSException.Create(10104,ErrorLCannotCreateFile,[FileName]);
  end
 else
  begin
   FHandle := FileOpen(FileName,Mode);
   if (FHandle < 0) then
    raise EABSException.Create(10105,ErrorLCannotOpenFile,[FileName,Mode]);
  end;
end; // Create


//------------------------------------------------------------------------------
// Destroy
//------------------------------------------------------------------------------
destructor TABSFileStream.Destroy;
begin
 if FHandle >= 0 then
  FileClose(FHandle);
 inherited;
end; // Destroy




////////////////////////////////////////////////////////////////////////////////
//
// TABSTemporaryStream
//
////////////////////////////////////////////////////////////////////////////////


//------------------------------------------------------------------------------
// sets new size of the stream
//------------------------------------------------------------------------------
procedure TABSTemporaryStream.InternalSetSize(const NewSize: Int64);
begin
 if (FInMemory) then
  FMemoryStream.Size := NewSize
 else
  FFileStream.Size := NewSize;
end; // InternalSetSize


//------------------------------------------------------------------------------
// sets new size of the stream
//------------------------------------------------------------------------------
procedure TABSTemporaryStream.SetSize(NewSize: Longint);
begin
 InternalSetSize(NewSize);
end; // SetSize


{$IFDEF D6H}
//------------------------------------------------------------------------------
// sets new size of the stream
//------------------------------------------------------------------------------
procedure TABSTemporaryStream.SetSize(const NewSize: Int64);
begin
 InternalSetSize(NewSize);
end; // SetSize
{$ENDIF}


//------------------------------------------------------------------------------
// read
//------------------------------------------------------------------------------
function TABSTemporaryStream.Read(var Buffer; Count: Longint): Longint;
begin
 if (FInMemory) then
  Result := FMemoryStream.Read(Buffer,Count)
 else
  Result := FFileStream.Read(Buffer,Count);
end; // Read


//------------------------------------------------------------------------------
// write
//------------------------------------------------------------------------------
function TABSTemporaryStream.Write(const Buffer; Count: Longint): Longint;
begin
 if (FInMemory) then
  begin
    if (FDisableTempFiles or (FMemoryStream.Size + Int64(Count) <= FMemoryLimit)) then
     Result := FMemoryStream.Write(Buffer,Count)
    else
     begin
      FFileName := GetTempFileName;
      FFileStream := TABSFileStream.Create(FFileName,fmCreate,True);
      FFileStream.LoadFromStream(FMemoryStream);
      FFileStream.Position := FMemoryStream.Position;
      FMemoryStream.Free;
      FMemoryStream := nil;
      FInMemory := false;
      Result := FFileStream.Write(Buffer,Count);
     end;
  end
 else
  begin
    Result := FFileStream.Write(Buffer,Count);
  end;
end; // Write


//------------------------------------------------------------------------------
// seek
//------------------------------------------------------------------------------
function TABSTemporaryStream.Seek(Offset: Longint; Origin: Word): Longint;
begin
 if (FInMemory) then
  Result := FMemoryStream.Seek(Offset,Origin)
 else
  Result := FFileStream.Seek(Offset,Origin);
end; // Seek


{$IFDEF D6H}
//------------------------------------------------------------------------------
// seek
//------------------------------------------------------------------------------
function TABSTemporaryStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
begin
 if (FInMemory) then
  Result := FMemoryStream.Seek(Offset,Origin)
 else
  Result := FFileStream.Seek(Offset,Origin);
end; // Seek
{$ENDIF}


//------------------------------------------------------------------------------
// Create
//------------------------------------------------------------------------------
constructor TABSTemporaryStream.Create(DisableTempFiles: Boolean);
begin
 FDisableTempFiles := DisableTempFiles;
 FBlockSize := DefaultTemporaryBlockSize;
 FMemoryLimit := DefaultTemporaryLimit;
 FFileName := '';
 FInMemory := True;
 FMemoryStream := TABSMemoryStream.Create;
 FFileStream := nil;
end; // Create


//------------------------------------------------------------------------------
// Destroy
//------------------------------------------------------------------------------
destructor TABSTemporaryStream.Destroy;
begin
 if (FMemoryStream <> nil) then
  FMemoryStream.Free;
 if (FFileStream <> nil) then
  FFileStream.Free;
 inherited;
end; // Destroy


////////////////////////////////////////////////////////////////////////////////
//
// TABSCompressedBLOBStream
//
////////////////////////////////////////////////////////////////////////////////


//------------------------------------------------------------------------------
// returns block size for creating a compressed blob stream with specified compression level
//------------------------------------------------------------------------------
function TABSCompressedBLOBStream.InternalGetBlockSize(CompressionMode: Byte): Integer;
begin
 if (CompressionMode = 0) then
  Result := DefaultBLOBBlockSize
 else
 if (CompressionMode <= 3) then
  Result := BlockSizeForFastest
 else
 if (CompressionMode <= 6) then
  Result := BlockSizeForNormal
 else
  Result := BlockSizeForMax;
end; // InternalGetBlockSize


//------------------------------------------------------------------------------
// set size of compressed stream
//------------------------------------------------------------------------------
procedure TABSCompressedBLOBStream.CalculateRate;
var i: 		Integer;
    f,f1:	Extended;
begin
 FCompressedSize := 0;
 if (FCompressionAlgorithm = acaNone) then
  begin
   FCompressedSize := FUncompressedSize;
   FCompressionRate := 0;
  end
 else
 if (FUncompressedSize <= 0) then
  begin
   FCompressedSize := FUncompressedSize;
   FCompressionRate := 0;
  end
 else
  begin
   for i := 0 to FHeaders.ItemCount-1 do
    Inc(FCompressedSize,FHeaders.Items[i].CompressedSize);
   f1 := FUncompressedSize;
   f := FCompressedSize;
   FCompressionRate := (1 - f / f1) * 100.0;
  end;
end; //CalculateRate


//------------------------------------------------------------------------------
// create
//------------------------------------------------------------------------------
procedure TABSCompressedBLOBStream.InternalCreate(ToCreate: Boolean);
begin
 // compression
 FHeaders := TABSCompressedStreamBlockHeadersArray.Create;
 if (ToCreate) then
  begin
   if (FBLOBDescriptor.BlockSize = 0) then
    FBLOBDescriptor.BlockSize := InternalGetBlockSize(FCompressionMode);
   FBLOBDescriptor.NumBlocks := 0;
  end; // compression
 FBlockSize := FBLOBDescriptor.BlockSize;
 FUncompressedSize := 0;
 FCompressedSize := 0;
 LoadHeaders; // loading headers
 FCurrentHeader := 0;
 FPosition := 0;
end; // InternalCreate


//------------------------------------------------------------------------------
// load  block headers
//------------------------------------------------------------------------------
procedure TABSCompressedBLOBStream.LoadHeaders;
var CHeader:    TABSCompressedStreamBlockHeader;
    Pos,NewPos: Int64;
    i,OldPos:   Int64;
begin
 FCompressedSize := 0;
 FUncompressedSize := 0;
 // always restore StartPosition as blob stream can be stored at the middle of the file
 if (FCompressedStream.Position <> FBLOBDescriptor.StartPosition) then
  raise EABSException.Create(10080,ErrorLCannotSetPosition,
    [FBLOBDescriptor.StartPosition,FCompressedStream.Position,
     FCompressedStream.Size]);
 FHeaders.SetSize(0);

 i := 0;
 while (i < FBLOBDescriptor.NumBlocks) do
  begin
   // store position of the current block
   Pos := FCompressedStream.Position;
   // check if we can read block header from current position
   if (
       (FCompressedStream.Size - FCompressedStream.Position) <
       sizeof(TABSCompressedStreamBlockHeader)
      ) then
    begin
     // stream too small
     if (FRepair) then
      begin
       // cut compressed file (end of file was cut)
       // repair this error
       FBLOBDescriptor.NumBlocks := i;
       FHeaders.SetSize(i);
       break;
      end
     else
      raise EABSException.Create(10082,ErrorLStreamSizeTooSmall,
        [FCompressedStream.Size,
        FCompressedStream.Position + sizeof(TABSCompressedStreamBlockHeader)]);
    end; // check if we can read block header from current position
   FCompressedStream.ReadBuffer(CHeader,sizeof(TABSCompressedStreamBlockHeader));
   Inc(FUncompressedSize,CHeader.UncompressedSize);
   Inc(FCompressedSize,CHeader.CompressedSize);
   FHeaders.AppendItem(CHeader,Pos);
// Commented By Leo Martin - changed from absolute to relative offset
//   NewPos := CHeader.OffsetToNextHeader;
   NewPos := Pos + CHeader.OffsetToNextHeader;
   OldPos := FCompressedStream.Position;
   FCompressedStream.Position := NewPos;
   if (FCompressedStream.Position <> NewPos) then
    raise EABSException.Create(10083,ErrorLCannotSetPosition,
      [NewPos,OldPos,FCompressedStream.Size]);
   Inc(i);
  end; //  while (i < FBLOBDescriptor.NumBlocks)
end; // LoadHeaders


//------------------------------------------------------------------------------
// prepare buffer for writing
//------------------------------------------------------------------------------
procedure TABSCompressedBLOBStream.PrepareBufferForWriting
                                     (
                                      InBuf:        TAbsPByte;
                                      InSize:       Integer;
                                      var OutBuf:   TAbsPByte;
                                      var Header:   TABSCompressedStreamBlockHeader
                                     );
begin
  OutBuf := nil;
  Header.UncompressedSize := inSize;
  Header.Crc32 := CRC32(0,InBuf,InSize);
  if (not ABSInternalCompressBuffer(FCompressionAlgorithm,FCompressionMode,
          InBuf,InSize,OutBuf,Header.CompressedSize)) then
   begin
    if (OutBuf <> nil) then
     FreeMem(OutBuf);
    raise EABSException.Create(10085,ErrorLCompressBufferFailed,
      [Byte(FCompressionAlgorithm),FCompressionMode,InSize,Header.CompressedSize]);
   end;
end; //PrepareBuffer;


//------------------------------------------------------------------------------
// load block from file, decompress it and checks crc
//------------------------------------------------------------------------------
procedure TABSCompressedBLOBStream.LoadBlock(
                                      CurHeader:  Int64;
                                      var OutBuf: TAbsPByte
                                            );

var UncompSize,CompSize:  Integer;
    BytesRead:            Integer;
    CheckSum:             LongWord;
    InBuf:                TAbsPByte;
begin
  CompSize := FHeaders.Items[curHeader].CompressedSize;
  InBuf := MemoryManager.GetMem(CompSize);
  try
   FCompressedStream.Position := FHeaders.Positions[CurHeader] +
    sizeof(TABSCompressedStreamBlockHeader);

   BytesRead := FCompressedStream.Read(InBuf^,CompSize);
   if (BytesRead <> CompSize) then
    begin
     raise EABSException.Create(10086,ErrorLCannotReadFromStream,
      [FHeaders.Positions[CurHeader] + sizeof(TABSCompressedStreamBlockHeader),
      FCompressedStream.Size,CompSize,BytesRead]);
    end;

   UncompSize := FHeaders.Items[CurHeader].UncompressedSize;
   if (not ABSInternalDecompressBuffer(FCompressionalgorithm,
           InBuf,CompSize,OutBuf,UncompSize)) then
    begin
     // decompression error
     raise EABSException.Create(10087,ErrorLDecompressBufferFailed,
      [Byte(FCompressionAlgorithm),CompSize,UncompSize]);
    end;
   if (FHeaders.Items[CurHeader].UncompressedSize <> UncompSize) then
    begin
     FreeMem(outBuf);
     OutBuf := nil;
     raise EABSException.Create(10088,ErrorLDecompressBufferFailedInvalidSize,
      [UncompSize,FHeaders.Items[CurHeader].UncompressedSize]);
    end;
   // check crc
   CheckSum := CRC32(0,OutBuf,UncompSize);
   if (FHeaders.Items[CurHeader].Crc32 <> CheckSum) then
    begin
    // decompression crc error
     FreeMem(outBuf);
     OutBuf := nil;
     raise EABSException.Create(10089,ErrorLDecompressBufferFailedInvalidCRC,
      [CheckSum,FHeaders.Items[CurHeader].Crc32]);
    end;
  finally
   MemoryManager.FreeAndNillMem(inBuf);
  end;
end; // LoadBlock


//------------------------------------------------------------------------------
// set size of compressed stream
//------------------------------------------------------------------------------
procedure TABSCompressedBLOBStream.InternalIncreaseSize(NewSize: Int64);
var Buf:                  TAbsPByte;
    ExtensionSize,Count:  Int64;
    OldPos:               Int64;
    WriteBytes,WriteSize: Integer;
begin
 Position := FUncompressedSize;
 ExtensionSize := NewSize - FUncompressedSize;
 if (ExtensionSize <= 0) then
  raise EABSException.Create(10098,ErrorLInvalidExtensionSize,
    [NewSize,FUncompressedSize,ExtensionSize]);
 Buf := MemoryManager.AllocMem(FBlockSize);
 try
  Count := 0;
  while (Count < ExtensionSize) do
   begin
    if ((ExtensionSize - Count) < FBlockSize) then
     WriteSize := ExtensionSize - Count
    else
     WriteSize := FBlockSize;
    OldPos := Self.Position;
    // write empty block
    WriteBytes := Self.Write(Buf^,WriteSize);
    if (WriteBytes <> WriteSize) then
      raise EABSException.Create(10097,ErrorLCannotWriteToStream,
        [OldPos,Self.Size,WriteSize,WriteBytes]);
    Inc(Count,WriteSize);
   end; // while
 finally
  MemoryManager.FreeAndNillMem(Buf);
 end;
end; // InternalIncreaseSize


//------------------------------------------------------------------------------
// set size of compressed stream
//------------------------------------------------------------------------------
procedure TABSCompressedBLOBStream.InternalDecreaseSize(NewSize: Int64);
var Buf,OutBuf:           TAbsPByte;
    ExtensionSize,OldPos: Int64;
    CurHdr,NumBlocks:     Int64;
    BytesWrite:           Integer;
begin
  CurHdr := NewSize div FBlockSize;
  NumBlocks := CurHdr;
  ExtensionSize := NewSize mod FBlockSize;
  OutBuf := nil;
  Buf := nil;
  try
    if (ExtensionSize > 0) then
     begin
      Inc(NumBlocks);
      LoadBlock(CurHdr,Buf);
      try
       PrepareBufferForWriting(Buf,ExtensionSize,OutBuf,FHeaders.Items[CurHdr]);
      finally
       if (Buf <> nil) then
        MemoryManager.FreeAndNillMem(Buf);
      end;
     end;
    FBLOBDescriptor.NumBlocks := NumBlocks;
    FHeaders.SetSize(NumBlocks);
    if (ExtensionSize > 0) then
     begin
      FCompressedStream.Size := FHeaders.Positions[CurHdr];
      if (FCompressedStream.Size <> FHeaders.Positions[CurHdr]) then
       raise EABSException.Create(10093,ErrorLInvalidStreamSize,
        [FCompressedStream.Size,FHeaders.Positions[CurHdr]]);
      FCompressedStream.Position := FCompressedStream.Size;
      if (FCompressedStream.Position <> FCompressedStream.Size) then
       raise EABSException.Create(10094,ErrorLCannotSetPosition,
        [FCompressedStream.Size,FCompressedStream.Position,
         FCompressedStream.Size]);
      FHeaders.Items[CurHdr].OffsetToNextHeader := FCompressedStream.Position +
        Int64(FHeaders.Items[CurHdr].CompressedSize) +
        sizeof(TABSCompressedStreamBlockHeader);
      FHeaders.Positions[CurHdr] := FCompressedStream.Position;
      OldPos := FCompressedStream.Position;
      BytesWrite := FCompressedStream.Write(FHeaders.Items[curHdr],
          sizeof(TABSCompressedStreamBlockHeader));
      if (BytesWrite <> sizeof(TABSCompressedStreamBlockHeader)) then
       raise EABSException.Create(10095,ErrorLWriteToStream,
        [OldPos,FCompressedStream.Size,sizeof(TABSCompressedStreamBlockHeader),BytesWrite]);
      OldPos := FCompressedStream.Position;
      BytesWrite := FCompressedStream.Write(OutBuf^,FHeaders.Items[CurHdr].CompressedSize);
      if (BytesWrite <> sizeof(TABSCompressedStreamBlockHeader)) then
       raise EABSException.Create(10096,ErrorLWriteToStream,
        [OldPos,FCompressedStream.Size,sizeof(TABSCompressedStreamBlockHeader),BytesWrite]);
     end;
  finally
   if (OutBuf <> nil) then
    MemoryManager.FreeAndNillMem(OutBuf);
  end;
end; // InternalDecreaseSize


//------------------------------------------------------------------------------
// set size of compressed stream
//------------------------------------------------------------------------------
procedure TABSCompressedBLOBStream.InternalSetSize(NewSize: Int64);
begin
 if (FCompressionAlgorithm = acaNone) then
  begin
   FCompressedStream.Size := FStartPosition + NewSize;
  end
 else
 if (NewSize > FUncompressedSize) then
  begin
    // go to last block
    InternalIncreaseSize(NewSize);
  end // NewSize > FUncompressedSize
 else
 if (NewSize < FUncompressedSize) then
  begin
    InternalDecreaseSize(NewSize);
  end; // NewSize < FUncompressedSize
 FUncompressedSize := NewSize;
 FBLOBDescriptor.UncompressedSize := NewSize;
 CalculateRate;
 if (FPosition > FUncompressedSize) then
  Position := FUncompressedSize;
end; // InternalSetSize


//------------------------------------------------------------------------------
// internal seek
//------------------------------------------------------------------------------
function TABSCompressedBLOBStream.InternalSeek(NewPosition: Int64): Int64;
begin
 if (FCompressionAlgorithm = acaNone) then
  begin
   FCompressedStream.Position := FStartPosition + NewPosition;
   Result := FCompressedStream.Position - FStartPosition;
   FPosition := Result;
  end // no compression
 else
  begin
   // compression
   FPosition := NewPosition;
   if (FPosition <= 0) then
    begin
     FPosition := 0;
     FCurrentHeader := 0;
    end
   else
    begin
     if (FUncompressedSize = 0) then
      FCurrentHeader := 0
     else
      FCurrentHeader := FHeaders.FindPosition(FPosition);
    end;
   Result := FPosition;
  end; // compression
end; // InternalSeek


//------------------------------------------------------------------------------
// set size of compressed stream
//------------------------------------------------------------------------------
procedure TABSCompressedBLOBStream.SetSize(NewSize: Longint);
begin
 InternalSetSize(NewSize);
end; // SetSize


//------------------------------------------------------------------------------
// set size of compressed stream
//------------------------------------------------------------------------------
{$IFDEF D6H}
procedure TABSCompressedBLOBStream.SetSize(const NewSize: Int64);
begin
 InternalSetSize(NewSize);
end; // SetSize
{$ENDIF}


//------------------------------------------------------------------------------
// gets compressed size
//------------------------------------------------------------------------------
function TABSCompressedBLOBStream.GetCompressedSize: Int64;
begin
 Result := FCompressedStream.Size;
end; // GetCompressedSize


//------------------------------------------------------------------------------
// returns compression rate (100.0 if there is no compression)
//------------------------------------------------------------------------------
function TABSCompressedBLOBStream.GetCompressionRate: Double;
begin
 CalculateRate;
 Result := FCompressionRate;
end; // GetCompressionRate


//------------------------------------------------------------------------------
// Create
//------------------------------------------------------------------------------
constructor TABSCompressedBLOBStream.Create(
						           Stream:                TStream;
                       BLOBDescriptor:        TABSBLOBDescriptor;
                       ToCreate:              Boolean = false;
                       ToRepair:              Boolean = false
                      );
begin
 FCompressedStream := Stream;
 FHeaders := nil;
 FRepair := ToRepair;
 FBLOBDescriptor := BLOBDescriptor;
 FCompressionAlgorithm := TABSCompressionAlgorithm(
    FBLOBDescriptor.CompressionAlgorithm);
 FCompressionMode := FBLOBDescriptor.CompressionMode;
 FCompressedStream.Position := FBLOBDescriptor.StartPosition;
 if (FCompressedStream.Position <> FBLOBDescriptor.StartPosition) then
  raise EABSException.Create(10103,ErrorLCannotSetPosition,
    [FBLOBDescriptor.StartPosition,FCompressedStream.Position,
    FCompressedStream.Size]);
 FBlockSize := BLOBDescriptor.BlockSize;
 FStartPosition := BLOBDescriptor.StartPosition;
 if (FCompressionAlgorithm = acaNone) then
  begin
   // no compression
   // try to set start position in source "compressed" stream
   if (FCompressedStream.Position <> FBLOBDescriptor.StartPosition) then
    raise EABSException.Create(10079,ErrorLCannotSetPosition,
      [FBLOBDescriptor.StartPosition,FCompressedStream.Position,
       FCompressedStream.Size]);
   // default block size
   if (ToCreate) then
    begin
     // create new stream
     FCompressedSize := 0;
     FUncompressedSize := 0;
    end
   else
    begin
     // open existing stream
     FUncompressedSize := FBLOBDescriptor.UncompressedSize;
     FCompressedSize := FUncompressedSize;
     // check if stream size is too small
     if (not FRepair) then
      if (FCompressedStream.Size - FCompressedStream.Position <
          FUncompressedSize) then
        raise EABSException.Create(10081,ErrorLStreamSizeTooSmall,
          [FCompressedStream.Size,
          (FUncompressedSize + FCompressedStream.Position)]);

    end;
  end // no compression
 else
  // create compressed stream, load headers
  InternalCreate(ToCreate);
end; // Create


//------------------------------------------------------------------------------
// Destroy
//------------------------------------------------------------------------------
destructor TABSCompressedBLOBStream.Destroy;
begin
 if (FHeaders <> nil) then
  FHeaders.Free;
 FHeaders := nil;
 inherited;
end; // Destroy


//------------------------------------------------------------------------------
// read from compressed stream
//------------------------------------------------------------------------------
function TABSCompressedBLOBStream.Read(var Buffer; Count: Longint): Longint;
var ReadSize:   Int64;
    OutBuf:     TAbsPByte;
begin
 if (FCompressionAlgorithm = acaNone) then
  begin
   Result := FCompressedStream.Read(Buffer,Count);
   FPosition := FCompressedStream.Position - FStartPosition;
  end // no compression
 else
  begin
   Result := 0;
   if ((Count > 0) and (FPosition >= 0) and (FPosition < FUncompressedSize)) then
    begin
     FCurrentHeader := FPosition div FBlockSize;
     while ((FPosition < FUncompressedSize) and (Result < Count)) do
      begin
       LoadBlock(FCurrentHeader,OutBuf);
       // read from current position to the end of the block
       ReadSize := FBlockSize -
        ((FPosition + FBlockSize) mod FBlockSize);
       // if we Result + ReadSize exceeds Count read only Count - Result
       if (Result + ReadSize > Count) then
        ReadSize := Count - Result;
       // reading only till EOF
       if (FPosition + ReadSize >= FUncompressedSize) then
        ReadSize := FUncompressedSize - FPosition;
       if (ReadSize <= 0) then
        raise EABSException.Create(10090,
          ErrorLCannotReadFromStreamInvalidReadSize,[ReadSize]);
       // move data from decompressed buffer to Buffer
       Move(TAbsPByte(OutBuf + ((FPosition + FBlockSize) mod FBlockSize))^,
        TAbsPByte(TAbsPByte(@Buffer) + Result)^,ReadSize);
       FreeMem(OutBuf);
       Inc(Result,ReadSize);
       if (Result < Count) then
        Inc(FCurrentHeader);
       Inc(FPosition,ReadSize);
      end; // reading loop
    end; // FPosition < FUncompressedSize
  end; // compression
end; // Read


//------------------------------------------------------------------------------
// write beyond EOF
//------------------------------------------------------------------------------
procedure TABSCompressedBLOBStream.InternalWriteBeyondEOF;
var OldPos: Int64;
begin
 OldPos := FPosition;
 Self.Position := 0;
 Self.SetSize(OldPos);
 Self.Position := OldPos;
 if (Self.Position <> OldPos) then
  raise EABSException.Create(10091,ErrorLCannotSetPosition,
    [OldPos,FPosition,FUncompressedSize]);
 if (FUncompressedSize <> OldPos) then
  raise EABSException.Create(10092,ErrorLInvalidStreamSize,
    [FUncompressedSize,OldPos]);
end; // InternalWriteBeyondEOF


//------------------------------------------------------------------------------
// write block
//------------------------------------------------------------------------------
procedure TABSCompressedBLOBStream.InternalWriteBlock(InBuf: TAbsPByte; InSize: Integer);
var OutBuf:         TAbsPByte;
    WriteBytes:     Integer;
    OldPos:         Int64;
begin
  PrepareBufferForWriting(InBuf,InSize,OutBuf,
    FHeaders.Items[FCurrentHeader]);
  try
// Commented By Leo Martin - changed from absolute to relative offset
    FHeaders.Items[FCurrentHeader].OffsetToNextHeader :=
//      FHeaders.Positions[FCurrentHeader] +
        sizeof(TABSCompressedStreamBlockHeader) +
        FHeaders.Items[FCurrentHeader].CompressedSize;

    FCompressedStream.Position := FHeaders.Positions[FCurrentHeader];
    if (FCompressedStream.Position <> FHeaders.Positions[FCurrentHeader]) then
     raise EABSException.Create(10099,ErrorLCannotSetPosition,
      [FHeaders.Positions[FCurrentHeader],
        FCompressedStream.Position,FCompressedStream.Size]);

    OldPos := FCompressedStream.Position;
    WriteBytes := FCompressedStream.Write(FHeaders.Items[FCurrentHeader],
      sizeof(TABSCompressedStreamBlockHeader));
    if (WriteBytes <> sizeof(TABSCompressedStreamBlockHeader)) then
     raise EABSException.Create(10100,ErrorLCannotWriteToStream,
      [OldPos,FCompressedStream.Size,sizeof(TABSCompressedStreamBlockHeader),WriteBytes]);

    OldPos := FCompressedStream.Position;
    WriteBytes := FCompressedStream.Write(OutBuf^,
      FHeaders.Items[FCurrentHeader].CompressedSize);
    if (WriteBytes <> FHeaders.Items[FCurrentHeader].CompressedSize) then
     raise EABSException.Create(10101,ErrorLCannotWriteToStream,
      [OldPos,FCompressedStream.Size,FHeaders.Items[FCurrentHeader].CompressedSize,WriteBytes]);
  finally
   if (OutBuf <> nil) then
    FreeMem(OutBuf);
  end;
end; // InternalWriteBlock


//------------------------------------------------------------------------------
// write prepare
//------------------------------------------------------------------------------
procedure TABSCompressedBLOBStream.InternalWritePrepare(Count, Result: Integer);
var
    NumBlocks,NewPos: Int64;
begin
  // calculate start position and current header number for next block
  if (FHeaders.ItemCount = 0) then
   begin
    NewPos := FBLOBDescriptor.StartPosition;
    FCurrentHeader := 0;
   end
  else
   begin
// Commented By Leo Martin - changed from absolute to relative offset
//    NewPos := FHeaders.Items[FCurrentHeader].OffsetToNextHeader;
    NewPos := FHeaders.Positions[FCurrentHeader] +
      FHeaders.Items[FCurrentHeader].OffsetToNextHeader;
    FCurrentHeader := FHeaders.ItemCount;
   end;
  NumBlocks := (Count - Result) div FBlockSize;
  if (((Count - Result) mod FBlockSize) > 0) then
   Inc(NumBlocks);
  FHeaders.SetSize(FHeaders.ItemCount + NumBlocks);
  // set new position
  FCompressedStream.Position := NewPos;
  if (FCompressedStream.Position <> NewPos) then
   raise EABSException.Create(10102,ErrorLCannotSetPosition,
    [NewPos,FCompressedStream.Position,FCompressedStream.Size]);
end; // InternalWritePrepare


//------------------------------------------------------------------------------
// write to compressed stream
//------------------------------------------------------------------------------
function TABSCompressedBLOBStream.Write(const Buffer; Count: Longint): Longint;
var WriteSize:        Integer;
    InBuf,TempBuf:    TAbsPByte;
    Offset:           Integer;
begin
 Result := 0;
 if (FCompressionAlgorithm = acaNone) then
  begin
   Result := FCompressedStream.Write(Buffer,Count);
   FUncompressedSize := FCompressedStream.Size - FStartPosition;
   FPosition := FCompressedStream.Position - FStartPosition;
  end // no compression
 else
  if ((Count > 0) and (FPosition >= FUncompressedSize)) then
   begin
    // write beyond end of the file
    if (FPosition > FUncompressedSize) then
      InternalWriteBeyondEOF;
    if (FHeaders.ItemCount > 0) then
     FCurrentHeader := FHeaders.ItemCount-1
    else
     FCurrentHeader := 0;
    Offset := FPosition mod FBlockSize;
    // rewrite last block
    if (Offset > 0) then
     begin
      // load last block
      InBuf := MemoryManager.GetMem(FBlockSize);
      try
        LoadBlock(FCurrentHeader,TempBuf);
        try
         Move(TempBuf^,InBuf^,FHeaders.Items[FCurrentHeader].UncompressedSize);
        finally
         FreeMem(TempBuf);
        end;

        if (Count < (FBlockSize - Offset)) then
         WriteSize := Count
        else
         WriteSize := FBlockSize - Offset;
        Move(TAbsPByte(@Buffer)^,TAbsPByte(InBuf + Offset)^,WriteSize);
        InternalWriteBlock(InBuf,Offset + WriteSize);
        Inc(Result,WriteSize);
        Inc(FCurrentHeader);
      finally
       MemoryManager.FreeAndNillMem(InBuf);
      end;
     end; // Offset > 0
    InBuf := nil;
    if (Result < Count) then
     begin
      InBuf := MemoryManager.GetMem(FBlockSize);
      if (Offset > 0) and (FCurrentHeader > 0) then
        Dec(FCurrentHeader);
      InternalWritePrepare(Count,Result);
     end; // Result < Count
    try
     while (Result < Count) do
      begin
        if ((Count - Result) < FBlockSize) then
         WriteSize := Count - Result
        else
         WriteSize := FBlockSize;
        Move(TAbsPByte(TAbsPByte(@Buffer) + Result)^,TAbsPByte(InBuf)^,WriteSize);
        FHeaders.Positions[FCurrentHeader] := FCompressedStream.Position;
        InternalWriteBlock(InBuf,WriteSize);
        // write nex block;
        Inc(Result,WriteSize);
        Inc(FCurrentHeader);
      end;
    finally
     if (InBuf <> nil) then
      MemoryManager.FreeAndNillMem(InBuf);
    end;
    Inc(FUncompressedSize,Result);
    Inc(FPosition,Result);
    FBLOBDescriptor.NumBlocks := FHeaders.ItemCount;
   end; // compression
 FBLOBDescriptor.UncompressedSize := FUncompressedSize;
 CalculateRate;
end; // Write


//------------------------------------------------------------------------------
// seek in compressed stream
//------------------------------------------------------------------------------
function TABSCompressedBLOBStream.Seek(Offset: Longint; Origin: Word): Longint;
var NewPosition: Int64;
begin
 NewPosition := FPosition;
 case (Origin) of
  soFromBeginning:
    NewPosition := Offset;
  soFromCurrent:
    NewPosition := Integer(FPosition) + Offset;
  soFromEnd:
    NewPosition := Integer(FUncompressedSize) + Offset;
  end;
 Result := InternalSeek(NewPosition);
end; // Seek


{$IFDEF D6H}
function TABSCompressedBLOBStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
var NewPosition: Int64;
begin
 NewPosition := FPosition;
 case (Origin) of
  soBeginning:
    NewPosition := Offset;
  soCurrent:
    NewPosition := Integer(FPosition) + Offset;
  soEnd:
    NewPosition := Integer(FUncompressedSize) + Offset;
  end;
 Result := InternalSeek(NewPosition);
end; // Seek
{$ENDIF}


//------------------------------------------------------------------------------
// compresses buffer
// returns true if successful
// outBuf - pointer to compressed data
// outSize - size of compressed data
//------------------------------------------------------------------------------
function ABSInternalCompressBuffer(
                          CompressionAlgorithm:   TABSCompressionAlgorithm;
                          CompressionMode:        Byte;
                          InBuf:                  TAbsPByte;
                          InSize:                 Integer;
                          out OutBuf:             TAbsPByte;
                          out OutSize:            Integer
                          ): Boolean;
begin
 Result := false;
 OutSize := 0;
 // empty buffer cannot be compressed
 // none compression is not allowed
 if ((CompressionAlgorithm = acaNone) or (InSize = 0)) then Exit;
 Result := true;
 case CompressionAlgorithm of
{$IFDEF ZLIB}
  acaZLIB:
   begin
    try
     ZLIBCompressBuf(InBuf,InSize,Pointer(Outbuf),Integer(OutSize),CompressionMode);
    except
     AsbCompressionLastError := zlib_last_error;
     Result := false;
    end;
    if (OutSize <= 0) then
     Result := false;
   end
{$ENDIF}
{$IFDEF BZIP}
  {$IFDEF ZLIB}
  ;
  {$ENDIF}
  acaBZIP:
   begin
    try
     bzCompressBuf(InBuf,InSize,Pointer(Outbuf),Integer(OutSize),CompressionMode)
    except
     Result := false;
    end;
    if (OutSize <= 0) then
     Result := false;
   end
{$ENDIF}
{$IFDEF PPMD}
  {$IFDEF ZLIB}
  ;
  {$ELSE}
    {$IFDEF ZLIB}
    ;
    {$ENDIF}
  {$ENDIF}
  acaPPM:
   begin
    try
     PPMCompressBuf(InBuf,InSize,Pointer(Outbuf),Integer(OutSize),CompressionMode)
    except
     Result := false;
    end;
    if (OutSize <= 0) then
     Result := false;
   end
{$ENDIF}
;
  else
   Result := false;
 end; // case compression ?????????
end; // ABSInternalCompressBuffer;


//------------------------------------------------------------------------------
// decompresse buffer
// Outsize must be set to uncompressed size
// return true if successful
// OutBuf - pointer to compressed data
// OutSize - size of compressed data
//------------------------------------------------------------------------------
function ABSInternalDecompressBuffer(
                          CompressionAlgorithm:   TABSCompressionAlgorithm;
                          InBuf:                  TAbsPByte;
                          InSize:                 Integer;
                          out OutBuf:             TAbsPByte;
                          out OutSize:            Integer
                          ): Boolean;
begin
 Result := false;
 if ((CompressionAlgorithm = acaNone) or (InSize = 0)) then Exit;
 Result := true;
 case CompressionAlgorithm of
{$IFDEF ZLIB}
  acaZLIB:
   begin
    try
     ZLIBDecompressBuf(InBuf,InSize,OutSize,Pointer(Outbuf),Integer(OutSize));
    except
     Result := false;
    end;
    if (OutSize <= 0) then
     Result := false;
    if not Result then
      AsbCompressionLastError := zlib_last_error;
   end
{$ENDIF}
{$IFDEF BZIP}
  {$IFDEF ZLIB}
  ;
  {$ENDIF}
  acaBZIP:
   begin
    try
     bzDecompressBuf(InBuf,InSize,OutSize,Pointer(Outbuf),Integer(OutSize));
    except
     Result := false;
    end;
    if (OutSize <= 0) then
     Result := false;
   end
{$ENDIF}
{$IFDEF PPMD}
  {$IFDEF ZLIB}
  ;
  {$ELSE}
    {$IFDEF ZLIB}
    ;
    {$ENDIF}
  {$ENDIF}
  acaPPM:
   begin
    try
     PPMDecompressBuf(InBuf,InSize,Pointer(Outbuf),Integer(OutSize));
    except
     Result := false;
    end;
    if (OutSize <= 0) then
     Result := false;
   end
{$ENDIF}
;
  else
   Result := false;
 end; //case compression algorithm
end; // ABSInternalDecompressBuffer;


//------------------------------------------------------------------------------
// CRC32
//------------------------------------------------------------------------------
{function CRC32(CRC: LongWord; Data: Pointer; DataSize: LongWord): LongWord; assembler;
asm
         AND    EDX,EDX
         JZ     @Exit
         AND    ECX,ECX
         JLE    @Exit
         PUSH   EBX
         PUSH   EDI
         XOR    EBX,EBX
         LEA    EDI,CS:[OFFSET @CRC32]
@Start:  MOV    BL,AL
         SHR    EAX,8
         XOR    BL,[EDX]
         XOR    EAX,[EDI + EBX * 4]
         INC    EDX
         DEC    ECX
         JNZ    @Start
         POP    EDI
         POP    EBX
@Exit:   RET
         DB 0, 0, 0, 0, 0 // Align Table
@CRC32:  DD 000000000h, 077073096h, 0EE0E612Ch, 0990951BAh
         DD 0076DC419h, 0706AF48Fh, 0E963A535h, 09E6495A3h
         DD 00EDB8832h, 079DCB8A4h, 0E0D5E91Eh, 097D2D988h
         DD 009B64C2Bh, 07EB17CBDh, 0E7B82D07h, 090BF1D91h
         DD 01DB71064h, 06AB020F2h, 0F3B97148h, 084BE41DEh
         DD 01ADAD47Dh, 06DDDE4EBh, 0F4D4B551h, 083D385C7h
         DD 0136C9856h, 0646BA8C0h, 0FD62F97Ah, 08A65C9ECh
         DD 014015C4Fh, 063066CD9h, 0FA0F3D63h, 08D080DF5h
         DD 03B6E20C8h, 04C69105Eh, 0D56041E4h, 0A2677172h
         DD 03C03E4D1h, 04B04D447h, 0D20D85FDh, 0A50AB56Bh
         DD 035B5A8FAh, 042B2986Ch, 0DBBBC9D6h, 0ACBCF940h
         DD 032D86CE3h, 045DF5C75h, 0DCD60DCFh, 0ABD13D59h
         DD 026D930ACh, 051DE003Ah, 0C8D75180h, 0BFD06116h
         DD 021B4F4B5h, 056B3C423h, 0CFBA9599h, 0B8BDA50Fh
         DD 02802B89Eh, 05F058808h, 0C60CD9B2h, 0B10BE924h
         DD 02F6F7C87h, 058684C11h, 0C1611DABh, 0B6662D3Dh
         DD 076DC4190h, 001DB7106h, 098D220BCh, 0EFD5102Ah
         DD 071B18589h, 006B6B51Fh, 09FBFE4A5h, 0E8B8D433h
         DD 07807C9A2h, 00F00F934h, 09609A88Eh, 0E10E9818h
         DD 07F6A0DBBh, 0086D3D2Dh, 091646C97h, 0E6635C01h
         DD 06B6B51F4h, 01C6C6162h, 0856530D8h, 0F262004Eh
         DD 06C0695EDh, 01B01A57Bh, 08208F4C1h, 0F50FC457h
         DD 065B0D9C6h, 012B7E950h, 08BBEB8EAh, 0FCB9887Ch
         DD 062DD1DDFh, 015DA2D49h, 08CD37CF3h, 0FBD44C65h
         DD 04DB26158h, 03AB551CEh, 0A3BC0074h, 0D4BB30E2h
         DD 04ADFA541h, 03DD895D7h, 0A4D1C46Dh, 0D3D6F4FBh
         DD 04369E96Ah, 0346ED9FCh, 0AD678846h, 0DA60B8D0h
         DD 044042D73h, 033031DE5h, 0AA0A4C5Fh, 0DD0D7CC9h
         DD 05005713Ch, 0270241AAh, 0BE0B1010h, 0C90C2086h
         DD 05768B525h, 0206F85B3h, 0B966D409h, 0CE61E49Fh
         DD 05EDEF90Eh, 029D9C998h, 0B0D09822h, 0C7D7A8B4h
         DD 059B33D17h, 02EB40D81h, 0B7BD5C3Bh, 0C0BA6CADh
         DD 0EDB88320h, 09ABFB3B6h, 003B6E20Ch, 074B1D29Ah
         DD 0EAD54739h, 09DD277AFh, 004DB2615h, 073DC1683h
         DD 0E3630B12h, 094643B84h, 00D6D6A3Eh, 07A6A5AA8h
         DD 0E40ECF0Bh, 09309FF9Dh, 00A00AE27h, 07D079EB1h
         DD 0F00F9344h, 08708A3D2h, 01E01F268h, 06906C2FEh
         DD 0F762575Dh, 0806567CBh, 0196C3671h, 06E6B06E7h
         DD 0FED41B76h, 089D32BE0h, 010DA7A5Ah, 067DD4ACCh
         DD 0F9B9DF6Fh, 08EBEEFF9h, 017B7BE43h, 060B08ED5h
         DD 0D6D6A3E8h, 0A1D1937Eh, 038D8C2C4h, 04FDFF252h
         DD 0D1BB67F1h, 0A6BC5767h, 03FB506DDh, 048B2364Bh
         DD 0D80D2BDAh, 0AF0A1B4Ch, 036034AF6h, 041047A60h
         DD 0DF60EFC3h, 0A867DF55h, 0316E8EEFh, 04669BE79h
         DD 0CB61B38Ch, 0BC66831Ah, 0256FD2A0h, 05268E236h
         DD 0CC0C7795h, 0BB0B4703h, 0220216B9h, 05505262Fh
         DD 0C5BA3BBEh, 0B2BD0B28h, 02BB45A92h, 05CB36A04h
         DD 0C2D7FFA7h, 0B5D0CF31h, 02CD99E8Bh, 05BDEAE1Dh
         DD 09B64C2B0h, 0EC63F226h, 0756AA39Ch, 0026D930Ah
         DD 09C0906A9h, 0EB0E363Fh, 072076785h, 005005713h
         DD 095BF4A82h, 0E2B87A14h, 07BB12BAEh, 00CB61B38h
         DD 092D28E9Bh, 0E5D5BE0Dh, 07CDCEFB7h, 00BDBDF21h
         DD 086D3D2D4h, 0F1D4E242h, 068DDB3F8h, 01FDA836Eh
         DD 081BE16CDh, 0F6B9265Bh, 06FB077E1h, 018B74777h
         DD 088085AE6h, 0FF0F6A70h, 066063BCAh, 011010B5Ch
         DD 08F659EFFh, 0F862AE69h, 0616BFFD3h, 0166CCF45h
         DD 0A00AE278h, 0D70DD2EEh, 04E048354h, 03903B3C2h
         DD 0A7672661h, 0D06016F7h, 04969474Dh, 03E6E77DBh
         DD 0AED16A4Ah, 0D9D65ADCh, 040DF0B66h, 037D83BF0h
         DD 0A9BCAE53h, 0DEBB9EC5h, 047B2CF7Fh, 030B5FFE9h
         DD 0BDBDF21Ch, 0CABAC28Ah, 053B39330h, 024B4A3A6h
         DD 0BAD03605h, 0CDD70693h, 054DE5729h, 023D967BFh
         DD 0B3667A2Eh, 0C4614AB8h, 05D681B02h, 02A6F2B94h
         DD 0B40BBE37h, 0C30C8EA1h, 05A05DF1Bh, 02D02EF8Dh
         DD 074726F50h, 0736E6F69h, 0706F4320h, 067697279h
         DD 028207468h, 031202963h, 020393939h, 048207962h
         DD 06E656761h, 064655220h, 06E616D64h, 06FBBA36Eh
end; // CRC32
}
function CRC32(CRC: longword; Data: Pointer; DataSize: longword): longword;
const
  crc_table : array[0..256-1] of longword = (
 $0,$77073096,$EE0E612C,$990951BA,
 $76DC419,$706AF48F,$E963A535,$9E6495A3,
 $EDB8832,$79DCB8A4,$E0D5E91E,$97D2D988,
 $9B64C2B,$7EB17CBD,$E7B82D07,$90BF1D91,
 $1DB71064,$6AB020F2,$F3B97148,$84BE41DE,
 $1ADAD47D,$6DDDE4EB,$F4D4B551,$83D385C7,
 $136C9856,$646BA8C0,$FD62F97A,$8A65C9EC,
 $14015C4F,$63066CD9,$FA0F3D63,$8D080DF5,
 $3B6E20C8,$4C69105E,$D56041E4,$A2677172,
 $3C03E4D1,$4B04D447,$D20D85FD,$A50AB56B,
 $35B5A8FA,$42B2986C,$DBBBC9D6,$ACBCF940,
 $32D86CE3,$45DF5C75,$DCD60DCF,$ABD13D59,
 $26D930AC,$51DE003A,$C8D75180,$BFD06116,
 $21B4F4B5,$56B3C423,$CFBA9599,$B8BDA50F,
 $2802B89E,$5F058808,$C60CD9B2,$B10BE924,
 $2F6F7C87,$58684C11,$C1611DAB,$B6662D3D,
 $76DC4190,$1DB7106,$98D220BC,$EFD5102A,
 $71B18589,$6B6B51F,$9FBFE4A5,$E8B8D433,
 $7807C9A2,$F00F934,$9609A88E,$E10E9818,
 $7F6A0DBB,$86D3D2D,$91646C97,$E6635C01,
 $6B6B51F4,$1C6C6162,$856530D8,$F262004E,
 $6C0695ED,$1B01A57B,$8208F4C1,$F50FC457,
 $65B0D9C6,$12B7E950,$8BBEB8EA,$FCB9887C,
 $62DD1DDF,$15DA2D49,$8CD37CF3,$FBD44C65,
 $4DB26158,$3AB551CE,$A3BC0074,$D4BB30E2,
 $4ADFA541,$3DD895D7,$A4D1C46D,$D3D6F4FB,
 $4369E96A,$346ED9FC,$AD678846,$DA60B8D0,
 $44042D73,$33031DE5,$AA0A4C5F,$DD0D7CC9,
 $5005713C,$270241AA,$BE0B1010,$C90C2086,
 $5768B525,$206F85B3,$B966D409,$CE61E49F,
 $5EDEF90E,$29D9C998,$B0D09822,$C7D7A8B4,
 $59B33D17,$2EB40D81,$B7BD5C3B,$C0BA6CAD,
 $EDB88320,$9ABFB3B6,$3B6E20C,$74B1D29A,
 $EAD54739,$9DD277AF,$4DB2615,$73DC1683,
 $E3630B12,$94643B84,$D6D6A3E,$7A6A5AA8,
 $E40ECF0B,$9309FF9D,$A00AE27,$7D079EB1,
 $F00F9344,$8708A3D2,$1E01F268,$6906C2FE,
 $F762575D,$806567CB,$196C3671,$6E6B06E7,
 $FED41B76,$89D32BE0,$10DA7A5A,$67DD4ACC,
 $F9B9DF6F,$8EBEEFF9,$17B7BE43,$60B08ED5,
 $D6D6A3E8,$A1D1937E,$38D8C2C4,$4FDFF252,
 $D1BB67F1,$A6BC5767,$3FB506DD,$48B2364B,
 $D80D2BDA,$AF0A1B4C,$36034AF6,$41047A60,
 $DF60EFC3,$A867DF55,$316E8EEF,$4669BE79,
 $CB61B38C,$BC66831A,$256FD2A0,$5268E236,
 $CC0C7795,$BB0B4703,$220216B9,$5505262F,
 $C5BA3BBE,$B2BD0B28,$2BB45A92,$5CB36A04,
 $C2D7FFA7,$B5D0CF31,$2CD99E8B,$5BDEAE1D,
 $9B64C2B0,$EC63F226,$756AA39C,$26D930A,
 $9C0906A9,$EB0E363F,$72076785,$5005713,
 $95BF4A82,$E2B87A14,$7BB12BAE,$CB61B38,
 $92D28E9B,$E5D5BE0D,$7CDCEFB7,$BDBDF21,
 $86D3D2D4,$F1D4E242,$68DDB3F8,$1FDA836E,
 $81BE16CD,$F6B9265B,$6FB077E1,$18B74777,
 $88085AE6,$FF0F6A70,$66063BCA,$11010B5C,
 $8F659EFF,$F862AE69,$616BFFD3,$166CCF45,
 $A00AE278,$D70DD2EE,$4E048354,$3903B3C2,
 $A7672661,$D06016F7,$4969474D,$3E6E77DB,
 $AED16A4A,$D9D65ADC,$40DF0B66,$37D83BF0,
 $A9BCAE53,$DEBB9EC5,$47B2CF7F,$30B5FFE9,
 $BDBDF21C,$CABAC28A,$53B39330,$24B4A3A6,
 $BAD03605,$CDD70693,$54DE5729,$23D967BF,
 $B3667A2E,$C4614AB8,$5D681B02,$2A6F2B94,
 $B40BBE37,$C30C8EA1,$5A05DF1B,$2D02EF8D);

var
 i: Integer;
begin
 Result:=CRC;
 for i:=0 to DataSize-1 do
 begin
   Result:=((Result shr 8) and $00FFFFFF) xor
           crc_table[(Result xor pByteArray(Data)[i]) and $000000FF];
 end;
end;


//------------------------------------------------------------------------------
// Get temp file name
//------------------------------------------------------------------------------
function GetTempFileName: string;
var
  TempPath : array[0..255] of Char;
  Prefix : array[0..10] of Char;
  lpTempName: array [0..MAX_PATH] of Char;
begin
  // get temp file name
  StrPCopy(Prefix, 'ABS');
  GetTempPath(255, @TempPath);
{$IFDEF D12H}
  Windows.GetTempFileNameW(@TempPath, @Prefix, 0, lpTempName);
{$ELSE}
  Windows.GetTempFileName(@TempPath, @Prefix, 0, lpTempName);
{$ENDIF}
  Result := lpTempName;
end;// GetTempFileName


//------------------------------------------------------------------------------
// save data
//------------------------------------------------------------------------------
procedure SaveDataToStream(var Data; DataSize: Integer; Stream: TStream; ErrorCode: Integer);
var OldPos:     Int64;
    WriteBytes: Integer;
begin
  OldPos := Stream.Position;
  WriteBytes := Stream.Write(Data,DataSize);
  if (WriteBytes <> DataSize) then
    raise EABSException.Create(ErrorCode,ErrorLCannotWriteToStream,
      [OldPos,Stream.Size,DataSize,WriteBytes]);
end; // SaveDataToStream


//------------------------------------------------------------------------------
// load data
//------------------------------------------------------------------------------
procedure LoadDataFromStream(var Data; DataSize: Integer; Stream: TStream; ErrorCode: Integer);
var OldPos:    Int64;
    ReadBytes: Integer;
begin
  OldPos := Stream.Position;
  ReadBytes := Stream.Read(Data,DataSize);
  if (ReadBytes <> DataSize) then
    raise EABSException.Create(ErrorCode,ErrorLCannotReadFromStream,
      [OldPos,Stream.Size,DataSize,ReadBytes]);
end; // LoadDataFromStream


//------------------------------------------------------------------------------
// set stream position
//------------------------------------------------------------------------------
procedure SetStreamPosition(Stream: TStream; NewPosition: Int64; ErrorCode: Integer);
var OldPos: Int64;
begin
 OldPos := Stream.Position;
 Stream.Position := NewPosition;
 if (Stream.Position <> NewPosition) then
  raise EABSException.Create(ErrorCode,ErrorLCannotSetPosition,
    [NewPosition,OldPos,Stream.Size]);
end; // SetStreamPosition


//------------------------------------------------------------------------------
// Return CompressionAlgorithm by its Name
//------------------------------------------------------------------------------
function GetCompressionAlgorithm(Name: string): TABSCompressionAlgorithm;
var i: Integer;
begin
  Result := acaNone;
  Name := UpperCase(Name);
  for i:=0 to High(ABSCompressionAlgorithmNames) do
    if (ABSCompressionAlgorithmNames[i] = Name) then
      begin
        Result := TABSCompressionAlgorithm(i);
        Exit;
      end;
end;//GetCompressionAlgorithm


function ABSCompressionGetLastError: String;
begin
  Result := AsbCompressionLastError;
end;

initialization
  AsbCompressionLastError := '';

end.


