home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #19 / NN_1992_19.iso / spool / comp / lang / pascal / 5048 < prev    next >
Encoding:
Internet Message Format  |  1992-08-25  |  9.8 KB

  1. Path: sparky!uunet!dtix!darwin.sura.net!jvnc.net!rutgers!cmcl2!psinntp!psinntp!bepcp!jnicholson
  2. From: jnicholson@bowker.com (Jim Nicholson)
  3. Newsgroups: comp.lang.pascal
  4. Subject: Compression Routines (2of2)
  5. Message-ID: <76e3PB2w164w@bowker.com>
  6. Date: 25 Aug 92 13:54:53 GMT
  7. Organization: Bowker Electronic Publishing, New Providence NJ
  8. Lines: 288
  9.  
  10. {*********************************************************************
  11. * UNLZW2.PAS - This program performs LZW decompression on the data   *
  12. *   from the input file and writes the expanded, original data to    *
  13. *   the output file. It uses the same coding scheme as LZW2.PAS      *
  14. *                                                                    *
  15. * To run : UNLZW <infile> <outfile>                                  *
  16. *                                                                    *
  17. *   IMPORTANT: The BuildString function is recursive. As such, it    *
  18. *   can use a lot of stack space depending on how deep it must go to *
  19. *   expand compression codes back into strings. Be sure to use the   *
  20. *   $M switch when compiling to make your stack larger than normal.  *
  21. *   Note: Reducing MAXENTRIES also reduces the max. recursion depth. *
  22. *                                                                    *
  23. * Author  : David Reid, The Cobb Group                               *
  24. * Last Revised : 12-31-91                                            *
  25. *********************************************************************}
  26.  
  27. USES Dos,Crt;
  28.  
  29. CONST
  30.                              {* This defines the string table size. *}
  31.   LZWENTRIES = 8192;         {* You can change LZWENTRIES to 512,   *}
  32.                              {* 1024, 2048, or 4096 as you see fit. *}
  33.   MAXENTRIES = 256+LZWENTRIES;
  34.   MAXSTRLEN = 1024;          {* Determines the size of workstr[]    *}
  35.   XInMask  : Word = $0000;        {* Points to next bit in xbitbuff *}
  36.   XBitBuff : Word = 0;            {* Holds residual output bits     *}
  37.   OutLength: Longint = 0;         {* Counter for bytes output       *}
  38.   InLength : Longint = 0;         {* Counter for bytes input        *}
  39.   EXIT_FAILURE = 1;               {* Error level return value       *}
  40.  
  41. {*----TYPE DEFINITIONS----------------------------------------------*}
  42.  
  43. TYPE
  44.   RULEtag = RECORD           {* This structure holds one LZW string *}
  45.                              {* table entry. It contains the prefix *}
  46.     Prefix : Integer;        {* character, the suffix character,    *}
  47.     Suffix : Integer;        {* and the linked-list pointer to the  *}
  48.     Next   : Integer;        {* next table entry that starts with   *}
  49.     END;                     {* the same prefix as this one does.   *}
  50.   WorkStrType = ARRAY[0..MAXSTRLEN-1] OF Char; {* Hold expanded strs*}
  51.   PtrType = RECORD           {* Define a pointer record             *}
  52.     Offset  : Word;          {*   so we can access the individual   *}
  53.     Segment : Word           {*   pointer elements for pointer      *}
  54.     END;                     {*   arithmetic.                       *}
  55.   CharPtr = ^Char;
  56.  
  57. {*----GLOBAL VARIABLES----------------------------------------------*}
  58.  
  59. VAR
  60.   RULE    : RULEtag;
  61.   Rules   : ARRAY[0..MAXENTRIES-1] OF RULEtag; {* The string table  *}
  62.   Bitmask : Integer;         {* Controls size of input characters   *}
  63.   Entries : Integer;         {* Number of entries in string table   *}
  64.   InFile  : FILE;            {* Input file                          *}
  65.   OutFile : FILE;            {* Output file                         *}
  66.   WorkStr : WorkStrType;     {* Holds expanded strings              *}
  67.  
  68. { Init - Checks command-line arguments, opens files, and displays an }
  69. {   informational prompt.                                            }
  70.  
  71. PROCEDURE Init;
  72. BEGIN
  73.   IF (ParamCount <>  2 ) THEN
  74.     BEGIN
  75.       WriteLn('syntax: ULZW2 <infile> <outfile>');
  76.       Halt(EXIT_FAILURE)
  77.     END;
  78.   {$I-}
  79.   Assign(InFile, ParamStr(1));
  80.   Reset(InFile,1);
  81.   {$I+}
  82.   IF IOResult <> 0 THEN
  83.     BEGIN
  84.       WriteLn('Error opening input file ', ParamStr(1));
  85.       Halt(EXIT_FAILURE)
  86.     END;
  87.   {$I-}
  88.   Assign(OutFile, ParamStr(2));
  89.   Rewrite(OutFile,1);
  90.   {$I+}
  91.   IF IOResult <> 0 THEN
  92.     BEGIN
  93.       WriteLn('Error opening output file ', ParamStr(2));
  94.       Halt(EXIT_FAILURE)
  95.     END;
  96.   WriteLn('Decompressing ...')
  97. END;
  98.  
  99. { Term - Closes files and displays file sizes.                       }
  100.  
  101. PROCEDURE Term;
  102. BEGIN
  103.   Close(InFile);
  104.   Close(OutFile);
  105.   WriteLn('Input file length : ',InLength:7,'  ',
  106.           'Output file length: ',OutLength:7);
  107. END;
  108.  
  109. { Output - Writes the expanded sequence of character in OutBuff to   }
  110. {   the output file and updates outlength accordingly.               }
  111.  
  112. PROCEDURE Output(OutBuff: WorkStrType; BuffLen: Integer);
  113. BEGIN
  114.   BlockWrite(OutFile,OutBuff,BuffLen);
  115.   Inc(OutLength,BuffLen)
  116. END;
  117.  
  118. { Input - Reads a 9, 10, 11, 12, or 13 bit code from the input file. }
  119. {   Stores any residual bits in the global variable xbitbuff. The    }
  120. {   global variable xinmask keeps track of the residual bits. The    }
  121. {   local variable xoutmask walks down the bits in xbitbuff and helps}
  122. {   transfer them to rval. The starting value for xoutmask comes     }
  123. {   from the global variable Bitmask.                                }
  124.  
  125. FUNCTION Input: Integer;
  126. VAR
  127.   RVal     : Word;
  128.   XOutMask : Word;
  129.   Ascii    : Boolean;
  130. BEGIN
  131.   IF XInMask = 0 THEN BEGIN
  132.     IF Eof(InFile) THEN BEGIN
  133.       Input := -1;
  134.       Exit
  135.       END;
  136.     BlockRead(InFile,XBitBuff,1);
  137.     XInMask := $0080;
  138.     Inc(InLength)
  139.     END;
  140.   {*----Read first bit: ASCII value or string table index?--------*}
  141.   IF (XBitBuff AND XInMask) <> 0 THEN BEGIN
  142.     Ascii := FALSE;
  143.     XOutMask := Bitmask;
  144.     RVal := $ffff
  145.     END
  146.   ELSE BEGIN
  147.     Ascii := TRUE;
  148.     XOutMask := $080;
  149.     XInMask := XInMask SHR 1;
  150.     RVal := 0
  151.     END;
  152.   WHILE XOutMask <> 0 DO BEGIN
  153.     IF XInMask = 0 THEN BEGIN
  154.       IF Eof(InFile) THEN BEGIN
  155.         Input := -1;
  156.         Exit
  157.         END;
  158.       BlockRead(InFile,XBitBuff,1);
  159.       XInMask := $0080;
  160.       Inc(InLength)
  161.     END;
  162.     IF (XBitBuff AND XInMask) <> 0 THEN RVal := RVal OR XOutMask
  163.     ELSE
  164.       IF NOT Ascii THEN RVal := RVal XOR XOutMask;
  165.     XInMask := XInMask SHR 1;
  166.     XOutMask := XOutMask SHR 1
  167.     END;
  168.   IF Ascii THEN
  169.     Input := RVal
  170.   ELSE
  171.     Input := 255 - RVal
  172. END;
  173.  
  174. { InitTable - Initializes the first 256 string table entries and     *
  175. {   sets the globals Bitmask and Entries to their starting values.   }
  176.  
  177. PROCEDURE InitTable;
  178. BEGIN
  179.   FOR Entries := 0 TO 255 DO BEGIN
  180.     Rules[Entries].Prefix := -1;
  181.     Rules[Entries].Suffix := Entries;
  182.     Rules[Entries].Next := -1
  183.     END;
  184.   Bitmask := 2;
  185.   Entries := 256
  186. END;
  187.  
  188. { IsInTable - Searches for the specified Prefix-Suffix pair in the   *
  189. {   string table. If found, the pair's position in the table is      *
  190. {   assigned to RuleNum. Returns TRUE if the pair is found or FALSE  *
  191. {   if it isn't found.                                               }
  192.  
  193. FUNCTION IsInTable(Prefix,Suffix:Integer; VAR RuleNum:Integer): Boolean;
  194. VAR
  195.   I : Integer;
  196. BEGIN
  197.   I := Prefix;
  198.   WHILE I <> -1 DO
  199.     IF (Rules[I].Prefix = Prefix) AND (Rules[I].Suffix = Suffix) THEN
  200.     ELSE
  201.       I := Rules[I].Next;
  202.   IF I <> -1 THEN BEGIN
  203.     RuleNum := I;
  204.     IsInTable := TRUE
  205.     END
  206.   ELSE
  207.     IsInTable := FALSE
  208. END;
  209.  
  210. { BuildString - Expands compression codes into the original strings. *
  211. { THIS PROCEDURE IS RECURSIVE! Make sure your stack can handle it.   }
  212.  
  213. PROCEDURE BuildString(RuleNum: Integer;
  214.                       VAR Str: CharPtr;
  215.                       VAR BuffLen: Integer);
  216. VAR
  217.   Position : Integer;
  218.   GenPtr   : CharPtr;
  219. BEGIN
  220.   Position := 0;
  221.   IF Rules[RuleNum].Prefix >= 0 THEN BEGIN
  222.     BuildString(Rules[RuleNum].Prefix, Str, BuffLen);
  223.     Inc(Position,BuffLen);
  224.     GenPtr := Str;
  225.     PtrType(GenPtr).Offset := PtrType(GenPtr).Offset + Position;
  226.     BuildString(Rules[RuleNum].Suffix, GenPtr, BuffLen);
  227.     Inc(BuffLen,Position)
  228.     END
  229.   ELSE BEGIN
  230.     Str^ := Chr(Rules[RuleNum].Suffix);
  231.     BuffLen := 1
  232.     END
  233. END;
  234.  
  235. { AddEntry - Adds a Prefix-Suffix pair to the table. Shifts Bitmask  *
  236. {   left one bit if this entry pushes us past the 512, 1024, 2048,   *
  237. {   or 4096 entry threshold.                                         }
  238.  
  239. PROCEDURE AddEntry(Prefix,Suffix: Integer);
  240. BEGIN
  241.   {*----Add Prefix/Suffix pair to the table----------------------*}
  242.   Rules[Entries].Prefix := Prefix;
  243.   Rules[Entries].Suffix := Suffix;
  244.   {*----Update the table's linked list---------------------------*}
  245.   Rules[Entries].Next := Rules[Prefix].Next;
  246.   Rules[Prefix].Next := Entries;
  247.   {*----Check for time to shift bitmask--------------------------*}
  248.   Inc(Entries);
  249.   IF Entries - 255 >= Bitmask THEN
  250.       Bitmask := Bitmask SHL 1
  251. END;
  252.  
  253. { Decompress - Decompresses the file. The outer while loop repeats   *
  254. {   each time the table fills up (adaptive reset). The inner while   *
  255. {   loop repeats for each character in the input file.               }
  256.  
  257. PROCEDURE Decompress;
  258. VAR
  259.   BuffLen   : Integer;
  260.   Prefix    : Integer;
  261.   Suffix    : Integer;
  262.   GenPtr    : CharPtr;
  263. BEGIN
  264.   Prefix  := 0;
  265.   BuffLen := 0;
  266.   WHILE Prefix <> -1 DO BEGIN
  267.     InitTable;
  268.     Prefix := Input;
  269.     {*----Stay in this loop until string table fills up---------*}
  270.     WHILE ((Entries < MAXENTRIES-1) AND (Prefix <> -1)) DO BEGIN
  271.       GenPtr := Addr(WorkStr);
  272.       BuildString(Prefix, GenPtr, BuffLen);
  273.       Output(WorkStr,BuffLen);
  274.       Suffix := Input;
  275.       IF Suffix = -1 THEN Exit;
  276.       GenPtr := Addr(WorkStr);
  277.       IF (Suffix < Entries) THEN {* Code is in table          *}
  278.         BuildString(Suffix, GenPtr, BuffLen)
  279.       ELSE             {* Handle code not yet in table        *}
  280.         BuildString(Prefix, GenPtr, BuffLen);
  281.       AddEntry(Prefix,Ord(WorkStr[0]));
  282.       Prefix := Suffix;
  283.       END;
  284.       {*----Output last character sequence before resetting-------*}
  285.     IF Prefix <> -1 THEN BEGIN
  286.       GenPtr := Addr(WorkStr);
  287.       BuildString(Prefix,GenPtr,BuffLen);
  288.       Output(WorkStr,BuffLen)
  289.       END
  290.     END
  291. END;
  292.  
  293. BEGIN
  294.   Init;
  295.   Decompress;
  296.   Term
  297. END.
  298.