forked from thargor6/mb3d
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDeflate.pas
1071 lines (961 loc) · 36.4 KB
/
Deflate.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
////////////////////////////////////////////////////////////////////////////////
//
// Deflate.pas - Deflate compression unit
// --------------------------------------
// Changed: 2003-04-25
// Maintain: Michael Vinther | mv@logicnet·dk
//
// Contains:
// (TBaseStream)
// (TFilterStream)
// (TBitStream)
// TDeflateStream
//
unit Deflate;
interface
uses
Windows, SysUtils, Streams, BufStream, BitStream, Huffman, Monitor;
resourcestring
rsErrorInCompressedData = 'Error in compressed data';
rsBadCompFormat = 'Bad compression parameters';
rsWriteDenied = 'Stream not open for write';
const
MaxDeflateHashChain = 64; // Must be integer power of 2
MaxDeflateHashBuffer = 65536;
LitLengthValue = -1;
DistValue = -2;
type
TDeflateHashTable = array[0..65535] of record
Chain : array[0..MaxDeflateHashChain-1] of LongInt; // 64kb*64k*4=16 MB!
Next : Byte;
end;
TDeflateCompressionMethod = (cmAutoHuffman,cmFixedHuffman,cmStore);
TDeflateHuffmanBuffer = array[0..MaxDeflateHashBuffer] of record
Value : Word;
Info : SmallInt;
end;
TDeflateStream = class(THuffmanBitStream)
protected
fCompressionMethod : TDeflateCompressionMethod;
procedure SetCompressionMethod(Method: TDeflateCompressionMethod);
public
property CompressionMethod : TDeflateCompressionMethod read fCompressionMethod write SetCompressionMethod;
constructor Create(NextStream: TBaseStream; Mode: Integer=-1); // fmRead, fmWrite or -1 for autodetect
destructor Destroy; override;
function Write(var Buf; Count: Integer): Integer; override;
function Read(var Buf; Count: Integer): Integer; override;
// Write data in look-ahead and reset 1-byte read/write buffer
procedure Flush; override;
// Return value is >0 if there is available data
function Available: Integer; override;
private
LookBack : PByteArray;
LookBackPtr, BufferPtr : Word;
BufferSize : DWord;
Final, FinishBlock, InBlock : Boolean;
LittLengthCodes, DistCodes : THuffmanCodes;
LittLengthTree, DistTree : THuffmanTree;
// Decoder
DecodeState : (dsNewBlock,dsBufferRead,dsDecodeSegment);
// Encoder
Position : Integer;
HashTable : ^TDeflateHashTable;
HuffmanBuffer : ^TDeflateHuffmanBuffer;
HuffmanBufferPos : Integer;
end;
const
fmRead = 0;
fmWrite = 1;
// Open compressed, buffered file. Makes it unnessacary to include other stream units
function OpenDeflateFile(Name: string; Mode: Integer): TDeflateStream;
implementation
uses MemUtils;
// Open defalte compressed, buffered file
function OpenDeflateFile(Name: string; Mode: Integer): TDeflateStream;
begin
if Mode=fmRead then Result:=TDeflateStream.Create(TBufferedStream.Create(-1,0,TFileStream.Create(Name,[fsRead,fsShareRead])))
else Result:=TDeflateStream.Create(TBufferedStream.Create(0,-1,TFileStream.Create(Name,fsRewrite)));
end;
//=======================================================================================================
// TDeflateStream
//=======================================================================================================
procedure LogCodes(var CodeLength: array of Byte; CodeCount: Integer; const FileName: string);
var
Log : TextFile;
I : Integer;
begin
AssignFile(Log,FileName);
Rewrite(Log);
WriteLn(Log,'Code count: ',CodeCount);
for I:=0 to CodeCount-1 do WriteLn(Log,I:3,': ',CodeLength[I]:3);
CloseFile(Log);
end;
const
MaxHuffmanCodeLength = 15;
procedure SetFixedLittLengthCodes(var LittLengthCodes: THuffmanCodes);
var
Value, Code, RevCode, Bits : Integer;
begin
// Set length codes
for Value:=0 to 143 do
begin
Code:=Value+48;
RevCode:=0;
for Bits:=0 to 7 do RevCode:=(RevCode shl 1) or ((Code shr Bits) and 1);
LittLengthCodes.List[Value].Code:=RevCode;
LittLengthCodes.List[Value].Length:=8;
end;
for Value:=144 to 255 do
begin
Code:=Value+(-144+400);
RevCode:=0;
for Bits:=0 to 8 do RevCode:=(RevCode shl 1) or ((Code shr Bits) and 1);
LittLengthCodes.List[Value].Code:=RevCode;
LittLengthCodes.List[Value].Length:=9;
end;
for Value:=256 to 279 do
begin
Code:=Value+(-256+0);
RevCode:=0;
for Bits:=0 to 6 do RevCode:=(RevCode shl 1) or ((Code shr Bits) and 1);
LittLengthCodes.List[Value].Code:=RevCode;
LittLengthCodes.List[Value].Length:=7;
end;
for Value:=280 to 285 do
begin
Code:=Value+(-280+192);
RevCode:=0;
for Bits:=0 to 7 do RevCode:=(RevCode shl 1) or ((Code shr Bits) and 1);
LittLengthCodes.List[Value].Code:=RevCode;
LittLengthCodes.List[Value].Length:=8;
end;
end;
//-----------------------------------------------------------------------------------
// Create a new deflate stream.
constructor TDeflateStream.Create(NextStream: TBaseStream; Mode: Integer);
begin
inherited Create(NextStream);
if Next.CanWrite and ((Mode=-1) or (Mode=fmWrite)) then
begin
fCanWrite:=True;
GetMem(HashTable,SizeOf(TDeflateHashTable));
FillChar(HashTable^,SizeOf(TDeflateHashTable),$80);
New(HuffmanBuffer);
end
else if Next.CanRead and ((Mode=-1) or (Mode=fmRead)) then
begin
fCanRead:=True;
LittLengthTree.Init(288);
DistTree.Init(32);
end;
Assert(fCanWrite or fCanRead);
LittLengthCodes.Init(288);
DistCodes.Init(32);
GetMem(LookBack,$10000);
end;
//-----------------------------------------------------------------------------------
// Flush and free memory
destructor TDeflateStream.Destroy;
begin
Final:=True;
Flush;
inherited Flush;
inherited Destroy;
if CanWrite then
begin
FreeMem(HuffmanBuffer);
FreeMem(HashTable);
end;
FreeMem(LookBack);
end;
//-----------------------------------------------------------------------------------
// Return value is >0 if there is available data
function TDeflateStream.Available;
begin
Available:=inherited Available+Integer(BufferSize);
end;
const
CodeOrder : array[0..18] of Integer = (16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15);
//-----------------------------------------------------------------------------------
function TDeflateStream.Read(var Buf; Count: Integer): Integer;
var
Get : Integer;
I, Code, CodePos : Cardinal;
Len, NLen, Dist, CopyPtr : Word;
BlockType, R : Byte;
CodeLengthTree : THuffmanTree;
LengthCodeCount : Cardinal;
DistCodeCount, LastCodeLengthCode : Byte;
CodeLength : array[0..285+32] of Byte;
begin
fCanWrite:=False;
Result:=0;
while Count>0 do
begin
case DecodeState of
dsNewBlock : begin
if Final then
begin
if NoDataExcept then raise EInOutError.Create(rs_ReadLessError)
else Break;
end;
ReadBits(Final,1);
ReadBits(BlockType,2);
case BlockType of
0 : begin // Read uncompressed block
inherited Flush;
Next.Read(Len,2);
Next.Read(NLen,2);
if Len<>not NLen then raise Exception.Create(rsErrorInCompressedData);
BufferSize:=Len;
BufferPtr:=LookBackPtr;
Get:=Len;
if Get>$10000-LookBackPtr then Get:=$10000-LookBackPtr;
Next.Read(LookBack^[LookBackPtr],Get);
Inc(LookBackPtr,Get);
Dec(Len,Get);
if Len>0 then
begin
Next.Read(LookBack^[LookBackPtr],Len);
Inc(LookBackPtr,Get);
end;
InBlock:=False;
DecodeState:=dsBufferRead;
end;
1 : begin // Compressed with fixed Huffman codes
SetFixedLittLengthCodes(LittLengthCodes);
LittLengthTree.Build(LittLengthCodes);
// Set distance codes
DistCodes.FixCodes(5);
DistTree.Build(DistCodes);
InBlock:=True;
DecodeState:=dsDecodeSegment;
end;
2 : begin // Compressed with dynamic Huffman codes
CodeLengthTree.Init(19);
LengthCodeCount:=0;
ReadBits(LengthCodeCount,5); Inc(LengthCodeCount,257);
ReadBits(DistCodeCount,5); Inc(DistCodeCount);
// Read Huffman codes for decompressing distance/length Huffman trees
ReadBits(LastCodeLengthCode,4); Inc(LastCodeLengthCode,3);
ZeroMem(CodeLength,SizeOf(CodeLength));
for I:=0 to LastCodeLengthCode do ReadBits(CodeLength[CodeOrder[I]],3);
//LogCodes(CodeLength,19,'x:\treecodes.txt');
CodeLengthTree.Build(CodeLength,7);
// Decompress length codes
CodePos:=0;
I:=LengthCodeCount+DistCodeCount;
while CodePos<I do
begin
Code:=GetSymbol(CodeLengthTree);
case Code of
0..15 : begin // Code length 0-15
CodeLength[CodePos]:=Code;
Inc(CodePos);
end;
16 : begin // Repeat last length code 3-6 times
ReadBits(R,2); Inc(R,3);
FillChar(CodeLength[CodePos],R,CodeLength[CodePos-1]);
Inc(CodePos,R);
end;
17 : begin // Repeat zero 3-10 times
ReadBits(R,3); Inc(R,3);
ZeroMem(CodeLength[CodePos],R);
Inc(CodePos,R);
end;
18 : begin // Repeat zero 11-138 times
ReadBits(R,7); Inc(R,11);
ZeroMem(CodeLength[CodePos],R);
Inc(CodePos,R);
end;
else raise Exception.Create(rsErrorInCompressedData);
end;
end;
//LogCodes(CodeLength[0],LengthCodeCount,'x:\LittLengthCodes.txt');
//LogCodes(CodeLength[LengthCodeCount],DistCodeCount,'x:\distcodes.txt');
DistTree.Build(CodeLength[LengthCodeCount],MaxHuffmanCodeLength);
ZeroMem(CodeLength[LengthCodeCount],33);
LittLengthTree.Build(CodeLength,MaxHuffmanCodeLength);
InBlock:=True;
DecodeState:=dsDecodeSegment;
end;
3 : raise Exception.Create(rsErrorInCompressedData);
end;
end;
dsDecodeSegment : begin
Code:=GetSymbol(LittLengthTree);
if Code<256 then // Litteral
begin
LookBack^[LookBackPtr]:=Code; // Store in look back
Inc(LookBackPtr);
TByteArray(Buf)[Result]:=Code; // Store in output buffer
Dec(Count);
Inc(Result);
DecodeState:=dsDecodeSegment
end
else if Code=256 then // End of block
begin
DecodeState:=dsNewBlock;
end
else // Look back reference
begin
case Code of // Decode length
257..264 : Len:=Code-254;
265..268 : Len:=(Code-265) shl 1+ReadBit+11;
269..272 : begin
ReadBits(R,2);
Len:=(Code-269) shl 2+R+19;
end;
273..276 : begin
ReadBits(R,3);
Len:=(Code-273) shl 3+R+35;
end;
277..280 : begin
ReadBits(R,4);
Len:=(Code-277) shl 4+R+67;
end;
281..284 : begin
ReadBits(R,5);
Len:=(Code-281) shl 5+R+131;
end;
285 : Len:=258;
else raise Exception.Create(rsErrorInCompressedData);
end;{}
Code:=GetSymbol(DistTree); // Decode distance
if Code<=3 then Dist:=Code+1
else
begin
Get:=(Code-4) div 2+1; // Number of extra bits to read
Dist:=0;
ReadBits(Dist,Get);
Dist:=(Dist or ((Code and 1) shl Get))+1 shl (Get+1)+1;
end;
BufferSize:=Len;
BufferPtr:=LookBackPtr;
// Copy from look back
CopyPtr:=LookBackPtr-Dist;
for I:=1 to Len do
begin
LookBack^[LookBackPtr]:=LookBack^[CopyPtr];
Inc(LookBackPtr);
Inc(CopyPtr);
end;
DecodeState:=dsBufferRead;
end
end;
dsBufferRead : begin // Just read from buffer
Get:=Count;
if Get>Integer(BufferSize) then Get:=BufferSize;
if Get>$10000-BufferPtr then Get:=$10000-BufferPtr;
Move(LookBack^[BufferPtr],TByteArray(Buf)[Result],Get);
Dec(Count,Get);
Dec(BufferSize,Get);
Inc(Result,Get);
Inc(BufferPtr,Get);
if BufferSize=0 then // Buffer empty
begin
if InBlock then DecodeState:=dsDecodeSegment
else DecodeState:=dsNewBlock;
end;
end;
end;
end;
end;
//-----------------------------------------------------------------------------------
procedure TDeflateStream.SetCompressionMethod(Method: TDeflateCompressionMethod);
begin
if InBlock and (Method<>fCompressionMethod) then Flush;
fCompressionMethod:=Method;
end;
//-----------------------------------------------------------------------------------
{$WARNINGS OFF}
function TDeflateStream.Write(var Buf; Count: Integer): Integer;
type
THashValue = packed record
case Integer of
0 : (Index : Word);
1 : (V_1, V0 : Byte);
end;
procedure MoveToLookBack(Count: Cardinal);
var
HashValue : THashValue;
I : Cardinal;
W : Word;
begin
if Position=0 then
begin
Inc(LookBackPtr);
Dec(BufferSize);
Dec(Count);
Inc(Position);
end;
W:=LookBackPtr-1; HashValue.V0:=LookBack^[W];
for I:=1 to Count do
begin
HashValue.V_1:=HashValue.V0;
HashValue.V0:=LookBack^[LookBackPtr];
with HashTable^[HashValue.Index] do
begin
Chain[Next and (MaxDeflateHashChain-1)]:=Position;
Inc(Next);
end;
Inc(LookBackPtr);
Inc(Position);
end;
Dec(BufferSize,Count);
end;
procedure WriteUncompressedBlock; // Write uncompressed data
var
NLEN : Word;
Get : Cardinal;
begin
WriteBits(Cardinal(Final),1);
WriteBits(0,2);
inherited Flush;
Next.Write(BufferSize,2);
NLen:=not BufferSize;
Next.Write(NLEN,2);
Get:=$10000-LookBackPtr;
if Get>BufferSize then Get:=BufferSize;
Next.Write(LookBack^[LookBackPtr],Get); // Write first part of buffer
MoveToLookBack(Get);
if BufferSize>0 then // Write rest of buffer if anything left
begin
Next.Write(LookBack^[LookBackPtr],BufferSize);
MoveToLookBack(BufferSize);
end;
FinishBlock:=False;
Final:=False;
end;
procedure CompressDataFixed; // Compress data using fixed Huffman codes
var
I, Dist, BestDist, BestLength, Extra, Code : Integer;
//BestLength1 : Integer;
Length : DWord;
HashValue : THashValue;
LookAheadSearchPtr, LookBackSearchPtr : Word;
begin
if not InBlock then // Start new block
begin
WriteBits(Cardinal(Final),1); // Write Final bit
Final:=False;
WriteBits(1,2); // Signal compressed with fixed Huffman codes
// Set length codes
SetFixedLittLengthCodes(LittLengthCodes);
// Set distance codes
DistCodes.FixCodes(5);
InBlock:=True;
end;
while (BufferSize>=258) or (FinishBlock and (BufferSize>=3)) do
begin
// Find hash value
HashValue.V_1:=LookBack^[LookBackPtr];
HashValue.V0:=LookBack^[Word(LookBackPtr+1) and $ffff];
{HashValue.V_1:=LookBack^[LookBackPtr] xor ((LookBack^[Word(LookBackPtr+2) and $ffff]) shr 2);
HashValue.V0:=LookBack^[Word(LookBackPtr+1) and $ffff] xor ((LookBack^[Word(LookBackPtr+2) and $ffff]) shl 2);{}
// Find longest match in hash chain
BestLength:=2;
with HashTable^[HashValue.Index] do
for I:=MaxDeflateHashChain-1 downto 0 do // Try nearest match first for shorter distance codes
begin
Dist:=Position-Chain[(I+Next) and (MaxDeflateHashChain-1)]+1;
if (Dist>0) and (Dist<=32768) then
begin
if (LookBack^[(LookBackPtr-Dist+BestLength) and $ffff]<>LookBack^[(LookBackPtr+BestLength) and $ffff]) then Continue;
Length:=2;
LookAheadSearchPtr:=LookBackPtr+2;
LookBackSearchPtr:=LookBackPtr-Dist+2;
while (LookBack^[LookBackSearchPtr]=LookBack^[LookAheadSearchPtr]) and (Length<BufferSize) and (Length<258) do
begin
Inc(Length);
Inc(LookAheadSearchPtr);
Inc(LookBackSearchPtr);
end;
if Length>DWord(BestLength) then
begin
BestLength:=Length;
BestDist:=Dist;
end;
end
else Break;
end;
// Find hash value for next byte - does it pay off to lave a byte uncompressed?
{BestLength1:=BestLength+1;
HashValue.V_1:=HashValue.V0;
HashValue.V0:=LookBack^[Word(LookBackPtr+2) and $ffff];
// Find longest match in hash chain for next byte
with HashTable^[HashValue.Index] do
for I:=0 to MaxDeflateHashChain-1 do
begin
Dist:=Position-Chain[I]+1;
if (Dist>0) and (Dist<=32768) then
begin
if (LookBack^[(LookBackPtr-Dist+BestLength1) and $ffff]<>LookBack^[(LookBackPtr+BestLength1) and $ffff]) then Continue;
Length:=2;
LookAheadSearchPtr:=LookBackPtr+3;
LookBackSearchPtr:=LookBackPtr-Dist+3;
while (LookBack^[LookBackSearchPtr]=LookBack^[LookAheadSearchPtr]) and (Length<BufferSize) and (Length<258) do
begin
Inc(Length);
Inc(LookAheadSearchPtr);
Inc(LookBackSearchPtr);
end;
if Length>BestLength1 then
begin
BestLength:=0;
Break;
end;
end;
end;{}
if BestLength>=3 then // At least 3 characters found in look back
begin
// Write length code
if BestLength<=10 then
begin
with LittLengthCodes.List[254+BestLength] do WriteBits(Code,Length);
end
else if BestLength<=257 then // 1-5 extra bits
begin
Extra:=(BestLength-3) shr 3;
for I:=1 to 5 do // Count number of bits used
begin
Extra:=Extra shr 1;
if Extra=0 then
begin
Extra:=I; // Number of extra bits found
Break;
end;
end;
I:=BestLength-(3+4 shl Extra);
with LittLengthCodes.List[261+Extra*4+I shr Extra] do WriteBits(Code,Length);
WriteBits(I and ((1 shl Extra)-1),Extra);
end
else // BestLength=258
begin
with LittLengthCodes.List[285] do WriteBits(Code,Length);
end;
// Write distance code
Dec(BestDist);
if BestDist<4 then
begin
with DistCodes.List[BestDist] do WriteBits(Code,Length);
end
else
begin
Extra:=BestDist shr 2;
for I:=1 to 13 do // Count number of bits used
begin
Extra:=Extra shr 1;
if Extra=0 then
begin
Extra:=I; // Number of extra bits found
Break;
end;
end;
Code:=(Extra+1) shl 1+(BestDist shr Extra) and 1;
with DistCodes.List[Code] do WriteBits(Code,Length); // Write code
WriteBits(BestDist and (1 shl Extra-1),Extra); // Write extra bits
end;
MoveToLookBack(BestLength); // Move bytes to look-back
end
else // Just Huffman compress next byte
begin
with LittLengthCodes.List[LookBack^[LookBackPtr]] do WriteBits(Code,Length);
MoveToLookBack(1);
end;
end;
if FinishBlock then
begin
while BufferSize>0 do
begin
with LittLengthCodes.List[LookBack^[LookBackPtr]] do WriteBits(Code,Length);
MoveToLookBack(1);
end;
with LittLengthCodes.List[256] do WriteBits(Code,Length); // Write end-of-block marker
FinishBlock:=False;
InBlock:=False;
end
end;
procedure CompressDataAuto; // Compress data using optimal Huffman codes
procedure HuffmanCompressData;
var
LengthLittStat, DistStat : PCardinalArray;
I, C, CompCodeCount, LittLengthCodeCount, DistCodeCount, LastCodeLengthCode, LastCode, CodeCount : Integer;
CodeLength : array[0..288+32] of Byte;
CompCodeLength : array[0..288+32-1] of record
Value : Byte;
Info : ShortInt;
end;
CodeLengthCodes : THuffmanCodes;
CodeLengthStat : array[0..18] of Cardinal;
begin
// Write block header
if (BufferSize=0) and Final then
begin
WriteBits(Cardinal(Final),1); // Write Final bit
Final:=False;
end
else WriteBits(0,1); // Write Final bit
WriteBits(2,2); // Signal compressed with dynamic Huffman codes
// Add end-of-block marker to code buffer
with HuffmanBuffer^[HuffmanBufferPos] do
begin
Value:=256;
Info:=LitLengthValue;
end;
Inc(HuffmanBufferPos);
// Find optimal Huffman codes
GetMem(LengthLittStat,288*SizeOf(Integer));
GetMem(DistStat,32*SizeOf(Integer));
try
// Calculate statistics
ZeroMem(LengthLittStat^,288*SizeOf(Integer));
ZeroMem(DistStat^,32*SizeOf(Integer));
for I:=0 to HuffmanBufferPos-1 do // Calculate litt/length and dist statistics
case HuffmanBuffer^[I].Info of
LitLengthValue : Inc(LengthLittStat^[HuffmanBuffer^[I].Value]);
DistValue : Inc(DistStat^[HuffmanBuffer^[I].Value]);
end;
// Find Huffman codes
LittLengthCodes.FindCodesStat(LengthLittStat,MaxHuffmanCodeLength);
DistCodes.FindCodesStat(DistStat,MaxHuffmanCodeLength);
finally
FreeMem(DistStat);
FreeMem(LengthLittStat);
end;{}
// Count number of codes used
LittLengthCodeCount:=1;
for I:=285 downto 1 do if LittLengthCodes.List[I].Length<>0 then
begin
LittLengthCodeCount:=I+1;
Break;
end;
DistCodeCount:=1;
for I:=29 downto 1 do if DistCodes.List[I].Length<>0 then
begin
DistCodeCount:=I+1;
Break;
end;
// Combine code lengths in one array
for I:=0 to LittLengthCodeCount-1 do CodeLength[I]:=LittLengthCodes.List[I].Length;
for I:=0 to DistCodeCount-1 do CodeLength[LittLengthCodeCount+I]:=DistCodes.List[I].Length;
// RLE compress code lengths
ZeroMem(CodeLengthStat,SizeOf(CodeLengthStat)); // Reset histogram accumulator
CompCodeCount:=0;
CodeCount:=1;
LastCode:=CodeLength[0];
CodeLength[LittLengthCodeCount+DistCodeCount]:=$ff; // Insert a code to terminate run length
for I:=1 to LittLengthCodeCount+DistCodeCount do if CodeLength[I]=LastCode then Inc(CodeCount)
else
begin
if LastCode<>0 then
begin
CompCodeLength[CompCodeCount].Info:=-1;
CompCodeLength[CompCodeCount].Value:=LastCode;
Inc(CodeLengthStat[LastCode]);
Inc(CompCodeCount);
Dec(CodeCount);
end;
while CodeCount>=3 do
begin
if LastCode<>0 then // Repeat 3-6 times
begin
CompCodeLength[CompCodeCount].Info:=-1;
CompCodeLength[CompCodeCount].Value:=16;
Inc(CodeLengthStat[16]);
Inc(CompCodeCount);
C:=CodeCount;
if C>6 then C:=6;
CompCodeLength[CompCodeCount].Info:=2; // 2 extra bits
CompCodeLength[CompCodeCount].Value:=C-3;
Inc(CompCodeCount);
Dec(CodeCount,C);
end
else if CodeCount<=10 then // 3-10 x 0
begin
CompCodeLength[CompCodeCount].Info:=-1;
CompCodeLength[CompCodeCount].Value:=17;
Inc(CodeLengthStat[17]);
Inc(CompCodeCount);
CompCodeLength[CompCodeCount].Info:=3; // 3 extra bits
CompCodeLength[CompCodeCount].Value:=CodeCount-3;
Inc(CompCodeCount);
CodeCount:=0;
end
else // 11-138 x 0
begin
CompCodeLength[CompCodeCount].Info:=-1;
CompCodeLength[CompCodeCount].Value:=18;
Inc(CodeLengthStat[18]);
Inc(CompCodeCount);
C:=CodeCount;
if C>138 then C:=138;
CompCodeLength[CompCodeCount].Info:=7; // 7 extra bits
CompCodeLength[CompCodeCount].Value:=C-11;
Inc(CompCodeCount);
Dec(CodeCount,C);
end
end;
for C:=1 to CodeCount do
begin
CompCodeLength[CompCodeCount].Info:=-1;
CompCodeLength[CompCodeCount].Value:=LastCode;
Inc(CodeLengthStat[LastCode]);
Inc(CompCodeCount);
end;
// Write code length I
LastCode:=CodeLength[I];
CodeCount:=1;
end;
// Find Huffman tree for codes
CodeLengthCodes.Init(19);
CodeLengthCodes.FindCodesStat(@CodeLengthStat,7); // Find optimal codes
// Find number of code length codes
LastCodeLengthCode:=0;
for I:=18 downto 1 do if CodeLengthCodes.List[CodeOrder[I]].Length<>0 then
begin
LastCodeLengthCode:=I;
Break;
end;
//LastCodeLengthCode:=18;
WriteBits(LittLengthCodeCount-257,5); // Number of literal/length codes
WriteBits(DistCodeCount-1,5); // Number of distance codes
WriteBits(LastCodeLengthCode+1-4,4); // Number of code length codes
// Write Huffman trees
for I:=0 to LastCodeLengthCode do WriteBits(CodeLengthCodes.List[CodeOrder[I]].Length,3); // Write 3 bit code lengths
for I:=0 to CompCodeCount-1 do
begin
if CompCodeLength[I].Info=-1 then with CodeLengthCodes.List[CompCodeLength[I].Value] do WriteBits(Code,Length)
else WriteBits(CompCodeLength[I].Value,CompCodeLength[I].Info);
end;
// Write data
for I:=0 to HuffmanBufferPos-1 do with HuffmanBuffer^[I] do
case Info of
LitLengthValue : with LittLengthCodes.List[Value] do WriteBits(Code,Length);
DistValue : with DistCodes.List[Value] do WriteBits(Code,Length);
else
WriteBits(Value,Info);
end;
FinishBlock:=False;
InBlock:=False;
end;
var
I, Dist, BestDist, BestLength, Extra, Code, Length : Integer;
//BestLength1 : Integer;
HashValue : THashValue;
LookAheadSearchPtr, LookBackSearchPtr : Word;
begin
if not InBlock then
begin
HuffmanBufferPos:=0;
InBlock:=True;
end;
while InBlock and ((BufferSize>=258) or (FinishBlock and (BufferSize>=3))) do
begin
// Find hash value
HashValue.V_1:=LookBack^[LookBackPtr];
HashValue.V0:=LookBack^[Word(LookBackPtr+1) and $ffff];
// Find longest match in hash chain
BestLength:=2;
with HashTable^[HashValue.Index] do
for I:=MaxDeflateHashChain-1 downto 0 do // Try nearest match first for shorter distance codes
begin
Dist:=Position-Chain[(I+Next) and (MaxDeflateHashChain-1)]+1;
if (Dist>0) and (Dist<=32768) then
begin
if (LookBack^[(LookBackPtr-Dist+BestLength) and $ffff]<>LookBack^[(LookBackPtr+BestLength) and $ffff]) then Continue;
Length:=2;
LookAheadSearchPtr:=LookBackPtr+2;
LookBackSearchPtr:=LookBackPtr-Dist+2;
while (LookBack^[LookBackSearchPtr]=LookBack^[LookAheadSearchPtr]) and (Length<Integer(BufferSize)) and (Length<258) do
begin
Inc(Length);
Inc(LookAheadSearchPtr);
Inc(LookBackSearchPtr);
end;
if Length>BestLength then
begin
BestLength:=Length;
BestDist:=Dist;
end;
end
else Break;
end;
{BestLength1:=BestLength; // Does it pay off to lave a byte uncompressed?
HashValue.V_1:=HashValue.V0;
HashValue.V0:=LookBack^[Word(LookBackPtr+2) and $ffff];
// Find longest match in hash chain
with HashTable^[HashValue.Index] do
for I:=0 to MaxDeflateHashChain-1 do
begin
Dist:=Position-Chain[I]+1;
if (Dist>0) and (Dist<=32768) then
begin
if (LookBack^[(LookBackPtr-Dist+BestLength1) and $ffff]<>LookBack^[(LookBackPtr+BestLength1) and $ffff]) then Continue;
Length:=2;
LookAheadSearchPtr:=LookBackPtr+3;
LookBackSearchPtr:=LookBackPtr-Dist+3;
while (LookBack^[LookBackSearchPtr]=LookBack^[LookAheadSearchPtr]) and (Length<BufferSize) and (Length<258) do
begin
Inc(Length);
Inc(LookAheadSearchPtr);
Inc(LookBackSearchPtr);
end;
if Length>BestLength1 then
begin
BestLength:=0;
Break;
end;
end;
end;{}
if BestLength>=3 then // At least 3 characters found in look-back
begin
// Write length code
if BestLength<=10 then
begin
with HuffmanBuffer^[HuffmanBufferPos] do
begin
Value:=254+BestLength;
Info:=LitLengthValue;
end;
Inc(HuffmanBufferPos);
end
else if BestLength<=257 then // 1-5 extra bits
begin
Extra:=(BestLength-3) shr 3;
for I:=1 to 5 do // Count number of bits used
begin
Extra:=Extra shr 1;
if Extra=0 then
begin
Extra:=I; // Number of extra bits found
Break;
end;
end;
I:=BestLength-(3+4 shl Extra);
with HuffmanBuffer^[HuffmanBufferPos] do
begin
Value:=261+Extra*4+I shr Extra;
Info:=LitLengthValue;
end;
Inc(HuffmanBufferPos);
with HuffmanBuffer^[HuffmanBufferPos] do
begin
Value:=I and ((1 shl Extra)-1);
Info:=Extra;
end;
Inc(HuffmanBufferPos);
end
else // BestLength=258
begin
with HuffmanBuffer^[HuffmanBufferPos] do
begin
Value:=285;
Info:=LitLengthValue;
end;
Inc(HuffmanBufferPos);
end;
// Write distance code
Dec(BestDist);
if BestDist<4 then
begin
with HuffmanBuffer^[HuffmanBufferPos] do
begin
Value:=BestDist;
Info:=DistValue;
end;
Inc(HuffmanBufferPos);
end
else
begin
Extra:=BestDist shr 2;
for I:=1 to 13 do // Count number of bits used
begin
Extra:=Extra shr 1;
if Extra=0 then
begin
Extra:=I; // Number of extra bits found
Break;
end;
end;
Code:=(Extra+1) shl 1+(BestDist shr Extra) and 1;
with HuffmanBuffer^[HuffmanBufferPos] do
begin
Value:=Code;
Info:=DistValue;
end;
Inc(HuffmanBufferPos);
with HuffmanBuffer^[HuffmanBufferPos] do
begin
Value:=BestDist and (1 shl Extra-1);
Info:=Extra;
end;
Inc(HuffmanBufferPos);
end;
MoveToLookBack(BestLength); // Move bytes to look-back
end
else // Just Huffman compress next byte
begin
with HuffmanBuffer^[HuffmanBufferPos] do
begin
Value:=LookBack^[LookBackPtr];