Skip to content

Instantly share code, notes, and snippets.

@XProger
Last active August 29, 2015 14:05
Show Gist options
  • Save XProger/3527690ee5673b33b4c3 to your computer and use it in GitHub Desktop.
Save XProger/3527690ee5673b33b4c3 to your computer and use it in GitHub Desktop.
Inflate zlib & gzip compressed data
unit unzip;
// based on C++ core "tiny inflate" by Joergen Ibsen / Jibz
// http://www.ibsensoftware.com/
interface
type
TByteArray = array [Word] of Byte;
TWordArray = array [Word] of Word;
PByteArray = ^TByteArray;
PWordArray = ^TWordArray;
{$R-}
const
TINF_OK = 0;
TINF_DATA_ERROR = -3;
type
TINF_TREE = record
table : array [0..15] of Word;
trans : array [0..288] of Word;
end;
TINF_DATA = record
source : PByteArray;
tag : LongWord;
bitcount : LongWord;
dest : PByteArray;
destLen : LongWord;
ltree : TINF_TREE;
dtree : TINF_TREE;
end;
TDecompressor = class
constructor Create;
private
sltree : TINF_TREE;
sdtree : TINF_TREE;
public
function ReadBlock(Source, Dest: PByteArray): LongInt;
function ReadZLIB(Source, Dest: PByteArray): LongInt;
function ReadGZIP(Source, Dest: PByteArray): LongInt;
function SizeGZIP(Source: PByteArray; CSize: LongWord): LongWord;
end;
implementation
var
gsltree : TINF_TREE;
gsdtree : TINF_TREE;
length_bits : array [0..29] of Byte;
length_base : array [0..29] of Word;
dist_bits : array [0..29] of Byte;
dist_base : array [0..29] of Word;
procedure tinf_build_bits_base(bits: PByteArray; base: PWordArray; delta, first: LongInt);
var
i, sum : LongInt;
begin
FillChar(bits[0], delta, 0);
for i := 0 to 30 - delta - 1 do
bits[i + delta] := i div delta;
sum := first;
for i := 0 to 29 do
begin
base[i] := sum;
sum := sum + 1 shl bits[i];
end;
end;
procedure tinf_build_fixed_trees(var lt, dt: TINF_TREE);
var
i : LongInt;
begin
FillChar(lt.table[0], 7 * SizeOf(lt.table[0]), 0);
lt.table[7] := 24;
lt.table[8] := 152;
lt.table[9] := 112;
for i := 0 to 23 do lt.trans[i] := 256 + i;
for i := 0 to 143 do lt.trans[i + 24] := i;
for i := 0 to 7 do lt.trans[i + 168] := 280 + i;
for i := 0 to 111 do lt.trans[i + 176] := 144 + i;
FillChar(dt.table[0], 5 * SizeOf(dt.table[0]), 0);
dt.table[5] := 32;
for i := 0 to 31 do dt.trans[i] := i;
end;
procedure tinf_build_tree(var t: TINF_TREE; lengths: PByteArray; num: LongInt);
var
offs : array [0..15] of Word;
i, sum : LongWord;
begin
FillChar(t.table[0], 16 * SizeOf(t.table[0]), 0);
for i := 0 to num - 1 do Inc(t.table[lengths[i]]);
t.table[0] := 0;
sum := 0;
for i := 0 to 15 do
begin
offs[i] := sum;
Inc(sum, t.table[i]);
end;
for i := 0 to num - 1 do
if lengths[i] <> 0 then
begin
t.trans[offs[lengths[i]]] := i;
Inc(offs[lengths[i]]);
end;
end;
function tinf_getbit(var d: TINF_DATA): LongInt;
var
bit : LongWord;
begin
if d.bitcount = 0 then
begin
d.tag := d.source[0];
d.source := @d.source[1];
d.bitcount := 8;
end;
Dec(d.bitcount);
bit := d.tag and 1;
d.tag := d.tag shr 1;
Result := bit;
end;
function tinf_read_bits(var d: TINF_DATA; num, base: LongInt): LongWord;
var
val, limit, mask : LongWord;
begin
val := 0;
if num <> 0 then
begin
limit := 1 shl num;
mask := 1;
while mask < limit do
begin
if tinf_getbit(d) <> 0 then
Inc(val, mask);
mask := mask * 2;
end;
end;
Result := val + LongWord(base);
end;
function tinf_decode_symbol(var d: TINF_DATA; const t: TINF_TREE): LongInt;
var
sum, cur, len : LongInt;
begin
sum := 0; cur := 0; len := 0;
repeat
cur := 2 * cur + tinf_getbit(d);
Inc(len);
Inc(sum, t.table[len]);
Dec(cur, t.table[len]);
until (cur < 0);
Result := t.trans[sum + cur];
end;
procedure tinf_decode_trees(var d: TINF_DATA; var lt, dt: TINF_TREE);
const
clcidx : array [0..18] of Byte = (16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15);
var
code_tree : TINF_TREE;
lengths : array [0..288+32 - 1] of Byte;
hlit, hdist, hclen, clen, i, num, length : LongWord;
sym : LongInt;
prev : Byte;
begin
hlit := tinf_read_bits(d, 5, 257);
hdist := tinf_read_bits(d, 5, 1);
hclen := tinf_read_bits(d, 4, 4);
FillChar(lengths[0], 19, 0);
for i := 0 to hclen - 1 do
begin
clen := tinf_read_bits(d, 3, 0);
lengths[clcidx[i]] := clen;
end;
tinf_build_tree(code_tree, @lengths, 19);
num := 0;
while num < hlit + hdist do
begin
sym := tinf_decode_symbol(d, code_tree);
case sym of
16 :
begin
prev := lengths[num - 1];
length := tinf_read_bits(d, 2, 3);
FillChar(lengths[num], length, prev);
Inc(num, length);
end;
17 :
begin
length := tinf_read_bits(d, 3, 3);
FillChar(lengths[num], length, 0);
Inc(num, length);
end;
18 :
begin
length := tinf_read_bits(d, 7, 11);
FillChar(lengths[num], length, 0);
Inc(num, length);
end;
else
lengths[num] := sym;
Inc(num);
end;
end;
tinf_build_tree(lt, @lengths, hlit);
tinf_build_tree(dt, @lengths[hlit], hdist);
end;
function tinf_inflate_block_data(var d: TINF_DATA; var lt, dt: TINF_TREE): LongInt;
var
Start : PByteArray;
i, length, dist, offs, sym : LongInt;
begin
start := d.dest;
while true do
begin
sym := tinf_decode_symbol(d, lt);
if sym = 256 then
begin
Inc(d.destLen, LongWord(d.dest) - LongWord(start));
break;
end;
if sym < 256 then
begin
d.dest[0] := sym;
d.dest := @d.dest[1];
end else
begin
Dec(sym, 257);
length := tinf_read_bits(d, length_bits[sym], length_base[sym]);
dist := tinf_decode_symbol(d, dt);
offs := tinf_read_bits(d, dist_bits[dist], dist_base[dist]);
for i := 0 to length - 1 do
d.dest[i] := d.dest[i - offs];
d.dest := @d.dest[length];
end;
end;
Result := TINF_OK;
end;
function tinf_inflate_uncompressed_block(var d: TINF_DATA): LongInt;
var
length, invlength : LongWord;
begin
length := d.source[0] + 256 * d.source[1];
invlength := d.source[2] + 256 * d.source[3];
if (length <> (not invlength) and $FFFF) then
begin
Result := TINF_DATA_ERROR;
Exit;
end;
d.source := @d.source[4];
Move(d.source[0], d.dest[0], length);
d.dest := @d.dest[length];
d.source := @d.source[length];
d.bitcount := 0;
Inc(d.destLen, length);
Result := TINF_OK;
end;
function tinf_inflate_dynamic_block(var d: TINF_DATA): LongInt;
begin
tinf_decode_trees(d, d.ltree, d.dtree);
Result := tinf_inflate_block_data(d, d.ltree, d.dtree);
end;
constructor TDecompressor.Create;
begin
inherited Create;
Move(gsltree, sltree, SizeOf(TINF_TREE));
Move(gsdtree, sdtree, SizeOf(TINF_TREE));
end;
function TDecompressor.ReadBlock(Source, Dest: PByteArray): LongInt;
var
d : TINF_DATA;
btype : LongWord;
bfinal, res : LongInt;
begin
d.source := Source;
d.bitcount := 0;
d.dest := Dest;
d.destLen := 0;
repeat
bfinal := tinf_getbit(d);
btype := tinf_read_bits(d, 2, 0);
case btype of
0 : res := tinf_inflate_uncompressed_block(d);
1 : res := tinf_inflate_block_data(d, sltree, sdtree);
2 : res := tinf_inflate_dynamic_block(d);
else
res := TINF_DATA_ERROR;
end;
until (bfinal <> 0) or (res <> TINF_OK);
Result := d.destLen;
end;
function TDecompressor.ReadZLIB(Source, Dest: PByteArray): LongInt;
begin
Result := ReadBlock(@Source[2], Dest);
end;
function TDecompressor.ReadGZIP(Source, Dest: PByteArray): LongInt;
const
FTEXT = 1;
FHCRC = 2;
FEXTRA = 4;
FNAME = 8;
FCOMMENT = 16;
var
Flag : Byte;
Start : PByteArray;
begin
Result := 0; // not inflate
if Source[2] <> 8 then
Exit;
Flag := Source[3];
if Flag and $E0 > 0 then
Exit;
Start := @Source[10];
// skip extra info
if Flag and FEXTRA > 0 then
Start := @Start[Start[0] + Start[1] * 256 + 2];
// skip file name
if Flag and FNAME > 0 then
begin
while Start[0] <> 0 do
Start := @Start[1];
Start := @Start[1];
end;
// skip file comment
if Flag and FCOMMENT > 0 then
begin
while Start[0] <> 0 do
Start := @Start[1];
Start := @Start[1];
end;
// skip crc
if Flag and FHCRC > 0 then
Start := @Start[Start[0] + Start[1] * 256 + 2];
Result := ReadBlock(Start, Dest);
end;
function TDecompressor.SizeGZIP(Source: PByteArray; CSize: LongWord): LongWord;
begin
Result := Source[CSize - 1];
Result := 256 * Result + Source[CSize - 2];
Result := 256 * Result + Source[CSize - 3];
Result := 256 * Result + Source[CSize - 4];
end;
initialization
tinf_build_fixed_trees(gsltree, gsdtree);
tinf_build_bits_base(@length_bits, @length_base, 4, 3);
tinf_build_bits_base(@dist_bits, @dist_base, 2, 1);
length_bits[28] := 0;
length_base[28] := 258;
end.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment