home *** CD-ROM | disk | FTP | other *** search
/ CD Actual 14 / CDACTUAL.iso / cdactual / demobin / share / program / Basic / SLENCODE.ZIP / SLENCODE.BAS < prev    next >
Encoding:
BASIC Source File  |  1992-08-25  |  12.9 KB  |  402 lines

  1. '/////////////////////////////////////////////////////////////////////////////
  2. '/                           SLENCODE.BAS v1.0                               /
  3. '/        An Implemention of the LZSS Pattern Compression Algorithm          /
  4. '/                         By Rich Geldreich 1992                            /
  5. '/             Original C Program by Haruhiko Okumura 4/6/1989               /
  6. '/                                                                           /
  7. '/   Anyone may freely distribute and use this program, as long as proper    /
  8. '/                        credit is given. Thanks!                           /
  9. '/                                                                           /
  10. '/               Any bugs, problems, questions, write/call:                  /
  11. '/                    Rich Geldreich                                         /
  12. '/                    410 Market St.                                         /
  13. '/                    Gloucester City, NJ 08030                              /
  14. '/                    (609)-742-8752 or (609)-456-0721                       /
  15. '/                                                                           /
  16. '/ The two programs SLDECODE.ASM and SLDECODE.BAS decompress files created   /
  17. '/ with this program.                                                        /
  18. '/////////////////////////////////////////////////////////////////////////////
  19.  
  20. 'ToDo list: add more terse error checking, move code from main module to
  21. 'a callable subroutine, CRC-32 routine, add a statistical modeling scheme
  22. 'for greater compression(dynamic Huffman coding is a very likely choice-
  23. 'arithmitic coding in QB might not work too well).
  24.  
  25. 'Warning: Do NOT press CTRL+Break and then continue this program while
  26. 'you are running it in the environment! At best, the resulting compressed
  27. 'file will be invalid. At worst, your machine will lock up.
  28.  
  29. 'QuickBASIC 4.5 users: As this program is, it will not work correctly with
  30. 'QB 4.5. To make it QB 4.5 compatible, simply change all of the "SSEG" strings
  31. 'in this program to "VARSEG" strings with the search and replace function.
  32. 'Note that the SLDECODE.ASM routine should not require any changes for it to
  33. 'work properly.
  34.  
  35. DEFINT A-Z
  36.  
  37. CONST BufferSize = 4096 'Ring buffer's size
  38. CONST MaxMatch = 74     'Maximum match length
  39. CONST Threshold = 2     'Minimum match length
  40. CONST Null = BufferSize
  41.  
  42. DECLARE SUB InitTree ()         'Initializes the multiple binary trees
  43. DECLARE SUB InsertNode (R)      'Inserts string R to R+MaxMatch-1 into tree
  44. DECLARE SUB DeleteNode (P)      'Deletes node P from tree
  45. DECLARE SUB FillInputBuffer ()  'Fills input buffer from disk
  46. DECLARE SUB PutData (Stuff, Codesize) 'Writes multibit codes to output buffer
  47.  
  48. 'Last Buffersize characters from input file go into ring buffer for searching
  49. DIM SHARED Ring.Buffer((BufferSize - 1) + (MaxMatch - 1))
  50. DIM SHARED Dad(BufferSize)      'Father to each entry
  51. DIM SHARED LeftSon(BufferSize)  'Left son of each entry
  52. DIM SHARED RightSon(BufferSize + 1 + 255) 'Right son of each entry+root of each
  53.                                           'binary tree.
  54. 'Note that node 4096 is a "null" node
  55.  
  56. 'Maximum match length and position returned by InsertNode
  57. DIM SHARED Match.Position, Match.Length
  58.  
  59. 'Input & output buffer stuff
  60. DIM SHARED InBuffer$, Iseg, IAddress, IEndAddress
  61. DIM SHARED OutBuffer$, Oseg, OAddress, OStartAddress, OEndAddress
  62.  
  63. 'PutData stuff
  64. DIM SHARED Shift(7) AS LONG, Char&, CurrentBit
  65.  
  66. PRINT "SLENCODE.BAS v1.0 - LZSS Encoder in QuickBASIC 4.5"
  67. PRINT "By Rich Geldreich 1992"
  68. A$ = COMMAND$
  69. IF A$ = "" THEN INPUT "File to compress"; A$
  70. IF A$ = "" THEN END
  71. IF INSTR(A$, "OUTPUT.SL1") THEN
  72.     PRINT "Cannot compress output file."
  73.     END
  74. END IF
  75.  
  76. 'Why use DATA/READ when we can just plop the values in ourselves?
  77.  Shift(0) = 1
  78.  Shift(1) = 2
  79.  Shift(2) = 4
  80.  Shift(3) = 8
  81.  Shift(4) = 16
  82.  Shift(5) = 32
  83.  Shift(6) = 64
  84.  Shift(7) = 128
  85.  
  86. 'Initialize the input and output buffers
  87.  InBuffer$ = SPACE$(4096)
  88.  IAddress = 0: IEndAddress = 1
  89.  
  90.  OutBuffer$ = SPACE$(4096)
  91. 'Make sure that the offset used to address the OutBuffer$ is always an
  92. 'integer for speed.
  93.  A& = SADD(OutBuffer$)
  94.  A& = A& - 65536 * (A& < 0)
  95.  Oseg& = SSEG(OutBuffer$) + (A& \ 16)
  96.  IF Oseg& > 32767 THEN Oseg = Oseg& - 65536 ELSE Oseg = Oseg&
  97.  OAddress = A& AND 15
  98.  OStartAddress = OAddress
  99.  OEndAddress = OAddress + 4096
  100.  
  101. 'Input input file
  102. OPEN A$ FOR BINARY AS #1
  103. BytesLeft& = LOF(1)
  104. IF BytesLeft& = 0 THEN
  105.     PRINT A$; " not found or null."
  106.     CLOSE #1
  107.     KILL A$
  108.     END
  109. END IF
  110. PRINT "Compressing "; A$; " -"; : xpos = POS(0)
  111.  
  112. 'Open output file
  113. OPEN "output.sl1" FOR BINARY AS #2
  114. IF LOF(2) <> 0 THEN
  115.     CLOSE #2
  116.     KILL "output.sl1"
  117.     OPEN "output.sl1" FOR BINARY AS #2
  118. END IF
  119.  
  120. GOSUB UpdatePercent
  121.  
  122. 'Put my little header
  123. A$ = "RG": PUT #2, , A$
  124. PUT #2, , BytesLeft&
  125.  
  126. 'Initialize the ring buffer with space characters.
  127. InitTree
  128. S = 0: R = BufferSize - MaxMatch
  129. FOR Work = 0 TO R - 1
  130.     Ring.Buffer(Work) = 32
  131. NEXT
  132.  
  133. 'Attempt to get MaxMatch characters from the file, and put them in the ring
  134. 'buffer.
  135. FOR LookAheadLength = 0 TO MaxMatch - 1
  136.      IAddress = IAddress + 1
  137.      IF IAddress = IEndAddress THEN FillInputBuffer
  138.      IF BytesLeft& = 0 THEN EXIT FOR
  139.     Ring.Buffer(R + LookAheadLength) = PEEK(IAddress)
  140.     BytesLeft& = BytesLeft& - 1
  141. NEXT
  142.  
  143. 'Insert the characters into the tree.
  144. FOR Work = 1 TO MaxMatch
  145.     InsertNode R - Work
  146. NEXT
  147. InsertNode R
  148. DO
  149.     'Match.Length must always be less than the look ahead buffer's length
  150.     IF Match.Length > LookAheadLength THEN Match.Length = LookAheadLength
  151.  
  152.     'Does the match length exceed the break even point?
  153.     IF Match.Length <= Threshold THEN
  154.         CodesOut& = CodesOut& + 1
  155.  
  156.         Match.Length = 1
  157.         PutData Ring.Buffer(R) * 2, 9 'Send 1 character unencoded
  158.     ELSE
  159.         CodesOut& = CodesOut& + 1
  160.  
  161.         'Send a position and match length pair
  162.         IF Match.Length <= 10 THEN 'do we send 3 or 6 bits for match length?
  163.             PutData 1, 2 'send 1 0
  164.             PutData (Match.Length - (Threshold + 1)), 3
  165.         ELSE
  166.             PutData 3, 2 'send 1 1
  167.             PutData (Match.Length - 11), 6
  168.         END IF
  169.  
  170.         'Send match position.
  171.         PutData Match.Position, 12
  172.     END IF
  173.  
  174.     'Get Match.Length chars from the input file and put them into the
  175.     'ring buffer.
  176.     Last.Match.Length = Match.Length
  177.     FOR Work = 0 TO Last.Match.Length - 1
  178.          'Check to see if any bytes left, and get a byte if there is.
  179.          'Otherwise exit this loop.
  180.          IF BytesLeft& = 0 THEN EXIT FOR
  181.          IAddress = IAddress + 1
  182.          IF IAddress = IEndAddress THEN FillInputBuffer
  183.          C = PEEK(IAddress)
  184.          BytesLeft& = BytesLeft& - 1
  185.  
  186.         DeleteNode S
  187.         Ring.Buffer(S) = C
  188.         
  189.         'Copy the char to a ghost buffer at the end of the table in case
  190.         'it's near the beginning. This simplifies the InsertNode
  191.         'procedure, because the ring buffer comparison does not have to
  192.         'increment the comparison positions modulo BufferSize.
  193.         
  194.         IF S < (MaxMatch - 1) THEN Ring.Buffer(S + BufferSize) = C
  195.  
  196.         S = (S + 1) AND (BufferSize - 1)
  197.         R = (R + 1) AND (BufferSize - 1)
  198.         InsertNode R
  199.     NEXT
  200.  
  201.     'At end of file, but still characters left in the look ahead buffer.
  202.     FOR Work = Work TO Last.Match.Length - 1
  203.         DeleteNode S
  204.         S = (S + 1) AND (BufferSize - 1)
  205.         R = (R + 1) AND (BufferSize - 1)
  206.         LookAheadLength = LookAheadLength - 1
  207.         IF LookAheadLength > 0 THEN InsertNode R
  208.     NEXT
  209.  
  210.     IF Tc = 0 THEN GOSUB UpdatePercent
  211.     Tc = (Tc + 1) AND 255
  212.         
  213. LOOP WHILE LookAheadLength > 0
  214.  
  215. 'Flush output buffer
  216. PutData 0, 12
  217. OutBuffer$ = LEFT$(OutBuffer$, OAddress - OStartAddress)
  218.  
  219. PUT #2, , OutBuffer$
  220. PUT #2, 3, CodesOut&    'Store # of codes sent
  221.  
  222. 'Report compression
  223. LOCATE , 1
  224. PRINT "Bytes in:"; LOF(1); "Bytes out:"; LOF(2);
  225. PRINT "Compression:";
  226. PRINT 100 - (LOF(2) * 100&) \ LOF(1); "%"; SPACE$(16)
  227. CLOSE #1, #2
  228. END
  229.  
  230. UpdatePercent:
  231.  LOCATE , xpos
  232.  PRINT (100& * (LOF(1) - BytesLeft&)) \ LOF(1); "% complete";
  233. RETURN
  234.  
  235. 'Deletes node P from binary tree
  236. SUB DeleteNode (P)
  237.     'not in tree yet?
  238.     IF Dad(P) = Null THEN EXIT SUB
  239.  
  240.     L = LeftSon(P)
  241.     R = RightSon(P)
  242.  
  243.     IF R = Null THEN        'no right son?
  244.         Q = L               'use left son then
  245.     ELSEIF L = Null THEN    'no left son?
  246.         Q = R               'use right son then
  247.     ELSE
  248.         Q = L               'great, it has two sons! find a place for
  249.                             'one of them...
  250.  
  251.         IF RightSon(Q) <> Null THEN
  252.             'find a leaf branch
  253.             DO
  254.                 Q = RightSon(Q)
  255.             LOOP WHILE RightSon(Q) <> Null
  256.             'RightSon(Q)=Null now
  257.  
  258.             'Make right son of Q's dad point tward the left son of Q
  259.  
  260.             D = Dad(Q)
  261.             RightSon(D) = LeftSon(Q)
  262.             Dad(LeftSon(Q)) = D
  263.  
  264.             'The left son of P is now the left son of Q
  265.             L = LeftSon(P)
  266.             LeftSon(Q) = L
  267.             Dad(L) = Q
  268.         END IF
  269.         'The right son of P is now the right son of Q
  270.         R = RightSon(P)
  271.         RightSon(Q) = R
  272.         Dad(R) = Q
  273.         'now P has no children
  274.     END IF
  275.     
  276.     'Delete node P- replace it with node Q
  277.     Dad(Q) = Dad(P)
  278.     IF RightSon(Dad(P)) = P THEN
  279.         RightSon(Dad(P)) = Q        'which way was P's father pointing twards
  280.     ELSE                            'P? Right or left?
  281.         LeftSon(Dad(P)) = Q
  282.     END IF
  283.     Dad(P) = Null                   'P bites the dust here
  284.  
  285. END SUB
  286.  
  287. SUB FillInputBuffer
  288.     GET #1, , InBuffer$
  289.     A& = SADD(InBuffer$): A& = A& - 65536 * (A& < 0)
  290.     Iseg& = SSEG(InBuffer$) + (A& \ 16)
  291.     IF Iseg& > 32767 THEN Iseg = Iseg& - 65536 ELSE Iseg = Iseg&
  292.     IAddress = A& AND 15
  293.     IEndAddress = IAddress + 4096
  294.     DEF SEG = Iseg
  295. END SUB
  296.  
  297. SUB InitTree
  298.     'Initialize RightSon(FirstChar)...RightSon(LastChar) to null
  299.     '(These make up the roots to the multiple binary search trees.)
  300.     FOR Work = BufferSize + 1 TO BufferSize + 256
  301.         RightSon(Work) = Null
  302.     NEXT
  303.     'Clear ring buffer's father pointers
  304.     FOR Work = 0 TO BufferSize - 1
  305.         Dad(Work) = Null
  306.     NEXT
  307. END SUB
  308.  
  309. 'Inserts string Ring.Buffer(R) into tree- finds maximum match length and
  310. 'position too. If match length is the maximum match length, then the old
  311. 'node will be deleted for the new one to avoid having two nodes of the
  312. 'same string in the tree.
  313. SUB InsertNode (R)
  314.     Test = 1
  315.     P = (BufferSize + 1) + Ring.Buffer(R) 'Find first tree
  316.     RightSon(R) = Null
  317.     LeftSon(R) = Null
  318.     Match.Length = 0
  319.     DO
  320.         IF Test >= 0 THEN 'which way did he go?
  321.             IF RightSon(P) <> Null THEN
  322.                 P = RightSon(P)
  323.             ELSE
  324.                 RightSon(P) = R
  325.                 Dad(R) = P
  326.                 EXIT SUB
  327.             END IF
  328.         ELSE
  329.             IF LeftSon(P) <> Null THEN
  330.                 P = LeftSon(P)
  331.             ELSE
  332.                 LeftSon(P) = R
  333.                 Dad(R) = P
  334.                 EXIT SUB
  335.             END IF
  336.         END IF
  337.  
  338.         'Do an alphabetical comparison on Ring.Buffer(R) & Ring.Buffer(P)
  339.         FOR Work = 1 TO MaxMatch - 1 'Compare
  340.             Test = Ring.Buffer(R + Work) - Ring.Buffer(P + Work)
  341.             IF Test <> 0 THEN EXIT FOR 'Exit if not equal
  342.         NEXT
  343.         IF Work > Match.Length THEN 'higher than current match length?
  344.             Match.Position = P      'save match position and match length
  345.             Match.Length = Work
  346.         END IF
  347.  
  348.     LOOP UNTIL Work >= MaxMatch
  349.     
  350.    'The following code is only executed when the string found is of the
  351.    'maximum match size.
  352.    'The old string is deleted so the binary tree doesn't have two copies
  353.    'of the same string for efficiency.
  354.    
  355.    'delete node P for node R
  356.  
  357.     Dad(R) = Dad(P)
  358.     LeftSon(R) = LeftSon(P)
  359.     RightSon(R) = RightSon(P)
  360.     Dad(LeftSon(P)) = R
  361.     Dad(RightSon(P)) = R
  362.  
  363.    'make P's father point twards R
  364.  
  365.     IF RightSon(Dad(P)) = P THEN
  366.         RightSon(Dad(P)) = R
  367.     ELSE
  368.         LeftSon(Dad(P)) = R
  369.     END IF
  370.  
  371.    'delete P
  372.  
  373.     Dad(P) = Null
  374.  
  375. END SUB
  376.  
  377. 'Sends a multibit code to the output file.
  378. SUB PutData (Stuff, Codesize) STATIC
  379.  
  380.     DEF SEG = Oseg 'switch to output segment
  381.  
  382.     Char& = Char& + Stuff * Shift(CurrentBit)  'attach bits to bit buffer
  383.     CurrentBit = CurrentBit + Codesize         '+Codesize more bits now
  384.     
  385.  
  386.     DO WHILE CurrentBit > 7 'we have at least one byte?
  387.          IF OAddress = OEndAddress THEN 'At end of output buffer?
  388.              PUT #2, , OutBuffer$
  389.              OAddress = OStartAddress
  390.          END IF
  391.          POKE OAddress, Char& AND 255   'save 8 bits
  392.          OAddress = OAddress + 1
  393.  
  394.         Char& = Char& \ 256
  395.         CurrentBit = CurrentBit - 8     '8 less bits now
  396.     LOOP
  397.  
  398.     DEF SEG = Iseg 'switch to input segment
  399.  
  400. END SUB
  401.  
  402.