home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / ECO30603.ZIP / ECO30603.LZH / ECOLIBII / ECO_LZW.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1992-08-29  |  13.0 KB  |  365 lines

  1. unit eco_lzw;
  2. { lzwunit - defines the lzwobj object to compress and uncompress      }
  3. {           files using the lzw compression algorithm                 }
  4. { if you would like to create a file with a copy of the string        }
  5. { table lzwunit used to compress the input file just define the       }
  6. { debugging value in the next line                                    }
  7.  
  8. { $ D EFINE debugging}
  9.  
  10. { ========================= } interface { =========================== }
  11.  
  12. uses dos,crt;
  13.  
  14. const
  15.   maxtableentries = 3000;               {set size of string code table}
  16.  
  17.  
  18. type
  19.   tablebytes = (prefix,suffix,link);
  20.   lzwobj = object
  21.     inf,outf       : file;
  22.     codetbl        : array[0..maxtableentries,prefix..link] of integer;
  23.     hittbl         : array[0..maxtableentries] of integer;
  24.     prefixcandidate: integer;
  25.     suffixcandidate: integer;
  26.     tablefull      : boolean;
  27.     tabletop       : integer;
  28.     constructor init;
  29.     function    managetbl: integer; virtual;
  30.     procedure   insertentry; virtual;
  31.     function    inputuncompvalue: byte; virtual;
  32.     function    inputcompvalue: integer; virtual;
  33.     procedure   outputuncompvalue(b: byte); virtual;
  34.     procedure   outputcompvalue(i: integer); virtual;
  35.     procedure   compressfile(infile,outfile : pathstr);
  36.     procedure   outputtable(f:pathstr); virtual;
  37.     function    expandvalue(inputvalue : integer;
  38.                          output     : boolean): integer; virtual;
  39.     procedure   uncompressfile(infile,outfile : pathstr);
  40.     destructor  compressdone;
  41.   end;
  42.  
  43. { ====================== } implementation { ========================= }
  44.  
  45.  
  46. constructor lzwobj.init; {--------------------------------------------}
  47. {  initializes the string code table with the atomic values           }
  48. {    and the table management values tablefull, tabletop, hittbl[]    }
  49. {  hittbl is an array that contains the head pointer for linked       }
  50. {    lists (yes multiple lists) of compression code to facilitate     }
  51. {    faster lookup of prefixcandidate-suffixcandidate pairs.          }
  52. {  if hittbl[prefix value] = 0 then no p-s entries with the prefix    }
  53. {    value have been added to the string table. if                    }
  54. {    hittbl[prefix value] <> 0, it contains the entry number of the   }
  55. {    first element in the string table with that prefix value         }
  56. {  the codetbl[x,link] element will contain a 0 if the string table   }
  57. {    does not have any other entries that start with the prefix in    }
  58. {    codetbl[x,prefix], otherwise codetbl[x,link] points to the next  }
  59. {    entry with a matching prefixcandidate value                      }
  60. {---------------------------------------------------------------------}
  61.  
  62. var i : integer;
  63. begin
  64.   tablefull := false;
  65.   tabletop := 255;
  66.   for i := 0 to maxtableentries do begin
  67.     hittbl[i] := 0;
  68.     codetbl[i, link] := 0;
  69.     if i > 255 then begin
  70.       codetbl[i, prefix] := 0;
  71.       codetbl[i, suffix] := 0
  72.       end
  73.     else begin
  74.       codetbl[i, prefix] := -1;
  75.       codetbl[i, suffix] := i
  76.       end
  77.     end
  78.   end;
  79.  
  80.  
  81. function lzwobj.managetbl: integer; {=================================}
  82. {  managetbl searches the table for prefixcandidate-suffixcandidate   }
  83. {    pairs of characters/codes. if the pair is not in the string      }
  84. {    table, it adds them and updates the linked list (see init)       }
  85. {    if the pair is found, it returns the entry number for the pair.  }
  86. {=====================================================================}
  87.  
  88. var
  89.   found,                {character pair found}
  90.   endoflinks : boolean; {end of linked list found while searching list}
  91.   curptr : integer;     {current element number in string table       }
  92. begin
  93.  
  94.   found := false;       {initialize values}
  95.   endoflinks := false;
  96.  
  97.   if hittbl[prefixcandidate] <> 0 then begin {entries exist for prefix}
  98.     curptr := hittbl[prefixcandidate];  {trace list starting at head  }
  99.     repeat
  100.       if (codetbl[curptr,prefix] = prefixcandidate) and
  101.          (codetbl[curptr,suffix] = suffixcandidate) then
  102.          found := true
  103.       else                                    {not found              }
  104.         if codetbl[curptr,link] <> 0 then     {check if at end of list}
  105.           curptr := codetbl[curptr,link]      {get next element to chk}
  106.         else
  107.           endoflinks := true                  {end of list            }
  108.       until found or endoflinks
  109.     end;
  110.  
  111.   if found then                               {if pair found          }
  112.     managetbl := curptr                       {  return element #     }
  113.   else begin                                  {otherwise, add to table}
  114.     if not tablefull then begin
  115.       inc(tabletop);
  116.       insertentry;
  117.       if hittbl[prefixcandidate] = 0 then     {adjust links           }
  118.         hittbl[prefixcandidate] := tabletop
  119.       else
  120.         codetbl[curptr,link] := tabletop
  121.       end;
  122.     managetbl := -1;                          {not found signal       }
  123.     end;
  124.   end;
  125.  
  126.  
  127. procedure lzwobj.insertentry; {---------------------------------------}
  128. { insert prefixcandidate-suffixcandidate into the next available      }
  129. {   entry in the table                                                }
  130. {---------------------------------------------------------------------}
  131.  
  132. begin
  133.   codetbl[tabletop, prefix] := prefixcandidate;
  134.   codetbl[tabletop, suffix] := suffixcandidate;
  135.   if tabletop = maxtableentries then tablefull := true;
  136.   end;
  137.  
  138. {---------------------------------------------------------------------}
  139. { the next four methods provide input and output for file i/o         }
  140. {---------------------------------------------------------------------}
  141.  
  142. function lzwobj.inputuncompvalue: byte;
  143. var
  144.   b : byte;
  145. begin
  146.   blockread(inf, b, 1);
  147.   inputuncompvalue := b
  148.   end;
  149.  
  150. function lzwobj.inputcompvalue: integer;
  151. var
  152.   i : integer;
  153. begin
  154.   blockread(inf, i, 1);
  155.   inputcompvalue := i
  156.   end;
  157.  
  158. procedure lzwobj.outputuncompvalue(b: byte);
  159. begin
  160.   blockwrite(outf, b, 1)
  161.   end;
  162.  
  163. procedure lzwobj.outputcompvalue(i: integer);
  164. begin
  165.   blockwrite(outf, i, 1)
  166.   end;
  167.  
  168. procedure lzwobj.outputtable(f:pathstr); {----------------------------}
  169. { outputtable dumps a formatted list of the string table into the file}
  170. {   specified in f.                                                   }
  171. {---------------------------------------------------------------------}
  172.  
  173. var
  174.   t : text;
  175.   i : integer;
  176.   j : tablebytes;
  177.   s : string;
  178. begin
  179.   assign(t,f);
  180.   rewrite(t);
  181.   for i:= 256 to tabletop do begin
  182.     write(t,i:4,'  ',
  183.           codetbl[i,prefix]:4,'  ',
  184.           codetbl[i,suffix]:4,'  ',
  185.           codetbl[i,link]:4,'  ');
  186.     for j := prefix to suffix do
  187.       if (codetbl[i,j] < 255) and (codetbl[i,j] >= 32) then begin
  188.         s := chr(byte(codetbl[i,j]));
  189.         write(t,s,' ')
  190.         end
  191.       else
  192.         write(t,'  ');
  193.     writeln(t,' ')
  194.     end;
  195.   close(t)
  196.   end;
  197.  
  198.  
  199. procedure lzwobj.compressfile(infile,outfile : pathstr); {------------}
  200. {  compressfile manages all the compression tasks                     }
  201.  
  202.  
  203. {---------------------------------------------------------------------}
  204.  
  205. var
  206.   ctr : longint;           {counter for onscreen display              }
  207.   foundcode : integer;     {used to manage results from managetbl code}
  208. begin
  209.  
  210.   assign(inf,infile);      {open input file as 1 byte/record file     }
  211.   reset(inf,1);
  212.   assign(outf,outfile);    {open output file as a 2 byte/record file  }
  213.   rewrite(outf,2);         {  because we write out integers           }
  214.  
  215.   ctr := 0;
  216.  
  217.   prefixcandidate := inputuncompvalue;
  218.  
  219.   repeat
  220.     inc(ctr);                          {manage counter display}
  221.     if (ctr and 127) = 127 then begin
  222.       gotoxy(10,10);
  223.       write(ctr);
  224.       end;
  225.  
  226.     suffixcandidate := inputuncompvalue;
  227.  
  228.     foundcode := managetbl;                 {search table for p-s pair}
  229.  
  230.     if foundcode >= 0 then                  {if pair found            }
  231.       prefixcandidate := foundcode          {  go look for next pair  }
  232.     else begin
  233.       outputcompvalue(prefixcandidate);     {otherwise, output prefix }
  234.       prefixcandidate := suffixcandidate    {  and reset for next pair}
  235.       end
  236.     until eof(inf);
  237.   outputcompvalue(prefixcandidate);         {write last character out }
  238.  
  239.   {$IFDEF Debugging}
  240.   outputtable('S:\COmpTbl.pas');
  241.   {$ENDIF}
  242.  
  243.   end;
  244.  
  245.  
  246. function lzwobj.expandvalue(inputvalue : integer; {-------------------}
  247.                             output:boolean) : integer;
  248. {  expandvalue expands compression codes. note, if the prefix value   }
  249. {    retrieved in kprefix is another compression code, expandvalue    }
  250. {    will recursively call itself until kprefix is an extended ascii  }
  251. {    character.                                                       }
  252. {                                                                     }
  253. {  input:                                                             }
  254. {    inputvalue is the value to expand                                }
  255. {    output turns on/off writing of expanded characters to            }
  256. {      file so you can retrieve (without writing) the first ascii     }
  257. {      character at the head of the compressed character chain. this  }
  258. {      becomes necessary when you must fill in the suffix value in    }
  259. {      string table for adjacent prefix pointers.                     }
  260. {  output:                                                            }
  261. {    returns the character at the head of compressed byte chain when  }
  262. {      you pass it a compressed byte. if you pass it an ascii         }
  263. {      character, it returns that character. this made coding simpler }
  264. {---------------------------------------------------------------------}
  265.  
  266. var
  267.   kprefix, ksuffix, kreturned : integer;
  268.  
  269.  begin
  270.   if inputvalue > 255 then begin                 {is compressed value?}
  271.     kprefix := codetbl[inputvalue,prefix];       {yes, get table entry}
  272.     ksuffix := codetbl[inputvalue,suffix];
  273.     if kprefix > 255 then                        {if prefix is a      }
  274.       kreturned := expandvalue(kprefix,output)   { compressed char    }
  275.     else begin                                   { recursively call   }
  276.       kreturned := kprefix;                      { expandvalue        }
  277.       if output then outputuncompvalue(kprefix)  {otherwise, set head }
  278.                                                  { value and output   }
  279.                                                  { uncompressed bytes }
  280.       end;                                       { to file if output  }
  281.                                                  { set true           }
  282.     if output then outputuncompvalue(ksuffix)
  283.     end
  284.   else
  285.     kreturned := inputvalue; {return ascii value if passed ascii value}
  286.   expandvalue := kreturned
  287.   end;
  288.  
  289.  
  290. procedure lzwobj.uncompressfile(infile,outfile : pathstr); {----------}
  291. { uncompresfile manages all aspects of uncompressing files            }
  292. {---------------------------------------------------------------------}
  293.  
  294. var
  295.   ctr : longint;                   {onscreen info                     }
  296.   found : integer;                 {returned from managetbl routine   }
  297.   dummy, suffixcopy, i :integer;
  298.  
  299. begin
  300.   assign(inf,infile);            {open input file to read integers    }
  301.   reset(inf,2);
  302.   assign(outf,outfile);          {open output file to write characters}
  303.  
  304.   rewrite(outf,1);
  305.  
  306.   ctr := 0;
  307.  
  308.   prefixcandidate := inputcompvalue;
  309.  
  310.   repeat
  311.  
  312.     inc(ctr);                               {manage onscreen display  }
  313.     if (ctr and 127) = 127 then begin
  314.       gotoxy(10,10);
  315.       write(ctr)
  316.       end;
  317.  
  318.     if prefixcandidate < 256 then           {output an ascii character}
  319.       outputuncompvalue(prefixcandidate);
  320.  
  321.     suffixcandidate := inputcompvalue;
  322.  
  323.     if suffixcandidate > 255 then begin     {compressed character?    }
  324.  
  325.       suffixcopy := suffixcandidate;   {save just in case we expand it}
  326.  
  327.       {handle special case when you need to expand an entry that you  }
  328.       { have not yet added to table                                   }
  329.  
  330.       if tabletop + 1 = suffixcandidate then begin
  331.         suffixcandidate := expandvalue(prefixcandidate,false);
  332.         found := managetbl;
  333.         suffixcandidate := suffixcopy;
  334.         dummy := expandvalue(suffixcandidate,true);
  335.         end
  336.       else begin
  337.         suffixcandidate := expandvalue(suffixcandidate,true); {normal }
  338.         found := managetbl;                                   {expand }
  339.         suffixcandidate := suffixcopy
  340.         end
  341.       end
  342.     else
  343.       found := managetbl;
  344.     prefixcandidate := suffixcandidate
  345.     until eof(inf);
  346.  
  347.   if prefixcandidate < 256 then            {output last character if  }
  348.     outputuncompvalue(prefixcandidate);    { not a compressed code    }
  349.  
  350.   {$IFDEF Debugging}
  351.   outputtable('S:\Ucomptbl.pas');
  352.   {$ENDIF}
  353.   end;
  354.  
  355.  
  356. destructor lzwobj.compressdone; {-------------------------------------}
  357. { compressdone closes the files.                                      }
  358. {---------------------------------------------------------------------}
  359.  
  360. begin
  361.   close(inf);
  362.   close(outf)
  363.   end;
  364. end.
  365.