
Распаковка данных в формате LZ4 на Ассемблере

Распаковка данных в формате LZ4 на Ассемблере
Алгоритм сжатия LZ4 был разработан Yann Collet в 2011-м году. При небольшом размере упаковщика и распаковщика, LZ4 обладает очень высокой скоростью обработки данных и хорошей степенью компрессии, поэтому используется в большом числе серьезных проектов. На офсайте есть ссылки на реализации этого алгоритма на различных языках программирования, в том числе и вариант на 16-битном Ассемблере от Jim Leonard. Для использования в своих программах я адаптировал его функцию распаковки LZ4.
В конце статьи вы найдете упаковщик, который сжимает данные по алгоритму LZ4. К нему прилагался bat-файл для демонстрации возможностей, меня удивило, что, несмотря на возможность прямой упаковки файлов, в нем сжимаемый файл передается упаковщику в качестве потока данных через STDIN. Выяснилось, что при упаковке файлов в начало упакованных данных добавляется маркер 03 21 4C 18, а при упаковке потока данных маркер меняется на 02 21 4C 18. На сайте с документацией по LZ4 такой маркер заявлен как устаревший формат блоков упакованных данных, который использовался в ранних версиях алгоритма. Но, как ни странно, при тестировании упаковщика на текстовых данных именно этот формат дал наибольшую степень сжатия. Да и на прочих данных результат оказался ничуть не хуже нового формата. Алгоритм распаковки для нового и старого формата отличается, поэтому функция распаковки учитывает этот момент. Соответственно, упакованные данные должны быть в legacy-формате. Более высокое сжатие дает упаковщик LZ4X от Ильи Муравьева, причем маркер в заголовке сжатых данных записывается правильный.
Code (Assembler) : Убрать нумерацию
- ;------------------------------------------------------------
- ; Распаковка данных в формате LZ4
- ;------------------------------------------------------------
- ; Оригинальный код: Jim Leonard, модификация: ManHunter / PCL
- ;------------------------------------------------------------
- ; На входе:
- ; lpCompressed - указатель на упакованные данные
- ; lpOut - указатель на буфер для распакованных данных
- ; На выходе:
- ; EAX = размер распакованных данных
- ;------------------------------------------------------------
- proc lz4_unpack lpCompressed:DWORD, lpOut:DWORD
- pusha
- mov esi,[lpCompressed]
- mov edi,[lpOut]
- lodsd
- cmp eax,184C2102h
- jne .done
- lodsd
- mov ebx,eax
- add ebx,esi
- xor eax,eax
- xor ecx,ecx
- .parsetoken:
- lodsb
- movzx edx,al
- .copyliterals:
- mov ecx,4
- shr al,cl
- call .buildfullcount
- .doliteralcopy:
- rep movsb
- cmp esi,ebx
- jae .done
- .copymatches:
- lodsw
- xchg edx,eax
- and al,0Fh
- call .buildfullcount
- .domatchcopy:
- push esi
- mov esi,edi
- sub esi,edx
- add ecx,4
- rep movsb
- pop esi
- jmp .parsetoken
- .buildfullcount:
- cmp al,0Fh
- xchg ecx,eax
- jne .builddone
- .buildloop:
- lodsb
- add ecx,eax
- cmp al,0FFh
- je .buildloop
- .builddone:
- retn
- .done:
- sub edi,[lpOut]
- mov [esp+28],edi
- popa
- ret
- endp
В заголовке LZ4 хранится информация о размере упакованных данных, но нет никакой информации об исходных данных. Хорошо, когда размер распакованных данных заранее известен или оперативную память под них можно выделить с запасом. Но если объем данных заранее не определен, то перед распаковкой его надо обязательно узнать. Для таких случаев я немного модифицировал функцию распаковки, чтобы она просто возвращала число байт, которые были бы заполнены в памяти после декомпрессии.
Code (Assembler) : Убрать нумерацию
- ;---------------------------------------------------
- ; Получение размера распакованных данных
- ;---------------------------------------------------
- ; На входе:
- ; lpCompressed - указатель на упакованные данные
- ; На выходе:
- ; EAX = размер распакованных данных
- ;---------------------------------------------------
- proc lz4_size lpCompressed:DWORD
- pusha
- mov esi,[lpCompressed]
- xor edi,edi
- lodsd
- cmp eax,184C2102h
- jne .done
- lodsd
- mov ebx,eax
- add ebx,esi
- xor eax,eax
- xor ecx,ecx
- .parsetoken:
- lodsb
- movzx edx,al
- .copyliterals:
- mov ecx,4
- shr al,cl
- call .buildfullcount
- .doliteralcopy:
- add edi,ecx
- add esi,ecx
- cmp esi,ebx
- jae .done
- .copymatches:
- lodsw
- xchg edx,eax
- and al,0Fh
- call .buildfullcount
- .domatchcopy:
- add ecx,4
- add edi,ecx
- jmp .parsetoken
- .buildfullcount:
- cmp al,0Fh
- xchg ecx,eax
- jne .builddone
- .buildloop:
- lodsb
- add ecx,eax
- cmp al,0FFh
- je .buildloop
- .builddone:
- retn
- .done:
- ; EAX = размер распакованных данных
- mov [esp+28], edi
- popa
- ret
- endp
В приложении пример программы с исходным текстом, которая извлекает из памяти иконку, упакованную по алгоритму LZ4, и выводит ее на форму. Там же в архиве оригинал ассемблерной функции распаковки от Jim Leonard, упаковщик LZ4 от Yann Collet с примерами и упаковщик LZ4X от Ильи Муравьева.
Просмотров: 1528 | Комментариев: 8
Метки: Assembler, распаковка

Внимание! Статья опубликована больше года назад, информация могла устареть!
Комментарии
Отзывы посетителей сайта о статье

ManHunter
(24.09.2022 в 13:12):
Да когда это 1.9.2 новомодным стал? Всегда был в тренде 2.6.1 или, на крайний случай 1.3. Необычайно информативный комментарий.

g0b
(24.09.2022 в 10:22):
Это все здорово, но вот с новомодным 1.9.2 не пашет.

user
(29.06.2021 в 11:06):
--Добавлено--
Ну, после небольших правок пример распаковывает и этот "XALZ" формат.
Респект.
Ну, после небольших правок пример распаковывает и этот "XALZ" формат.
Респект.

user
(29.06.2021 в 01:45):
Есть ещё такой Xamarin XALZ формат,
пакуют им, полдецы, PE-DLL'и для Android'а.
Вроде используется LZ4, но с заголовками непонятно.
Столкнулся с этой пакостью, надо распаковать и запаковать обратно,
пока ещё не решил как
пакуют им, полдецы, PE-DLL'и для Android'а.
Вроде используется LZ4, но с заголовками непонятно.
Столкнулся с этой пакостью, надо распаковать и запаковать обратно,
пока ещё не решил как

котя
(12.03.2021 в 17:07):
Ого, бомбаракета.
Автору - низкий поклон.
Забрал под unix
Автору - низкий поклон.
Забрал под unix

ManHunter
(11.12.2020 в 10:04):
Только если будет пробегать рядом вместе с толковой документацией. Специально искать не планирую.
Так что отрезаешь заголовок и распаковываешь обычным LZ4. Может быть.
Так что отрезаешь заголовок и распаковываешь обычным LZ4. Может быть.

JSONLZ4
(11.12.2020 в 03:57):
А как насчет распаковки мозилловского формата JSONLZ4?
Там вроде LZ4 модифицированный какой-то
Там вроде LZ4 модифицированный какой-то

Добавить комментарий
Заполните форму для добавления комментария

пардон, 1.9.4 - актуальная версия. 2.6.1 где-то на гитхабе в подвале