631 lines
22 KiB
ObjectPascal

(* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* 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 TurboPower Abbrevia
*
* The Initial Developer of the Original Code is Craig Peterson
*
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Craig Peterson <capeterson@users.sourceforge.net>
* Pierre le Riche <pierre_le_riche@users.sourceforge.net>
*
* ***** END LICENSE BLOCK ***** *)
{*********************************************************}
{* ABBREVIA: AbLZMA.pas *}
{*********************************************************}
{* ABBREVIA: Lzma compression/decompression procedures. *}
{*********************************************************}
unit AbLZMA;
{$I AbDefine.inc}
interface
uses
Classes, Windows, SysUtils, AbCrtl, AbUtils;
{ Raw LZMA decompression =================================================== }
{ Decompresses the LZMA compressed data in ASrc to ADes. ASrc should not have
the header used by the other compression/decompression routines, and
AProperties should contain any necessary data. }
procedure LzmaDecodeStream(AProperties: PByte; APropSize: Integer; ASrc, ADes: TStream;
AUncompressedSize: Int64 = -1); overload;
{ Stream compression and decompression (taken from LzmaUtil.c) ============= }
procedure LzmaDecodeStream(ASourceStream, ATargetStream: TStream); overload;
procedure LzmaEncodeStream(ASourceStream, ATargetStream: TStream; ASourceSize: Int64);
{ In-memory compression and decompression ================================== }
{ Given a pointer to the compressed data, this will return the size of the
decompressed data. }
function LzmaGetUncompressedSize(APCompressedData: Pointer; ACompressedSize: Integer): Integer;
{ Decompresses the LZMA compressed data at APCompressedData to the buffer
pointed to by APUncompressedData. The buffer at APUncompressedData should be
large enough to hold the number of bytes as returned by LzmaGetDecompressedSize. }
procedure LzmaDecodeBuffer(APCompressedData: Pointer; ACompressedSize: Integer;
APUncompressedData: Pointer);
{ Compresses the data at APUncompressedData to the buffer at APCompressedData,
and returns the number of bytes written. If ACompressedDataBufferCapacity is
less than the number of bytes required to store the entire compressed stream,
or any other error occurs, then an exception is raised. (A safe number for
ACompressedDataBufferCapacity is slightly more than AUncompressedDataBufferSize.)
Leave ACompressionLevel and ADictionarySize at -1 in order to use the default
values (5 and 16MB respectively). }
function LzmaEncodeBuffer(APUncompressedData: Pointer; AUncompressedSize: Integer;
APCompressedData: Pointer; ACompressedDataBufferCapacity: Integer;
ACompressionLevel: Integer = -1; ADictionarySize: Integer = -1): Integer;
{ Types.h declarations ===================================================== }
const
SZ_OK = 0;
SZ_ERROR_DATA = 1;
SZ_ERROR_MEM = 2;
SZ_ERROR_CRC = 3;
SZ_ERROR_UNSUPPORTED = 4;
SZ_ERROR_PARAM = 5;
SZ_ERROR_INPUT_EOF = 6;
SZ_ERROR_OUTPUT_EOF = 7;
SZ_ERROR_READ = 8;
SZ_ERROR_WRITE = 9;
SZ_ERROR_PROGRESS = 10;
SZ_ERROR_FAIL = 11;
SZ_ERROR_THREAD = 12;
SZ_ERROR_ARCHIVE = 16;
SZ_ERROR_NO_ARCHIVE = 17;
type
SRes = Integer;
ISeqInStream = packed record
Read: function(p: Pointer; var buf; var size: size_t): SRes; cdecl;
end;
PISeqInStream = ^ISeqInStream;
ISeqOutStream = packed record
Write: function(p: Pointer; const buf; size: size_t): size_t; cdecl;
end;
PISeqOutStream = ^ISeqOutStream;
ICompressProgress = packed record
Progress: function(p: Pointer; inSize, outSize: Int64): SRes; cdecl;
end;
PICompressProgress = ^ICompressProgress;
ISzAlloc = packed record
Alloc: function(p: Pointer; size: size_t): Pointer; cdecl;
Free: procedure(p: Pointer; address: Pointer); cdecl;
end;
PISzAlloc = ^ISzAlloc;
{ LzmaDec.h declarations =================================================== }
type
CLzmaProb = Word;
// LZMA Properties
const
LZMA_PROPS_SIZE = 5;
type
CLzmaProps = packed record
lc, lp, pb: Cardinal;
dicSize: UInt32;
end;
// LZMA Decoder state
const
LZMA_REQUIRED_INPUT_MAX = 20;
type
CLzmaDec = packed record
prop: CLzmaProps;
probs: ^CLzmaProb;
dic: PByte;
buf: PByte;
range, code: UInt32;
dicPos: size_t;
dicBufSize: size_t;
processedPos: UInt32;
checkDicSize: UInt32;
state: Cardinal;
reps: array[0..3] of UInt32;
remainLen: Cardinal;
needFlush: Integer;
needInitState: Integer;
numProbs: UInt32;
tempBufSize: Cardinal;
tempBuf: array[0..LZMA_REQUIRED_INPUT_MAX - 1] of Byte;
end;
type
ELzmaFinishMode = LongInt;
const
LZMA_FINISH_ANY = 0; // finish at any point
LZMA_FINISH_END = 1; // block must be finished at the end
type
ELzmaStatus = LongInt;
const
LZMA_STATUS_NOT_SPECIFIED = 0; // use main error code instead
LZMA_STATUS_FINISHED_WITH_MARK = 1; // stream was finished with end mark.
LZMA_STATUS_NOT_FINISHED = 3; // stream was not finished
LZMA_STATUS_NEEDS_MORE_INPUT = 4; // you must provide more input bytes
LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK = 5; // there is probability that stream was finished without end mark
procedure LzmaDec_Construct(var p: CLzmaDec); cdecl;
procedure LzmaDec_Init(var p: CLzmaDec); cdecl; external;
function LzmaDec_DecodeToBuf(var p: CLzmaDec; dest: PByte; var destLen: size_t;
src: PByte; var srcLen: size_t; finishMode: ELzmaFinishMode;
var status: ELzmaStatus): SRes; cdecl; external;
function LzmaDec_Allocate(var state: CLzmaDec; prop: PByte; propsSize: Integer;
alloc: PISzAlloc): SRes; cdecl; external;
procedure LzmaDec_Free(var state: CLzmaDec; alloc: PISzAlloc); cdecl; external;
// One call decoding interface
function LzmaDecode(dest: PByte; var destLen: size_t; src: PByte;
var srcLen: size_t; propData: PByte; propSize: Integer;
finishMode: ELzmaFinishMode; var status: ELzmaStatus;
alloc: PISzAlloc): SRes; cdecl; external;
{ LzmaEnc.h declarations =================================================== }
type
CLzmaEncHandle = Pointer;
CLzmaEncProps = packed record
level: Integer; // 0 <= level <= 9
dictSize: UInt32; // (1 << 12) <= dictSize <= (1 << 27) for 32-bit version
// (1 << 12) <= dictSize <= (1 << 30) for 64-bit version
// default = (1 << 24)
lc: Integer; // 0 <= lc <= 8, default = 3
lp: Integer; // 0 <= lp <= 4, default = 0
pb: Integer; // 0 <= pb <= 4, default = 2
algo: Integer; // 0 - fast, 1 - normal, default = 1
fb: Integer; // 5 <= fb <= 273, default = 32
btMode: Integer; // 0 - hashChain Mode, 1 - binTree mode - normal, default = 1
numHashBytes: Integer; // 2, 3 or 4, default = 4
mc: UInt32; // 1 <= mc <= (1 << 30), default = 32
writeEndMark: Cardinal; // 0 - do not write EOPM, 1 - write EOPM, default = 0
numThreads: Integer; // 1 or 2, default = 2
end;
procedure LzmaEncProps_Init(var p: CLzmaEncProps); cdecl; external;
function LzmaEnc_Create(Alloc: PISzAlloc): CLzmaEncHandle; cdecl; external;
procedure LzmaEnc_Destroy(p: CLzmaEncHandle; Alloc, allocBig: PISzAlloc); cdecl; external;
function LzmaEnc_SetProps(p: CLzmaEncHandle; var props: CLzmaEncProps): SRes; cdecl; external;
function LzmaEnc_WriteProperties(p: CLzmaEncHandle; properties: PByte;
var size: size_t): SRes; cdecl; external;
function LzmaEnc_Encode(p: CLzmaEncHandle; outStream: PISeqOutStream;
inStream: PISeqInStream; Progress: PICompressProgress;
Alloc, allocBig: PISzAlloc): SRes; cdecl; external;
function LzmaEnc_MemEncode(p: CLzmaEncHandle; dest: PByte; var destLen: size_t;
src: PByte; srcLen: size_t; writeEndMark: Integer; Progress: PICompressProgress;
Alloc, allocBig: PISzAlloc): SRes; cdecl; external;
// One call encoding interface
function LzmaEncode(dest: PByte; var destLen: size_t; src: PByte;
srcLen: size_t; var props: CLzmaEncProps; propsEncoded: PByte;
var propsSize: size_t; writeEndMark: Integer; progress: PICompressProgress;
alloc: pISzAlloc; allocBig: PISzAlloc): SRes; cdecl; external;
{ LzFind.h declarations ==================================================== }
procedure MatchFinder_NeedMove; external;
procedure MatchFinder_GetPointerToCurrentPos; external;
procedure MatchFinder_MoveBlock; external;
procedure MatchFinder_ReadIfRequired; external;
procedure MatchFinder_Construct; external;
procedure MatchFinder_Create; external;
procedure MatchFinder_Free; external;
procedure MatchFinder_Normalize3; external;
procedure MatchFinder_ReduceOffsets; external;
procedure GetMatchesSpec1; external;
procedure MatchFinder_Init; external;
procedure MatchFinder_CreateVTable; external;
{ LzFindMt.h declarations ================================================== }
procedure MatchFinderMt_Construct; external;
procedure MatchFinderMt_Destruct; external;
procedure MatchFinderMt_Create; external;
procedure MatchFinderMt_CreateVTable; external;
procedure MatchFinderMt_ReleaseStream; external;
{ Lzma header fields ======================================================= }
type
// The condensed compression properties
TLZMAPropertyData = array[0..LZMA_PROPS_SIZE - 1] of Byte;
// The header usually stored in front of LZMA compressed data
TLZMAHeader = packed record
PropertyData: TLZMAPropertyData;
UncompressedSize: Int64;
end;
PLZMAHeader = ^TLZMAHeader;
{ Error handling =========================================================== }
type
EAbLZMAException = class(Exception);
procedure LzmaCheck(AResultCode: SRes);
procedure RaiseLzmaException(AResultCode: SRes);
{ Linker directives ======================================================== }
{$WARN BAD_GLOBAL_SYMBOL OFF}
{$IF DEFINED(WIN32)}
{$L Win32\LzFind.obj}
{$L Win32\LzFindMt.obj}
{$L Win32\LzmaDec.obj}
{$L Win32\LzmaEnc.obj}
{$L Win32\Threads.obj}
{$ELSEIF DEFINED(WIN64)}
{$L Win64\LzFind.obj}
{$L Win64\LzFindMt.obj}
{$L Win64\LzmaDec.obj}
{$L Win64\LzmaEnc.obj}
{$L Win64\Threads.obj}
{$IFEND}
implementation
{ Error handling =========================================================== }
procedure LzmaCheck(AResultCode: SRes);
begin
if AResultCode <> SZ_OK then
RaiseLzmaException(AResultCode);
end;
{ -------------------------------------------------------------------------- }
procedure RaiseLzmaException(AResultCode: SRes);
begin
case AResultCode of
SZ_ERROR_DATA: raise EAbLZMAException.Create('LZMA Data Error.');
SZ_ERROR_MEM: raise EAbLZMAException.Create('LZMA Memory Error.');
SZ_ERROR_CRC: raise EAbLZMAException.Create('LZMA CRC Error.');
SZ_ERROR_UNSUPPORTED: raise EAbLZMAException.Create('LZMA "Unsupported" Error.');
SZ_ERROR_PARAM: raise EAbLZMAException.Create('LZMA Parameter Error.');
SZ_ERROR_INPUT_EOF: raise EAbLZMAException.Create('LZMA Input EOF Error.');
SZ_ERROR_OUTPUT_EOF: raise EAbLZMAException.Create('LZMA Output EOF Error.');
SZ_ERROR_READ: raise EAbLZMAException.Create('LZMA Read Error.');
SZ_ERROR_WRITE: raise EAbLZMAException.Create('LZMA Write Error.');
SZ_ERROR_PROGRESS: raise EAbLZMAException.Create('LZMA Progress Error.');
SZ_ERROR_FAIL: raise EAbLZMAException.Create('LZMA "Fail" Error.');
SZ_ERROR_THREAD: raise EAbLZMAException.Create('LZMA Thread Error.');
SZ_ERROR_ARCHIVE: raise EAbLZMAException.Create('LZMA Archive Error.');
SZ_ERROR_NO_ARCHIVE: raise EAbLZMAException.Create('LZMA "No Archive" Error.');
else
raise EAbLZMAException.CreateFmt('Unknown LZMA error (%d)', [AResultCode]);
end;
end;
{ Helper Routines ========================================================== }
procedure LzmaDec_Construct(var p: CLzmaDec); cdecl;
begin
p.dic := nil;
p.probs := nil;
end;
{ -------------------------------------------------------------------------- }
function SzAlloc(p: Pointer; size: size_t): Pointer; cdecl;
begin
Result := GetMemory(size);
end;
{ -------------------------------------------------------------------------- }
procedure SzFree(p, address: Pointer); cdecl;
begin
FreeMemory(address);
end;
var
DelphiMMInterface: ISzAlloc = (Alloc: SzAlloc; Free: SzFree);
{ CSeq*Stream implementation =============================================== }
type
CSeqInStream = packed record
Intf: ISeqInStream;
Stream: TStream;
end;
CSeqOutStream = packed record
Intf: ISeqOutStream;
Stream: TStream;
end;
{ -------------------------------------------------------------------------- }
function ISeqInStream_Read(p: Pointer; var buf; var size: size_t): SRes; cdecl;
begin
try
size := CSeqInStream(p^).Stream.Read(buf, size);
Result := SZ_OK;
except
Result := SZ_ERROR_DATA;
end;
end;
{ -------------------------------------------------------------------------- }
function ISeqOutStream_Write(p: Pointer; const buf; size: size_t): size_t; cdecl;
begin
try
Result := CSeqOutStream(p^).Stream.Write(buf, size);
except
Result := 0;
end;
end;
{ Raw LZMA decompression =================================================== }
{ Decompress an Lzma compressed stream. Based on LzmaUtil.c::Decode2 }
function LzmaDecode2(var aState: CLzmaDec; aOutStream, aInStream: TStream;
aUncompressedSize: Int64 = -1): SRes;
const
IN_BUF_SIZE = 1 shl 16;
OUT_BUF_SIZE = 1 shl 16;
var
LHasSize: Boolean;
LInBuf: array [0..IN_BUF_SIZE - 1] of Byte;
LOutBuf: array [0..OUT_BUF_SIZE - 1] of Byte;
LInPos, LInSize, LOutPos: size_t;
LInProcessed, LOutProcessed: size_t;
LFinishMode: ELzmaFinishMode;
LStatus: ELzmaStatus;
begin
Result := 0;
LHasSize := aUncompressedSize <> -1;
LInPos := 0;
LInSize := 0;
LOutPos := 0;
LzmaDec_Init(aState);
while True do
begin
if LInPos = LInSize then
begin
LInSize := aInStream.Read(LInBuf, IN_BUF_SIZE);
LInPos := 0;
if LInSize = 0 then
Break;
end
else
begin
LInProcessed := LInSize - LInPos;
LOutProcessed := OUT_BUF_SIZE - LOutPos;
LFinishMode := LZMA_FINISH_ANY;
if LHasSize and (LOutProcessed > aUncompressedSize) then
begin
LOutProcessed := size_t(aUncompressedSize);
LFinishMode := LZMA_FINISH_END;
end;
Result := LzmaDec_DecodeToBuf(aState, @LOutBuf[LOutPos], LOutProcessed,
@LInBuf[LInPos], LInProcessed, LFinishMode, LStatus);
Inc(LInPos, LInProcessed);
Inc(LOutPos, LOutProcessed);
Dec(aUncompressedSize, LOutProcessed);
if (aOutStream <> nil) and (aOutStream.Write(LOutBuf, LOutPos) <> LOutPos) then
begin
Result := SZ_ERROR_WRITE;
Exit;
end;
LOutPos := 0;
if (Result <> SZ_OK) or (LHasSize and (aUncompressedSize = 0)) then
Exit;
if (LInProcessed = 0) and (LOutProcessed = 0) then
begin
if LHasSize or (LStatus <> LZMA_STATUS_FINISHED_WITH_MARK) then
Result := SZ_ERROR_DATA;
Exit;
end;
end;
end;
end;
{ -------------------------------------------------------------------------- }
{ Decompress an LZMA compressed stream. Pass AUncompressedSize = -1 if the
uncompressed size is not known. }
procedure LzmaDecodeStream(AProperties: PByte; APropSize: Integer;
ASrc, ADes: TStream; AUncompressedSize: Int64);
var
LLZMADecState: CLzmaDec;
begin
LzmaDec_Construct(LLZMADecState);
try
LzmaCheck(LzmaDec_Allocate(LLZMADecState, AProperties, APropSize, @DelphiMMInterface));
LzmaCheck(LzmaDecode2(LLZMADecState, ADes, ASrc, AUncompressedSize));
finally
LzmaDec_Free(LLZMADecState, @DelphiMMInterface);
end;
end;
{ Stream to stream compression and decompression =========================== }
{ Decompresses streams compressed with the LZMA SDK's LzmaUtil.exe.
Based on LzmaUtil.c::Decode }
procedure LzmaDecodeStream(ASourceStream, ATargetStream: TStream);
var
LUncompressedSize: Int64;
// Header: 5 bytes of LZMA properties and 8 bytes of uncompressed size
LHeader: TLZMAHeader;
begin
// Read and parse header
ASourceStream.ReadBuffer(LHeader, SizeOf(LHeader));
LUncompressedSize := LHeader.UncompressedSize;
LzmaDecodeStream(PByte(@LHeader.PropertyData), LZMA_PROPS_SIZE, ASourceStream,
ATargetStream, LUncompressedSize);
end;
{ -------------------------------------------------------------------------- }
{ Compresses a stream so it's compatible with the LZMA SDK's LzmaUtil.exe.
Based on LzmaUtil.c::Encode }
procedure LzmaEncodeStream(ASourceStream, ATargetStream: TStream; ASourceSize: Int64);
var
LEncHandle: CLzmaEncHandle;
LEncProps: CLzmaEncProps;
LHeader: TLZMAHeader;
LPropDataSize: size_t;
LInStreamRec: CSeqInStream;
LOutStreamRec: CSeqOutStream;
begin
LInStreamRec.Intf.Read := ISeqInStream_Read;
LInStreamRec.Stream := ASourceStream;
LOutStreamRec.Intf.Write := ISeqOutStream_Write;
LOutStreamRec.Stream := ATargetStream;
LEncHandle := LzmaEnc_Create(@DelphiMMInterface);
if LEncHandle = nil then
LzmaCheck(SZ_ERROR_MEM);
try
LzmaEncProps_Init(LEncProps);
LzmaCheck(LzmaEnc_SetProps(LEncHandle, LEncProps));
LPropDataSize := LZMA_PROPS_SIZE;
LzmaCheck(LzmaEnc_WriteProperties(LEncHandle, PByte(@LHeader.PropertyData),
LPropDataSize));
LHeader.UncompressedSize := ASourceSize;
ATargetStream.WriteBuffer(LHeader, SizeOf(LHeader));
LzmaCheck(LzmaEnc_Encode(LEncHandle, @LOutStreamRec.Intf,
@LInStreamRec.Intf, nil, @DelphiMMInterface, @DelphiMMInterface));
finally
LzmaEnc_Destroy(LEncHandle, @DelphiMMInterface, @DelphiMMInterface);
end;
end;
{ In-memory compression and decompression ================================== }
{ Given a pointer to the compressed data, this will return the size of the
decompressed data. }
function LzmaGetUncompressedSize(APCompressedData: Pointer; ACompressedSize: Integer): Integer;
begin
if ACompressedSize <= SizeOf(TLZMAHeader) then
raise EAbLZMAException.Create('The LZMA compressed data is invalid (not enough bytes)');
Result := PLZMAHeader(APCompressedData).UncompressedSize;
end;
{ -------------------------------------------------------------------------- }
{ Decompresses the LZMA compressed data at APCompressedData to the buffer
pointed to by APUncompressedData. The buffer at APUncompressedData should be
large enough to hold the number of bytes as returned by LzGetDecompressedSize. }
procedure LzmaDecodeBuffer(APCompressedData: Pointer; ACompressedSize: Integer;
APUncompressedData: Pointer);
var
LPropertyData: TLZMAPropertyData;
LUncompressedSize: Int64;
LInputByteCount, LOutputByteCount: size_t;
LStatus: ELzmaStatus;
begin
if ACompressedSize <= SizeOf(TLZMAHeader) then
raise EAbLZMAException.Create('The LZMA compressed data is invalid (not enough bytes)');
// Read the header from the compressed data.
LPropertyData := PLZMAHeader(APCompressedData).PropertyData;
LUncompressedSize := PLZMAHeader(APCompressedData).UncompressedSize;
Inc(PAnsiChar(APCompressedData), SizeOf(TLZMAHeader));
Dec(ACompressedSize, SizeOf(TLZMAHeader));
// Decompress from the input to the output buffer. This will change the byte
// count variables to the actual number of bytes consumed/written.
LInputByteCount := ACompressedSize;
LOutputByteCount := LUncompressedSize;
LzmaCheck(LzmaDecode(APUncompressedData, LOutputByteCount,
APCompressedData, LInputByteCount, PByte(@LPropertyData), LZMA_PROPS_SIZE,
LZMA_FINISH_END, LStatus, @DelphiMMInterface));
// Check that the input buffer was fully consumed and the output buffer was filled up.
if (LOutputByteCount <> LUncompressedSize) or (LInputByteCount <> ACompressedSize) then
raise EAbLZMAException.Create('LZMA decompression data error');
end;
{ -------------------------------------------------------------------------- }
{ Compresses the data at APUncompressedData to the buffer at APCompressedData,
and returns the number of bytes written. If ACompressedDataBufferCapacity is
less than the number of bytes required to store the entire compressed stream,
or any other error occurs, then an exception is raised. (A safe number for
ACompressedDataBufferCapacity is slightly more than AUncompressedDataBufferSize.)
Leave ACompressionLevel and ADictionarySize at -1 in order to use the default
values (5 and 16MB respectively). }
function LzmaEncodeBuffer(APUncompressedData: Pointer; AUncompressedSize: Integer;
APCompressedData: Pointer;
ACompressedDataBufferCapacity, ACompressionLevel, ADictionarySize: Integer): Integer;
var
LEncProps: CLzmaEncProps;
LPropsSize: size_t;
LPOutBuf: PByte;
LOutputBytes: size_t;
begin
if ACompressedDataBufferCapacity <= SizeOf(TLZMAHeader) then
raise EAbLZMAException.Create('LZMA output buffer too small');
// Set the uncompressed size in the header
PLZMAHeader(APCompressedData).UncompressedSize := AUncompressedSize;
// Set the properties
LzmaEncProps_Init(LEncProps);
if ACompressionLevel >= 0 then
LEncProps.level := ACompressionLevel;
if ADictionarySize >= 0 then
LEncProps.dictSize := ADictionarySize;
LPOutBuf := PByte(PtrUInt(APCompressedData) + SizeOf(TLZMAHeader));
LOutputBytes := ACompressedDataBufferCapacity - SizeOf(TLZMAHeader);
LPropsSize := LZMA_PROPS_SIZE;
LzmaCheck(LzmaEncode(LPOutBuf, LOutputBytes, APUncompressedData,
AUncompressedSize, LEncProps, APCompressedData, LPropsSize, 0, nil,
@DelphiMMInterface, @DelphiMMInterface));
Result := LOutputBytes + SizeOf(TLZMAHeader);
end;
initialization
// The LZMA routines are multithreaded and use the Delphi memory manager.
IsMultiThread := True;
end.