home *** CD-ROM | disk | FTP | other *** search
/ Photo CD Demo 1 / Demo.bin / formats / gif / atari / stgif.arc / COMPRESS.MOD < prev    next >
Encoding:
Modula Implementation  |  1987-06-09  |  10.8 KB  |  368 lines

  1. IMPLEMENTATION MODULE Compressor;
  2.  
  3. (*$S-,$T-*)
  4.  
  5. (*-----------------------------------------------------------------------*)
  6. (* Copyright (c) 1987                                                    *)
  7. (* by CompuServe Inc., Columbus, Ohio.  All Rights Reserved              *)
  8. (*-----------------------------------------------------------------------*)
  9.  
  10.  
  11. (*
  12.  * VERSION: 1.0
  13.  *
  14.  * ABSTRACT:
  15.  *
  16.  *      This module implements the Lempel-Ziv adaptive compression
  17.  *      algorithm as given in the June 1984 issue of IEEE Computer,
  18.  *      "A Technique for High-Performance Data Compression" by Terry
  19.  *      A. Welch.
  20.  *
  21.  *      The compression algorithm builds a string translation table that maps
  22.  *      substrings from the input string into fixed-length codes.  These codes
  23.  *      are used by the expansion algorithm to rebuild the compressor's table
  24.  *      and reconstruct the original data stream.  In it's simplest form, the
  25.  *      algorithm can be stated as:
  26.  *
  27.  *              "if <w>k is in the table, then <w> is in the table"
  28.  *
  29.  *      <w> is a code which represents a string in the table.  When a new
  30.  *      character k is read in, the table is searched for <w>k.  If this
  31.  *      combination is found, <w> is set to the code for that combination
  32.  *      and the next character is read in.  Otherwise, this combination is
  33.  *      added to the table, the code <w> is written to the output stream and
  34.  *      <w> is set to k.
  35.  *
  36.  *      The expansion algorithm builds an identical table by parsing each
  37.  *      received code into a prefix string and suffix character.  The suffix
  38.  *      character is pushed onto the stack and the prefix string translated
  39.  *      again until it is a single character.  This completes the expansion.
  40.  *      The expanded code is then output by popping the stack and a new entry
  41.  *      is made in the table.
  42.  *
  43.  *      The algorithm used here has one additional feature.  The output codes
  44.  *      are variable length.  They start at a specified number of bits.  Once
  45.  *      the number of codes exceeds the current code size, the number of bits
  46.  *      in the code is incremented.  When the table is completely full, a
  47.  *      clear code is transmitted for the expander and the table is reset.
  48.  *      This program uses a maximum code size of 12 bits for a total of 4096
  49.  *      codes.
  50.  *
  51.  *      The expander realizes that the code size is changing when it's table
  52.  *      size reaches the maximum for the current code size.  At this point,
  53.  *      the code size in increased.  Remember that the expander's table is
  54.  *      identical to the compressor's table at any point in the original data
  55.  *      stream.
  56.  *
  57.  *      The compressed data stream is structured as follows:
  58.  *              first byte denoting the minimum code size
  59.  *              one or more counted byte strings. The first byte contains the
  60.  *              length of the string. A null string denotes "end of data"
  61.  *
  62.  *      This format permits a compressed data stream to be embedded within a
  63.  *      non-compressed context.
  64.  *
  65.  * ENVIRONMENT: LogiTech, TDI
  66.  *
  67.  * AUTHOR: Steve Wilhite, CREATION DATE: 4-Jan-87
  68.  *
  69.  * REVISION HISTORY:
  70.  *
  71.  *)
  72.  
  73. (* LogiTech
  74.  
  75. FROM SYSTEM IMPORT
  76.     TSIZE, SETREG, CODE, AX, CX, DI, ES, ADDRESS, ADR;
  77. *)
  78. (* TDI *)
  79.  
  80. FROM SYSTEM IMPORT
  81.     SETREG, CODE, ADR,
  82.     TSIZE;
  83.  
  84. FROM Storage IMPORT
  85.     ALLOCATE, DEALLOCATE;
  86.  
  87. FROM Shifter IMPORT
  88.     ShiftLeft;
  89.  
  90. FROM ErrorCodes IMPORT
  91.     Normal, EndOfFile, NoMemory, BadArgs;
  92.  
  93.  
  94. CONST LargestCode = 4095;
  95. CONST TableSize = 5003;
  96.  
  97. TYPE CodeEntry =
  98.     RECORD
  99.         AddedByte: CHAR;
  100.         PriorCode: CARDINAL;
  101.         CodeId: CARDINAL;
  102.     END;
  103.  
  104. TYPE CodeTableType = ARRAY [0 .. TableSize - 1] OF CodeEntry;
  105.  
  106. VAR Read: ReadProc;
  107. VAR Write: WriteProc;
  108. VAR Status: CARDINAL;
  109. VAR ClearCode, EOFCode, CodeSize, MaxCode: CARDINAL;
  110. VAR CodeTable: POINTER TO CodeTableType;
  111. VAR NextEntry: CARDINAL;
  112. VAR BitOffset: CARDINAL;
  113. VAR CodeBuffer: ARRAY [0 .. 258] OF CHAR;
  114.  
  115. (*.page*)(*.sbttl CompressData *)
  116.  
  117. PROCEDURE CompressData (minCodeSize: CARDINAL;
  118.                         read: ReadProc;
  119.                         write: WriteProc): CARDINAL;
  120. BEGIN
  121.     Read := read;
  122.     Write := write;
  123.     ALLOCATE(CodeTable, TSIZE(CodeTableType));
  124.  
  125.     IF CodeTable = NIL THEN
  126.         RETURN NoMemory;
  127.     END;
  128.  
  129.     CompressTheData(minCodeSize);
  130.     DEALLOCATE(CodeTable, TSIZE(CodeTableType));
  131.     RETURN Status;
  132. END CompressData;
  133.  
  134. (*.page*)(*.sbttl CompressTheData *)
  135.  
  136. PROCEDURE CompressTheData(MinCodeSize: CARDINAL);
  137.  
  138.     VAR SuffixChar: CHAR;
  139.     VAR PrefixCode: CARDINAL;
  140.     VAR H, D: CARDINAL;
  141. BEGIN
  142.     BitOffset := 0;
  143.  
  144.     IF (MinCodeSize < 2) OR (MinCodeSize > 9) THEN
  145.         IF MinCodeSize = 1 THEN
  146.             MinCodeSize := 2;
  147.         ELSE
  148.             Status := BadArgs;
  149.             RETURN;
  150.         END;
  151.     END;
  152.  
  153.     InitTable(MinCodeSize);
  154.     Status := Write(CHR(MinCodeSize));
  155.  
  156.     IF Status # Normal THEN
  157.         RETURN;
  158.     END;
  159.  
  160.     WriteCode(ClearCode);
  161.  
  162.     IF Status # Normal THEN
  163.         RETURN;
  164.     END;
  165.  
  166.     Status := Read(SuffixChar);
  167.  
  168.     IF Status = Normal THEN
  169.         PrefixCode := ORD(SuffixChar);
  170.         Status := Read(SuffixChar);
  171.  
  172.         WHILE Status = Normal DO
  173.  
  174.             (* Search the string table for the active string.  The search uses
  175.                hashing with secondary quadratic probing. *)
  176.  
  177.             H := (PrefixCode + ORD(SuffixChar)*32) MOD TableSize;
  178.             D := 1;
  179.  
  180.             LOOP
  181.                 WITH CodeTable^[H] DO
  182.                     IF CodeId = 0 THEN (* new entry *)
  183.                         WriteCode(PrefixCode);
  184.  
  185.                         IF Status # Normal THEN
  186.                             RETURN;
  187.                         END;
  188.  
  189.                         D := NextEntry;
  190.  
  191.                         IF NextEntry <= LargestCode THEN
  192.                             PriorCode := PrefixCode;
  193.                             AddedByte := SuffixChar;
  194.                             CodeId := NextEntry;
  195.                             INC(NextEntry);
  196.                         END;
  197.  
  198.                         IF D = MaxCode THEN
  199.                             IF CodeSize < 12 THEN
  200.                                 CodeSize := CodeSize + 1;
  201.                                 MaxCode := 2*MaxCode;
  202.                             ELSE
  203.                                 WriteCode(ClearCode);
  204.  
  205.                                 IF Status # Normal THEN
  206.                                     RETURN;
  207.                                 END;
  208.  
  209.                                 InitTable(MinCodeSize);
  210.                             END;
  211.                         END;
  212.  
  213.                         PrefixCode := ORD(SuffixChar);
  214.                         EXIT;
  215.                     END;
  216.  
  217.                     IF (PriorCode = PrefixCode) AND (AddedByte = SuffixChar)
  218.                     THEN
  219.                         PrefixCode := CodeId;
  220.                         EXIT;
  221.                     END;
  222.                 END;
  223.  
  224.                 (* Collision *)
  225.  
  226.                 H := H + D;
  227.                 D := D + 2;
  228.  
  229.                 IF H >= TableSize THEN
  230.                     H := H - TableSize;
  231.                 END;
  232.             END (*LOOP*);
  233.  
  234.             Status := Read(SuffixChar);
  235.         END (*WHILE*);
  236.  
  237.         IF Status # EndOfFile THEN
  238.             RETURN;
  239.         END;
  240.  
  241.         WriteCode(PrefixCode);
  242.  
  243.         IF Status # Normal THEN
  244.             RETURN;
  245.         END;
  246.  
  247.     ELSIF Status # EndOfFile THEN
  248.         RETURN;
  249.     END (*IF*);
  250.  
  251.     WriteCode(EOFCode);
  252.  
  253.     IF Status # Normal THEN
  254.         RETURN;
  255.     END;
  256.  
  257.     (* Flush CodeBuffer to the output stream *)
  258.  
  259.     IF BitOffset > 0 THEN
  260.         FlushBuffer((BitOffset + 7) DIV 8);
  261.  
  262.         IF Status # Normal THEN
  263.             RETURN;
  264.         END;
  265.     END;
  266.  
  267.     Status := Write(0C);
  268. END CompressTheData;
  269.  
  270. (*.page*)(*.sbttl InitTable *)
  271.  
  272. PROCEDURE InitTable (MinCodeSize: CARDINAL);
  273.  
  274.     VAR I: CARDINAL;
  275. BEGIN
  276.     CodeSize := MinCodeSize + 1;
  277.     ClearCode := ShiftLeft(1, MinCodeSize);
  278.     EOFCode := ClearCode + 1;
  279.     NextEntry := ClearCode + 2;
  280.     MaxCode := ShiftLeft(1, CodeSize);
  281.  
  282.     FOR I := 0 TO TableSize - 1 DO
  283.         CodeTable^[I].CodeId := 0;
  284.     END;
  285. END InitTable;
  286.  
  287. (*.page*)(*.sbttl WriteCode *)
  288.  
  289. PROCEDURE WriteCode (Code: CARDINAL);
  290.  
  291.     VAR ByteOffset, BitsLeft: CARDINAL;
  292. (*  VAR Temp: ADDRESS; (* LogiTech *)*)
  293. BEGIN
  294.     ByteOffset := BitOffset DIV 8;
  295.     BitsLeft := BitOffset MOD 8;
  296.  
  297.     IF ByteOffset >= 254 THEN
  298.         FlushBuffer(ByteOffset);
  299.  
  300.         IF Status # Normal THEN
  301.             RETURN;
  302.         END;
  303.  
  304.         CodeBuffer[0] := CodeBuffer[ByteOffset];
  305.         BitOffset := BitsLeft;
  306.         ByteOffset := 0;
  307.     END;
  308.  
  309.     IF BitsLeft > 0 THEN
  310.         (* 8086
  311.         Temp := ADR(CodeBuffer[ByteOffset]);
  312.         SETREG(AX, Code);
  313.         SETREG(CX, BitsLeft);
  314.         SETREG(DI, Temp.OFFSET);
  315.         SETREG(ES, Temp.SEGMENT);
  316.         CODE(033H, 0D2H,                (*      xor     DX,DX           *)
  317.              0D1H, 0E0H,                (* L1:  shl     AX,1            *)
  318.              0D1H, 0D2H,                (*      rcl     DX,1            *)
  319.              0E2H, 0FAH,                (*      loop    L1              *)
  320.              026H, 00AH, 005H,          (*      or      AL,ES:[DI]      *)
  321.              0ABH,                      (*      stosw                   *)
  322.              08AH, 0C2H,                (*      mov     AL,DL           *)
  323.              0AAH);                     (*      stosb                   *)
  324.         *)
  325.         (* 68000 *)
  326.         SETREG(0, LONGCARD(Code));      (*      move.w  Code,d0         *)
  327.         SETREG(7, LONGCARD(BitsLeft));  (*      move.w  BitsLeft,d7     *)
  328.         SETREG(8, ADR(CodeBuffer[ByteOffset]));
  329.         CODE(0EFA8H);                   (*      lsl.l   d7,d0           *)
  330.         CODE(08010H);                   (*      or.b    (a0),d0         *)
  331.         CODE(010C0H);                   (*      move.b  d0,(a0)+        *)
  332.         CODE(0E088H);                   (*      lsr.l   #8,d0           *)
  333.         CODE(010C0H);                   (*      move.b  d0,(a0)+        *)
  334.         CODE(0E088H);                   (*      lsr.l   #8,d0           *)
  335.         CODE(010C0H);                   (*      move.b  d0,(a0)+        *)
  336.     ELSE
  337.         CodeBuffer[ByteOffset] := CHR(Code MOD 256);
  338.         CodeBuffer[ByteOffset + 1] := CHR(Code DIV 256);
  339.     END;
  340.  
  341.     INC(BitOffset, CodeSize);
  342.     Status := Normal;
  343. END WriteCode;
  344.  
  345. (*.page*)(*.sbttl FlushBuffer *)
  346.  
  347. PROCEDURE FlushBuffer(Count: CARDINAL);
  348.  
  349.     VAR I: CARDINAL;
  350. BEGIN
  351.     Status := Write(CHR(Count));
  352.  
  353.     IF Status # Normal THEN
  354.         RETURN;
  355.     END;
  356.  
  357.     FOR I := 0 TO Count - 1 DO
  358.         Status := Write(CodeBuffer[I]);
  359.  
  360.         IF Status # Normal THEN
  361.             RETURN;
  362.         END;
  363.     END;
  364. END FlushBuffer;
  365.  
  366.  
  367. END Compressor.
  368.