Delphi. Трохи щодо методів упаковки даних h2>
Running
- Це найпростіший з методів упаковки інформації. Припустіть що Ви маєте
рядок тексту, і в кінці рядка стоїть 40 пробілів. Це явна надмірність
наявної інформації. Проблема стискання цього рядка вирішується дуже просто - ці
40 пробілів (40 байт) стискаються в 3 байти з допомогою упаковки їх за методом
символів, що повторюються (running). Перший байт, що стоїть замість 40 прогалин у
стислій рядку, фактично буде явлться пробілом (послідовність була з
пробілів). Другий байт - спеціальний байт "прапорця" який
вказує що ми повинні розгорнути попередній в рядку байт в
послідовність при відновленні рядка. Третій байт - байт рахунку (в
нашому випадку це буде 40). Як Ви самі можете бачити, достатньо щоб будь-який
разу, коли ми маємо послідовність з більш 3-х однакових символів,
замінювати їх вище описаною послідовністю, щоб на виході отримати блок
інформації менший за розміром, але допускає відновлення інформації в
початковому вигляді. p>
Залишаючи
все сказане вище істинним, додам лише те, що в цьому методі основний
проблемою є вибір того самого байти "прапорця", тому що в
реальних блоках інформації як правило використовуються всі 256 варіантів байтів і
немає можливості мати 257 варіант - "прапорець". На перший погляд ця
проблема здається нерозв'язною, але до неї є ключик, який Ви знайдете
прочитавши про кодування за допомогою алгоритму Гоффмана (Huffman). p>
LZW
- Історія цього алгоритму починається з опублікування в травні 1977 р. Дж. Зівом (
J. Ziv) і А. Лемпела (A. Lempel) статті у журналі "Інформаційні
теорії "під назвою" IEEE Trans ". Надалі цей
алгоритм був допрацьований Террі А. Велч (Terry A. Welch) і в остаточному
варіанті відображено у статті "IEEE Compute" в червні 1984. У цій
статті описувалися подробиці алгоритму і деякі загальні проблеми з якими
можна p>
зіткнутися
при його реалізації. Пізніше цей алгоритм одержав назву - LZW (Lempel - Ziv --
Welch). P>
Алгоритм
LZW являє собою алгоритм кодування послідовностей неоднакових
символів. Візьмемо для прикладу рядок "Об'єкт TSortedCollection породжений
від TCollection. ". Аналізуючи цей рядок ми можемо бачити, що слово
"Collection" повторюється двічі. У цьому слові 10 символів - 80 біт. І
якщо ми зможемо замінити це слово у вихідному файлі, у другому його включенні, на
посилання на першому включення, то отримаємо стиснення інформації. Якщо розглядати
вхідний блок інформації розміром не більше 64К і обмежиться довгою кодованої
рядка в 256 символів, то з огляду байт "прапор" отримаємо, що рядок з
80 біт замінюється 8 +16 +8 = 32 біта. Алгоритм LZW як-б "навчається" в
процесі стиснення файлу. Якщо існують повторювані рядки у файлі, то вони будуть
закодірованни в таблицю. Очевидною перевагою алгоритму є те, що немає
необхідності включати таблицю кодування в стиснутий файл. Іншою важливою
особливістю є те, що стиснення по алгоритму LZW є однопрохідної
операцією на противагу алгоритму Гоффмана (Huffman), якому
потрібно два проходи. p>
Huffman
- Спочатку здається що створення файлу менших розмірів з початкового без
кодування послідовностей або виключення повтору байтів буде неможливою
завданням. Але давайте ми примусимо себе зробити кілька розумових зусиль і
зрозуміти алгоритм Гоффмана (Huffman). Втративши не так багато часу ми
придбаємо знання і додаткове місце на дисках. p>
Стискаючи
файл за алгоритмом Хаффмана перше що ми повинні зробити - це необхідно
прочитати файл повністю і підрахувати скільки разів зустрічається кожен символ з
розширеного набору ASCII. Якщо ми будемо враховувати всі 256 символів, то для нас
не буде різниці в стисненні текстового й EXE файлу. p>
Після
підрахунку частоти входження кожного символу, необхідно переглянути таблицю
кодів ASCII і сформувати уявну компонування між кодами за зменшенням. Тобто
не змінюючи місцезнаходження кожного символу з таблиці в пам'яті відсортувати
таблицю посилань на них за спаданням. Кожну посилання з останньої таблиці назвемо
"вузлом". В подальшому (у лісі) ми будемо пізніше розміщувати покажчики
які будуть вказує на цей "вузол". Для ясності давайте
розглянемо приклад: p>
Ми
маємо файл довжиною в 100 байт і має 6 різних символів в p>
собі
. Ми підрахували входження кожного із символів у файл і отримали p>
наступне
: p>
+-----------------+-----+-----+-----+-----+---- -+-----+ p>
|
Символ | A | B | C | D | E | F | p>
+-----------------+-----+-----+-----+-----+---- -+-----| p>
|
число входжень | 10 | 20 | 30 | 5 | 25 | 10 | p>
+-----------------+-----+-----+-----+-----+---- -+-----+ p>
Тепер
ми беремо ці числа і будемо називати їх частотою входження для кожного символу.
Розмістимо таблицю як нижче. P>
+-----------------+-----+-----+-----+-----+---- -+-----+ p>
|
Символ | C | E | B | F | A | D | p>
+-----------------+-----+-----+-----+-----+---- -+-----| p>
|
число входжень | 30 | 25 | 20 | 10 | 10 | 5 | p>
+-----------------+-----+-----+-----+-----+---- -+-----+ p>
Ми
візьмемо з останньої таблиці символи з найменшою частотою. У нашому випадку це
D (5) і будь-якої символ з F або A (10), можна взяти будь-який з них наприклад A.
Сформуємо з "вузлів" D і A новий "вузол", частота входження
для якого буде дорівнює сумі частот D і A: p>
Частота
30 10 5 10 20 25 p>
Символу
C A D F B E p>
|
| p>
+--+--+ p>
++-+ p>
| 15 |
= 5 + 10 p>
+--+ p>
Номер
в рамці - сума частот символів D і A. Тепер ми знову шукаємо два символи з
самими низькими частотами входження. За винятком з перегляду D і A і розглядаючи
замість них новий "вузол" з сумарною частотою входження. Найнижча
частота тепер у F і нового "вузла". Знову зробимо операцію злиття
вузлів: p>
Частота
30 10 5 10 20 25 p>
Символу
C A D F B E p>
|
| | P>
|
| | P>
|
+--+| | P>
+ - | 15 + +
| p>
+ + - +
| p>
|
| p>
|
+ - + | P>
+----| 25 + - +
= 10 + 15 p>
+--+ p>
Розглядаємо
таблицю знову для наступних двох символів (B і E). Ми продовжуємо в цей режим
поки все "дерево" не сформований, тобто поки все не зведеться до одного
вузла. p>
Частота
30 10 5 10 20 25 p>
Символу
C A D F B E p>
|
| | | | | P>
|
| | | | | P>
|
| +--+| | | | P>
|
+ - | 15 + + | | | p>
|
+ + - + | | | P>
|
| | | | P>
|
| + - + | | + - + | P>
|
+----| 25 + - + + - | 45 +-+ p>
|
+ + - + ++-+ P>
|
+ - + | | P>
+----| 55 +------+
| p>
+ - + +
| p>
|
+------------+ | P>
+---|
Root (100) +----+ p>
+------------+ p>
Тепер
коли наше дерево створено, ми можемо кодувати файл. Ми повинні завжди починати
з кореня (Root). Кодуючи перший символ (лист дерева С) Ми простежуємо вгору
по дереву всі повороти гілок і якщо ми робимо лівий поворот, то запам'ятовуємо 0-й
біт, і аналогічно 1-й біт для правого повороту. Так для C, ми будемо йти вліво
до 55 (і запам'ятаємо 0), потім знову ліворуч (0) до самого символу. Код Хаффмана
для нашого символу C - 00. Для наступного символу (А) у нас виходить --
ліво, право, ліво, ліво, що виливається в послідовність 0100. Виконавши вище
сказане для всіх символів отримаємо p>
C
= 00 (2 біта) p>
A
= 0100 (4 біти) p>
D
= 0101 (4 біти) p>
F
= 011 (3 біта) p>
B
= 10 (2 біта) p>
E
= 11 (2 біта) p>
Кожен
символ початку представлявся 8-ма бітами (один байт), і так як ми
зменшили число бітів необхідних для подання кожного символу, ми
отже зменшили розмір вихідного файлу. Стиснення складивется наступним
так: p>
+----------+----------------+------------------ -+--------------+ p>
|
Частота | спочатку | ущільнені біти | зменшено на | p>
+----------+----------------+------------------ -+--------------| p>
|
C 30 | 30 x 8 = 240 | 30 x 2 = 60 | 180 | p>
|
A 10 | 10 x 8 = 80 | 10 x 3 = 30 | 50 | p>
|
D 5 | 5 x 8 = 40 | 5 x 4 = 20 | 20 | p>
|
F 10 | 10 x 8 = 80 | 10 x 4 = 40 | 40 | p>
|
B 20 | 20 x 8 = 160 | 20 x 2 = 40 | 120 | p>
|
E 25 | 25 x 8 = 200 | 25 x 2 = 50 | 150 | p>
+----------+----------------+------------------ -+--------------+ p>
Початковий
розмір файлу: 100 байт - 800 біт; p>
Розмір
стисненого файлу: 30 байт - 240 біт; p>
240
- 30% із 800, так що ми стиснули цей файл на 70%. P>
Всі
це досить добре, але неприємність знаходиться в тому факті, що для
відновлення початкового файлу, ми повинні мати декодуючі дерево, так
як дерева будуть різноманітні для різних файлів. Отже ми повинні
зберігати дерево разом з файлом. Це перетворюється в підсумку у збільшення
розмірів вихідного файлу. p>
В
нашою методикою стиснення і кожному вузлі знаходяться 4 байти покажчика, з цього,
повна таблиця для 256 байт буде приблизно 1 Кбайт довгою. Таблиця в
нашому прикладі має 5 вузлів плюс 6 вершин (де і знаходяться наші символи),
всього 11. 4 байти 11 раз - 44. Якщо ми додамо після невелика кількість
байтів для збереження місця вузла і деяку іншу статистику - наша таблиця
буде приблизно 50 байт довжини. Додавши до 30 байтам стислій інформації,
50 байтів таблиці отримуємо, що загальна довжина архівного файлу виросте до 80
байт. Враховуючи, що первісна довжина файлу в розглянутому прикладі
була 100 байт - ми отримали 20% стиснення інформації. Не погано. Те що ми
справді виконали - трансляція символьного ASCII набору в наш новий набір
потребує меншу кількість знаків в порівнянні з стандартним. p>
Що
ми можемо отримати на цьому шляху? p>
Розглянемо
максимум що ми можемо отримати для різних розрядних комбінацій в
оптимальному дереві, яке є несиметричним. p>
Ми
отримаємо що можна мати тільки: p>
4
- 2 розрядних коду; p>
8
- 3 розрядних кодів; p>
16
- 4 розрядних кодів; p>
32
- 5 розрядних кодів; p>
64
- 6 розрядних кодів; p>
128
- 7 розрядних кодів; p>
Необхідно
ще два 8 розрядних коду. p>
4
- 2 розрядних коду; p>
8
- 3 розрядних кодів; p>
16
- 4 розрядних кодів; p>
32
- 5 розрядних кодів; p>
64
- 6 розрядних кодів; p>
128
- 7 розрядних кодів; p>
-------- p>
254 p>
Отже
ми маємо підсумок з 256 різних комбінацій якими можна кодувати байт. З
цих комбінацій лише 2 по довжині рівні 8 бітам. Якщо ми складемо число бітів
які це представляє, то в підсумку отримаємо 1554 біт або 195 байтів. Так в
максимумі, ми стиснули 256 байт до 195 або 33%, таким чином максимально
ідеалізований Huffman може досягати стиснення в 33% коли використовується на
рівні байти Всі ці підрахунки проводилися для не префіксних кодів Хаффмана тобто
кодів, які не можна ідентифікувати однозначно. Наприклад код A - 01011 і код
B - 0101. Якщо ми будемо отримувати ці коди побітне, то отримавши біти 0101 ми не
зможемо сказати який код ми отримали A або B, тому що наступний біт може бути
як початком наступного коду, так і продовженням попереднього. p>
Необхідно
додати, що ключем до побудови префіксних кодів служить звичайне бінарне
дерево і якщо уважно розглянути попередній приклад з побудовою дерева,
можна переконатися, що усі отримані коди там префіксние. p>
Одне
Останнім примітка - алгоритм Хаффмана вимагає читати вхідний файл двічі,
один раз вважаючи частоти входження символів, інший разпроізводя
безпосередньо кодування. p>
P.S.
Про "Ключик" дає дорогу алгоритму Running. P>
----
Прочитавши оглядову інформацію про Huffman кодуванні подумайтенад тим, що на
нашому бінарному дереві може бути і 257 листів. p>
Список літератури h2>
1)
Опис архіватора Narc фірми Infinity Design Concepts, Inc.; P>
2)
Чарльз Сейтер, 'Стиснення даних', "Мир ПК", N2 1991; p>
Додаток h2>
($ A +, B-, D +, E +, F-, G-, I-, L +, N-, O-, R +, S +, V +, X-) p>
($ M
16384,0,655360) p>
{********************************************** ********} p>
(*
Алгоритм ущільнення даних за методом *) p>
(* Хафмана. *) p>
{********************************************** ********} p>
Program Hafman; p>
Uses Crt, Dos, Printer; p>
Type PCodElement = ^ CodElement; p>
CodElement = record p>
NewLeft, NewRight, p>
P0, P1: PCodElement; (елемент входить одночасно) p>
LengthBiteChain: byte; (в масив, черга і
дерево) p>
BiteChain: word; p>
CounterEnter: word; p>
Key: boolean; p>
Index: byte; p>
end; p>
TCodeTable = array [0 .. 255] of
PCodElement; p>
Var CurPoint, HelpPoint, p>
LeftRange, RightRange: PCodElement; p>
CodeTable: TCodeTable; p>
Root: PCodElement; p>
InputF, OutputF, InterF: file; p>
TimeUnPakFile: longint; p>
AttrUnPakFile: word; p>
NumRead, NumWritten: Word; p>
InBuf: array [0 .. 10239] of byte; p>
OutBuf: array [0 .. 10239] of byte; p>
BiteChain: word; p>
CRC, p>
CounterBite: byte; p>
OutCounter: word; p>
InCounter: word; p>
OutWord: word; p>
St: string; p>
LengthOutFile, LengthArcFile:
longint; p>
Create: boolean; p>
NormalWork: boolean; p>
ErrorByte: byte; p>
DeleteFile: boolean; p>
{---------------------------------------------- ---} p>
procedure ErrorMessage; p>
(--- висновок повідомлення про помилку ---) p>
begin p>
If ErrorByte 0 then p>
begin p>
Case ErrorByte of p>
2: Writeln ( 'File not found ...'); p>
3: Writeln ( 'Path not found ...'); p>
5: Writeln ( 'Access denied ...'); p>
6: Writeln ( 'Invalid handle ...'); p>
8: Writeln ( 'Not enough memory
...'); p>
10: Writeln ( 'Invalid environment
...'); p>
11: Writeln ( 'Invalid format ...'); p>
18: Writeln ( 'No more files ...'); p>
else Writeln ( 'Error #', ErrorByte, '
...'); p>
end; p>
NormalWork: = False; p>
ErrorByte: = 0; p>
end; p>
end; p>
procedure ResetFile; p>
(--- відкриття файлу для резервного копіювання ---) p>
Var St: string; p>
begin p>
Assign (InputF, ParamStr (3 )); p>
Reset (InputF, 1); p>
ErrorByte: = IOResult; p>
ErrorMessage; p>
If NormalWork then Writeln ( 'Pak file
: ', ParamStr (3 ),'...'); p>
end; p>
procedure ResetArchiv; p>
(
--- Відкриття файлу архіву, або його створення ---) p>
begin p>
St: = ParamStr (2); p>
If Pos ('.', St) 0 then
Delete (St, Pos ('.', St), 4); p>
St: = St + '. vsg'; p>
Assign (OutputF, St); p>
Reset (OutPutF, 1); p>
Create: = False; p>
If IOResult = 2 then p>
begin p>
Rewrite (OutputF, 1); p>
Create: = True; p>
end; p>
If NormalWork then p>
If Create then Writeln ( 'Create
archiv: ', St ,'...') p>
else Writeln ( 'Open archiv:
', St ,'...') p>
end; p>
procedure SearchNameInArchiv; p>
(--- надалі - пошук імені файлу в
архіві ---) p>
begin p>
Seek (OutputF, FileSize (OutputF )); p>
ErrorByte: = IOResult; p>
ErrorMessage; p>
end; p>
procedure DisposeCodeTable; p>
(
--- Знищення кодової таблиці і черги ---) p>
Var I: byte; p>
begin p>
For I: = 0 to 255 do
Dispose (CodeTable [I ]); p>
end; p>
procedure ClosePakFile; p>
(--- закриття архівіруемого файлу ---) p>
Var I: byte; p>
begin p>
If DeleteFile then Erase (InputF); p>
Close (InputF); p>
end; p>
procedure CloseArchiv; p>
(--- закриття архівного файлу ---) p>
begin p>
If FileSize (OutputF) = 0 then
Erase (OutputF); p>
Close (OutputF); p>
end; p>
procedure InitCodeTable; p>
(
--- Ініціалізація таблиці кодування ---) p>
Var I: byte; p>
begin p>
For I: = 0 to 255 do p>
begin p>
New (CurPoint); p>
CodeTable [I]: = CurPoint; p>
With CodeTable [I] ^ do p>
begin p>
P0: = Nil; p>
P1: = Nil; p>
LengthBiteChain: = 0; p>
BiteChain: = 0; p>
CounterEnter: = 1; p>
Key: = True; p>
Index: = I; p>
end; p>
end; p>
For I: = 0 to 255 do p>
begin p>
If I> 0 then
CodeTable [I-1] ^. NewRight: = CodeTable [I]; p>
If I
CurPoint ^. NewRight ^. CounterEnter then p>
begin p>
HelpPoint: = CurPoint ^. NewRight; p>
HelpPoint ^. NewLeft: = CurPoint ^. NewLeft; p>
CurPoint ^. NewLeft: = HelpPoint; p>
If HelpPoint ^. NewRightNil
then HelpPoint ^. NewRight ^. NewLeft: = CurPoint; p>
CurPoint ^. NewRight: = HelpPoint ^. NewRight; p>
HelpPoint ^. NewRight: = CurPoint; p>
If HelpPoint ^. NewLeftNil
then HelpPoint ^. NewLeft ^. NewRight: = HelpPoint; p>
If CurPoint = LeftRange then
LeftRange: = HelpPoint; p>
If HelpPoint = RightRange then
RightRange: = CurPoint; p>
CurPoint: = CurPoint ^. NewLeft; p>
If CurPoint = LeftRange then
CurPoint: = CurPoint ^. NewRight p>
else CurPoint: = CurPoint ^. NewLeft; p>
end p>
else CurPoint: = CurPoint ^. NewRight; p>
end; p>
end; p>
procedure CounterNumberEnter; p>
(--- підрахунок частот входжень байтів в
блоці ---) p>
Var C: word; p>
begin p>
For C: = 0 to NumRead-1 do p>
Inc (CodeTable [(InBuf [C ])]^. CounterEnter); p>
end; p>
function SearchOpenCode: boolean; p>
(
--- Пошук в черзі пари відкритих за Key мінімальних значень ---) p>
begin p>
CurPoint: = LeftRange; p>
HelpPoint: = LeftRange; p>
HelpPoint: = HelpPoint ^. NewRight; p>
While not CurPoint ^. Key do p>
CurPoint: = CurPoint ^. NewRight; p>
While (not (HelpPoint = RightRange))
and (not HelpPoint ^. Key) do p>
begin p>
HelpPoint: = HelpPoint ^. NewRight; p>
If (HelpPoint = CurPoint) and
(HelpPointRightRange) then p>
HelpPoint: = HelpPoint ^. NewRight; p>
end; p>
If HelpPoint = CurPoint then
SearchOpenCode: = False else SearchOpenCode: = True; p>
end; p>
procedure CreateTree; p>
(
--- Створення дерева частот входження ---) p>
begin p>
While SearchOpenCode do p>
begin p>
New (Root); p>
With Root ^ do p>
begin p>
P0: = CurPoint; p>
P1: = HelpPoint; p>
LengthBiteChain: = 0; p>
BiteChain: = 0; p>
CounterEnter: = P0 ^. CounterEnter +
P1 ^. CounterEnter; p>
Key: = True; p>
P0 ^. Key: = False; p>
P1 ^. Key: = False; p>
end; p>
HelpPoint: = LeftRange; p>
While (HelpPoint ^. CounterEnter <
Root ^. CounterEnter) and p>
(HelpPointNil) do
HelpPoint: = HelpPoint ^. NewRight; p>
If HelpPoint = Nil then (додавання в
кінець) p>
begin p>
Root ^. NewLeft: = RightRange; p>
RightRange ^. NewRight: = Root; p>
Root ^. NewRight: = Nil; p>
RightRange: = Root; p>
end p>
else p>
begin (вставка перед HelpPoint) p>
Root ^. NewLeft: = HelpPoint ^. NewLeft; p>
HelpPoint ^. NewLeft: = Root; p>
Root ^. NewRight: = HelpPoint; p>
If Root ^. NewLeftNil then
Root ^. NewLeft ^. NewRight: = Root; p>
end; p>
end; p>
end; p>
procedure ViewTree (P: PCodElement
); p>
(
--- Перегляд дерева частот і присвоювання кодіровочних ланцюгів листю ---) p>
Var Mask, I: word; p>
begin p>
Inc (CounterBite); p>
If P ^. P0Nil then ViewTree (
P ^. P0); p>
If P ^. P1Nil then p>
begin p>
Mask: = (1 SHL (16-CounterBite )); p>
BiteChain: = BiteChain OR Mask; p>
ViewTree (P ^. P1); p>
Mask: = (1 SHL (16-CounterBite )); p>
BiteChain: = BiteChain XOR Mask; p>
end; p>
If (P ^. P0 = Nil) and (P ^. P1 = Nil) then p>
begin p>
P ^. BiteChain: = BiteChain; p>
P ^. LengthBiteChain: = CounterBite-1; p>
end; p>
Dec (CounterBite); p>
end; p>
procedure CreateCompressCode; p>
(
--- Обнулення змінних і запуск перегляду дерева з вершини ---) p>
begin p>
BiteChain: = 0; p>
CounterBite: = 0; p>
Root ^. Key: = False; p>
ViewTree (Root); p>
end; p>
procedure DeleteTree; p>
(
--- Видалення дерева ---) p>
Var P: PCodElement; p>
begin p>
CurPoint: = LeftRange; p>
While CurPointNil do p>
begin p>
If (CurPoint ^. P0Nil) and
(CurPoint ^. P1Nil) then p>
begin p>
If CurPoint ^. NewLeft Nil
then p>
CurPoint ^. NewLeft ^. NewRight: = CurPoint ^. NewRight; p>
If CurPoint ^. NewRight Nil
then p>
CurPoint ^. NewRight ^. NewLeft: = CurPoint ^. NewLeft; p>
If CurPoint = LeftRange then
LeftRange: = CurPoint ^. NewRight; p>
If CurPoint = RightRange then
RightRange: = CurPoint ^. NewLeft; p>
P: = CurPoint; p>
CurPoint: = P ^. NewRight; p>
Dispose (P); p>
end p>
else CurPoint: = CurPoint ^. NewRight; p>
end; p>
end; p>
procedure SaveBufHeader; p>
(--- запис у
буфер заголовка архіву ---) p>
Type p>
ByteField = array [0 .. 6] of byte; p>
Const p>
Header: ByteField = ($ 56, $ 53,
$ 31, $ 00, $ 00, $ 00, $ 00); p>
begin p>
If Create then p>
begin p>
Move (Header, OutBuf [0], 7); p>
OutCounter: = 7; p>
end p>
else p>
begin p>
Move (Header [3], OutBuf [0], 4); p>
OutCounter: = 4; p>
end; p>
end; p>
procedure SaveBufFATInfo; p>
(
--- Запис у буфер всієї інформації по файлу ---) p>
Var I: byte; p>
St: PathStr; p>
R: SearchRec; p>
begin p>
St: = ParamStr (3); p>
For I: = 0 to Length (St) +1 do p>
begin p>
OutBuf [OutCounter]: = byte (Ord (St [I ])); p>
Inc (OutCounter); p>
end; p>
FindFirst (St, $ 00, R); p>
Dec (OutCounter); p>
Move (R. Time, OutBuf [OutCounter], 4); p>
OutCounter: = OutCounter 4; p>
OutBuf [OutCounter]: = R. Attr; p>
Move (R. Size, OutBuf [OutCounter 1], 4); p>
OutCounter: = OutCounter 5; p>
end; p>
procedure SaveBufCodeArray; p>
(
--- Зберегти масив частот входжень в архівному файлі ---) p>
Var I: byte; p>
begin p>
For I: = 0 to 255 do p>
begin p>
OutBuf [OutCounter]: = Hi (CodeTable [I] ^. CounterEnter); p>
Inc (OutCounter); p>
OutBuf [OutCounter]: = Lo (CodeTable [I] ^. CounterEnter); p>
Inc (OutCounter); p>
end; p>
end; p>
procedure CreateCodeArchiv; p>
(
--- Створення коду стиснення ---) p>
begin p>
InitCodeTable; (
ініціалізація кодової таблиці) p>
CounterNumberEnter; (підрахунок
числа входжень байт в блок) p>
SortQueueByte; (сортування за зростанням
числа входжень) p>
SaveBufHeader; (зберегти
заголовок архіву в буфері) p>
SaveBufFATInfo; (зберігається
FAT інформація по файлу
) p>
SaveBufCodeArray; (зберегти
масив частот входжень в архівному файлі) p>
CreateTree; (створення дерева
частот) p>
CreateCompressCode; (Створення коду стиснення) p>
DeleteTree; (видалення дерева
частот) p>
end; p>
procedure PakOneByte; p>
(
--- Стиск і пересилання у вихідний буфер одного байта ---) p>
Var Mask: word; p>
Tail: boolean; p>
begin p>
CRC: = CRC XOR InBuf [InCounter]; p>
Mask: = CodeTable [InBuf [InCounter]] ^. BiteChain
SHR CounterBite; p>
OutWord: = OutWord OR Mask; p>
CounterBite: = CounterBite + CodeTable [InBuf [InCounter]] ^. LengthBiteChain; p>
If CounterBite> 15 then Tail: = True
else Tail: = False; p>
While CounterBite> 7 do p>
begin p>
OutBuf [OutCounter]: = Hi (OutWord); p>
Inc (OutCounter); p>
If OutCounter = (SizeOf (OutBuf) -4)
then p>
begin p>
BlockWrite (OutputF, OutBuf, OutCounter, NumWritten); p>
OutCounter: = 0; p>
end; p>
CounterBite: = CounterBite-8; p>
If CounterBite0 then
OutWord: = OutWord SHL 8 else OutWord: = 0; p>
end; p>
If Tail then p>
begin p>
Mask: = CodeTable [InBuf [InCounter]] ^. BiteChain
SHL p>
(CodeTable [InBuf [InCounter]] ^. LengthBiteChain-CounterBite); p>
OutWord: = OutWord OR Mask; p>
end; p>
Inc (InCounter); p>
If (InCounter = (SizeOf (InBuf))) or
(InCounter = NumRead) then p>
begin p>
InCounter: = 0; p>
BlockRead (InputF, InBuf, SizeOf (InBuf), NumRead); p>
end; p>
end; p>
procedure PakFile; p>
(--- процедура безпосереднього стиснення файлу ---) p>
begin p>
ResetFile; p>
SearchNameInArchiv; p>
If NormalWork then p>
begin p>
BlockRead (InputF, InBuf, SizeOf (InBuf), NumRead); p>
OutWord: = 0; p>
CounterBite: = 0; p>
OutCounter: = 0; p>
InCounter: = 0; p>
CRC: = 0; p>
CreateCodeArchiv; p>
While (NumRead0) do
PakOneByte; p>
OutBuf [OutCounter]: = Hi (OutWord); p>
Inc (OutCounter); p>
OutBuf [OutCounter]: = CRC; p>
Inc (OutCounter); p>
BlockWrite (OutputF, OutBuf, OutCounter, NumWritten); p>
DisposeCodeTable; p>
ClosePakFile; p>
end; p>
end; p>
procedure ResetUnPakFiles; p>
(
--- Відкриття файлу для розпакування ---) p>
begin p>
InCounter: = 7; p>
St :=''; p>
repeat p>
St [InCounter-7]: = Chr (InBuf [InCounter ]); p>
Inc (InCounter); p>
until InCounter = InBuf [7] 8; p>
Assign (InterF, St); p>
Rewrite (InterF, 1); p>
ErrorByte: = IOResult; p>
ErrorMessage; p>
If NormalWork then p>
begin p>
WriteLn ( 'UnPak file:', St ,'...'); p>
Move (InBuf [InCounter], TimeUnPakFile, 4); p>
InCounter: = InCounter 4; p>
AttrUnPakFile: = InBuf [InCounter]; p>
Inc (InCounter); p>
Move (InBuf [InCounter], LengthArcFile, 4); p>
InCounter: = InCounter 4; p>
end; p>
end; p>
procedure CloseUnPakFile; p>
(--- закриття файла для розпакування ---) p>
begin p>
If not NormalWork then Erase (InterF) p>
else p>
begin p>
SetFAttr (InterF, AttrUnPakFile); p>
SetFTime (InterF, TimeUnPakFile); p>
end; p>
Close (InterF); p>
end; p>
procedure RestoryCodeTable; p>
(--- відтворення кодової таблиці по архівного файлу ---) p>
Var I: byte; p>
begin p>
InitCodeTable; p>
For I: = 0 to 255 do p>
begin p>
CodeTable [I] ^. CounterEnter: = InBuf [InCounter]; p>
CodeTable [I] ^. CounterEnter: = CodeTable [I] ^. CounterEnter
SHL 8; p>
Inc (InCounter); p>
CodeTable [I] ^. CounterEnter: = CodeTable [I] ^. CounterEnter + InBuf [InCounter]; p>
Inc (InCounter); p>
end; p>
end; p>
procedure UnPakByte (P: PCodElement
); p>
(
--- Розпакування одного байта ---) p>
Var Mask: word; p>
begin p>
If (P ^. P0 = Nil) and (P ^. P1 = Nil) then p>
begin p>
OutBuf [OutCounter]: = P ^. Index; p>
Inc (OutCounter); p>
Inc (LengthOutFile); p>
If OutCounter = (SizeOf (OutBuf) -1)
then p>
begin p>
BlockWrite (InterF, OutBuf, OutCounter, NumWritten); p>
OutCounter: = 0; p>
end; p>
end p>
else p>
begin p>
Inc (CounterBite); p>
If CounterBite = 9 then p>
begin p>
Inc (InCounter); p>
If InCounter = (SizeOf (InBuf)) then p>
begin p>
InCounter: = 0; p>
BlockRead (OutputF, InBuf, SizeOf (InBuf), NumRead); p>
end; p>
CounterBite: = 1; p>
end; p>
Mask: = InBuf [InCounter]; p>
Mask: = Mask SHL (CounterBite-1); p>
Mask: = Mask OR $ FF7F; (встановлення всіх бітів крім старшого) p>
If Mask = $ FFFF then UnPakByte (P ^. P1) p>
else UnPakByte (P ^. P0); p>
end; p>
end; p>
procedure UnPakFile; p>
(--- розпакування одного файлу ---) p>
begin p>
BlockRead (OutputF, InBuf, SizeOf (InBuf), NumRead); p>
ErrorByte: = IOResult; p>
ErrorMessage; p>
If NormalWork then ResetUnPakFiles; p>
If NormalWork then p>
begin p>
RestoryCodeTable; p>
SortQueueByte; p>
CreateTree; (створення дерева
частот) p>
CreateCompressCode; p>
CounterBite: = 0; p>
OutCounter: = 0; p>
LengthOutFile: = 0; p>
While LengthOutFile LengthArcFile do p>
UnPakByte (Root); p>
BlockWrite (InterF, OutBuf, OutCounter, NumWritten); p>
DeleteTree; p>
DisposeCodeTable; p>
end; p>
CloseUnPakFile; p>
end; p>
(------------------------- main
text -------------------------) p>
begin p>
DeleteFile: = False; p>
NormalWork: = True; p>
ErrorByte: = 0; p>
WriteLn; p>
WriteLn ( 'ArcHaf version 1.0 (c)
Copyright VVS Soft Group, 1992 .'); p>
ResetArchiv; p>
If NormalWork then p>
begin p>
St: = ParamStr (1); p>
Case St [1] of p>
'a', 'A': PakFile; p>
'm', 'M': begin p>
DeleteFile: = True; p>
PakFile; p>
end; p>
'e', 'E': UnPakFile; p>
else; p>
end; p>
end; p>
CloseArchiv; p>
end. p>
Список літератури h2>
Для
підготовки даної роботи були використані матеріали з сайту http://www.hostmake.ru/
p>