xtool/sources/lz4.pas

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.