home *** CD-ROM | disk | FTP | other *** search
- (* ------------------------------------------------------ *)
- (* LZHUF.PAS *)
- (* Verschlüsseln und Packen von Daten mit dem *)
- (* Huffman-Algorithmus *)
- (* (c) 1990 Ralf Hensmann & TOOLBOX *)
- (* ------------------------------------------------------ *)
- PROGRAM LZHuf;
-
- TYPE
- CARDINAL = WORD;
-
- VAR
- InBuffer, OutBuffer : ARRAY [0..2051] OF BYTE;
- InBufP, OutBufP : 0..2050;
- InBitP, OutBitP : 0..8;
- InFile, OutFile : FILE;
- MaxIn : INTEGER;
- test : TEXT;
- ReadBuf, WriteBuf : ARRAY [0..2048] OF BYTE;
- ReadPtr, WritePtr,
- ReadMax : INTEGER;
-
- (* ----- Hilfsfunktionen zum Lesen von Bits ----------- *)
-
- FUNCTION BufRead : BYTE;
- BEGIN
- IF ReadPtr = ReadMax THEN BEGIN
- BlockRead(InFile, ReadBuf, 2048, ReadMax);
- ReadPtr := 0;
- END;
- BufRead := ReadBuf[ReadPtr];
- Inc(ReadPtr);
- END;
-
- PROCEDURE BufWrite(B : BYTE);
- VAR
- written : CARDINAL;
- BEGIN
- IF WritePtr = 2048 THEN BEGIN
- Blockwrite(OutFile, WriteBuf, 2048, written);
- IF written <> 2048 THEN BEGIN
- WriteLn('Kein Platz');
- Halt;
- END;
- WritePtr := 0;
- END;
- WriteBuf[WritePtr] := B;
- Inc(WritePtr);
- END;
-
- PROCEDURE BufFlush;
- VAR
- written : CARDINAL;
- BEGIN
- Blockwrite(OutFile, WriteBuf, WritePtr, written);
- IF written <> WritePtr THEN BEGIN
- WriteLn('Kein Platz');
- Halt;
- END;
- WritePtr := 0;
- END;
-
- PROCEDURE PutCode(Bits : CARDINAL; Len : CARDINAL);
- VAR
- hilf : CARDINAL;
- BEGIN
- (* unbenutzte Bits löschen *)
- Bits := Bits AND ($FFFF SHL(16 - Len));
- (* wenn notwendig, Flush *)
- IF OutBufP >= 2048 THEN BEGIN
- BlockWrite(OutFile, OutBuffer, 2048, hilf);
- IF hilf <> 2048 THEN BEGIN
- WriteLn('Schreibfehler: Platte ist voll');
- Halt;
- END;
- FOR hilf := 2048 TO OutBufP DO
- OutBuffer[hilf-2048] := OutBuffer[hilf];
- Dec(OutBufP, 2048);
- END;
- IF OutBitP + Len > 16 THEN
- OutBuffer[OutBufP + 2] := Hi(Bits SHL(16 - OutBitP));
- IF OutBitP > 0 THEN
- Bits := (Bits SHR OutBitP) OR
- (OutBuffer[OutBufP] SHL 8);
- OutBuffer[OutBufP] := Hi(Bits);
- OutBuffer[OutBufP+1] := Lo(Bits);
- Inc(OutBufP, (OutBitP + Len) SHR 3);
- OutBitP := (OutBitP + Len) AND 7;
- END;
-
- PROCEDURE FlushCode;
- VAR
- written : CARDINAL;
- BEGIN
- IF OutBitP > 0 THEN Inc(OutBufP);
- BlockWrite(OutFile, OutBuffer, OutBufP, written);
- IF written <> OutBufP THEN BEGIN
- WriteLn('Platte ist voll');
- Halt;
- END;
- END;
-
- FUNCTION GetBit : BYTE;
- BEGIN
- IF InBitP = 8 THEN BEGIN
- InBitP := 0;
- Inc(InBufP);
- END;
- IF (InBufP > MaxIn) THEN BEGIN
- BlockRead(InFile, InBuffer, 2048, MaxIn);
- Dec(MaxIn);
- InBufP := 0;
- END;
- Inc(InBitP);
- GetBit := InBuffer[InBufP] SHR (8 - InBitP) AND 1;
- END;
-
- FUNCTION GetByte : BYTE;
- VAR
- HilfB : BYTE;
- BEGIN
- HilfB := InBuffer[InBufP] SHL InBitP;
- Inc(InBufP);
- IF InBufP > MaxIn THEN BEGIN
- BlockRead(InFile, InBuffer, 2048, MaxIn);
- Dec(MaxIn);
- InBufP := 0;
- END;
- GetByte := HilfB OR InBuffer[InBufP] SHR (8 - InBitP);
- END;
-
- (* ----- Multiple Binary Trees ------------------------ *)
-
- CONST
- BufMax = 4096; (* Puffergröße *)
- LookAhead = 60; (* vorausschauender Puffer *)
- ThresHold = 2; (* Minimaler Wert für LZSS *)
- NUL = BufMax; (* Null-Zeiger *)
- TextMax = BufMax + LookAhead - 1;
-
- VAR
- TextBuf : ARRAY [0..TextMax] OF BYTE; (* Textpuffer *)
- LSon, Dad : ARRAY [0..BufMax] OF CARDINAL; (* "Zeiger" *)
- RSon : ARRAY [0..BufMax+256] OF CARDINAL;
- (* "Zeiger" - die oberen Elemente sind die Root- *)
- (* Zeiger der einzelnen Zeichen *)
- MatchPos,
- MatchLen : CARDINAL;
- (* Pos. und Länge des besten Matching *)
-
- PROCEDURE InitTree; (* Initialisiert den Binärbaum *)
- VAR
- i : CARDINAL;
- BEGIN
- (* Root-Zeiger auf NUL setzen *)
- FOR i := BufMax + 1 TO BufMax + 256 DO
- RSon[i] := NUL;
- (* Zeiger der Tabelle löschen *)
- FOR i := 0 TO BufMax - 1 DO
- Dad[i] := NUL;
- END;
-
- PROCEDURE InsertNode(pos : CARDINAL);
- (* Fügt String an Stelle pos in Baum ein *)
- VAR
- cmp : INTEGER; (* Vergleich der Zeichenketten *)
- i, hilf : CARDINAL;
- node : CARDINAL; (* gerade untersuchter Knoten *)
- BEGIN
- node := TextBuf[pos] + BufMax+1; (* Wurzel des Baums *)
- RSon[pos] := NUL; (* Zeiger "erden" *)
- LSon[pos] := NUL;
- MatchLen := 0;
- cmp := 1; (* Root steht in RSon... *)
- REPEAT
- (* Knoten weiterbewegen *)
- IF cmp >= 0 THEN
- IF RSon[node] <> NUL THEN
- node := RSon[node] (* Wurzel weitersetzen *)
- ELSE BEGIN
- (* Baumende ist erreicht, Element hier anfügen *)
- RSon[node] := pos;
- Dad[pos] := node;
- Exit;
- END
- ELSE IF LSon[node] <> NUL THEN
- node := LSon[node] (* Wurzel weitersetzen *)
- ELSE BEGIN
- (* Baumende ist erreicht, Element hier anfügen *)
- LSon[node] := pos;
- Dad[pos] := node;
- Exit;
- END;
- (* Knoten mit Element vergleichen *)
- i := 1;
- REPEAT
- cmp := INTEGER(TextBuf[pos + i]) - TextBuf[node +i];
- IF cmp = 0 THEN Inc(i);
- UNTIL (i >= LookAhead) OR (cmp <> 0);
-
- (* i enthält die Anzahl der gleichen Zeichen ... *)
- IF i > ThresHold THEN BEGIN
- IF (i > MatchLen) THEN BEGIN
- (* neue Position *)
- MatchPos := (pos - node) AND (BufMax - 1) - 1;
- MatchLen := i;
- END;
- IF (i = MatchLen) AND (i < LookAhead) THEN BEGIN
- hilf := (pos - node) AND (BufMax - 1) - 1;
- IF hilf < MatchPos THEN
- MatchPos := hilf;
- END;
- END;
- UNTIL (MatchLen >= LookAhead);
- (* Sonderfall: node wird durch pos ersetzt, *)
- (* da beide gleich sind. *)
- Dad[pos] := Dad[node];
- LSon[pos] := LSon[node];
- RSon[pos] := RSon[node];
- Dad[LSon[node]] := pos;
- Dad[RSon[node]] := pos;
- IF RSon[Dad[node]] = node THEN
- RSon[Dad[node]] := pos
- ELSE
- LSon[Dad[node]] := pos;
- Dad[node] := NUL; (* node als gelöscht eintragen *)
- END;
-
- PROCEDURE DeleteNode(pos : CARDINAL);
- VAR
- node : CARDINAL;
- BEGIN
- IF Dad[pos] = NUL THEN Exit; (* bereits gelöscht *)
- IF RSon[pos] = NUL THEN
- node := LSon[pos]
- ELSE IF LSon[pos] = NUL THEN
- node := RSon[pos]
- ELSE BEGIN
- node := LSon[pos];
- IF RSon[node] <> NUL THEN BEGIN
- (* Unterstes rechtes Element suchen und vor den *)
- (* linken Ast hängen *)
- REPEAT
- node := RSon[node];
- UNTIL RSon[node] = NUL;
- RSon[Dad[node]] := LSon[node];
- Dad[LSon[node]] := Dad[node];
- LSon[node] := LSon[pos];
- Dad[LSon[pos]] := node;
- END;
- RSon[node] := RSon[pos];
- Dad[RSon[pos]] := node;
- END;
- (* node enthält nun das Element, um pos zu ersetzen *)
- Dad[node] := Dad[pos];
- IF RSon[Dad[pos]] = pos THEN
- RSon[Dad[pos]] := node
- ELSE
- LSon[Dad[pos]] := node;
- Dad[pos] := NUL;
- END;
-
- (* ----- Positionstabellen für LZSS ------------------- *)
- (* Tabellen zur Ver- und Entschlüsselung der ersten *)
- (* 6 Bit in ein Alphabet mit variablen Längen, da *)
- (* kleinere 6-Bit-Werte sehr viel häufiger auftauchen *)
- (* als längere. *)
-
- (* Kompression: *)
-
- CONST
- c_len : ARRAY [0..63] OF BYTE =
- ($03, $04, $04, $04, $05, $05, $05, $05,
- $05, $05, $05, $05, $06, $06, $06, $06,
- $06, $06, $06, $06, $06, $06, $06, $06,
- $07, $07, $07, $07, $07, $07, $07, $07,
- $07, $07, $07, $07, $07, $07, $07, $07,
- $07, $07, $07, $07, $07, $07, $07, $07,
- $08, $08, $08, $08, $08, $08, $08, $08,
- $08, $08, $08, $08, $08, $08, $08, $08);
-
- c_code : ARRAY [0..63] OF CARDINAL =
- ($00, $20, $30, $40, $50, $58, $60, $68,
- $70, $78, $80, $88, $90, $94, $98, $9C,
- $A0, $A4, $A8, $AC, $B0, $B4, $B8, $BC,
- $C0, $C2, $C4, $C6, $C8, $CA, $CC, $CE,
- $D0, $D2, $D4, $D6, $D8, $DA, $DC, $DE,
- $E0, $E2, $E4, $E6, $E8, $EA, $EC, $EE,
- $F0, $F1, $F2, $F3, $F4, $F5, $F6, $F7,
- $F8, $F9, $FA, $FB, $FC, $FD, $FE, $FF);
-
- (* Dekompression: *)
-
- VAR
- d_len : ARRAY [0..255] OF BYTE;
- d_code : ARRAY [0..255] OF CARDINAL;
-
- PROCEDURE MakeTable;
- VAR
- i, entry : CARDINAL;
- BEGIN
- FOR entry := 0 TO 62 DO BEGIN
- i := c_code[entry];
- WHILE (i < c_code[entry+1]) DO BEGIN
- d_len[i] := c_len[entry];
- d_code[i] := entry SHL 6;
- Inc(i);
- END;
- END;
- i := c_code[63];
- WHILE (i <= 255) DO BEGIN
- d_len[i] := c_len[entry];
- d_code[i] := 63 SHL 6;
- Inc(i);
- END;
- FOR i := 0 TO 63 DO
- c_code[i] := c_code[i] SHL 8;
- END;
-
- (* ----- Adaptive Huffman Coding ---------------------- *)
-
- CONST
- NChar = 256 + (LookAhead-ThresHold);
- (* ASCII-Char + Längen der LZSS-Codierungen *)
- NTable = 2*NChar - 1;
- (* Anzahl der N-1 Knoten + N Blätter *)
- Root = NTable - 1; (* Position der Wurzel *)
- MaxFreq = $100; (* Neuaufbau des Huffman-Trees *)
-
- VAR
- Freq : ARRAY [0..NTable] OF CARDINAL;
- (* Häufigkeiten *)
- Prnt : ARRAY [0..NTable+NChar-1] OF CARDINAL;
- (* Zeiger auf den "oberen" Knoten. *)
- (* Die Elemente NTable..NTable+NChar-1 *)
- (* zeigen auf die Grundknoten des Baums *)
- Son : ARRAY [0..NTable-1] OF CARDINAL;
- (* Zeiger auf die Zweig-Elemente Son[] und *)
- (* Son[+1] *)
-
- PROCEDURE InitHuff; (* Initialisiert den Baum *)
- VAR
- i, j : CARDINAL;
- BEGIN
- (* Blätter initialisieren *)
- FOR i := 0 TO NChar-1 DO BEGIN
- Freq[i] := 1; Son[i] := i + NTable;
- Prnt[i + NTable] := i;
- END;
- (* Knoten initialisieren *)
- i := 0;
- FOR j := NChar TO Root DO BEGIN
- Freq[j] := Freq[i] + Freq[i+1];
- Son[j] := i; Prnt[i] := j; Prnt[i+1]:= j;
- i := i + 2;
- END;
- Freq[NTable] := $FFFF; (* Frequenzzähler *)
- Prnt[Root] := 0; (* Wurzel *)
- END;
-
- PROCEDURE Reconstruct;
- VAR
- i, j : CARDINAL;
- f, k : CARDINAL;
- l : CARDINAL;
- BEGIN
- (* Teil 1: Blätter suchen und Frequenzen halbieren *)
- j := 0;
- FOR i := 0 TO Root DO BEGIN
- IF Son[i] >= NTable THEN BEGIN
- (* Blatt gefunden und wieder an Anfang der Liste *)
- (* schreiben *)
- Freq[j] := (Freq[i] + 1) DIV 2;
- Son[j] := Son[i];
- Inc(j);
- END;
- END;
- (* Teil 2: Knoten aus den Blättern bilden *)
- i := 0; (* "jüngerer" Knoten *)
- FOR j := NChar TO Root DO BEGIN (* j : freier Knoten *)
- f := Freq[i] + Freq[i+1]; (* Neue Frequenz *)
- (* Platz zum Einsetzen suchen *)
- k := j - 1;
- Freq[j] := f;
- WHILE f < Freq[k] DO Dec(k);
- Inc(k);
- l := (j-k) * 2;
- Move(Freq[k], Freq[k+1], l);
- Freq[k] := f;
- Move(Son[k], Freq[k+1], l);
- Son[k] := i;
- END;
- (* Parent-Zweige verbinden *)
- FOR i := 0 TO Root DO BEGIN
- k := Son[i];
- Prnt[k] := i;
- IF k < NTable THEN
- Prnt[k+1] := i; (* älterer Bruder, wenn Son *)
- (* nicht auf Verweis zeigt *)
- END;
- END;
-
- PROCEDURE Update( c : CARDINAL);
- (* korrigiert Huffman-Tree *)
- VAR
- x, f, y : CARDINAL;
- BEGIN
- IF Freq[Root] = MaxFreq THEN
- Reconstruct;
- c := Prnt[c + NTable];(* c: Zeiger auf unteren Knoten *)
- REPEAT
- Inc(Freq[c]); f := Freq[c]; x := c + 1;
- IF f > Freq[x] THEN BEGIN (* Knoten austauschen *)
- REPEAT
- Inc(x);
- UNTIL f <= Freq[x];
- Dec( x);
- (* x zeigt auf das Element, gegen das es *)
- (* ausgetauscht werden soll *)
- Freq[c] := Freq[x];
- Freq[x] := f;
- f := Son[c];
- y := Son[x];
- Prnt[f] := x;
- IF (f < NTable) THEN Prnt[f+1] := x;
- Prnt[y] := c;
- IF (y < NTable) THEN Prnt[y+1] := c;
- Son[c] := y; Son[x] := f; c := x;
- END;
- c := Prnt[c];
- UNTIL (c = 0); (* Wurzel erreicht *)
- END;
-
- PROCEDURE EncodeChar( c : CARDINAL);
- (* Verschlüsselt Buchstaben nach Huffman *)
- VAR
- bits, len, node : CARDINAL;
- BEGIN
- bits := 0; len := 0;
- node := Prnt[c+NTable];
- (* Der Code wird rückwärts aufgebaut *)
- REPEAT
- IF len = 16 THEN BEGIN (* Puffer voll *)
- PutCode(bits, 16);
- bits := 0; len := 0;
- END;
- bits := bits SHR 1;
- IF ODD(node) THEN
- (* älterer Sohn - dann 1 abspeichern *)
- bits := bits + $8000;
- Inc(len);
- node := Prnt[node];
- UNTIL node = Root;
- PutCode(bits, len); Update(c);
- END;
-
- PROCEDURE EncodePosition(c : CARDINAL);
- VAR
- i : CARDINAL;
- BEGIN
- (* Obere 6 Bit durch Tabelle codieren *)
- i := c SHR 6;
- PutCode(c_code[i], c_len[i]);
- (* untere 6 Bit normal ... *)
- PutCode(c SHL 10, 6);
- END;
-
- PROCEDURE EncodeEnd;
- BEGIN
- FlushCode;
- END;
-
- FUNCTION DecodeChar : CARDINAL;
- VAR
- c : CARDINAL;
- BEGIN
- (* Suche des Zeichens im Baum von der Wurzel aus. *)
- (* Wird 0 gelesen, ist der "jüngere" Sohn gemeint. *)
- c := Son[Root];
- WHILE c < NTable DO c := Son[c+GetBit];
- (* c steht auf dem gemeinten Zeichen *)
- Dec(c, NTable);
- Update(c);
- DecodeChar := c;
- END;
-
- FUNCTION DecodePosition : CARDINAL;
- VAR
- c, i, len : CARDINAL;
- BEGIN
- (* Bits aus Tabelle suchen *)
- i := GetByte; c := d_code[i]; len := d_len[i];
- (* restliche untere Bits holen *)
- Dec(len, 2);
- WHILE len > 0 DO BEGIN
- i := i SHL 1 + GetBit;
- Dec(len);
- END;
- DecodePosition := c OR (i AND $3F);
- END;
-
- PROCEDURE Encode;
- VAR
- i : CARDINAL; (* Laufvariable *)
- c : BYTE; (* Zeichen, das gelesen wird *)
- TextSize : LONGINT;
- (* Anzahl der Zeichen, die verarbeitet wurden *)
- Len : CARDINAL;
- (* Anzahl der unverarbeiteten Zeichen im Puffer *)
- R : CARDINAL;
- (* Position, von der geschrieben wird *)
- S : CARDINAL;
- (* Position, in die Zeichen gelesen werden *)
- LastMatchLen : CARDINAL;
- (* Anzahl der Zeichen bei Schreiben von LZSS *)
- PrintCount : LONGINT; (* Zähler für Hilfsausgabe *)
- OldSize : LONGINT;
- BEGIN
- PrintCount := 0;
- TextSize := FileSize(InFile);
- OldSize := TextSize;
- BlockWrite(OutFile, TextSize, SizeOf(TextSize));
- InitHuff; InitTree;
- R := BufMax - LookAhead; S := 0;
- (* Puffer mit Leerzeichen füllen *)
- FillChar(TextBuf, R, ' ');
- (* erste Zeichen einlesen *)
- BlockRead(InFile, TextBuf[R], LookAhead, Len);
- TextSize := Len;
- FOR i := R - 1 DOWNTO R - LookAhead DO
- InsertNode(i);
- (* Zeiger im Leerzeichenbereich setzen *)
- InsertNode(R); (* ersten Zeiger setzen... *)
- REPEAT
- IF MatchLen > Len THEN MatchLen := Len;
- IF MatchLen <= ThresHold THEN BEGIN
- (* Zeichen verschlüsseln *)
- MatchLen := 1;
- EncodeChar(TextBuf[R]);
- END ELSE BEGIN
- (* LZSS verschlüsseln *)
- EncodeChar(255 + MatchLen - ThresHold);
- EncodePosition(MatchPos);
- END;
- LastMatchLen := MatchLen;
- (* Anzahl der geschriebenen Zeichen *)
- i := 0;
- WHILE (i < LastMatchLen) AND
- (TextSize < OldSize) DO BEGIN
- (* Neue Zeichen einlesen *)
- BlockRead(InFile, c, 1);
- (* c := BufRead; *)
- DeleteNode(S);
- TextBuf[S] := c;
- IF (S < LookAhead - 1) THEN
- TextBuf[BufMax+S] := c; (* Zum Stringvergleich *)
- S := (S + 1) AND (BufMax - 1);
- R := (R + 1) AND (BufMax - 1);
- InsertNode(R);
- (* Zeichen wird in Zeigerliste aufgenommen *)
- Inc(i);
- Inc(TextSize);
- END;
- IF TextSize >= PrintCount THEN BEGIN
- Write('.');
- Inc(PrintCount, 1024);
- END;
- (* Diese Schleife wird aufgerufen, wenn nicht alle *)
- (* Zeichen gelesen werden konnten (Ende der Datei) *)
- WHILE i < LastMatchLen DO BEGIN
- DeleteNode( S);
- S := (S + 1) AND (BufMax - 1);
- R := (R + 1) AND (BufMax - 1);
- IF Len > 0 THEN BEGIN
- InsertNode(R);
- (* Wenn noch Zeichen im Puffer stehen, die *)
- Dec(Len); (* nicht verarbeitet sind *)
- END;
- Inc(i);
- END;
- UNTIL (Len = 0);
- EncodeEnd;
- END;
-
- PROCEDURE Decode;
- VAR
- S, R, c : CARDINAL;
- TextSize, count,
- PrintCount : LONGINT;
- newpos, newlen, k : CARDINAL;
- BEGIN
- (* Filelänge lesen *)
- BlockRead(InFile, TextSize, SizeOf(TextSize), S);
- IF S <> SizeOf(TextSize) THEN Exit;
- R := BufMax - LookAhead;
- S := 0;
- PrintCount := 0;
- InitHuff;
- FillChar(TextBuf, R, ' ');
- Count := 0;
- WHILE Count < TextSize DO BEGIN
- c := DecodeChar;
- IF c < 256 THEN BEGIN
- BufWrite(c);
- TextBuf[R] := c;
- R := (R + 1) AND (BufMax - 1);
- Inc(Count);
- END ELSE BEGIN
- newpos := (R - DecodePosition - 1) AND (BufMax - 1);
- newlen := c + ThresHold - 255;
- FOR k := 0 TO newlen - 1 DO BEGIN
- c := TextBuf[(newpos + k) AND (BufMax - 1)];
- BufWrite(c);
- TextBuf[R] := c;
- R := (R + 1) AND (BufMax - 1);
- Inc(Count);
- END;
- END;
- IF Count > PrintCount THEN BEGIN
- Write('.');
- Inc(PrintCount, 1024);
- END;
- END;
- BufFlush;
- END;
-
- VAR
- p : STRING;
-
- BEGIN
- WritePtr := 0; ReadPtr := 0; ReadMax := 0;
- MakeTable;
- FillChar(OutBuffer, SizeOf(OutBuffer), #0);
- OutBufP := 0; InBufP := 0;
- OutBitP := 0; InBitP := 0;
- MaxIn := -1;
- p := ParamStr(1); p[1] := UpCase(p[1]);
- IF (ParamCount <> 3) OR (Length(p) <> 1) OR
- (p[1] < 'D') OR (p[1] > 'E') THEN BEGIN
- WriteLn('"LZHUF e file1 file2"',
- ' komprimiert file1 in file2');
- WriteLn('"LZHUF d file1 file2"',
- ' dekomprimiert file1 in file2');
- Halt;
- END;
- Assign(InFile, ParamStr(2));
- Assign(OutFile, ParamStr(3));
- (*$I-*)
- Rewrite(OutFile, 1); Reset(InFile, 1);
- (*$I+*)
- IF IOResult <> 0 THEN BEGIN
- WriteLn(' Files konnten nicht geöffnet werden...');
- Halt;
- END;
- IF p[1] = 'E' THEN Encode ELSE Decode;
- Close(InFile); Close(OutFile);
- END.
- (* ------------------------------------------------------ *)
- (* Ende von LZHUF.PAS *)