首页  编辑  

一个简单的压缩流算法

Tags: /超级猛料/Stream.File.流、文件和目录/流操作/   Date Created:

一个简单的压缩流算法

一个控件,说是Zip压缩,但是我实验了一下,压缩后ZIP不能解压,但是他自己可以解压的。

压缩效果比WinZIP或者WinRAR差许多。不过还可以。

unit zip;

interface

uses

Windows, Messages, SysUtils, Classes ;

const maxt=2048;

    bsize=4096;

    isize=2048;

    eof_code=256;

    next_len=257;

    start_len=258;

    empty=259;

type

Tzip = class(TComponent)

private

       tabl:array[0..2049] of integer;

       bbuf:array[0..bsize] of byte;

       ibuf:array[0..isize] of integer;

       tabl_count,bpos,ipos,blen:integer;

       code_len:integer;

       free_bits,full_bits:integer;

       out_c:integer;

       Foutstream:Tstream;

       Finstream:tstream;

       Fetime:integer;

       Fpos:integer;

       outstr:string;

       firstchar:char;

       FOnupdate: TNotifyEvent;

    { Private declarations }

  procedure init;

  procedure addtotable(str: integer);

  procedure setfinstream(const Value: Tstream);

  procedure setfoutstream(const Value: Tstream);

  procedure open_unzip;

  procedure open_zip;

  procedure close_zip;

  procedure close_unzip;

  function getbyte: integer;

  function indexof(str, ch: integer): integer;

  procedure putcode(code: integer);

  procedure put_str(ix: integer; first: boolean);

  function read_code: integer;

protected

public

  procedure Zip;

  procedure Unzip;

  property instream:Tstream write setfinstream;

  property outstream:Tstream write setfoutstream;

published

   property Elapsed:integer read fetime;

    property Onupdate:TNotifyEvent read fonupdate write fonupdate;

    property Position:integer read fpos;

end;

procedure Register;

implementation

procedure Register;

begin

RegisterComponents('Samples', [Tzip]);

end;

procedure tzip.init;

var i:integer;

begin

   code_len:=9;

   tabl_count:=empty;

   for i:=0 to 255 do tabl[i]:=$ffff00+i;

end;

procedure tzip.addtotable(str:integer);

begin

   if tabl_count>=maxt then tabl_count:=empty

   else

   begin

        tabl[tabl_count]:=str;

        inc(tabl_count);

   end;

end;

procedure tzip.open_zip;

begin

   blen:=finstream.Read(bbuf,bsize);

   bpos:=0;

   ipos:=0;

   out_c:=0;

   free_bits:=32;

   fpos:=0;

end;

procedure tzip.close_zip;

begin

   if ipos>0 then foutstream.Write(ibuf,ipos*4);

end;

procedure tzip.close_unzip;

begin

   if bpos>0 then foutstream.Write(bbuf,bpos);

end;

procedure tzip.open_unzip;

begin

   full_bits:=0;

   ipos:=0;

   bpos:=0;

   finstream.Read(ibuf,isize*4);

   fpos:=0;

end;

function tzip.getbyte:integer;

begin

   if (bpos>=blen) then

   begin

        if blen<bsize then

        begin

             result:=eof_code;

             exit;

        end

       else

        begin

             blen:=finstream.Read(bbuf,bsize);

             bpos:=0;

             fpos:=finstream.Position;

             if assigned(fonupdate) then fonupdate(nil);

        end;

   end;

   result:=bbuf[bpos];

   inc(bpos);

end;

function tzip.indexof(str,ch:integer):integer;

var cd,mn,mx:integer;

begin

   cd:=(str shl 8) + ch;

   mn:=str+1;if mn<empty then mn:=empty;

   mx:=tabl_count-1;

   for result:=mn to mx do if tabl[result]=cd then exit;

   result:=-1;

end;

procedure tzip.putcode(code:integer);

var shift_bits,tc,tf:integer;

  procedure store_out_c;

  begin

        ibuf[ipos]:=out_c;

        inc(ipos);

        if ipos>=isize then

        begin

             foutstream.Write(ibuf,isize*4);

             ipos:=0;

        end;

        out_c:=0;

        free_bits:=32;

  end;

begin

   shift_bits:=free_bits-code_len;

   if shift_bits<0 then

   begin

        tf:=free_bits;

        shift_bits:=-shift_bits;

        tc:=code shr shift_bits;

        out_c:=out_c or tc;

        store_out_c;

        shift_bits:=32-shift_bits;

        inc(free_bits,tf);          //add alredy pushed bits

   end;

   tc:=code shl shift_bits;

   out_c:=out_c or tc;

   dec(free_bits,code_len);

   if (free_bits=0) or (code=eof_code) then store_out_c;

end;

procedure tzip.zip;

var str,ch,t:integer;

begin

   init;

   open_zip;

   Fetime:=gettickcount;

   str:=getbyte;

   repeat

         ch:=getbyte;

         if ch=eof_code then break;

         t:=indexof(str,ch);

         if t<>-1 then str:=t else

         begin

              putcode(str);

              if (tabl_count=512) or (tabl_count=1024) then

              begin

                   putcode(next_len);

                   inc(code_len);

              end;

              if tabl_count=maxt then

              begin

                   putcode(start_len);

                   code_len:=9;

              end;

              addtotable((str shl 8) +ch);

              str:=ch;

         end;

   until false;

   putcode(str);

   putcode(eof_code);

   fetime:=gettickcount-fetime;

   close_zip;

end;

procedure tzip.put_str(ix:integer;first:boolean);

var i,l:integer;

begin

   outstr:='';

   repeat

         i:=tabl[ix];

         ix:=i shr 8;

         outstr:=outstr+chr(i and $ff);

   until ix=$ffff;

   l:=length(outstr);

   for i:=l downto 1 do

   begin

        bbuf[bpos]:=byte(outstr[i]);

        inc(bpos);

        if bpos>=bsize then

        begin

             foutstream.Write(bbuf,bsize);

             bpos:=0;

        end

   end;

   firstchar:=outstr[l];

   if first then

   begin

        bbuf[bpos]:=byte(firstchar);

        inc(bpos);

        if bpos>=bsize then

        begin

             foutstream.Write(bbuf,bsize);

             bpos:=0;

        end

   end;

end;

function tzip.read_code:integer;

var mask,shift_bits,tf:integer;

procedure get_out_c;

begin

   out_c:=ibuf[ipos];

   inc(ipos);

   if ipos>=isize then

   begin

        finstream.Read(ibuf,isize*4);

        ipos:=0;

        fpos:=finstream.Position;

        if assigned(fonupdate) then fonupdate(nil);

   end;

   full_bits:=32;

end;

begin

  if full_bits=0 then get_out_c;

   mask:=(1 shl code_len) -1; //0111111

   result:=0;

   shift_bits:=full_bits-code_len;

   if shift_bits<0 then

   begin

        tf:=full_bits;

        shift_bits:=-shift_bits;

        result:=(out_c shl shift_bits) and mask;

        get_out_c;

        shift_bits:=32-shift_bits;

        inc(full_bits,tf);

   end;

   result:=result or ((out_c shr shift_bits) and mask);

   dec(full_bits,code_len);

end;

procedure tzip.unzip;

var old,cod:integer;

begin

   init;

   open_unzip;

   fetime:=gettickcount;

   old:=read_code;

   put_str(old,false);

   while true do

   begin

        cod:=read_code;

        if cod=eof_code then break;

        if cod=next_len then begin inc(code_len);continue;end;

        if cod=start_len then begin code_len:=9;continue;end;

        if cod>=tabl_count then put_str(old,true) else put_str(cod,false);

        addtotable(old shl 8 +byte(firstchar));

        old:=cod;

   end;

   fetime:=gettickcount-fetime;

   close_unzip;

end;

procedure Tzip.setfinstream(const Value: Tstream);

begin

finstream := Value;

end;

procedure Tzip.setfoutstream(const Value: Tstream);

begin

foutstream := Value;

end;

end.