Упаковка и распаковка данных в формате LZ48 на Ассемблере
Упаковка и распаковка данных в формате LZ48 на Ассемблере
На просторах этих ваших интернетов обнаружился вот такой простенький алгоритм упаковки, обозначенный автором как LZ48. Собственно, это даже не совсем алгоритм, а исходник утилиты для упаковки и распаковки данных за авторством roudoudou. Больше никакой информации нет, поэтому пояснить, что означают цифры "48", я не могу, все вопросы к автору, если, конечно, найдете первоисточник. Вряд ли вы когда-нибудь встретите этот упаковщик в "дикой природе", но для коллекции сгодится. Да и для какой-нибудь лабораторной работы тоже может оказаться полезным.
Для упаковки и распаковки файлов используется упомянутая утилита, я ее скомпилировал из оригинального исходника в исполняемый файл. Степень сжатия по сравнению с другими алгоритмами слабенькая, тем не менее, это вполне себе полноценный алгоритм компрессии, имеющий право на существование. Текстовые файлы жмет примерно в половину, с бинарниками и уникальными наборами данных ситуация похуже. Скорость обработки данных как на упаковку, так и на распаковку, очень хорошая. Я портировал алгоритм распаковки с языка C на Ассемблер, вот что у меня получилось.
Code (Assembler) : Убрать нумерацию
- ;------------------------------------------------------------
- ; Распаковка данных в формате LZ48
- ; by ManHunter / PCL (www.manhunter.ru)
- ;------------------------------------------------------------
- ; На входе:
- ; lpCompressed - указатель на упакованные данные
- ; lpOut - указатель на буфер для распакованных данных
- ; На выходе:
- ; EAX = размер распакованных данных
- ;------------------------------------------------------------
- proc lz48_unpack lpCompressed:DWORD, lpOut:DWORD
- pusha
- mov esi,[lpCompressed]
- mov edi,[lpOut]
- xor eax,eax
- movsb
- .lz48_unpack:
- lodsb
- movzx ecx,al
- mov edx,ecx
- and cl,0xF0
- shr cl,4
- and dl,0x0F
- cmp cl,15
- jne .lz48_unpack_1
- .lz48_literal:
- lodsb
- add ecx,eax
- cmp al,255
- je .lz48_literal
- .lz48_unpack_1:
- rep movsb
- cmp dl,15
- jne .lz48_unpack_2
- .lz48_matchkey:
- lodsb
- add edx,eax
- cmp al,255
- je .lz48_matchkey
- .lz48_unpack_2:
- inc edx
- inc edx
- inc edx
- lodsb
- cmp al,0xFF
- je .done
- inc eax
- push esi
- mov esi,edi
- sub esi,eax
- mov ecx,edx
- rep movsb
- pop esi
- jmp .lz48_unpack
- .done:
- sub edi,[lpOut]
- mov [esp+28],edi
- popa
- ret
- endp
Если посмотреть исходник упаковщика, то там обнаружится код, который определяет размер буфера для приема распакованных данных. Поскольку больше никакой информации о размере исходных данных не сохраняется, придется им воспользоваться. Этот код я тоже портировал на Ассемблер:
Code (Assembler) : Убрать нумерацию
- ;---------------------------------------------------
- ; Получение размера распакованных данных
- ;---------------------------------------------------
- ; На входе:
- ; lpCompressed - указатель на упакованные данные
- ; На выходе:
- ; EAX = размер распакованных данных
- ;---------------------------------------------------
- proc lz48_size lpCompressed:DWORD
- pusha
- mov esi,[lpCompressed]
- inc esi
- xor edi,edi
- inc edi
- xor eax,eax
- .lz48_size:
- lodsb
- movzx ecx,al
- mov edx,ecx
- and cl,0xF0
- shr cl,4
- and dl,0x0F
- cmp cl,15
- jne .lz48_size_1
- .lz48_size_literal:
- lodsb
- add ecx,eax
- cmp al,255
- je .lz48_size_literal
- .lz48_size_1:
- add edi,ecx
- add esi,ecx
- cmp dl,15
- jne .lz48_size_2
- .lz48_size_matchkey:
- lodsb
- add edx,eax
- cmp al,255
- je .lz48_size_matchkey
- .lz48_size_2:
- inc edx
- inc edx
- inc edx
- cmp byte[esi],0xFF
- je .done
- inc esi
- add edi,edx
- jmp .lz48_size
- .done:
- ; EAX = размер распакованных данных
- mov [esp+28],edi
- popa
- ret
- endp
Функция упаковки данных в формате LZ48. За основу взят все тот же исходник. На оптимизацию особо не налегал, кроме уж совсем очевидных мест, в остальном чистый подстрочник. Уверен, можно сделать и получше. Скорость работы при этом хорошая.
Code (Assembler) : Убрать нумерацию
- ;------------------------------------------------------------
- ; Упаковка данных в формате LZ48
- ; by ManHunter / PCL (www.manhunter.ru)
- ;------------------------------------------------------------
- ; На входе:
- ; lpOut - указатель на исходные данные
- ; dLen - размер исходных данных
- ; lpCompressed - указатель на буфер для упакованных данных
- ; На выходе:
- ; EAX = размер упакованных данных
- ;------------------------------------------------------------
- proc lz48_pack lpInput:DWORD,dLen:DWORD,lpCompressed:DWORD
- locals
- startscan dd ?
- current dd ?
- curscan dd ?
- maxoffset dd ?
- maxlength dd ?
- matchlength dd ?
- literal dd ?
- literaloffset dd ?
- endl
- pusha
- mov esi,[lpInput]
- mov edi,[lpCompressed]
- mov [current],1
- mov [literal],0
- mov [literaloffset],1
- ; ioutput=1
- xor ebx,ebx
- inc ebx
- ; first byte always literal
- movsb
- dec esi
- dec edi
- ; force short data encoding
- cmp [dLen],5
- jae .lz48_encode_loop
- movsb
- mov eax,[dLen]
- dec eax
- mov ecx,eax
- shl al,4
- stosb
- rep movsb
- mov al,0xFF
- stosb
- add ebx,[dLen]
- inc ebx
- jmp .loc_done
- .lz48_encode_loop:
- ; while (current<length) {
- mov eax,[dLen]
- cmp [current],eax
- jae .lz48_encode_loop_done
- ; maxlength=0
- mov [maxlength],0
- ; startscan=current-255
- ; if (startscan<0) startscan=0
- mov [startscan],0
- mov eax,[current]
- sub eax,255
- js .lz48_encode_01
- mov [startscan],eax
- .lz48_encode_01:
- ; while (startscan<current) {
- mov eax,[current]
- cmp [startscan],eax
- jae .lz48_encode_11
- ; matchlength=0
- mov [matchlength],0
- ; curscan=current
- mov eax,[current]
- mov [curscan],eax
- ; for (i=startscan;curscan<length;i++) {
- mov ecx,[startscan]
- .lz48_encode_20:
- mov eax,[dLen]
- cmp [curscan],eax
- jae .lz48_encode_22
- ; if (data[i]==data[curscan++])
- mov eax,[curscan]
- mov al,byte[esi+eax]
- inc [curscan]
- cmp al,byte[esi+ecx]
- jne .lz48_encode_22
- ; matchlength++
- inc [matchlength]
- inc ecx
- jmp .lz48_encode_20
- ; }
- .lz48_encode_22:
- ; if (matchlength>=3 && matchlength>maxlength) {
- mov eax,[matchlength]
- cmp eax,3
- jb .lz48_encode_21
- cmp eax,[maxlength]
- jbe .lz48_encode_21
- ; maxlength=matchlength
- mov [maxlength],eax
- ; maxoffset=startscan
- mov eax,[startscan]
- mov [maxoffset],eax
- .lz48_encode_21:
- ; }
- ; startscan++;
- inc [startscan]
- jmp .lz48_encode_01
- ; }
- .lz48_encode_11:
- ; if (maxlength) {
- cmp [maxlength],0
- je .lz48_encode_31
- ; ioutput+=LZ48_encode_block(odata+ioutput,data,
- ; literaloffset,literal,current-maxoffset,maxlength)
- push [maxlength]
- mov eax,[current]
- sub eax,[maxoffset]
- push eax
- push [literal]
- push [literaloffset]
- push esi
- mov eax,edi
- add eax,ebx
- push eax
- stdcall LZ48_encode_block
- add ebx,eax
- ; current+=maxlength
- mov eax,[maxlength]
- add eax,[current]
- mov [current],eax
- ; literaloffset=current
- mov [literaloffset],eax
- ; literal=0
- mov [literal],0
- jmp .lz48_encode_32
- .lz48_encode_31:
- ; } else {
- ; literal++;
- inc [literal]
- ; current++;
- inc [current]
- .lz48_encode_32:
- ; }
- jmp .lz48_encode_loop
- .lz48_encode_loop_done:
- ; ioutput+=LZ48_encode_block(odata+ioutput,data,
- ; literaloffset,literal,0,0)
- push 0
- push 0
- push [literal]
- push [literaloffset]
- push esi
- mov eax,edi
- add eax,ebx
- push eax
- stdcall LZ48_encode_block
- add ebx,eax
- .loc_done:
- mov [esp+28],ebx
- popa
- ret
- endp
- proc LZ48_encode_extended_length lpCompressed:DWORD, dLen:DWORD
- pusha
- mov eax,[dLen]
- mov edi,[lpCompressed]
- ; int ioutput=0
- xor ebx,ebx
- xor ecx,ecx
- dec cl
- ; while (length>255) {
- .leel_01:
- cmp eax,ecx
- jbe .leel_02
- ; odata[ioutput++]=0xFF
- mov byte [edi+ebx],cl
- inc ebx
- ; length-=255
- sub eax,ecx
- jmp .leel_01
- .leel_02:
- ; odata[ioutput++]=length
- mov byte [edi+ebx],al
- inc ebx
- mov [esp+28],ebx
- popa
- ret
- endp
- proc LZ48_encode_block lpCompressed:DWORD, lpData:DWORD,\
- literaloffset:DWORD, literalcpt:DWORD, _offset:DWORD,\
- maxlength:DWORD
- pusha
- mov edi,[lpCompressed]
- ; int ioutput=1
- xor ebx,ebx
- inc ebx
- ; int token=0
- xor edx,edx
- ; if (literalcpt<15) {
- cmp [literalcpt],15
- jae .leb_01
- ; token=literalcpt<<4
- mov edx,[literalcpt]
- shl edx,4
- jmp .leb_02
- .leb_01:
- ; } else {
- ; token=0xF0
- mov dl,0xF0
- ; ioutput+=LZ48_encode_extended_length(odata+ioutput,literalcpt-15)
- mov eax,[literalcpt]
- sub eax,15
- push eax
- mov eax,edi
- add eax,ebx
- push eax
- stdcall LZ48_encode_extended_length
- add ebx,eax
- .leb_02:
- ; }
- ; for (i=0;i<literalcpt;i++) odata[ioutput++]=data[literaloffset++]
- xor ecx,ecx
- mov esi,[lpData]
- .leb_11:
- cmp ecx,[literalcpt]
- jae .leb_12
- mov eax,[literaloffset]
- mov al,byte[esi+eax]
- inc [literaloffset]
- mov byte[edi+ebx],al
- inc ebx
- inc ecx
- jmp .leb_11
- .leb_12:
- ; if (maxlength<18) {
- cmp [maxlength],18
- jae .leb_21
- ; if (maxlength>2) {
- cmp [maxlength],2
- jbe .leb_22
- ; token|=(maxlength-3)
- mov eax,[maxlength]
- sub eax,3
- or edx,eax
- ; }
- jmp .leb_22
- .leb_21:
- ; } else {
- ; token|=0xF
- or dl,0xF
- ; ioutput+=LZ48_encode_extended_length(odata+ioutput,maxlength-18)
- mov eax,[maxlength]
- sub eax,18
- push eax
- mov eax,edi
- add eax,ebx
- push eax
- stdcall LZ48_encode_extended_length
- add ebx,eax
- .leb_22:
- ; }
- ; odata[ioutput++]=offset-1
- mov eax,[_offset]
- dec eax
- mov byte [edi+ebx],al
- inc ebx
- ; odata[0]=token
- mov byte [edi],dl
- ; return ioutput
- mov [esp+28],ebx
- popa
- ret
- endp
В приложении примеры программ с исходными текстами. Это простейший упаковщик данных в формат LZ48, работающий через командную строку, и программа, которая извлекает из памяти иконку, упакованную по алгоритму LZ48, а затем выводит ее на форму. Там же в архиве утилита для упаковки и распаковки данных от roudoudou вместе с авторским исходником.
Просмотров: 1083 | Комментариев: 0
Метки: Assembler, распаковка
Внимание! Статья опубликована больше года назад, информация могла устареть!
Комментарии
Отзывы посетителей сайта о статье
Комментариeв нет
Добавить комментарий
Заполните форму для добавления комментария