GZip und deflate (ZLib) unterscheidet sich lediglich durch den Header und die Checksumme, denn beide benutzt im Eigentlichen die selbe Methode zum Komprimieren (deflate = Luft heraus lassen :) )
Unter Free Pascal steht das Package paszlib zur Verfügung, welches die benötigten Funktionen zum Behandeln von deflated Blöcken zur Verfügung stellt. Mit Hilfe dieser Funktionen lassen sich alle benötigten Funktionen deflate (raw ), GZip , ZLib , inflate (raw ) und ZUncompressStream erzeugen. ZUncompessStream übernimmt die Identifizierung des Typs und nutzt die Funktion inflate zum entpacken des deflated Teils.
Die Routinen sollten dazu in der Lage sein sowohl deflated (ZLib) und gziped Streams zum Versand zu erzeugen, als auch Empfangene Streams dieser Formate wieder zu entpacken!
Aufbau
Deflate (ZLib): ZLIB Compressed Data Format Specification
2 bytes Defines the compression mode
1 byte $78 CMF Compression Method and flags "deflate" compression method with a window size up to 32K
1 byte FLG
bit 0-4 FCHECK (check bits for CMF and FLG)
bit 5 FDICT (preset dictionary)
bit 6-7 FLEVEL (compression level)
Examples: (List of file signatures )
$01 No Compression (no preset dictionary)
$5E Best speed (no preset dictionary)
$9C Default Compression (no preset dictionary)
$DA Best Compression (no preset dictionary)
$20 No Compression (with preset dictionary)
$7D Best speed (with preset dictionary)
$BB Default Compression (with preset dictionary)
$F9 Best Compression (with preset dictionary)
4 bytes DICTID Present only when FLG.FDICT is set.
deflated stream
4 bytes adler32 checksum
GZip: GZIP file format specification
2 bytes $1f $8b (IDentification)
1 byte $08 Compression Method = deflate
1 byte $00 FLaGs
bit 0 FTEXT - indicates file is ASCII text (can be safely ignored)
bit 1 FHCRC - there is a CRC16 for the header immediately following the header
bit 2 FEXTRA - extra fields are present
bit 3 FNAME - the zero-terminated filename is present. encoding; ISO-8859-1.
bit 4 FCOMMENT - a zero-terminated file comment is present. encoding: ISO-8859-1
bit 5-7 reserved
4 bytes $00000000 Modification TIME = no time stamp is available
1 byte $00 eXtra FLags
00 - default compression
02 - compressor used maximum compression, slowest algorithm
04 - compressor used fastest algorithm
1 byte $0b Operating System = NTFS filesystem (NT) for textfiles (line ending)
00 - FAT filesystem (MS-DOS, OS/2, NT/Win32)
01 - Amiga 02 - VMS (or OpenVMS)
03 - Unix
04 - VM/CMS
05 - Atari TOS
06 - HPFS filesystem (OS/2, NT)
07 - Macintosh
08 - Z-System
09 - CP/M
0A - TOPS-20
0B - NTFS filesystem (NT)
0C - QDOS
0D - Acorn RISCOS
FF - unknown
deflated stream
crc32 checksum
input size
GZIPUtils.pas Pascal (9,94 kByte) 16.04.2022 09:02
unit GZIPUtils;
{$ifdef fpc}
{$mode objfpc}
{$endif}
{$H+}
interface
uses Classes, SysUtils, PasZLib, zbase;
type
TZCompressionLevel = (zcNone, zcFastest, zcDefault, zcMax);
TZStreamType = (
zsZLib, zsGZip, zsRaw, zsNo );
TGZipHeaderFLags = (
FTEXT, FHCRC, FEXTRA, FNAME, FCOMMENT );
TFlags = set of TGZipHeaderFLags;
const
Z_BUFSIZE = 65536 ; GZIP_WBITS = MAX_WBITS + 16 ; ZLIB_WBITS = MAX_WBITS; RAW_WBITS = - MAX_WBITS;
ZLevels: array [TZCompressionLevel] of Shortint = (
Z_NO_COMPRESSION,
Z_BEST_SPEED,
Z_DEFAULT_COMPRESSION,
Z_BEST_COMPRESSION
);
function zipStream (inStream, outStream: TMemoryStream; level: TZCompressionLevel = zcDefault; streamType: TZStreamType = zsZLib): boolean;
function unzipStream (inStream, outStream: TMemoryStream): boolean;
implementation
function zipStream (inStream, outStream: TMemoryStream; level: TZCompressionLevel = zcDefault; streamType: TZStreamType = zsZLib): boolean;
var
zstream: z_stream;
crc, size, adler, headerSize: longword;
begin
result := false ;
inStream.Position := 0 ; outStream.Position := 0 ; if StreamType = zsGZip then begin
size := inStream.Size;
crc := crc32(0 , Pointer(inStream.Memory), size);
outStream.WriteWord($8b1f ); outStream.WriteByte($08 ); outStream.WriteByte($00 ); outStream.WriteDWord($00000000 ); outStream.WriteByte($00 ); outStream.WriteByte({$ifdef win32} $0b{$else} $03{$endif} ); end
else if StreamType = zsZLib then begin
outStream.WriteWord($9c78 ); adler := adler32(0 , Z_NULL, 0 );
adler := adler32(adler, Pointer(inStream.Memory), inStream.Size);
end ;
headerSize := outStream.Position;
zstream.next_in := inStream.Memory;
zstream.avail_in := inStream.Size;
outStream.SetSize(headerSize + ((inStream.Size + (inStream.Size div 10 ) + 12 ) + 255 ) and not 255 );
zstream.next_out := outStream.Memory + headerSize;
zstream.avail_out := outStream.Size - headerSize;
if deflateInit2(zstream, ZLevels[level], Z_DEFLATED, RAW_WBITS, 8 , Z_DEFAULT_STRATEGY) < Z_OK then Exit ;
deflate(zstream, Z_FINISH);
result := not (deflateEnd(zstream) < 0 );
outStream.SetSize(zstream.total_out + headerSize);
outStream.Position := zstream.total_out + headerSize;
if result and (StreamType = zsGZip) then begin
outStream.WriteDWord(crc); outStream.WriteDWord(size); end
else if result and (StreamType = zsZLib) then outStream.WriteDWord(SwapEndian(adler));
outStream.Position := 0 ; end ;
function crc16 (crc: word; data: Pbyte; len: Cardinal): word;
var sum: Cardinal;
begin
sum := crc;
while len > 1 do
begin
inc(sum, PWord(data)^);
inc(data, 2 );
dec(len, 2 )
end ;
if len > 0 then
inc(sum, PByte(data)^);
while (sum shr 16 ) > 0 do
sum := (sum and $ffff ) + (sum shr 16 );
result := not word(sum);
end ;
function unzipStream (inStream, outStream: TMemoryStream): boolean;
var
streamType: TZStreamType;
zstream: z_stream;
hdr, crc, adler, adler32in, crcGZin, sizeGZin, delta, headerSize, modificationtime: longword;
len, crcH, crcHeader: word;
b: byte;
flags: TFlags;
begin
result := false ;
inStream.Position := 0 ; outStream.Position := 0 ; sizeGZin := 0 ;
hdr := inStream.ReadDWord;
if (hdr and $00088B1F ) = $00088B1F then begin
streamType := zsGZip; modificationtime := inStream.ReadDWord; inStream.ReadWord; flags := TFlags(hdr shr 24 ); if (FEXTRA in flags) then begin
len := inStream.ReadWord; inStream.Seek(len, soFromCurrent); end ;
if (FNAME in flags) then begin
b := inStream.ReadByte;
while b <> 0 do
begin
b := inStream.ReadByte;
end ;
end ;
if (FCOMMENT in flags) then begin
b := inStream.ReadByte;
while b <> 0 do
begin
b := inStream.ReadByte;
end ;
end ;
if (FHCRC in flags) then begin
crcH := crc16 (0 , pointer(inStream.Memory), inStream.Position); crcHeader := inStream.ReadWord; if crcH<> crcHeader then
; end ;
headerSize := inStream.Position;
inStream.Seek(- 8 , soFromEnd);
crcGZin := inStream.ReadDWord; sizeGZin := inStream.ReadDWord; inStream.Size := inStream.Size- 8 ; end
else if (hdr and $00000078 ) = $00000078 then begin
streamType := zsZLib; if (hdr and $00002000 ) = $00002000 then headerSize := 6
else
headerSize := 2 ;
inStream.Seek(- 4 , soFromEnd); adler32in := SwapEndian(inStream.ReadDWord);
inStream.Size := inStream.Size- 4 ; end
else
begin
streamType := zsRaw; headerSize := 0 ;
end ;
inStream.Position := headerSize; zstream.next_in := inStream.Memory + headerSize;
zstream.avail_in := inStream.Size - headerSize;
delta := (inStream.Size + 255 ) and not 255 ;
if (streamType = zsGZip) then
outStream.SetSize(sizeGZin)
else
outStream.SetSize(delta);
zstream.next_out := outStream.Memory;
zstream.avail_out := outStream.Size;
if inflateInit2(zstream, RAW_WBITS) < 0 then Exit ;
while inflate(zstream, Z_NO_FLUSH) = Z_OK do
begin
outStream.SetSize(outStream.Size + delta);
zstream.next_out := outStream.Memory + zstream.total_out;
zstream.avail_out := delta;
end ;
result := not (inflateEnd(zstream) < 0 );
outStream.SetSize(zstream.total_out);
if result and (streamType = zsGZip) then begin
crc := crc32(0 , Pointer(outStream.Memory), outStream.Size); result := (crc = crcGZin) and (outStream.Size = sizeGZin); end
else if result and (streamType = zsZLib) then begin
adler := adler32(0 , Z_NULL, 0 );
adler := adler32(adler, Pointer(outStream.Memory), outStream.Size);
result := (adler = adler32in);
end ;
inStream.Position := 0 ; outStream.Position := 0 ; end ;
end .
Beispiel:
TestGzip.pas Pascal (2,13 kByte) 19.10.2016 15:50
program TestGzip;
{$mode objfpc} {$H+}
uses
{$IFDEF UNIX} {$IFDEF UseCThreads}
cthreads,
{$ENDIF} {$ENDIF}
Classes, SysUtils, GZIPUtils
;
procedure TestGZipUnGzip ;
var
MemoryStream, GZipStream: TMemoryStream;
str1, str2: string ;
StartTime, EndTime: QWord;
i: integer;
begin
StartTime := GetTickCount64();
MemoryStream := TMemoryStream.Create;
MemoryStream.LoadFromFile('browscap.ini' );
WriteLn('source size:' + IntToStr(MemoryStream.Size));
MemoryStream.Seek(0 , soFromBeginning);
SetLength(str1, MemoryStream.Size);
MemoryStream.ReadBuffer(PChar(str1)^, MemoryStream.Size);
GZipStream := TMemoryStream.Create;
StartTime := GetTickCount64();
if zipStream(MemoryStream, GZipStream, zcDefault, zsGZip) then
begin
EndTime := GetTickCount64();
WriteLn('gzip ok: ' + IntToStr(EndTime - StartTime) + ' ms' );
end
else
WriteLn('gzip failed' );
GZipStream.SaveToFile('browscap.ini.gz' );
MemoryStream.Clear;
GZipStream.LoadFromFile('browscap.ini.gz' );
StartTime := GetTickCount64();
if unzipStream(GZipStream, MemoryStream) then
begin
EndTime := GetTickCount64();
WriteLn('ungzip ok: ' + IntToStr(EndTime - StartTime) + ' ms' );
end
else
WriteLn('ungzip failed' );
WriteLn('ungzip size:' + IntToStr(MemoryStream.Size));
MemoryStream.SaveToFile('browscap2.ini' );
MemoryStream.Seek(0 , soFromBeginning);
SetLength(str2, MemoryStream.Size);
MemoryStream.ReadBuffer(PChar(str2)^, MemoryStream.Size);
if str1 <> str2 then
WriteLn('Strings do not match.' )
else
WriteLn('Strings match.' );
FreeAndNil(MemoryStream);
FreeAndNil(GZipStream);
end ;
begin
TestGZipUnGzip ;
Writeln('Finished' );
Readln;
end .