- Код: Выделить всё
Unit CompressUnit;
// Модуль LZW паковщика и распаковщика
// требует модуля FileBuffer
interface
uses SysUtils,
{$IFDEF MSWindows}
Windows,
{$ENDIF}
{$IFDEF UNIX}
Linux,
{$ENDIF}
FileBuffer, ProgressUnit;
Procedure CompressProc(Var Data, Arc: TByteFile);
Procedure DecompressProc(Var Arc, Data: TByteFile);
Const
MaxBits=16;
MaxCode=65535;
TopChar=255;
ProgressStep=8192;
ClearDictValue=256;
FreezDict=ClearDictValue+1;
StepWordLength=ClearDictValue+2;
EndOfStream=ClearDictValue+3;
Signature: Array [1..4] of Byte=($50, $41, $47, $21);
Var
MaxWordSize: Byte;
implementation
Type
DictR=Record
Up, Left, Right, Code: Word;
AddChar: Byte;
End;
TDecodeBuffer=Array of Byte;
Var
Overflow: Boolean;
BitSize, AddChar: Byte;
CurMaxCode, DictPos, MaxDictSize: Word;
DecodeBufferSize: Cardinal;
Dict: Array of DictR;
DecodeBuffer: TDecodeBuffer;
Function GetMaxDictSize(PowNum: Byte): Word;
const
BaseNum=2;
var
i: Byte;
s: Cardinal;
begin
s:=1;
For i:=1 to PowNum do
s:=s*BaseNum;
GetMaxDictSize:=s-1;
end;
Procedure InitDict;
Begin
MaxDictSize:=GetMaxDictSize(MaxWordSize);
SetLength(Dict, MaxDictSize);
End;
// =========================Dictionary Begin==================================
Procedure AddNode(s: Word; C: Byte);
Var
dc: Word;
Begin
If DictPos<MaxDictSize then
Begin
If Dict[s].Up=ClearDictValue then
Begin
// Никого нет на этой ноде
Dict[s].Up:=DictPos;
Dict[DictPos].Up:=ClearDictValue;
Dict[DictPos].Left:=ClearDictValue;
Dict[DictPos].Right:=ClearDictValue;
Dict[DictPos].Code:=s;
Dict[DictPos].AddChar:=C;
End
Else
Begin
// Кто-то живёт тут
If C>Dict[Dict[s].Up].AddChar then
Begin
// Смотрим куда податься
// Вперёд
dc:=Dict[s].Up;
While Dict[dc].Right<>ClearDictValue do
dc:=Dict[dc].Right;
Dict[dc].Right:=DictPos;
Dict[DictPos].Up:=ClearDictValue;
Dict[DictPos].Left:=DictPos;
Dict[DictPos].Right:=ClearDictValue;
Dict[DictPos].Code:=s;
Dict[DictPos].AddChar:=C;
End
Else
Begin
// Назад
dc:=Dict[s].Up;
While Dict[dc].Left<>ClearDictValue do
dc:=Dict[dc].Left;
Dict[dc].Left:=DictPos;
Dict[DictPos].Up:=ClearDictValue;
Dict[DictPos].Left:=ClearDictValue;
Dict[DictPos].Right:=DictPos;
Dict[DictPos].Code:=s;
Dict[DictPos].AddChar:=C;
End;
End;
Inc(DictPos);
End;
End;
Function FindNode(s: Word; C: Byte): LongInt;
Var
dc: Word;
Begin
Result:= - 1;
If Dict[s].Up<>ClearDictValue then
Begin
dc:=Dict[s].Up;
If Dict[dc].AddChar<>C then
Begin
If Dict[dc].AddChar<C then
Begin
dc:=Dict[dc].Right;
While dc<>ClearDictValue do
Begin
If Dict[dc].AddChar=C then
Begin
FindNode:=dc;
Exit;
End;
dc:=Dict[dc].Right;
End;
End;
If Dict[dc].AddChar>C then
Begin
dc:=Dict[dc].Left;
While dc<>ClearDictValue do
Begin
If Dict[dc].AddChar=C then
Begin
FindNode:=dc;
Exit;
End;
dc:=Dict[dc].Left;
End;
End;
End
Else
FindNode:=dc;
End;
End;
Procedure InitCoder;
Var
i: Word;
Begin
BitSize:=9;
Overflow:=False;
CurMaxCode:=GetMaxDictSize(BitSize);
For i:=0 to MaxDictSize-1 do
Begin
Dict[i].Code:=0;
Dict[i].AddChar:=0;
Dict[i].Up:=ClearDictValue;
Dict[i].Left:=ClearDictValue;
Dict[i].Right:=ClearDictValue;
End;
For i:=0 to TopChar do
Begin
Dict[i].Code:=ClearDictValue;
Dict[i].AddChar:=i;
End;
DictPos:=EndOfStream+1;
End;
// ======================Dictionary End=====================================
Procedure CompressProc(Var Data, Arc: TByteFile);
Var
Code: Word;
Index: LongInt;
Begin
InitDict;
BeginRead;
BeginWrite;
InitCoder;
WriteMode:=1;
Code:=GetBytes(Data);
FSize:=GetFSize(Data);
InitProgress(FSize);
While DataPos<FSize do
Begin
AddChar:=GetBytes(Data);
Index:=FindNode(Code, AddChar);
If Index<> - 1 then
Begin
Code:=Index;
End
Else
Begin
If DictPos<MaxCode then
AddNode(Code, AddChar)
Else
Overflow:=True and (not Freeze);
If (Code>CurMaxCode)and(BitSize<MaxBits) then
Begin
BitWrite(Arc, StepWordLength, BitSize);
Inc(BitSize);
CurMaxCode:=GetMaxDictSize(BitSize);
End;
BitWrite(Arc, Code, BitSize);
Code:=AddChar;
If Overflow then
Begin
BitWrite(Arc, AddChar, BitSize);
BitWrite(Arc, ClearDictValue, BitSize);
InitCoder;
End;
End;
SetProgress(DataPos);
End;
BitWrite(Arc, Code, BitSize);
BitWrite(Arc, EndOfStream, BitSize);
EndBitWrite(Arc);
ResetBuffer(Arc);
End;
Procedure OutPutDecodeBuffer(Var F: TByteFile; Buff: TDecodeBuffer);
Var
le, i: Cardinal;
Begin
le:=Length(Buff);
For i:=0 to le-1 do
OutputBytes(F, Buff[i]);
End;
Procedure DecodeString(DeCode: Word);
Var
dc: Word;
ReversC, ForwC: Cardinal;
DS: TDecodeBuffer;
Begin
dc:=DeCode;
DecodeBufferSize:=0;
Repeat
SetLength(DS, DecodeBufferSize+1);
DS[DecodeBufferSize]:=Dict[dc].AddChar;
dc:=Dict[dc].Code;
Inc(DecodeBufferSize);
Until dc=ClearDictValue;
SetLength(DecodeBuffer, DecodeBufferSize);
ReversC:=0;
For ForwC:=DecodeBufferSize-1 downto 0 do
Begin
DecodeBuffer[ReversC]:=DS[ForwC];
Inc(ReversC);
End;
End;
Procedure DecompressProc(Var Arc, Data: TByteFile);
Var
NewCode, OldCode: Word;
Begin
InitDict;
NewCode:=0;
InitCoder;
FSize:=GetFSize(Arc);
InitProgress(FSize);
OldCode:=BitRead(Arc, BitSize);
OutputBytes(Data, OldCode);
AddChar:=Byte(OldCode);
While NewCode<>EndOfStream do
Begin
NewCode:=BitRead(Arc, BitSize);
Case NewCode of
EndOfStream:
break;
ClearDictValue:
Begin
InitCoder;
OldCode:=BitRead(Arc, BitSize);
AddChar:=Byte(OldCode);
NewCode:=BitRead(Arc, BitSize);
End;
StepWordLength:
Begin
Inc(BitSize);
CurMaxCode:=GetMaxDictSize(BitSize);
NewCode:=BitRead(Arc, BitSize);
End;
End;
If DictPos<=NewCode then
Begin
DecodeString(OldCode);
Inc(DecodeBufferSize);
SetLength(DecodeBuffer, DecodeBufferSize);
DecodeBuffer[DecodeBufferSize-1]:=AddChar;
End
Else
DecodeString(NewCode);
OutPutDecodeBuffer(Data, DecodeBuffer);
AddChar:=DecodeBuffer[0];
AddNode(OldCode, AddChar);
OldCode:=NewCode;
SetProgress(DataPos);
End;
ResetBuffer(Data);
End;
end.
А вот и сам FileBuffer
- Код: Выделить всё
Unit FileBuffer;
{$I DefineType.pas}
// Модуль буферизированного ввода/вывода, реально ускаряет файловые
// операции из за ввода/вывода в память, а только потом, как буфер
// переполнится, в файл.
interface
uses
{$IFDEF StreamType}
Classes,
{$ENDIF}
SysUtils;
Type
{$IFDEF StreamType}
TByteFile=TStream;
{$ENDIF}
{$IFDEF FileType}
TByteFile=File of Byte;
{$ENDIF}
Procedure BeginRead;
Procedure BeginWrite;
Procedure OpenFile(var F: TByteFile; FileName:String);
Procedure CloseFile(var F: TByteFile);
Function GetFSize(Var F: TByteFile): Int64;
Function SeekBuffer(Var F: TByteFile; SeekPos: Int64): Byte;
Function GetBytes(var F: TByteFile): Byte;
Procedure ResetBuffer(Var F: TByteFile);
Procedure OutputBytes(Var F: TByteFile; B: Byte);
Procedure BitWrite(Var F: TByteFile; Num: Word; NumBits: Byte);
Function BitRead(Var F: TByteFile; NumBits: Byte): Word;
Procedure EndBitWrite(Var F: TByteFile);
Function ReadDWord(Var F: TByteFile): Cardinal;
Function ReadWord(Var F: TByteFile): Word;
Procedure WriteDWord(Var F: TByteFile; DW: Cardinal);
Procedure WriteWord(Var F: TByteFile; W: Word);
Var
ArcFile, DataFile: TByteFile;
WriteMode: Byte;
ArcSize, DataPos, FSize: Int64;
implementation
Const
BufLength=1024*1024;
Var
ReadCounterBit, WriteCounterBit: Byte;
BufsCount, OutBufPos, ReadBitsBuffer, WriteBitsBuffer: Cardinal;
FPos: Int64;
DWordRec: Record LowLo, LowHi, HiLo, HiHi: Byte;
End;
DWordData:
Cardinal Absolute DWordRec;
WordRec:
Record Low, Hi: Byte;
End;
WordData:
Cardinal Absolute WordRec;
InBuffer:Array of Byte;
OutBuffer:Array of Byte;
Procedure OpenFile(var F: TByteFile; FileName:String);
Begin
{$IFDEF StreamType}
F:=TFileStream.Create(FileName, fmCreate);
{$ENDIF}
{$IFDEF FileType}
AssignFile(F, FileName);
ReWrite(F);
{$ENDIF}
End;
Procedure CloseFile(var F: TByteFile);
Begin
{$IFDEF StreamType}
F.Free;
{$ENDIF}
{$IFDEF FileType}
CloseFile(F);
{$ENDIF}
End;
Procedure GetBuff(var F: TByteFile);
Var
CountBytes: Cardinal;
Begin
If FSize>=FPos then
Begin
If BufsCount=0 then
FPos:=0;
{$IFDEF FileType}
Seek(F, (DataPos div BufLength)*BufLength);
BlockRead(F, InBuffer[0], BufLength, CountBytes);
{$ENDIF}
{$IFDEF StreamType}
F.Seek((DataPos div BufLength)*BufLength, 0);
CountBytes:=F.Read(InBuffer[0], BufLength);
{$ENDIF}
Inc(FPos, CountBytes);
BufsCount:=((FPos-1)div BufLength)+1;
End;
End;
Function GetBytes(var F: TByteFile): Byte;
Begin
GetBytes:=0;
If BufsCount=0 then
Begin
GetBuff(F);
DataPos:=0;
End;
If ((DataPos div BufLength)+1)<>BufsCount then
GetBuff(F);
If DataPos<=FSize then
Begin
GetBytes:=InBuffer[DataPos-((BufsCount-1)*BufLength)];
Inc(DataPos);
End;
End;
Procedure ResetBuffer(Var F: TByteFile);
Begin
If WriteMode=1 then
{$IFDEF StreamType}
F.Write(OutBuffer[0], OutBufPos);
{$ENDIF}
{$IFDEF FileType}
BlockWrite(F, OutBuffer[0], OutBufPos);
{$ENDIF}
OutBufPos:=0;
End;
Procedure OutputBytes(Var F: TByteFile; B: Byte);
Begin
OutBuffer[OutBufPos]:=B;
Inc(ArcSize);
Inc(OutBufPos);
If OutBufPos=BufLength then
ResetBuffer(F);
End;
// ======================Bit read==========================================
Function BitRead(Var F: TByteFile; NumBits: Byte): Word;
var
B: Word;
begin
{ Пока в буфере не хватает бит - читаем их из файла }
While ReadCounterBit<NumBits do
Begin
B:=GetBytes(F);
ReadBitsBuffer:=ReadBitsBuffer or(B shl ReadCounterBit);
{ Добавляем его в буфер }
Inc(ReadCounterBit, 8);
End;
BitRead:=Word(ReadBitsBuffer and((1 shl NumBits)-1));
{ Получаем из буфера нужное кол-во бит }
ReadBitsBuffer:=ReadBitsBuffer shr NumBits;
{ Отчищаем буфер от выданных бит }
Dec(ReadCounterBit, NumBits);
end;
// ======================Bit read End======================================
// ======================Bit Write=========================================
Procedure BitWrite(Var F: TByteFile; Num: Word; NumBits: Byte);
Var
B: Byte;
BitBuffer: Cardinal;
begin
If WriteMode=1 then
Begin
BitBuffer:=Num;
WriteBitsBuffer:=WriteBitsBuffer or(BitBuffer shl WriteCounterBit);
{ Добавляем в буфер новые биты }
Inc(WriteCounterBit, NumBits);
While (WriteCounterBit>=8) do
Begin
B:=Byte(WriteBitsBuffer and $FF); { Получаем первый байт из буфера }
OutputBytes(F, B);
WriteBitsBuffer:=WriteBitsBuffer shr 8;
{ Отчищам буфер от записанных бит }
Dec(WriteCounterBit, 8);
End;
End;
end;
Procedure EndBitWrite(Var F: TByteFile);
Var
B: Byte;
begin
If WriteMode=1 then
Begin
If (WriteCounterBit>0) then
Begin
B:=WriteBitsBuffer;
OutputBytes(F, B);
WriteCounterBit:=0;
WriteBitsBuffer:=0;
End;
BufsCount:=0;
FPos:=0;
End;
end;
// ====================Bit Write End=======================================
Function SeekBuffer(Var F: TByteFile; SeekPos: Int64): Byte;
Var
B: Byte;
OldPos: Int64;
Begin
If (((BufsCount-1)*BufLength)<=SeekPos)and(((BufsCount)*BufLength)>=SeekPos) then
Result:=InBuffer[SeekPos-((BufsCount-1)*BufLength)]
Else
Begin
// не повезло не попали в буфер
{$IFDEF StreamType}
OldPos:=F.Position;
F.Seek(SeekPos, 0);
F.Read(B, 1);
F.Seek(OldPos, 0);
{$ENDIF}
{$IFDEF FileType}
OldPos:=FilePos(F);
Seek(F, SeekPos);
BlockRead(F, B, 1);
Seek(F, OldPos);
{$ENDIF}
Result:=B;
End;
End;
Procedure WriteWord(Var F: TByteFile; W: Word);
Begin
WordData:=W;
BitWrite(F, WordRec.Low, 8);
BitWrite(F, WordRec.Hi, 8);
End;
Procedure WriteDWord(Var F: TByteFile; DW: Cardinal);
Begin
DWordData:=DW;
BitWrite(F, DWordRec.LowLo, 8);
BitWrite(F, DWordRec.LowHi, 8);
BitWrite(F, DWordRec.HiLo, 8);
BitWrite(F, DWordRec.HiHi, 8);
End;
Function ReadWord(Var F: TByteFile): Word;
Begin
WordRec.Low:=BitRead(F, 8);
WordRec.Hi:=BitRead(F, 8);
Result:=WordData;
End;
Function ReadDWord(Var F: TByteFile): Cardinal;
Begin
DWordRec.LowLo:=BitRead(F, 8);
DWordRec.LowHi:=BitRead(F, 8);
DWordRec.HiLo:=BitRead(F, 8);
DWordRec.HiHi:=BitRead(F, 8);
Result:=DWordData;
End;
/// /////////////////////////////////////////////////////////////////
Procedure BeginRead;
Begin
BufsCount:=0;
FPos:=0;
ReadBitsBuffer:=0;
ReadCounterBit:=0;
End;
Procedure BeginWrite;
Begin
WriteCounterBit:=0;
WriteBitsBuffer:=0;
End;
Function GetFSize(Var F: TByteFile): Int64;
Begin
{$IFDEF StreamType}
Result:=F.Size;
{$ENDIF}
{$IFDEF FileType}
Result:=FileSize(F);
{$ENDIF}
FSize:=Result;
End;
Begin
SetLength(OutBuffer, BufLength+1);
SetLength(InBuffer, BufLength+1);
End.
Компрессировать и декомпрессировать так:
- Код: Выделить всё
CompressProc(DataFile, ArcFile, False, False);
DeCompressProc(ArcFile, DataFile);
причём файлы или потоки DataFile, ArcFile должны быть уже открыты.
И закрывать вы их тоже сами должны.
Кстати, скоростью компрессии Вы будете довольны.