home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 7 / 07.iso / c / c019 / 5.ddi / SIXPACK.ZIP / SIXPACK.PAS next >
Encoding:
Pascal/Delphi Source File  |  1991-11-21  |  22.1 KB  |  849 lines

  1. {$A+,B-,D+,E-,F-,G-,I+,L+,N-,O-,R-,S-,V-,X-}
  2. {$M 16384,0,655360}
  3. {******************************************}
  4. {  SIXPACK.C -- Data compression program   }
  5. {  Written by Philip G. Gage, April 1991   }
  6. {  Translated into Pascal Nov. 1991        }
  7. {   by Douglas Webb                        }
  8. {******************************************}
  9.  
  10. CONST
  11.   TEXTSEARCH = 1000;   { Max strings to search in text file - smaller -> Faster compression}
  12.   BINSEARCH  =  200;   { Max strings to search in binary file }
  13.   TEXTNEXT   =   50;   { Max search at next character in text file - Smaller -> better compression }
  14.   BINNEXT    =   20;   { Max search at next character in binary file }
  15.   MAXFREQ    = 2000;   { Max frequency count before table reset }
  16.   MINCOPY    =    3;   { Shortest string COPYING length }
  17.   MAXCOPY    =   64;   { Longest string COPYING length }
  18.   SHORTRANGE =    3;   { Max distance range for shortest length COPYING }
  19.   COPYRANGES =    6;   { Number of string COPYING distance bit ranges @@@}
  20.   CopyBits : Array[0..PRED(COPYRANGES)] OF INTEGER = (4,6,8,10,12,14);   { Distance bits }
  21.   CODESPERRANGE = (MAXCOPY - MINCOPY + 1);
  22.  
  23.   NUL = -1;                    { End of linked list marker }
  24.   HASHSIZE = 16384;            { Number of entries in hash table }
  25.   HASHMASK = (HASHSIZE - 1);   { Mask for hash key wrap }
  26.  
  27. { Adaptive Huffman variables }
  28.   TERMINATE = 256;             { EOF code }
  29.   FIRSTCODE = 257;             { First code for COPYING lengths }
  30.   MAXCHAR = (FIRSTCODE+COPYRANGES*CODESPERRANGE-1);
  31.   SUCCMAX = (MAXCHAR+1);
  32.   TWICEMAX = (2*MAXCHAR+1);
  33.   ROOT = 1;
  34.   MAXBUF = 4096;
  35.  
  36. {** Bit packing routines **}
  37.   Input_Bit_Count : WORD = 0;                { Input bits buffered }
  38.   Input_Bit_Buffer: WORD = 0;                   { Input buffer }
  39.   Output_Bit_Count: WORD = 0;                   { Output bits buffered }
  40.   Output_Bit_Buffer : WORD = 0;                 { Output buffer }
  41.   Bytes_Out : Longint = 0;                      { File size counters }
  42.   Bytes_In : LongINT = 0;
  43.  
  44.   OutBufCount : INTEGER = 0;
  45.   InBufCount  : INTEGER = 0;
  46.  
  47.  
  48. TYPE
  49.   Copy_Type = Array[0..PRED(CopyRanges)] OF Integer;
  50.  
  51.  
  52. CONST
  53.   CopyMin : Copy_Type = (0,16,80,336,1360,5456);
  54.   CopyMax : Copy_Type = (15,79,335,1359,5455,21839);
  55.   MaxDistance : Integer = CopyMax[PRED(COPYRANGES)];
  56.   MaxSize = 21839 + MAXCOPY;   { @@@ }
  57.  
  58.  
  59. TYPE
  60.   HashType = Array[0..PRED(HashSize)] OF Integer;
  61.   Hash_Ptr = ^HashType;
  62.   ListType = Array[0..MaxSize] OF Integer;
  63.   List_Ptr = ^ListType;
  64.   Buffer_Type = Array[0..MaxSize] OF BYTE;            { Convenient typecast. }
  65.   Buffer_Ptr = ^Buffer_Type;
  66.   HTree_Type = Array[0..MaxChar] OF WORD;
  67.   THTree_Type = Array[0..TwiceMax] OF WORD;
  68.   BufType = Array[0..PRED(MAXBUF)] OF BYTE;
  69.   BufPtr = ^BufType;
  70.   WDBufType = Array[0..PRED(MAXBUF)] OF WORD;
  71.   WDBufPtr = ^WDBufType;
  72.  
  73.  
  74. VAR
  75.   Head, Tail : Hash_Ptr;         { Hash table }
  76.   Next, Prev : List_Ptr;       { Doubly linked lists }
  77.   Buffer : Buffer_Ptr;           { Text buffer }
  78.   Distance, Insrt, DictFile, Binary : Integer;
  79.   LeftC, RightC : HTree_Type;  { Huffman tree }
  80.   Parent,Freq : THTree_Type;
  81.   InBuf,OutBuf : BufPtr;
  82.   WDBuf : WDBufPtr;
  83.   InFIle,OutFile : FILE;
  84.  
  85.  
  86.  
  87. {***************** Compression & Decompression *****************}
  88.  
  89. { Initialize data for compression or decompression }
  90.  
  91. Procedure initialize;
  92. VAR
  93.    I, J : Integer;
  94. BEGIN
  95.   { Initialize Huffman frequency tree }
  96.   FOR I := 2 TO TWICEMAX DO
  97.     BEGIN
  98.       Parent[I] := I DIV 2;
  99.       Freq[I] := 1;
  100.     END;
  101.   FOR I := 1 TO MAXCHAR DO
  102.     BEGIN
  103.       LeftC[I] := 2*I;
  104.       RightC[I] := 2*I+1;
  105.     END;
  106. END;
  107.  
  108.  
  109.  
  110.  
  111. {********************* Compression Routines ***********************}
  112.  
  113.  
  114.  
  115. { Write one bit to output file }
  116. Procedure Output_Bit(Bit: Integer);
  117.  
  118. BEGIN
  119.   Output_Bit_Buffer := Output_Bit_Buffer SHL 1;
  120.   IF Boolean(Bit) THEN Output_Bit_Buffer := Output_Bit_Buffer OR 1;
  121.   INC(OutPut_Bit_Count);
  122.   IF (Output_Bit_Count = 16) THEN
  123.     BEGIN
  124.       WdBuf^[OutBufCount] := Output_Bit_Buffer;
  125.       INC(OutBufCount);
  126.       Output_Bit_Count := 0;
  127.       INC(Bytes_Out,2);
  128.       IF OutBufCount = MAXBUF THEN
  129.         BEGIN
  130.           BlockWrite(OutFile,WdBuf^,MAXBUF*2);
  131.           OutBufCount := 0;
  132.         END;
  133.     END;
  134. END;
  135.  
  136.  
  137.  
  138. { Write multibit code to output file }
  139. Procedure Output_Code(Code, Bits : Integer);
  140. VAR
  141.   I : Integer;
  142. BEGIN
  143.   FOR I := 0 TO PRED(Bits) DO
  144.     BEGIN
  145.       Output_Bit(Code AND $1);
  146.       Code := Code SHR 1;
  147.     END;
  148. END;
  149.  
  150.  
  151. { Flush any remaining bits to output file before closing file }
  152. Procedure Flush_Bits;
  153.  
  154. BEGIN
  155.   IF (Output_Bit_Count > 0) THEN
  156.     BEGIN
  157.       Output_Bit_Buffer := Output_Bit_Buffer SHL (16-Output_Bit_Count);
  158.       WdBuf^[OutBufCount] := Output_Bit_Buffer;
  159.       INC(OutBufCount);
  160.       Output_Bit_Count := 0;
  161.       INC(Bytes_Out,2);
  162.     END;
  163.   BlockWrite(OutFile,WdBuf^,OutBufCount*2);
  164. END;
  165.  
  166.  
  167.  
  168.  
  169.  
  170. { Update frequency counts from leaf to root }
  171. Procedure Update_Freq(A,B : Integer);
  172. BEGIN
  173.   REPEAT
  174.     Freq[Parent[A]] := Freq[A] + Freq[B];
  175.     A := Parent[A];
  176.     IF (A <> ROOT) THEN
  177.       BEGIN
  178.         IF (LeftC[Parent[A]] = A) THEN
  179.           B := RightC[Parent[A]]
  180.         ELSE B := LeftC[Parent[A]];
  181.       END;
  182.   UNTIL A = ROOT;
  183.  
  184.   { Periodically scale frequencies down by half to avoid overflow }
  185.   { This also provides some local adaption and better compression }
  186.  
  187.   IF (Freq[ROOT] = MAXFREQ) THEN
  188.     FOR A := 1 TO TWICEMAX DO
  189.       Freq[a] := Freq[a] SHR 1;
  190. END;
  191.  
  192.  
  193.  
  194. { Update Huffman model for each character code }
  195. Procedure Update_Model(Code : Integer);
  196. VAR
  197.   A, B, C, Ua, Uua : Integer;
  198.  
  199. BEGIN
  200.   A := Code + SUCCMAX;
  201.   INC(Freq[A]);
  202.   IF (Parent[A] <> ROOT) THEN
  203.     BEGIN
  204.       ua := Parent[a];
  205.       IF (LeftC[ua] = a) THEN update_freq(a,RightC[ua])
  206.       ELSE update_freq(a,LeftC[ua]);
  207.       REPEAT
  208.         uua := Parent[ua];
  209.         IF (LeftC[uua] = ua) THEN
  210.           b := RightC[uua]
  211.         ELSE b := LeftC[uua];
  212.  
  213.         { IF high Freq lower in tree, swap nodes }
  214.         IF Freq[a] > Freq[b] THEN
  215.           BEGIN
  216.             IF LeftC[Uua] = ua THEN
  217.               RightC[Uua] := A
  218.             ELSE LeftC[Uua] := A;
  219.             IF (LeftC[ua] = a) THEN
  220.               BEGIN
  221.                 LeftC[Ua] := B;
  222.                 C := RightC[ua];
  223.               END
  224.             ELSE
  225.               BEGIN
  226.                 RightC[Ua] := B;
  227.                 C := LeftC[Ua];
  228.               END;
  229.             Parent[b] := Ua;
  230.             Parent[a] := Uua;
  231.             Update_Freq(B,C);
  232.             A := B;
  233.           END;
  234.         A := Parent[A];
  235.         Ua := Parent[A];
  236.       UNTIL Ua = ROOT;
  237.     END;
  238. END;
  239.  
  240.  
  241.  
  242. { Compress a character code to output stream }
  243. Procedure Compress(code: Integer);
  244. VAR
  245.   a, sp : Integer;
  246.   Stack : Array[0..49] OF Integer;
  247. BEGIN
  248.   Sp := 0;
  249.   A := Code + SUCCMAX;
  250.   REPEAT
  251.     Stack[Sp] := Integer(RightC[Parent[A]] = A);
  252.     INC(Sp);
  253.     A := Parent[A];
  254.   UNTIL (A = ROOT);
  255.  
  256.   REPEAT
  257.     DEC(Sp);
  258.     Output_Bit(Stack[Sp]);
  259.   UNTIL sp = 0;
  260.   Update_Model(Code);
  261. END;
  262.  
  263.  
  264.  
  265. {** Hash table linked list string search routines **}
  266.  
  267. { Add node to head of list }
  268. Procedure Add_Node(N: Integer);
  269. VAR
  270.   Key :  Integer;
  271.  
  272. BEGIN
  273. { Define hash key function using MINCOPY characters of string prefix }
  274.   Key := (Buffer^[N] XOR (Buffer^[(N+1) MOD MaxSize] SHL 4)) XOR
  275.                    (Buffer^[(N+2) MOD Maxsize] SHL 8) AND HASHMASK;
  276.   IF (Head^[Key] = NUL) THEN
  277.     BEGIN
  278.       Tail^[Key] := N;
  279.       Next^[N] := NUL;
  280.     END
  281.   ELSE
  282.     BEGIN
  283.       Next^[N] := Head^[Key];
  284.       Prev^[Head^[Key]] := N;
  285.     END;
  286.   Head^[Key] := N;
  287.   Prev^[N] := NUL;
  288. END;
  289.  
  290.  
  291.  
  292. { Delete node from tail of list }
  293. Procedure Delete_Node(N : Integer);
  294. VAR
  295.   K : Real;
  296.   Key :  Integer;
  297.  
  298. BEGIN
  299. { Define hash key function using MINCOPY characters of string prefix }
  300.   Key := (Buffer^[N] XOR (Buffer^[(N+1) MOD MaxSize] SHL 4)) XOR
  301.                    (Buffer^[(N+2) MOD Maxsize] SHL 8) AND HASHMASK;
  302.   IF (Head^[Key] = Tail^[Key]) THEN
  303.     Head^[Key] := NUL
  304.   ELSE
  305.     BEGIN
  306.       Next^[Prev^[Tail^[Key]]] := NUL;
  307.       Tail^[Key] := Prev^[Tail^[Key]];
  308.     END;
  309. END;
  310.  
  311.  
  312.  
  313. { Find longest string matching lookahead buffer string }
  314. Function Match(N,Depth: Integer): Integer;
  315. LABEL 1;
  316. VAR
  317.    I, J, Index, Key, Dist, Len, Best, Count  : Integer;
  318. BEGIN
  319.   Best := 0;
  320.   Count := 0;
  321.  
  322.   IF (N = MaxSize) THEN
  323.     N := 0;
  324.  
  325. { Define hash key function using MINCOPY characters of string prefix }
  326.   Key := (Buffer^[N] XOR (Buffer^[(N+1) MOD MaxSize] SHL 4)) XOR
  327.                    (Buffer^[(N+2) MOD Maxsize] SHL 8) AND HASHMASK;
  328.   Index := Head^[Key];
  329.   WHILE (Index <> NUL) DO
  330.     BEGIN
  331.       INC(Count);
  332.       IF (Count > Depth) THEN Goto 1;     { Quit IF depth exceeded }
  333.       IF (Buffer^[(N+Best) MOD MaxSize] = Buffer^[(Index+Best) MOD MaxSize]) THEN
  334.         BEGIN
  335.           Len := 0;
  336.           I := N;
  337.           J := Index;
  338.           WHILE (Buffer^[I] = Buffer^[J]) AND (Len<MAXCOPY) AND ((J<>N) AND (I<>Insrt)) DO
  339.             BEGIN
  340.               INC(Len);
  341.               INC(I);
  342.               IF (I = MaxSize) THEN
  343.                 I := 0;
  344.               INC(J);
  345.               IF (J = MaxSize) THEN
  346.                 J := 0;
  347.             END;
  348.           Dist := N - Index;
  349.           IF (Dist < 0) THEN
  350.             Dist := Dist + MaxSize;
  351.           Dist := Dist - Len;
  352.       { IF dict file, quit at shortest distance range }
  353.           IF (DictFile AND Dist > CopyMax[0]) THEN Goto 1;
  354.           IF (Len > Best) AND (Dist <= MaxDistance) THEN
  355.             BEGIN     { Update best match }
  356.               IF (Len > MINCOPY) OR (Dist <= CopyMax[SHORTRANGE+Binary]) THEN
  357.                 BEGIN
  358.                   Best := Len;
  359.                   Distance := Dist;
  360.                 END;
  361.             END;
  362.         END;
  363.       Index := Next^[Index];
  364.     END;
  365. 1: Match := Best;
  366. END;
  367.  
  368.  
  369.  
  370.  
  371. {** Finite Window compression routines **}
  372.  
  373. CONST
  374.   IDLE = 0;    { Not processing a COPYING }
  375.   COPYING = 1;    { Currently processing COPYING }
  376.  
  377. { Check first buffer for ordered dictionary file }
  378. { Better compression using short distance copies }
  379.  
  380. Procedure Dictionary;
  381. VAR
  382.   i, j, k, count : Integer;
  383. BEGIN
  384.   I := 0;
  385.   J := 0;
  386.   Count := 0;
  387.  
  388.   { Count matching chars at start of adjacent lines }
  389.   INC(J);
  390.   WHILE (J < MINCOPY+MAXCOPY) DO
  391.     BEGIN
  392.       IF (Buffer^[J-1] = 10) THEN
  393.         BEGIN
  394.           K := J;
  395.           WHILE (Buffer^[I] = Buffer^[K]) DO
  396.             BEGIN
  397.               INC(I);
  398.               INC(K);
  399.               INC(count);
  400.             END;
  401.           I := J;
  402.         END;
  403.       INC(J);
  404.     END;
  405.   { IF matching line prefixes > 25% assume dictionary }
  406.   IF (Count > (MINCOPY+MAXCOPY) DIV 4) THEN
  407.     DictFile := 1;
  408. END;
  409.  
  410.  
  411.  
  412.  
  413.  
  414. { Encode file from input to output }
  415. Procedure Encode;
  416. LABEL 1,2;
  417.  
  418. VAR
  419.   C, I, N, Addpos, Len, Full, State, Nextlen, Result: Integer;
  420.  
  421. BEGIN
  422.   N := MINCOPY;
  423.   Addpos := 0;
  424.   Len := 0;
  425.   Full := 0;
  426.   State := IDLE;
  427.   C := 0;
  428.   initialize;
  429.   New(InBuf);
  430.   New(WdBuf);
  431.   GetMem(Head,HASHSIZE*Sizeof(INTEGER));
  432.   GetMem(Tail,HASHSIZE*Sizeof(INTEGER));
  433.   GetMem(Next,MaxSize*Sizeof(INTEGER));
  434.   GetMem(Prev,MaxSize*Sizeof(INTEGER));
  435.   GetMem(Buffer,MaxSize*Sizeof(BYTE));
  436.   IF (head=NIL) OR (Tail=NIL) OR (Next=NIL) OR (Prev=NIL) OR (Buffer=NIL) THEN
  437.     BEGIN
  438.       Writeln('Error allocating memory');
  439.       Halt(1);
  440.     END;
  441.  
  442.   { Initialize hash table to empty }
  443.   FOR I := 0 TO PRED(HASHSIZE) DO
  444.     BEGIN
  445.       Head^[I] := NUL;
  446.     END;
  447.  
  448.   BlockRead(InFile,InBuf^,MAXBUF,Result);
  449.   { Compress first few characters using Huffman }
  450.   FOR I := 0 TO PRED(MINCOPY) DO
  451.     BEGIN
  452.       C := InBuf^[InBufCount];
  453.       INC(InBufCount);
  454.       IF InBufCount = Result THEN
  455.         BEGIN
  456.           Compress(TERMINATE);
  457.           Flush_bits;
  458.           FreeMem(Head,HASHSIZE*Sizeof(INTEGER));
  459.           FreeMem(Tail,HASHSIZE*Sizeof(INTEGER));
  460.           FreeMem(Next,MaxSize*Sizeof(INTEGER));
  461.           FreeMem(Prev,MaxSize*Sizeof(INTEGER));
  462.           FreeMem(buffer,MaxSize*Sizeof(BYTE));
  463.           Dispose(Wdbuf);
  464.           Dispose(InBuf);
  465.           Exit;
  466.         END;
  467.       Compress(C);
  468.       INC(Bytes_In);
  469.       Buffer^[I] := C;
  470.     END;
  471.  
  472.  
  473.   { Preload next few characters into lookahead buffer }
  474.   FOR I := 0 To PRED(MAXCOPY) DO
  475.     BEGIN
  476.       C := InBuf^[InBufCount];
  477.       INC(InBufCount);
  478.       IF InBufCount = Result THEN Goto 1;
  479.       Buffer^[Insrt] := C;
  480.       INC(Insrt);
  481.       INC(Bytes_In);
  482.       IF (C > 127) THEN
  483.         Binary := 1;     { Binary file ? }
  484.     END;
  485.  
  486. 1:
  487.   Dictionary;  { Check for dictionary file }
  488.  
  489.   WHILE (N <> Insrt) Do
  490.     BEGIN
  491.     { Check compression to insure really a dictionary file }
  492.       IF (Boolean(dictfile) AND ((Bytes_In MOD MAXCOPY) = 0)) THEN
  493.         IF (Bytes_In/Bytes_Out < 2) THEN
  494.           Dictfile := 0;     { Oops, not a dictionary file ! }
  495.  
  496.     { Update nodes in hash table lists }
  497.       IF BOOLEAN(Full)  THEN Delete_Node(Insrt);
  498.       Add_node(Addpos);
  499.  
  500.     { IF doing COPYING, process character, ELSE check for new COPYING }
  501.       IF (State = COPYING) THEN
  502.         BEGIN
  503.           DEC(Len);
  504.           IF (len = 1) THEN
  505.             State := IDLE;
  506.         END
  507.       ELSE
  508.         BEGIN
  509.  
  510.       { Get match length at next character and current char }
  511.           IF BOOLEAN(binary) THEN
  512.             BEGIN
  513.               Nextlen := Match(N+1,BINNEXT);
  514.               Len := Match(N,BINSEARCH);
  515.             END
  516.           ELSE
  517.             BEGIN
  518.               Nextlen := Match(N+1,TEXTNEXT);
  519.               Len := Match(N,TEXTSEARCH);
  520.             END;
  521.  
  522.       { IF long enough and no better match at next char, start COPYING }
  523.           IF (Len >= MINCOPY) AND (len >= NextLen) THEN
  524.             BEGIN
  525.               State := COPYING;
  526.  
  527.         { Look up minimum bits to encode distance }
  528.               FOR I := 0 To PRED(COPYRANGES) DO
  529.                 BEGIN
  530.                   IF (distance <= CopyMax[i]) THEN
  531.                     BEGIN
  532.                       Compress(FIRSTCODE-MINCOPY+Len+I*CODESPERRANGE);
  533.                       Output_code(Distance-CopyMin[I],CopyBits[I]);
  534.                       Goto 2;
  535.                     END;
  536.                 END;
  537. 2:
  538.             END
  539.           ELSE   { ELSE output single literal character }
  540.             Compress(Buffer^[N]);
  541.         END;
  542.  
  543.     { Advance buffer pointers }
  544.       INC(N);
  545.       IF (N = MaxSize) THEN
  546.         N := 0;
  547.       INC(Addpos);
  548.       IF (Addpos = MaxSize) THEN
  549.         Addpos := 0;
  550.  
  551.     { Add next input character to buffer }
  552.       IF InBufCount < Result THEN
  553.         BEGIN
  554.           C := InBuf^[InBufCount];
  555.           INC(InBufCount);
  556.           IF InBufCount = MAXBUF THEN
  557.             BEGIN
  558.               BlockRead(InFile,InBuf^,MAXBUF,Result);
  559.               InBufCount := 0;
  560.             END;
  561.           Buffer^[Insrt] := C;
  562.           Inc(Insrt);
  563.           INC(Bytes_In);
  564.           IF (Insrt = MaxSize) THEN
  565.             BEGIN
  566.               Insrt := 0;
  567.               Full := 1;
  568.             END;
  569.         END
  570.       ELSE Full := 0;
  571.     END;
  572.  
  573.   { Output EOF code and free memory }
  574.   compress(TERMINATE);
  575.   Flush_Bits;
  576.   FreeMem(Head,HASHSIZE*Sizeof(INTEGER));
  577.   FreeMem(Tail,HASHSIZE*Sizeof(INTEGER));
  578.   FreeMem(Next,MaxSize*Sizeof(INTEGER));
  579.   FreeMem(Prev,MaxSize*Sizeof(INTEGER));
  580.   FreeMem(buffer,MaxSize*Sizeof(BYTE));
  581.   Dispose(WDBuf);
  582.   Dispose(InBuf);
  583. END;
  584.  
  585.  
  586.  
  587.  
  588.  
  589. {********************* Decompression Routines ********************}
  590.  
  591.  
  592.  
  593.  
  594.  
  595.  
  596.  
  597. { Read multibit code from input file }
  598. Function Input_Code(Bits:Integer): WORD;
  599. CONST
  600.   Bit : Array[1..14] OF WORD = (1,2,4,8,16,32,64,128,256,512,1024,
  601.                                 2048,4096,8192);
  602. VAR
  603.   I, Code, Result : WORD;
  604. BEGIN
  605.   Code := 0;
  606.   FOR I := 1 TO Bits DO
  607.     BEGIN
  608.       IF (Input_Bit_Count = 0) THEN
  609.         BEGIN
  610.           IF (InBufCount = MAXBUF) THEN
  611.             BEGIN
  612.               BlockRead(InFile,WdBuf^,MAXBUF*2,Result);
  613.               INC(Bytes_In,Result);
  614.               InBufCount := 0;
  615.               IF (Result = 0) THEN
  616.                 BEGIN
  617.                   Writeln('UNEXPECTED END OF FILE');
  618.                   HALT(1);
  619.                 END;
  620.             END;
  621.           Input_Bit_Buffer := Wdbuf^[InBufCount];
  622.           INC(InBufCount);
  623.           Input_Bit_Count := 15;
  624.         END
  625.       ELSE DEC(Input_Bit_Count);
  626.       IF Input_Bit_Buffer > $7FFF THEN Code := Code OR Bit[I];
  627.       Input_Bit_Buffer :=  Input_Bit_Buffer SHL 1;
  628.     END;
  629.   Input_Code := Code;
  630. END;
  631.  
  632.  
  633.  
  634.  
  635.  
  636. { Uncompress a character code from input stream }
  637. Function Uncompress: WORD;
  638. LABEL
  639.  TOP,AFT,OVER,NOREAD;
  640. VAR
  641.   Result : WORD;
  642. BEGIN
  643.   ASM
  644.     MOV BX, 1
  645.     MOV DX, Input_Bit_Count
  646.     MOV CX, Input_Bit_Buffer
  647.     MOV AX, InBufCount
  648. TOP:                           { REPEAT                               }
  649.     OR   DX, DX                {  IF Input_Bit_Count <> 0 THEN        }
  650.     JNE  AFT                   {    BEGIN                             }
  651.     CMP  AX, MAXBUF            {      IF InBufCount = MAXBUF THEN     }
  652.     JNE  NOREAD                {        BEGIN                         }
  653.     PUSH BX
  654.     PUSH CX
  655.     PUSH DX
  656.   END;
  657.   BlockRead(InFile,WdBuf^,MAXBUF*2,Result);
  658.   INC(Bytes_In,Result);
  659.   IF (Result = 0) THEN
  660.     BEGIN
  661.       Writeln('UNEXPECTED END OF FILE');
  662.       HALT(1);
  663.     END;
  664.   ASM
  665.     POP DX
  666.     POP CX
  667.     POP BX
  668.     XOR AX, AX                 {          InBufCount := 0;            }
  669. NOREAD:                        {        END;                          }
  670.     SHL AX,1                   {      Input_Bit_Buffer := InBuf^[InBufCount];}
  671.     LES DI,[WdBuf]
  672.     ADD DI,AX
  673.     SHR AX,1
  674.     MOV CX,ES:[DI]
  675.     INC AX                     {      INC(InBufCount);                }
  676.     MOV DX,$F                  {      Input_Bit_Count := 15;          }
  677.     JMP OVER                   {    END                               }
  678. AFT:
  679.     DEC DX                     {  ELSE DEC(Input_Bit_Count);          }
  680. OVER:
  681.     CMP CX,$7FFF               {  IF Input_Bit_Buffer > $7FFF THEN    }
  682.     JBE @Less
  683.     MOV DI,BX                  {    A := RightC[A];                   }
  684.     SHL DI,1
  685.     MOV BX,[DI+OFFSET RightC]
  686.     JMP @After
  687. @Less:
  688.     MOV DI,BX                  {  ELSE A := LeftC[A];                 }
  689.     SHL DI,1
  690.     MOV BX,[DI+OFFSET LeftC]
  691. @After:
  692.     SHL  CX,1                  {  Input_BitBuffer := Input_Bit_Buffer SHL 1;}
  693.     CMP  BX, MAXCHAR           { UNTIL A > MAXCHAR;                   }
  694.     JLE  TOP
  695.     SUB  BX, SUCCMAX           { DEC(A,SUCCMAX);                      }
  696.     MOV  Input_Bit_Count, DX
  697.     MOV  Input_Bit_Buffer, CX
  698.     MOV  InBufCount, AX
  699.     PUSH BX
  700.     PUSH BX
  701.     CALL UPDATE_MODEL          { Model_Update(A);                     }
  702.     POP  AX
  703.     MOV  [BP-2],AX             { Uncompress := A;                     }
  704.   END;
  705. END;
  706.  
  707.  
  708.  
  709.  
  710.  
  711. { Decode file from input to output }
  712. Procedure decode;
  713.  
  714. VAR
  715.   I, J, Dist, Len, Index, K, T : INTEGER;
  716.   N, Result, C : WORD;
  717.  
  718. BEGIN
  719.   New(WDBuf);
  720.   New(OutBuf);
  721.   N := 0;
  722.   InBufCount := MAXBUF;
  723.   initialize;
  724.   GetMem(Buffer,MaxSize*Sizeof(BYTE));
  725.   IF (Buffer = NIL) THEN
  726.     BEGIN
  727.       Writeln('Error allocating memory');
  728.       HALT(1);
  729.     END;
  730.   C := Uncompress;
  731.   WHILE (C <> TERMINATE) DO
  732.     BEGIN
  733.       IF (C < 256) THEN
  734.         BEGIN     { Single literal character ? }
  735.           OutBuf^[OutBufCount] := C;
  736.           INC(OutBufCount);
  737.           IF OutBufCount = MAXBUF THEN
  738.             BEGIN
  739.               BlockWrite(OutFile,OutBuf^,MAXBUF,Result);
  740.               OutBufCount := 0;
  741.               INC(Bytes_Out,Result);
  742.             END;
  743.           Buffer^[N] := C;
  744.           INC(N);
  745.           IF (N = MaxSize) THEN
  746.             N := 0;
  747.         END
  748.       ELSE
  749.         BEGIN            { ELSE string copy length/distance codes }
  750.           T := C - FIRSTCODE;
  751.           Index := (T) DIV CODESPERRANGE;
  752.           Len := T + MINCOPY - Index*CODESPERRANGE;
  753.           Dist := Input_Code(CopyBits[Index]) + Len + CopyMin[Index];
  754.           J := N;
  755.           K := N - Dist;
  756.           IF (K < 0) THEN
  757.             INC(K,MaxSize);
  758.           FOR i := 0 To PRED(Len) DO
  759.             BEGIN
  760.               OutBuf^[OutBufCount] := Buffer^[K];
  761.               INC(OutBufCount);
  762.               IF OutBufCount = MAXBUF THEN
  763.                 BEGIN
  764.                   BlockWrite(OutFile,OutBuf^,MAXBUF,Result);
  765.                   OutBufCount := 0;
  766.                   INC(Bytes_Out,Result);
  767.                 END;
  768.               Buffer^[J] := Buffer^[K];
  769.               INC(J);
  770.               INC(K);
  771.               IF (J = Maxsize) THEN J := 0;
  772.               IF (K = Maxsize) THEN K := 0;
  773.             END;
  774.           INC(N,Len);
  775.           IF (N >= Maxsize) THEN
  776.             DEC(N,MaxSize);
  777.         END;
  778.       C := Uncompress;
  779.     END;
  780.   BlockWrite(OutFile,OutBuf^,OutBufCount,Result);
  781.   INC(Bytes_Out, Result);
  782.   FreeMem(buffer,MaxSize*Sizeof(BYTE));
  783.   Dispose(OutBuf);
  784.   Dispose(WdBuf);
  785. END;
  786.  
  787.  
  788.  
  789.  
  790. { Main program }
  791. BEGIN
  792.   Insrt := MINCOPY;
  793.   Dictfile := 0;
  794.   Binary := 0;
  795.   Input_Bit_Count := 0;           { Input bits buffered }
  796.   Input_Bit_Buffer := 0;          { Input buffer }
  797.   Output_Bit_Count := 0;          { Output bits buffered }
  798.   Output_Bit_Buffer := 0;         { Output buffer }
  799.   Bytes_In := 0;
  800.   Bytes_Out := 0;                 { File size counters }
  801.  
  802.  
  803.   IF (ParamCount < 2) OR (ParamCount > 4) THEN
  804.     BEGIN
  805.       Writeln('Usage: ',ParamStr(0),' inputfile outputfile [decompress]');
  806.       HALT;
  807.     END;
  808.   IF (ParamStr(1) = ParamStr(2)) THEN
  809.     BEGIN
  810.       Writeln('File names must be different');
  811.       HALT;
  812.     END;
  813.  
  814.   Assign(Infile,ParamStr(1));
  815. {$I-}
  816.   Reset(infile,1);
  817.   IF IOResult <> 0 THEN
  818.     BEGIN
  819.       Writeln('Error opening input file ',ParamStr(1));
  820.       HALT;
  821.     END;
  822.  
  823.   Assign(OutFile,ParamStr(2));
  824.   ReWrite(outFile,1);
  825. {$I+}
  826.   IF IOResult <> 0 THEN
  827.     BEGIN
  828.       Writeln('Error opening output file ',ParamStr(2));
  829.       HALT;
  830.     END;
  831.  
  832.    IF (ParamCount <> 3) THEN
  833.      BEGIN
  834.         Encode;
  835.         Writeln('Packed from ',Bytes_In,' bytes to ',Bytes_Out,' bytes');
  836.      END
  837.    ELSE
  838.      BEGIN
  839.        Decode;
  840.        Writeln('Unpacked from ',Bytes_In,' bytes to ',Bytes_out,' bytes');
  841.     END;
  842.   Close(outfile);
  843.   Close(infile);
  844. END.
  845.  
  846.  
  847.  
  848.  
  849.