Mega Code Archive

 
Categories / Delphi / Algorithm Math
 

Simple Implementation of LZW CompressionDecompression Algorithm

Title: Simple Implementation of LZW Compression/Decompression Algorithm Question: How do I Compress and Decompress fils using LZW Algorithm. Answer: Here is a simple implemntation of LZW compression/Decompression algorithm. It is not fast and compression ratio is very small. Here is the code. ------------------------------------------------------------------------------- unit RevLZW; interface uses sysutils,classes,dialogs,windows; const tabsize:integer=4095; copybyte:integer=0; compbyte:integer=1; endlist:integer=-1; nochar:integer=-2; empty:integer=-3; eofchar:integer=-4; bufsize:integer=32768; maxstack:integer=4096; type TStringObject = record prevchar:integer; nextchar:integer; next:integer; used:boolean; nused:integer; flocked:boolean; end; procedure Initialize; procedure Terminate; function OpenInputFile(fname:string):boolean; function OpenOutputFile(fname:string):boolean; function getbyte:integer; procedure putbyte(c:integer); procedure compress; procedure decompress; procedure putcode(code:integer;lbyte:boolean=false); function getcode:integer; function GetHashCode(prevc,nextc:integer):integer; function findstring(prevc,nextc:integer):integer; function MakeTableEntry(prevc:integer;nextc:integer):boolean; procedure push(c:integer); procedure pop(var c:integer); procedure InitializeStringTable; var fsize:integer; fread,fwrote:integer; ihandle,ohandle:integer; inbufpos,outbufpos:integer; objectid:integer; stringtable:array[0..4095] of TstringObject; inblock:array[0..65535{32767}] of char; outblock:array[0..65535{32767}] of char; stack:array[0..4095] of char; stackpointer:integer; rembits:integer; lastbyte:boolean; rembitcount:integer; lzwerr:boolean; imap,omap:integer; implementation function OpenInputFile(fname:string):boolean; begin result:=true; ihandle:=fileopen(fname,fmShareExclusive or fmOpenRead); fsize:=getfilesize(ihandle,nil); if fsize fileread(ihandle,inblock,fsize) else fileread(ihandle,inblock,32768); if ihandle=-1 then result:=false; end; function OpenOutputFile(fname:string):boolean; begin result:=true; ohandle:=filecreate(fname); if ohandle=-1 then result:=false; end; function getbyte:integer; begin if inbufpos=32768 then begin inbufpos:=0; fileread(ihandle,inblock,32768); end; if fread=fsize then result:=eofchar else result:=integer(inblock[inbufpos]); inc(inbufpos); inc(fread); end; procedure putbyte(c:integer); begin if outbufpos=32768 then begin outbufpos:=0; filewrite(ohandle,outblock,32768); end; outblock[outbufpos]:=char(c); inc(outbufpos); inc(fwrote); end; procedure Initialize; begin inbufpos:=0; outbufpos:=0; fread:=0; fwrote:=0; objectid:=0; stackpointer:=0; lastbyte:=false; rembits:=empty; rembitcount:=0; lzwerr:=false; InitializeStringtable; end; procedure InitializeStringTable; var i:integer; begin objectid:=0; for i:=0 to 4095 do begin with stringtable[i] do begin if not flocked then begin prevchar:=nochar; nextchar:=nochar; next:=endlist; used:=false; nused:=0; flocked:=false; end; end; if i begin stringtable[i].nextchar:=i; stringtable[i].used:=true; inc(objectid); end; end; end; procedure Terminate; begin if outbufpos0 then filewrite(ohandle,outblock,outbufpos); setendoffile(ohandle); fileclose(ihandle); fileclose(ohandle); end; function GetHashCode(prevc,nextc:integer):integer; var index,newindex:integer; begin index:= ((prevc shl 5) xor nextc) and tabsize; if not stringtable[index].used then result:=index else begin while stringtable[index].nextendlist do index:=stringtable[index].next; newindex:=index and tabsize; while stringtable[newindex].used do newindex:=succ(newindex) and tabsize; stringtable[index].next:=newindex; result:=newindex; end; end; function findstring(prevc,nextc:integer):integer; var index:integer; found:boolean; begin result:=endlist; if (prevc=nochar) and (nextc result:=nextc else begin index:=((prevc shl 5) xor nextc) and tabsize; repeat found:=(stringtable[index].prevchar=prevc) and(stringtable[index].nextchar=nextc); if not found then index:=stringtable[index].next; until found or (index = endlist); if found then begin result:=index; inc(stringtable[index].nused); end; end; end; function MakeTableEntry(prevc:integer;nextc:integer):boolean; var index:integer; begin result:=true; if objectid begin index:=gethashcode(prevc,nextc); with stringtable[index] do begin prevchar:=prevc; nextchar:=nextc; used:=true; end; inc(objectid); if objectid=tabsize+1 then result:=false; end; end; procedure putcode(code:integer;lbyte:boolean); var tmpcode:integer; begin if stringtable[code].prevchar=nochar then begin if rembitcount begin tmpcode:=(rembits shl (8-rembitcount)) or (copybyte shl (7-rembitcount)) or ((code shr (rembitcount+1)) and ($7F shr rembitcount)); putbyte(tmpcode); inc(fwrote); rembits:= code and ($FF shr(7-rembitcount)); inc(rembitcount); end else if rembitcount=7 then begin tmpcode:=(rembits shl 1) or copybyte; putbyte(tmpcode); inc(fwrote,2); putbyte(code); rembits:=empty; rembitcount:=0; end; end else begin tmpcode:=(rembits shl (8-rembitcount)) or (compbyte shl(7-rembitcount)) or (code shr (5+rembitcount) and ($7F shr rembitcount)); putbyte(tmpcode); inc(fwrote); rembitcount:=rembitcount+5; if rembitcount rembits:=code and($FF shr(8-rembitcount)); if rembitcount=8 then begin rembits:=(code shr(rembitcount-8)) and $FF; inc(fwrote); putbyte(rembits); rembitcount:=rembitcount-8; rembits:=code and ($FF shr(8-rembitcount)); end; end; if lbyte and (rembitcount0) then begin tmpcode:=((rembits and ($FF shr (8-rembitcount))) shl (8-rembitcount)); putbyte(tmpcode); inc(fwrote); end; end; function getcode:integer; var part1,part2:integer; iscomp:integer; c1,c2:integer; begin result:=eofchar; if (fread=fsize) and (rembitcount=0) then begin result:=eofchar; exit; end; if rembitcount=0 then begin part1:=getbyte; part2:=getbyte; iscomp:=(part1 shr 7) and 1; if iscomp=1 then begin c1:=part1 and $7F; c2:=(part2 shr 3) and $1F; rembits:=part2 and $7; rembitcount:=3; result:=(c1 shl 5) or c2; end else if iscomp=0 then begin c1:=part1 and $7F; c2:=(part2 shr 7) and $1; result:=(c1 shl 1) or c2; rembits:=part2 and $7F; rembitcount:=7; end; end else if rembitcount=1 then begin part1:=getbyte; iscomp:=rembits; if iscomp=1 then begin part2:=getbyte; c1:=part1 and $FF; c2:=(part2 shr 4) and $F; rembits:=part2 and $F; rembitcount:=4; result:=(c1 shl 4) or c2; end else if iscomp=0 then begin c1:=part1 and $FF; result:=c1; rembits:=empty; rembitcount:=0; end; end else if rembitcount=2 then begin part1:=getbyte; iscomp:=(rembits shr 1) and 1; if iscomp=1 then begin part2:=getbyte; c1:=((rembits and 1) shl 7) or ((part1 shr 1) and $7F); c2:=((part1 and 1) shl 3) or ((part2 shr 5) and $7); rembits:=part2 and $1F; rembitcount:=5; result:=(c1 shl 4) or (c2 and $F); end else if iscomp=0 then begin c1:=((rembits and 1) shl 7) or ((part1 shr 1) and $7F); result:=c1; rembits:=part1 and 1; rembitcount:=1; end; end else if rembitcount=3 then begin part1:=getbyte; iscomp:=(rembits shr 2) and 1; if iscomp=1 then begin part2:=getbyte; c1:=((rembits and $3) shl 6) or ((part1 shr 2) and $3F); c2:=((part1 and $3) shl 2) or ((part2 shr 6) and $3); rembits:=part2 and $3F; rembitcount:=6; result:=(c1 shl 4) or (c2 and $F); end else if iscomp=0 then begin c1:=((rembits and $3) shl 6) or ((part1 shr 2) and $3F); result:=c1; rembits:=part1 and $3; rembitcount:=2; end; end else if rembitcount=4 then begin part1:=getbyte; iscomp:=(rembits shr 3) and 1; if iscomp=1 then begin part2:=getbyte; c1:=((rembits and $7) shl 5) or ((part1 shr 3) and $1F); c2:=((part1 and $7) shl 1) or ((part2 shr 7) and $1); rembits:=part2 and $7F; rembitcount:=7; result:=(c1 shl 4) or (c2 and $F); end else if iscomp=0 then begin c1:=((rembits and $7) shl 5) or ((part1 shr 3) and $1F); result:=c1; rembits:=part1 and $7; rembitcount:=3; end; end else if rembitcount=5 then begin part1:=getbyte; iscomp:=(rembits shr 4) and 1; if iscomp=1 then begin c1:=((rembits and $F) shl 4) or ((part1 shr 4) and $F); c2:=part1 and $F; rembits:=empty; rembitcount:=0; result:=(c1 shl 4) or (c2 and $F); end else if iscomp=0 then begin c1:=((rembits and $F) shl 4) or ((part1 shr 4) and $F); result:=c1; rembits:=part1 and $F; rembitcount:=4; end; end else if rembitcount=6 then begin part1:=getbyte; iscomp:=(rembits shr 5) and 1; if iscomp=1 then begin c1:=((rembits and $1F) shl 3) or ((part1 shr 5) and $7); c2:=(part1 shr 1) and $F; rembits:=part1 and 1; rembitcount:=1; result:=(c1 shl 4) or (c2 and $F); end else if iscomp=0 then begin c1:=((rembits and $1F) shl 3) or ((part1 shr 5) and $7); result:=c1; rembits:=part1 and $1F; rembitcount:=5; end; end else if rembitcount=7 then begin part1:=getbyte; iscomp:=(rembits shr 6) and 1; if iscomp=1 then begin c1:=((rembits and $3F) shl 2) or ((part1 shr 6) and $3); c2:=(part1 shr 2) and $F; rembits:=part1 and $3; rembitcount:=2; result:=(c1 shl 4) or (c2 and $F); end else if iscomp=0 then begin c1:=((rembits and $3F) shl 2) or ((part1 shr 6) and $3); result:=c1; rembits:=part1 and $3F; rembitcount:=6; end; end; end; procedure compress; var c,wc,w:integer; begin initialize; c:=getbyte; w:=findstring(nochar,c); c:=getbyte; while fread begin if lastbyte then begin putcode(w); lastbyte:=false; InitializeStringtable; c:=getbyte; w:=findstring(nochar,c); c:=getbyte; end; wc:=findstring(w,c); if wc=endlist then begin lastbyte:=not(MakeTableEntry(w,c)); putcode(w); w:=findstring(nochar,c); end else w:=wc; if not lastbyte then c:=getbyte; end; putcode(w,true); end; procedure decompress; var unknown:boolean; finchar,lastchar:integer; code,oldcode,incode:integer; c,tempc:integer; begin initialize; unknown:=false; lastchar:=empty; oldcode:=getcode; code:=oldcode; c:=stringtable[code].nextchar; putbyte(c); finchar:=c; incode:=getcode; while incodeeofchar do begin if lastbyte then begin lastbyte:=false; InitializeStringTable; stackpointer:=0; unknown:=false; lastchar:=empty; oldcode:=getcode; code:=oldcode; c:=stringtable[code].nextchar; putbyte(c); finchar:=c; incode:=getcode; end; code:=incode; if not stringtable[code].used then begin lastchar:=finchar; code:=oldcode; unknown:=true; end; while(stringtable[code].prevcharnochar) do begin push(stringtable[code].nextchar); if lzwerr=true then break; code:=stringtable[code].prevchar; end; if lzwerr=true then break; finchar:=stringtable[code].nextchar; putbyte(finchar); pop(tempc); while(tempcempty) do begin putbyte(tempc); pop(tempc); end; if unknown then begin finchar:=lastchar; putbyte(finchar); unknown:=false; end; lastbyte:=not(maketableentry(oldcode,finchar)); if not lastbyte then begin oldcode:=incode; incode:=getcode; end end; end; procedure push(c:integer); var s:string; begin if stackpointer begin inc(stackpointer); stack[stackpointer]:=char(c); end; if stackpointer=4096 then begin s:='Stack full at ' +inttostr(inbufpos); lzwerr:=true; showmessage(s); end; end; procedure pop(var c:integer); begin if stackpointer0 then begin c:=integer(stack[stackpointer]); dec(stackpointer); end else c:=empty; end; end. ------------------------------------------------------------------------------- To compress the file add the following code to a button openinputfile('C:\cdidxtmp\myfile.exe'); openoutputfile('C:\cdidxtmp\myfile.bak'); initialize; compress; To Decompress openinputfile('C:\cdidxtmp\myfile.bak'); openoutputfile('C:\cdidxtmp\myfile.exe'); initialize; decompress;