Смекни!
smekni.com

Алгоритм сжатия "Unbuffered RLE" (стр. 2 из 2)

mov bl, 0 // BL = очистить счетчик повторов

mov al, ah // восстановить в AL текущий байт

//

Put_Byte: //------------------------

//

out Number_of_OutputPort, al // записать байт в выходной поток

jmp Get_from_InputStream // взять из входного потока следующий байт

Итак, регистр BL играет роль счетчика, увеличивающегося в тот момент, когда из входного потока приходят одинаковые байты. Разумеется, каждый следующий одинаковый байт увеличивает счетчик (регистр BL) на 1. Регистр AL принимает в себя тот байт, который только что поступил из входного потока. В регистре AH хранится предыдущий байт входного потока. Поскольку код ориентирован под конструкцию считывающего приемника, то есть входной порт микропроцессора подключен к свето-фотодиодной матрице, а его выходной порт подключен прямо к транспортной магистрали, поэтому и извлечение байт из входного потока и выталкивание байт в выходной поток выполнено при помощи команд чтения/записи портов ввода-вывода (номера требуемых портов заданы константами Number_of_InputPort и Number_of_OutputPort). Кроме того, работа по сжатию входного потока сделана в форме бесконечного цикла, подразумевая тот случай, когда аппаратный приемник без перерыва сканирует некую внешнюю информацию, соответственно, также постоянно эта информация (уже сжатая) поступает в транспортную магистраль.

И ниже то же самое, но уже для программистов в языках высокого уровня. Ну, на всякий случай, вдруг нет желания разбираться в ассемблерном коде.

Счетчик = 0

Предыдущий_байт = Взять_байт_из_входного_потока

Выходной_поток = Предыдущий_байт

Бесконечный цикл

Текущий_байт = Взять_байт_из_входного_потока

Если Текущий_байт = Предыдущий_байт тогда

Если Счетчик = 0 тогда Выходной_поток = Текущий_байт

Счетчик = Счетчик + 1

Если Счетчик = 255 тогда

Выходной_поток = Счетчик – 1

Счетчик = 0

Конец если

Иначе

Если Счетчик > 0 тогда Выходной_поток = Счетчик – 1

Выходной_поток = Текущий_байт

Предыдущий_байт = Текущий_байт

Счетчик = 0

Конец если

Конец цикла

Как работает декодер

Здесь алгоритм еще проще. При декодировании бывший выходной поток является уже входным потоком, а получающийся выходной поток – это декодированная информация. Так вот, извлекая из входного потока байт, сразу же выбрасываем его в выходной поток. Далее сравниваем текущий байт с предыдущим байтом. Как только они одинаковы, извлекаем из входного потока байт, который является счетчиком повторов. Именно столько раз в выходной поток копируем текущий байт (что был перед счетчиком).

Предыдущий_байт = Взять_байт_из_входного_потока

Выходной_поток = Предыдущий_байт

Бесконечный цикл

Текущий_байт = Взять_байт_из_входного_потока

Выходной_поток = Текущий_байт

Если Текущий_байт = Предыдущий_байт тогда

Счетчик = Взять_байт_из_входного_потока

Цикл Счетчик раз Выходной_поток = Текущий_байт

Конец если

Предыдущий_байт = Текущий_байт

Конец цикла

И на всякий случай привожу ассемблерный код алгоритма декодирования. Код, естественно, рассчитан опять под аппаратное устройство, то есть дешевую плату с микропроцессором (нет памяти, используются три регистра, работа через порты ввода-вывода), которое находится на противоположной стороне транспортной магистрали и занимается банальным декодированием приходящего из транспортной магистрали потока. Тут входной порт микропроцессора подключен к транспортной магистрали, а выходной порт - к чему-нибудь еще, куда затем направляется раскодированный поток.

Decode_Unbuffred_RLE: // -----------------------

//

in al, Number_of_InputPort // AL = первый байт из входного потока

out Number_of_OutputPort, al // записать байт в выходной поток

mov ah, al // этот байт теперь становится предыдущим

//

Get_from_InputStream: // -----------------------

//

in al, Number_of_InputPort // AL = следующий байт из входного потока

out Number_of_OutputPort, al // записать байт в выходной поток

cmp al, ah // равен ли он предыдущему байту?

mov ah, al // этот байт теперь становится предыдущим

jnz Get_from_InputStream // если байты не одинаковы, взять следующий байт

//

in al, Number_of_InputPort // AL = следующий байт из входного потока

mov bl, al // BL = счетчик повторов

mov al, ah // восстановить в AL текущий байт

//

Copy_Byte: //------------------------

//

cmp bl, 0 // есть ли еще повторы байта?

jz Get_from_InputStream // если нет, взять следующий байт

out Number_of_OutputPort, al // записать байт в выходной поток

dec bl // BL = еще один байт скопировали

jmp Copy_Byte // копировать байт дальше

Все, рассказ закончен. Будем полагать, вы осведомлены теперь, а значит подготовлены. Возникни у вас похожая задача, решение готово. Напомню только, что алгоритм эффективен с позиций строгих ограничений, накладываемых на исполняющие аппаратные устройства. Никаких привилегий в степени сжатия он не дает, ибо это всего лишь собрат RLE, который используют ту же самую методику сжатия. Если в RLE за счет принудительного введения поля счетчика оказывается по одному лишнему байту на каждую "запись" для разнобоев, то в Unbuffered RLE этот байт не затрачивается ввиду отсутствия "записей" для разнобоев, но зато каждая "запись" для повторов теперь занимает по 3 байта, и вот здесь затрачивается один лишний байт.