473 lines
12 KiB
ObjectPascal
473 lines
12 KiB
ObjectPascal
(*
|
|
LZ4Delphi
|
|
Copyright (C) 2015, Jose Pascoa (atelierwebgm@gmail.com)
|
|
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
|
|
|
|
*************************************************************************
|
|
LZ4 - Fast LZ compression algorithm
|
|
xxHash - Fast Hash algorithm
|
|
LZ4 source repository : http://code.google.com/p/lz4/
|
|
xxHash source repository : http://code.google.com/p/xxhash/
|
|
Copyright (c) 2011-2014, Yann Collet
|
|
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are
|
|
met:
|
|
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above
|
|
copyright notice, this list of conditions and the following disclaimer
|
|
in the documentation and/or other materials provided with the
|
|
distribution.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
******************************************************************************
|
|
*)
|
|
|
|
{$POINTERMATH ON}
|
|
unit lz4;
|
|
|
|
interface
|
|
|
|
uses Windows;
|
|
|
|
const
|
|
MINMATCH = 4;
|
|
COPYLENGTH = 8;
|
|
LASTLITERALS = 5;
|
|
_MFLIMIT = COPYLENGTH + MINMATCH;
|
|
MAXD_LOG = 16;
|
|
MAX_DISTANCE = (1 shl MAXD_LOG) - 1;
|
|
STEPSIZE = sizeof(size_t);
|
|
ML_BITS = 4;
|
|
ML_MASK = (1 shl ML_BITS) - 1;
|
|
RUN_BITS = 8 - ML_BITS;
|
|
RUN_MASK = (1 shl RUN_BITS) - 1;
|
|
|
|
type
|
|
ppByte = ^pByte;
|
|
dict_directive = (noDict = 0, withPrefix64k, usingExtDict);
|
|
endCondition_directive = (endOnOutputSize = 0, endOnInputSize = 1);
|
|
earlyEnd_directive = (full = 0, partial = 1);
|
|
|
|
function LZ4_decompress_generic(const source: pointer; const dest: pointer;
|
|
inputSize: integer; outputSize: integer;
|
|
endOnInput: integer = integer(endOnOutputSize);
|
|
partialDecoding: integer = integer(full); targetOutputSize: integer = 0;
|
|
dict: integer = integer(noDict); const lowPrefix: pByte = nil;
|
|
const dictStart: pByte = nil; const dictSize: size_t = 0): integer;
|
|
|
|
implementation
|
|
|
|
function LZ4_read_ARCH(const p: pointer): size_t; inline;
|
|
begin
|
|
{$IFDEF WIN64}
|
|
result := size_t(pUint64(p)^)
|
|
{$ELSE}
|
|
result := size_t(pCardinal(p)^);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function LZ4_read32(const memPtr: pointer): cardinal;
|
|
begin
|
|
result := pCardinal(memPtr)^;
|
|
end;
|
|
|
|
{$IFDEF WIN64}
|
|
|
|
function LZ4_NbCommonBytesx64(value: size_t): cardinal;
|
|
asm
|
|
bsf rax, rcx // value comes in rcx register
|
|
shr eax, 3
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function LZ4_count(pIn: pByte; pMatch: pByte; const pInLimit: pByte): cardinal;
|
|
var
|
|
pStart: pByte;
|
|
diff: size_t;
|
|
incValue: cardinal;
|
|
|
|
calcedPByte: pByte;
|
|
begin
|
|
pStart := pIn;
|
|
calcedPByte := pInLimit - (STEPSIZE - 1);
|
|
|
|
while pIn < calcedPByte do
|
|
begin
|
|
diff := LZ4_read_ARCH(pMatch) xor LZ4_read_ARCH(pIn);
|
|
if (diff = 0) then
|
|
begin
|
|
inc(pIn, STEPSIZE);
|
|
inc(pMatch, STEPSIZE);
|
|
continue;
|
|
end;
|
|
{$IFDEF WIN32}
|
|
asm
|
|
bsf eax, diff
|
|
shr eax, 3
|
|
mov incValue, eax
|
|
end;
|
|
{$ELSE}
|
|
incValue := LZ4_NbCommonBytesx64(diff);
|
|
// x64 mode does not allow asm inline
|
|
{$ENDIF}
|
|
inc(pIn, incValue);
|
|
exit(cardinal(pIn - pStart));
|
|
end;
|
|
{$IFDEF WIN64}
|
|
if (pIn < (pInLimit - 3)) and (pCardinal(pMatch)^ = pCardinal(pIn)^) then
|
|
begin
|
|
inc(pIn, 4);
|
|
inc(pMatch, 4);
|
|
end;
|
|
{$ENDIF}
|
|
if ((pIn < (pInLimit - 1)) and (pWord(pMatch)^ = pWord(pIn)^)) then
|
|
begin
|
|
inc(pIn, 2);
|
|
inc(pMatch, 2);
|
|
end;
|
|
if ((pIn < pInLimit) and (pMatch^ = pIn^)) then
|
|
inc(pIn);
|
|
result := cardinal(pIn - pStart);
|
|
end;
|
|
|
|
function LZ4_read16(const memPtr: pointer): word; inline;
|
|
begin
|
|
result := pWord(memPtr)^;
|
|
end;
|
|
|
|
function LZ4_read64(const memPtr: pointer): uint64; inline;
|
|
begin
|
|
result := pUint64(memPtr)^;
|
|
end;
|
|
|
|
{$IFDEF WILDCOPY_ASM}
|
|
{$IFDEF WIN32}
|
|
|
|
procedure LZ4_wildCopy;
|
|
// (dstPtr: pointer; const srcPtr: pointer; dstEnd: pointer);
|
|
asm
|
|
push edi
|
|
push esi
|
|
mov edi, eax
|
|
mov esi, edx
|
|
// copyCount := (((e - d) - 1) div 8) * 8 + 8;
|
|
sub ecx, eax // (e - d)
|
|
dec ecx // e - d) - 1)
|
|
shr ecx, 3 // ((e - d) - 1) div 8)
|
|
shl ecx, 3 // ((e - d) - 1) div 8) * 8
|
|
add ecx, 8 // ((e - d) - 1) div 8) * 8 + 8
|
|
|
|
// if copyCount <= 0 then
|
|
// copyCount := 8;
|
|
mov eax, 8
|
|
cmp ecx, 0
|
|
cmovbe ecx, eax
|
|
shr ecx, 2
|
|
rep movsd
|
|
pop esi
|
|
pop edi
|
|
end;
|
|
|
|
{$ELSE}
|
|
|
|
procedure LZ4_wildCopy;
|
|
// (dstPtr: pointer; const srcPtr: pointer; dstEnd: pointer);
|
|
asm
|
|
mov r10, rdi
|
|
mov r11, rsi
|
|
mov rdi, rcx
|
|
mov rsi, rdx
|
|
// copyCount := (((e - d) - 1) div 8) * 8 + 8;
|
|
sub r8, rcx // (dstEnd - dest)
|
|
mov rax, 8
|
|
dec r8 // e - d) - 1)
|
|
shr r8, 3 // ((e - d) - 1) div 8)
|
|
shl r8, 3 // ((e - d) - 1) div 8) * 8
|
|
add r8, rax // ((e - d) - 1) div 8) * 8 + 8
|
|
cmp r8, 0
|
|
cmovbe r8, rax
|
|
mov rcx, r8
|
|
shr rcx, 3
|
|
rep movsq
|
|
mov rdi, r10
|
|
mov rsi, r11
|
|
end;
|
|
{$ENDIF}
|
|
{$ELSE}
|
|
|
|
procedure LZ4_wildCopy(dstPtr: pointer; const srcPtr: pointer;
|
|
dstEnd: pointer); inline;
|
|
var
|
|
d: pByte;
|
|
s: pByte;
|
|
e: pByte;
|
|
begin
|
|
d := dstPtr;
|
|
s := srcPtr;
|
|
e := dstEnd;
|
|
repeat
|
|
{$IFDEF WIN32}
|
|
// pCardinal(d)[0] := pCardinal(s)[0];
|
|
// pCardinal(d)[1] := pCardinal(s)[1];
|
|
pUint64(d)^ := pUint64(s)^;
|
|
{$ELSE}
|
|
pUint64(d)^ := pUint64(s)^;
|
|
{$ENDIF}
|
|
inc(d, 8);
|
|
inc(s, 8);
|
|
until not(d < e);
|
|
end;
|
|
|
|
{$ENDIF}
|
|
|
|
procedure LZ4_writeLE16(memPtr: pointer; value: word); inline;
|
|
begin
|
|
pWord(memPtr)^ := value;
|
|
|
|
end;
|
|
|
|
function LZ4_decompress_generic(const source: pointer; const dest: pointer;
|
|
inputSize: integer; outputSize: integer; endOnInput: integer;
|
|
partialDecoding: integer; targetOutputSize: integer; dict: integer;
|
|
const lowPrefix: pByte; const dictStart: pByte;
|
|
const dictSize: size_t): integer;
|
|
var
|
|
ip: pByte;
|
|
iend: pByte;
|
|
op: pByte;
|
|
oend: pByte;
|
|
cpy: pByte;
|
|
oexit: pByte;
|
|
lowLimit: pByte;
|
|
dictEnd: pByte;
|
|
safeDecode: Boolean;
|
|
checkOffset: Boolean;
|
|
token: cardinal;
|
|
length: size_t;
|
|
match: pByte;
|
|
s: cardinal;
|
|
booleantest: Boolean;
|
|
copySize: size_t;
|
|
endOfMatch: pByte;
|
|
copyFrom: pByte;
|
|
dec64: size_t;
|
|
const
|
|
dec32table: array [0 .. 7] of size_t = (4, 1, 2, 1, 4, 4, 4, 4);
|
|
dec64table: array [0 .. 7] of size_t = (0, 0, 0, size_t(-1), 0, 1, 2, 3);
|
|
label
|
|
_output_error;
|
|
begin
|
|
ip := pByte(source);
|
|
iend := ip + inputSize;
|
|
op := pByte(dest);
|
|
oend := op + outputSize;
|
|
oexit := op + targetOutputSize;
|
|
lowLimit := lowPrefix - dictSize;
|
|
dictEnd := pByte(dictStart) + dictSize;
|
|
safeDecode := (endOnInput = integer(endOnInputSize));
|
|
checkOffset := ((safeDecode) and (dictSize < 65536));
|
|
|
|
if (partialDecoding <> 0) and (oexit > oend - _MFLIMIT) then
|
|
oexit := oend - _MFLIMIT;
|
|
if (endOnInput <> 0) and (outputSize = 0) then
|
|
begin
|
|
if (inputSize = 1) and (ip^ = 0) then
|
|
exit(0)
|
|
else
|
|
exit(-1);
|
|
end;
|
|
if (endOnInput = 0) and (outputSize = 0) then
|
|
begin
|
|
if ip^ = 0 then
|
|
exit(1)
|
|
else
|
|
exit(-1);
|
|
end;
|
|
|
|
while True do
|
|
begin
|
|
token := ip^;
|
|
inc(ip);
|
|
length := token shr ML_BITS;
|
|
if length = RUN_MASK then
|
|
begin
|
|
while True do
|
|
begin
|
|
s := ip^;
|
|
inc(ip);
|
|
inc(length, s);
|
|
if endOnInput <> 0 then
|
|
begin
|
|
if not(ip < iend - RUN_MASK) then
|
|
break;
|
|
end;
|
|
if s <> 255 then
|
|
break;
|
|
end;
|
|
if safeDecode and (size_t(op + length) < size_t(op)) then
|
|
goto _output_error;
|
|
if safeDecode and (size_t(ip + length) < size_t(ip)) then
|
|
goto _output_error;
|
|
end;
|
|
cpy := op + length;
|
|
if partialDecoding <> 0 then
|
|
booleantest := cpy > oexit
|
|
else
|
|
booleantest := cpy > oend - _MFLIMIT;
|
|
|
|
if ((endOnInput <> 0) and ((booleantest) or
|
|
(ip + length > iend - (2 + 1 + LASTLITERALS)))) or
|
|
((endOnInput = 0) and (cpy > oend - COPYLENGTH)) then
|
|
begin
|
|
if partialDecoding <> 0 then
|
|
begin
|
|
if (cpy > oend) then
|
|
goto _output_error;
|
|
if ((endOnInput <> 0) and (ip + length > iend)) then
|
|
goto _output_error;
|
|
end
|
|
else
|
|
begin
|
|
if ((endOnInput = 0) and (cpy <> oend)) then
|
|
goto _output_error;
|
|
if ((endOnInput <> 0) and ((ip + length <> iend) or (cpy > oend))) then
|
|
goto _output_error;
|
|
end;
|
|
move(ip^, op^, length);
|
|
inc(ip, length);
|
|
inc(op, length);
|
|
break;
|
|
end;
|
|
LZ4_wildCopy(op, ip, cpy);
|
|
inc(ip, length);
|
|
op := cpy;
|
|
match := cpy - LZ4_read16(ip); // LZ4_readLE16 = LZ4_read16 for unaligned
|
|
inc(ip, 2);
|
|
|
|
if checkOffset and (match < lowLimit) then
|
|
goto _output_error;
|
|
|
|
length := token and ML_MASK;
|
|
if length = ML_MASK then
|
|
begin
|
|
while True do
|
|
begin
|
|
if ((endOnInput <> 0) and (ip > iend - LASTLITERALS)) then
|
|
goto _output_error;
|
|
s := ip^;
|
|
inc(ip);
|
|
inc(length, s);
|
|
if s <> 255 then
|
|
break;
|
|
end;
|
|
if safeDecode and (size_t(op + length) < size_t(op)) then
|
|
goto _output_error;
|
|
end;
|
|
inc(length, MINMATCH);
|
|
if (dict = integer(usingExtDict)) and (match < lowPrefix) then
|
|
begin
|
|
if op + length > oend - LASTLITERALS then
|
|
goto _output_error;
|
|
if (length <= size_t(lowPrefix - match)) then
|
|
begin
|
|
match := dictEnd - (lowPrefix - match);
|
|
move(match^, op^, length);
|
|
inc(op, length);
|
|
end
|
|
else
|
|
begin
|
|
copySize := size_t(lowPrefix - match);
|
|
move((dictEnd - copySize)^, op^, copySize);
|
|
inc(op, copySize);
|
|
copySize := length - copySize;
|
|
if copySize > size_t(op - lowPrefix) then
|
|
begin
|
|
endOfMatch := op + copySize;
|
|
copyFrom := lowPrefix;
|
|
while (op < endOfMatch) do
|
|
begin
|
|
op^ := copyFrom^;
|
|
inc(op);
|
|
inc(copyFrom);
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
move(lowPrefix^, op^, copySize);
|
|
inc(op, copySize);
|
|
end;
|
|
end;
|
|
continue;
|
|
end;
|
|
cpy := op + length;
|
|
if (op - match) < 8 then
|
|
begin
|
|
dec64 := dec64table[op - match];
|
|
op[0] := match[0];
|
|
op[1] := match[1];
|
|
op[2] := match[2];
|
|
op[3] := match[3];
|
|
inc(match, dec32table[op - match]);
|
|
pCardinal(op + 4)^ := pCardinal(match)^;
|
|
inc(op, 8);
|
|
dec(match, dec64);
|
|
end
|
|
else
|
|
begin
|
|
{$IFDEF WIN64}
|
|
pUint64(op)^ := pUint64(match)^;
|
|
{$ELSE}
|
|
pCardinal(op)[0] := pCardinal(match)[0];
|
|
pCardinal(op)[1] := pCardinal(match)[1];
|
|
{$ENDIF}
|
|
inc(op, 8);
|
|
inc(match, 8);
|
|
end;
|
|
|
|
if cpy > oend - 12 then
|
|
begin
|
|
if (cpy > oend - LASTLITERALS) then
|
|
goto _output_error;
|
|
if (op < oend - 8) then
|
|
begin
|
|
LZ4_wildCopy(op, match, oend - 8);
|
|
inc(match, (oend - 8) - op);
|
|
op := oend - 8;
|
|
end;
|
|
while (op < cpy) do
|
|
begin
|
|
op^ := match^;
|
|
inc(op);
|
|
inc(match);
|
|
end;
|
|
end
|
|
else
|
|
LZ4_wildCopy(op, match, cpy);
|
|
op := cpy;
|
|
end;
|
|
if (endOnInput <> 0) then
|
|
result := integer(op - pByte(dest))
|
|
else
|
|
result := integer(ip - pByte(source));
|
|
exit;
|
|
_output_error:
|
|
result := -(ip - pByte(source)) - 1;
|
|
end;
|
|
|
|
end.
|