pker:
再贴一个compression engine吧
这个也是刚放假写的,思想和benny的一样,但在encode和decode上对bit stream的处理不同(compress的第5步和整个decompress过程),我觉得还是我这个更好理解写,嘻嘻~~~~~~不过效率会低一些。。。
正准备写个别的算法的。。。。。。。不过还没想好,呵呵
;
; pker's Compression Engine for Win32
; -- Semi-Huffman Algorithm Version
; ===================================
;
;
; Description
; -----------
;
; Well, this is my first compression engine. This idea of this engine is from
; Benny's BCE32 (thnx Benny : P), BUT they not the same, NOT even a little. In
; this engine, I also compress bit groupz instead of bytez and I use the semi-
; huffman (I think this is a good name for that code :P) code which shows below,
; the diagram is ordered by frequency of each bit group of the byte streamz:
;
; frequency code
; --------- ----
;
; 0. highest 1B
; 1. 2nd highest 01B
; 2. 2nd lowest 001B
; 3. lowest 000B
;
; Now, I'll explain the biggest difference between this engine and the BCE32. I
; think an example is very good enough:
;
; Suppose we're gonna compress the byte stream: ADCBD, it's binary code shows
; blow:
;
; 0110 0101 0110 1000 0110 0111 0110 0110 0110 1000
; A D C B D
;
; Now, calculate the frequency of bit groupz and determin the semi-huffman code:
;
; Group Count Semi-Huffman Code
; ----- ----- -----------------
;
; 00 2 001
; 01 9 1
; 10 8 01
; 11 1 000
;
; Now comes the good part :D. Usually, also in BCE32, we regard the left side of
; the huffman code as the higher bitz, so we should encode the stream above like
; this (output in bytez):
;
; byte stream:
; 1101 1001 0101 1000 1011 0110 1100 1010 0000 0011
;
;
; When decoding, we have to read from the high bit to the low one. But remember,
; the lower byte's lowest bit is connected with the higher byte's highest bit in
; the semi-huffman code and the lower byte's highest bit is connected with the
; higher byte's lowest bit... @#$%%&, what a mess... I don't like that kind of
; thing. So, I decided to regard the right side of the semi-huffman code as the
; higher bit and fill the bit stream from lwo to high, bit to bit. According
; to the example above, my engine will output the encoded bit stream like this:
;
; bit stream:
; 1101100101011000101101101100101011
;
; With this bit stream, we just read bit after bit from the bit stream can we
; finally decompress the code. This process is much more clearer to me :D
;
;
; How to use it?
; --------------
;
; This engine is designed for FASM. Before using it, like BCE32, you have to
; prepare to bufferz to save the frequency and the sorted frequency. In order to
; use this engine properly, do the following:
;
; freq dd 0,0,0,0
; freq_s dd 0,0,0,0
; compressed: times compressed_size db 0
; decompressed: times buf_size db 0
;
; ; ------------ compression ------------
;
; compress: mov esi,buffer
; mov edi,compressed
; mov ecx,buf_size
; mov ebx,freq
; mov edx,freq_s
; call __pkce32_compress
;
; ; ----------- decompression -----------
;
; decompress: mov esi,compressed
; mov edi,decompressed
; mov ecx,real_compressed_size
; call __pkce32_decompress
;
; Like what I've said, this engine is designed for FASM. But by adding PTR dire-
; ctive to every DWORD or WORD or BYTE directive, the engine could also use with
; TASM or MASM :)
;
; Enjoy it!!!
;
;
; Copyright
; ---------
;
; (c) 2004. No rightz reserved. Use without permission :P.
;
;
; __pkce32_compress procedure
; ===========================
;
;
; Parameterz and Return Valuez
; ----------------------------
;
; Input:
; esi --- points to data which will be compressed
; edi --- points to a buffer to save compressed data
; ecx --- size of the buffer, which will be compressed
; ebx --- points to the word frequency buffer 1
; edx --- points to the word frequency buffer 2, this buffer is different
; with buffer 1, 'cause this buffer will hold the sorted frequency
; data. it will be used to determine which double-bitz has the
; highest frequency, which the second highest...which...
;
; Output:
; eax --- the compressed data length
; CF --- CF = 0, positive compression
; CF = 1, negative compression
;
__pkce32_compress:
pushad
;
; 1st step...
; calculate how many timez 00, 01, 10, 11 appears, and save each number in the
; corresponding buffer (4 dwordz altogether)
;
pkc_next_byte: push ecx
push 4
pop ecx ; 2 bitz * 4 = 1 byte
xor eax,eax
lodsb
pkc_next_2bitz: mov ebp,eax
and ebp,3 ; lowest 2 bitz
inc dword [ebx+4*ebp] ; increase the corresponding
inc dword [edx+4*ebp] ; frequency field
shr eax,2 ; next 2 bitz
loop pkc_next_2bitz
pop ecx
loop pkc_next_byte
;
; 2nd step...
; we don't allow same frequency number exists, so we have to distinguish them
;
push 4
pop ecx
pkc_c_index: xor ebp,ebp ; use EBP as index
pkc_dif_l: mov eax,[ebx+4*ebp]
inc dword [ebx+4*ebp] ; differ from myself...
inc dword [edx+4*ebp] ; ...
cmp eax,[ebx] ; same as 1st number?
jz pkc_d_inc
cmp eax,[ebx+4] ; same as 2nd number?
jz pkc_d_inc
cmp eax,[ebx+8] ; same as 3rd number?
jz pkc_d_inc
cmp eax,[ebx+12] ; same as 4th number?
jnz pkc_d_ninc
pkc_d_inc: inc dword [ebx+4*ebp] ; distinguishing
inc dword [edx+4*ebp] ; ...
inc ecx ; have to loop one more time
pkc_d_ninc: cmp bp,3
jz pkc_c_index
inc ebp
loop pkc_dif_l
;
; 3rd step...
; bubble sort the frequency buffer (4 dwordz), save the result in buffer 2
;
mov esi,edx ; let both ESI n' EDI
mov edi,edx ; point to the buffer
push 3 ; and sortin' beginz...
pop ecx
pkc_sort_ol: push ecx
push esi
push edi
pkc_sort_il: lodsd ; load a DWORD
xchg eax,ebp
lodsd ; another DWORD
lea esi,[esi-4] ; back up 4 bytez
cmp ebp,eax
jg pkc_no_swap
stosd ; exchange the two DWORDz
xchg ebp,eax ; ...
stosd ; ...
lea edi,[edi-8] ; and EDI + 4
pkc_no_swap: lea edi,[edi+4] ; ...
loop pkc_sort_il
pop edi
pop esi
pop ecx
loop pkc_sort_ol
;
; 4th step...
; check the relationship of the two bufferz, perparing for encoding
;
push 4
pop ecx
mov esi,ebx ; unsorted buffer
xor ebp,ebp ; initialize the index
push ebp ; squeeze out a DWORD
; in the stack...
pkc_cr_l: xor ebx,ebx
push esi ; save it
mov edi,[edx+4*ebp]
lodsd
cmp edi,eax
jz pkc_cr_wr_en
inc ebx
lodsd
cmp edi,eax
jz pkc_cr_wr_en
inc ebx
lodsd
cmp edi,eax
jz pkc_cr_wr_en
inc ebx
pkc_cr_wr_en: pop esi ; restore
shl dword [esp],2 ; save the encrypt code
or [esp],bl
inc ebp
loop pkc_cr_l
pop ebx ; now EBX containz the
; encrypt code...
;
; 5th step...
; we could encode the buffer finally, i will encode the buffer with the code
; shows below according to the frequency:
;
; frequency code
; --------- ----
;
; 0. highest 1B
; 1. 2nd highest 01B
; 2. 2nd lowest 001B
; 3. lowest 000B
;
mov ecx,[esp+24] ; restore ECX
inc ecx
mov esi,[esp+4] ; restore ESI
mov edi,[esp] ; restore EDI
mov al,bl ; decrypt code
stosb ; store...
xor ebp,ebp
xor eax,eax
cdq
pkc_ec_nxt_byt: lodsb ; load one byte to encode
xchg bp,ax ; save it
push ecx ; save byte counter
push 4
pop ecx
pkc_ec_nxt_btz: mov ax,bp ; restore the byte to encode
shr bp,2
and al,3 ; use lowest 2-bitz
mov dl,bl ; get encrypt code
and dl,3 ; also keep lowest 2-bitz
cmp al,dl
jz pkc_ec_3 ; lowest-freq. bit-group
mov dl,bl ; restore
shr dl,2 ; next bit-group
and dl,3
cmp al,dl
jz pkc_ec_2 ; 2nd-lowest-freq. bit-group
mov dl,bl
shr dl,4
and dl,3
cmp al,dl
jz pkc_ec_1 ; 2nd-highest-freq. bit-group
pkc_ec_0: stc ; highest-freq. bit-group
call pkc_ec_wr
pkc_ec_rl: loop pkc_ec_nxt_btz
pop ecx ; restore byte counter
loop pkc_ec_nxt_byt
sub edi,[esp] ; cal. new size
mov [esp+28],edi ; save new size
popad
cmp eax,ecx ; negative compress?
jg pkc_ec_neg
clc
ret
pkc_ec_neg: stc
ret
pkc_ec_1: clc
call pkc_ec_wr
jmp pkc_ec_0
pkc_ec_2: clc
call pkc_ec_wr
jmp pkc_ec_1
pkc_ec_3: clc
call pkc_ec_wr
clc
call pkc_ec_wr
clc
call pkc_ec_wr
jmp pkc_ec_rl
;
; This procedure use CF as bit stream to construct output (edcoded) bit stream,
; use high word of EBP as bit counter, and output the bit stream when it equalz
; 8 (1 byte). The bit stream will be saved in the high byte of EAX.
;
pkc_ec_wr: rcl edx,1 ; get one bit
and edx,1
push ecx ; save byte counter
rol ebp,16 ; bit counter :P
push 8
pop ecx
sub cx,bp
inc bp ; increase it
rol eax,cl ; bit stream
or al,dl ; one more bit
cmp bp,8 ; finish one byte?
jnz pkc_ec_no_out
rol eax,7
stosb
xor eax,eax
xor bp,bp ; clear bit counter
pkc_ec_no_out: ror eax,cl ; restore EAX
ror ebp,16 ; restore EBP
pop ecx ; restore byte counter
ret
;
; __pkce32_decompress procedure
; =============================
;
;
; Parameterz and Return Valuez
; ----------------------------
;
; Input:
; esi --- points to data which will be decompressed
; edi --- points to a buffer to save decompressed data
; ecx --- size of the buffer, which will be decompressed
;
; Output:
; eax --- size of decompressed buffer
;
__pkce32_decompress:
pushad
xor eax,eax
lodsb ; load the decrypt code
mov ebx,eax ; let EBX hold it
xor ebp,ebp ; clear bit counter
cdq
call pkc_dc_load ; get one byte to decode
pkc_dc_bit_alz: shr al,1
rcl dl,1 ; move the bit to DL
inc bp
cmp bp,8
jnz pkc_dc_b0
call pkc_dc_load
pkc_dc_b0: test dl,1
jnz pkc_dc_0
shr al,1
rcl dl,1
inc bp
cmp bp,8
jnz pkc_dc_b1
call pkc_dc_load
pkc_dc_b1: test dl,1
jnz pkc_dc_1
shr al,1
rcl dl,1
inc bp
cmp bp,8
jnz pkc_dc_b2
call pkc_dc_load
pkc_dc_b2: test dl,1
jnz pkc_dc_2
mov dl,bl ; lowest
and dl,3
call pkc_dc_wr
pkc_dc_lr: or ecx,ecx
jz pkc_dc_ret
jmp pkc_dc_bit_alz
pkc_dc_ret: sub edi,[esp] ; cal. new size
mov [esp+28],edi
popad
ret
pkc_dc_2: mov dl,bl ; 2nd-lowest
shr dl,2
and dl,3
call pkc_dc_wr
jmp pkc_dc_lr
pkc_dc_1: mov dl,bl ; 2nd-highest
shr dl,4
and dl,3
call pkc_dc_wr
jmp pkc_dc_lr
pkc_dc_0: mov dl,bl ; highest
shr dl,6
call pkc_dc_wr
jmp pkc_dc_lr
pkc_dc_load: pushf
lodsb ; load one byte
xor bp,bp
dec ecx
popf
ret
;
; This procedure BH as bit stream, high byte of EBP as bitz counter. The lowest
; byte of EBP is also a bit counter that used somewhere else.
;
pkc_dc_wr: ror dx,2
rol ebp,16 ; get the bit counter
inc bp ; increase a bit
cmp bp,4 ; bit stream full?
jnz pkc_dc_no_out
xchg dh,al ; output bit stream
stosb ; ...
xchg dh,al ; ...
xor bp,bp ; clear bit counter
xor dh,dh ; clear bit stream
pkc_dc_no_out: ror ebp,16 ; restore EBP
ret