830c466a98
git-svn-id: svn://kolibrios.org@6639 a494cfbc-eb01-0410-851d-a64ba20cac60
2924 lines
81 KiB
NASM
2924 lines
81 KiB
NASM
; deflate.asm -- compress data using the deflation algorithm
|
|
; Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler
|
|
; For conditions of distribution and use, see copyright notice in zlib.inc
|
|
|
|
; ALGORITHM
|
|
|
|
; The "deflation" process depends on being able to identify portions
|
|
; of the input text which are identical to earlier input (within a
|
|
; sliding window trailing behind the input currently being processed).
|
|
|
|
; The most straightforward technique turns out to be the fastest for
|
|
; most input files: try all possible matches and select the longest.
|
|
; The key feature of this algorithm is that insertions into the string
|
|
; dictionary are very simple and thus fast, and deletions are avoided
|
|
; completely. Insertions are performed at each input character, whereas
|
|
; string matches are performed only when the previous match ends. So it
|
|
; is preferable to spend more time in matches to allow very fast string
|
|
; insertions and avoid deletions. The matching algorithm for small
|
|
; strings is inspired from that of Rabin & Karp. A brute force approach
|
|
; is used to find longer strings when a small match has been found.
|
|
; A similar algorithm is used in comic (by Jan-Mark Wams) and freeze
|
|
; (by Leonid Broukhis).
|
|
; A previous version of this file used a more sophisticated algorithm
|
|
; (by Fiala and Greene) which is guaranteed to run in linear amortized
|
|
; time, but has a larger average cost, uses more memory and is patented.
|
|
; However the F&G algorithm may be faster for some highly redundant
|
|
; files if the parameter max_chain_length (described below) is too large.
|
|
|
|
; ACKNOWLEDGEMENTS
|
|
|
|
; The idea of lazy evaluation of matches is due to Jan-Mark Wams, and
|
|
; I found it in 'freeze' written by Leonid Broukhis.
|
|
; Thanks to many people for bug reports and testing.
|
|
|
|
; REFERENCES
|
|
|
|
; Deutsch, L.P.,"DEFLATE Compressed Data Format Specification".
|
|
; Available in http://tools.ietf.org/html/rfc1951
|
|
|
|
; A description of the Rabin and Karp algorithm is given in the book
|
|
; "Algorithms" by R. Sedgewick, Addison-Wesley, p252.
|
|
|
|
; Fiala,E.R., and Greene,D.H.
|
|
; Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595
|
|
|
|
|
|
deflate_copyright db ' deflate 1.2.8 Copyright 1995-2013 Jean-loup Gailly and Mark Adler ',0
|
|
|
|
; If you use the zlib library in a product, an acknowledgment is welcome
|
|
; in the documentation of your product. If for some reason you cannot
|
|
; include such an acknowledgment, I would appreciate that you keep this
|
|
; copyright string in the executable of your product.
|
|
|
|
; ===========================================================================
|
|
; Function prototypes.
|
|
|
|
;enum block_state
|
|
need_more equ 1 ;block not completed, need more input or more output
|
|
block_done equ 2 ;block flush performed
|
|
finish_started equ 3 ;finish started, need only more output at next deflate
|
|
finish_done equ 4 ;finish done, accept no more input or output
|
|
|
|
; ===========================================================================
|
|
; Local data
|
|
|
|
NIL equ 0
|
|
; Tail of hash chains
|
|
|
|
TOO_FAR equ 4096
|
|
; Matches of length 3 are discarded if their distance exceeds TOO_FAR
|
|
|
|
; Values for max_lazy_match, good_match and max_chain_length, depending on
|
|
; the desired pack level (0..9). The values given below have been tuned to
|
|
; exclude worst case performance for pathological files. Better values may be
|
|
; found for specific files.
|
|
|
|
struct config_s ;config
|
|
good_length dw ? ;uint_16 ;reduce lazy search above this match length
|
|
max_lazy dw ? ;uint_16 ;do not perform lazy search above this match length
|
|
nice_length dw ? ;uint_16 ;quit search above this match length
|
|
max_chain dw ? ;uint_16
|
|
co_func dd ? ;compress_func
|
|
ends
|
|
|
|
align 16
|
|
configuration_table:
|
|
config_s 0, 0, 0, 0, deflate_stored ;store only
|
|
config_s 4, 4, 8, 4, deflate_fast ;max speed, no lazy matches
|
|
if FASTEST eq 0
|
|
config_s 4, 5, 16, 8, deflate_fast
|
|
config_s 4, 6, 32, 32, deflate_fast
|
|
config_s 4, 4, 16, 16, deflate_slow ;lazy matches
|
|
config_s 8, 16, 32, 32, deflate_slow
|
|
config_s 8, 16, 128, 128, deflate_slow
|
|
config_s 8, 32, 128, 256, deflate_slow
|
|
config_s 32, 128, 258, 1024, deflate_slow
|
|
config_s 32, 258, 258, 4096, deflate_slow ;max compression
|
|
end if
|
|
|
|
; Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4
|
|
; For deflate_fast() (levels <= 3) good is ignored and lazy has a different
|
|
; meaning.
|
|
|
|
|
|
EQUAL equ 0
|
|
; result of memcmp for equal strings
|
|
|
|
; rank Z_BLOCK between Z_NO_FLUSH and Z_PARTIAL_FLUSH
|
|
macro RANK f, reg
|
|
{
|
|
local .end0
|
|
xor reg,reg
|
|
cmp f,4
|
|
jle .end0
|
|
sub reg,9
|
|
.end0:
|
|
add reg,f
|
|
add reg,f
|
|
}
|
|
|
|
; ===========================================================================
|
|
; Update a hash value with the given input byte
|
|
; IN assertion: all calls to to UPDATE_HASH are made with consecutive
|
|
; input characters, so that a running hash key can be computed from the
|
|
; previous key instead of complete recalculation each time.
|
|
|
|
macro UPDATE_HASH s,h,c
|
|
{
|
|
push ebx ecx
|
|
mov ebx,h
|
|
mov ecx,[s+deflate_state.hash_shift]
|
|
shl ebx,cl
|
|
xor ebx,c
|
|
and ebx,[s+deflate_state.hash_mask]
|
|
mov h,ebx
|
|
pop ecx ebx
|
|
}
|
|
|
|
; ===========================================================================
|
|
; Insert string str in the dictionary and set match_head to the previous head
|
|
; of the hash chain (the most recent string with same hash key). Return
|
|
; the previous length of the hash chain.
|
|
; If this file is compiled with -DFASTEST, the compression level is forced
|
|
; to 1, and no hash chains are maintained.
|
|
; IN assertion: all calls to to INSERT_STRING are made with consecutive
|
|
; input characters and the first MIN_MATCH bytes of str are valid
|
|
; (except for the last MIN_MATCH-1 bytes of the input file).
|
|
|
|
macro INSERT_STRING s, str, match_head
|
|
{
|
|
mov eax,[s+deflate_state.window]
|
|
add eax,str
|
|
add eax,MIN_MATCH-1
|
|
movzx eax,byte[eax]
|
|
UPDATE_HASH s, [s+deflate_state.ins_h], eax
|
|
mov eax,[s+deflate_state.ins_h]
|
|
shl eax,2
|
|
add eax,[s+deflate_state.head]
|
|
mov eax,[eax]
|
|
mov match_head,eax
|
|
if FASTEST eq 0
|
|
push ebx
|
|
mov ebx,[s+deflate_state.w_mask]
|
|
and ebx,str
|
|
add ebx,[s+deflate_state.prev]
|
|
mov byte[ebx],al
|
|
pop ebx
|
|
end if
|
|
mov eax,[s+deflate_state.ins_h]
|
|
shl eax,2
|
|
add eax,[s+deflate_state.head]
|
|
push str
|
|
pop dword[eax]
|
|
}
|
|
|
|
; ===========================================================================
|
|
; Initialize the hash table (avoiding 64K overflow for 16 bit systems).
|
|
; prev[] will be initialized on the fly.
|
|
|
|
macro CLEAR_HASH s
|
|
{
|
|
mov eax,[s+deflate_state.hash_size]
|
|
dec eax
|
|
shl eax,2
|
|
add eax,[s+deflate_state.head]
|
|
mov dword[eax],NIL
|
|
mov eax,[s+deflate_state.hash_size]
|
|
dec eax
|
|
shl eax,2 ;sizeof(*s.head)
|
|
stdcall zmemzero, [s+deflate_state.head], eax
|
|
}
|
|
|
|
align 4
|
|
proc deflateInit, strm:dword, level:dword
|
|
stdcall deflateInit_, [strm], [level], ZLIB_VERSION, sizeof.z_stream
|
|
ret
|
|
endp
|
|
|
|
; =========================================================================
|
|
;int (strm, level, version, stream_size)
|
|
; z_streamp strm
|
|
; int level
|
|
; const char *version
|
|
; int stream_size
|
|
align 4
|
|
proc deflateInit_, strm:dword, level:dword, version:dword, stream_size:dword
|
|
stdcall deflateInit2_, [strm], [level], Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL,\
|
|
Z_DEFAULT_STRATEGY, [version], [stream_size]
|
|
; To do: ignore strm->next_in if we use it as window
|
|
ret
|
|
endp
|
|
|
|
align 4
|
|
proc deflateInit2, strm:dword, level:dword, method:dword, windowBits:dword, memLevel:dword, strategy:dword
|
|
stdcall deflateInit2_, [strm],[level],[method],[windowBits],[memLevel],\
|
|
[strategy], ZLIB_VERSION, sizeof.z_stream
|
|
ret
|
|
endp
|
|
|
|
; =========================================================================
|
|
;int (strm, level, method, windowBits, memLevel, strategy,
|
|
; version, stream_size)
|
|
; z_streamp strm
|
|
; int level
|
|
; int method
|
|
; int windowBits
|
|
; int memLevel
|
|
; int strategy
|
|
; const char *version
|
|
; int stream_size
|
|
align 4
|
|
proc deflateInit2_ uses ebx ecx edx edi, strm:dword, level:dword, method:dword,\
|
|
windowBits:dword, memLevel:dword, strategy:dword, version:dword, stream_size:dword
|
|
locals
|
|
wrap dd 1 ;int
|
|
overlay dd ? ;uint_16p
|
|
endl
|
|
; We overlay pending_buf and d_buf+l_buf. This works since the average
|
|
; output size for (length,distance) codes is <= 24 bits.
|
|
|
|
mov eax,[version]
|
|
cmp eax,Z_NULL
|
|
je @f
|
|
mov ebx,dword[ZLIB_VERSION]
|
|
cmp dword[eax],ebx
|
|
jne @f
|
|
cmp dword[stream_size],sizeof.z_stream
|
|
je .end0
|
|
@@: ;if (..==0 || ..[0]!=..[0] || ..!=..)
|
|
mov eax,Z_VERSION_ERROR
|
|
jmp .end_f
|
|
.end0:
|
|
mov ebx,[strm]
|
|
cmp ebx,Z_NULL
|
|
jne @f ;if (..==0) return ..
|
|
mov eax,Z_STREAM_ERROR
|
|
jmp .end_f
|
|
@@:
|
|
|
|
mov dword[ebx+z_stream.msg],Z_NULL
|
|
cmp dword[ebx+z_stream.zalloc],0
|
|
jne @f ;if (..==0)
|
|
if Z_SOLO eq 1
|
|
mov eax,Z_STREAM_ERROR
|
|
jmp .end_f
|
|
else
|
|
mov dword[ebx+z_stream.zalloc],zcalloc
|
|
mov dword[ebx+z_stream.opaque],0
|
|
end if
|
|
@@:
|
|
cmp dword[ebx+z_stream.zfree],0
|
|
jne @f ;if (..==0)
|
|
if Z_SOLO eq 1
|
|
mov eax,Z_STREAM_ERROR
|
|
jmp .end_f
|
|
else
|
|
mov dword[ebx+z_stream.zfree],zcfree
|
|
end if
|
|
@@:
|
|
|
|
if FASTEST eq 1
|
|
cmp dword[level],0
|
|
je @f ;if (..!=0)
|
|
mov dword[level],1
|
|
@@:
|
|
else
|
|
cmp dword[level],Z_DEFAULT_COMPRESSION
|
|
jne @f ;if (..==0)
|
|
mov dword[level],6
|
|
@@:
|
|
end if
|
|
|
|
cmp dword[windowBits],0
|
|
jge @f ;if (..<0) ;suppress zlib wrapper
|
|
mov dword[wrap],0
|
|
neg dword[windowBits]
|
|
inc dword[windowBits]
|
|
jmp .end1
|
|
@@:
|
|
if GZIP eq 1
|
|
cmp dword[windowBits],15
|
|
jle .end1 ;else if (..>15)
|
|
mov dword[wrap],2 ;write gzip wrapper instead
|
|
sub dword[windowBits],16
|
|
end if
|
|
.end1:
|
|
cmp dword[memLevel],1
|
|
jl .end2
|
|
cmp dword[memLevel],MAX_MEM_LEVEL
|
|
jg .end2
|
|
cmp dword[method],Z_DEFLATED
|
|
jne .end2
|
|
cmp dword[windowBits],8
|
|
jl .end2
|
|
cmp dword[windowBits],15
|
|
jg .end2
|
|
cmp dword[level],0
|
|
jl .end2
|
|
cmp dword[level],9
|
|
jg .end2
|
|
cmp dword[strategy],0
|
|
jl .end2
|
|
cmp dword[strategy],Z_FIXED
|
|
jle @f
|
|
.end2: ;if (..<.. || ..>.. || ..!=.. || ..<.. || ..>.. || ..<0 || ..>.. || ..<0 || ..>..)
|
|
mov eax,Z_STREAM_ERROR
|
|
jmp .end_f
|
|
@@:
|
|
cmp dword[windowBits],8
|
|
jne @f ;if (..==..)
|
|
inc dword[windowBits] ;until 256-byte window bug fixed
|
|
@@:
|
|
ZALLOC ebx, 1, sizeof.deflate_state
|
|
;eax = s
|
|
cmp eax,Z_NULL
|
|
jne @f ;if (..==0)
|
|
mov eax,Z_MEM_ERROR
|
|
jmp .end_f
|
|
@@:
|
|
mov edi,eax ;edi = s
|
|
mov [ebx+z_stream.state],edi
|
|
mov [edi+deflate_state.strm],ebx
|
|
|
|
mov eax,[wrap]
|
|
mov [edi+deflate_state.wrap],eax
|
|
mov [edi+deflate_state.gzhead],Z_NULL
|
|
mov ecx,[windowBits]
|
|
mov [edi+deflate_state.w_bits],ecx
|
|
xor eax,eax
|
|
inc eax
|
|
shl eax,cl
|
|
mov [edi+deflate_state.w_size],eax
|
|
dec eax
|
|
mov [edi+deflate_state.w_mask],eax
|
|
|
|
mov ecx,[memLevel]
|
|
add ecx,7
|
|
mov [edi+deflate_state.hash_bits],ecx
|
|
xor eax,eax
|
|
inc eax
|
|
shl eax,cl
|
|
mov [edi+deflate_state.hash_size],eax
|
|
dec eax
|
|
mov [edi+deflate_state.hash_mask],eax
|
|
add ecx,MIN_MATCH-1
|
|
xor edx,edx
|
|
mov eax,ecx
|
|
mov ecx,MIN_MATCH
|
|
div ecx
|
|
mov [edi+deflate_state.hash_shift],eax
|
|
|
|
ZALLOC ebx, [edi+deflate_state.w_size], 2 ;2*sizeof(Byte)
|
|
mov [edi+deflate_state.window],eax
|
|
ZALLOC ebx, [edi+deflate_state.w_size], 4 ;sizeof(Pos)
|
|
mov [edi+deflate_state.prev],eax
|
|
ZALLOC ebx, [edi+deflate_state.hash_size], 4 ;sizeof(Pos)
|
|
mov [edi+deflate_state.head],eax
|
|
|
|
mov dword[edi+deflate_state.high_water],0 ;nothing written to s->window yet
|
|
|
|
mov ecx,[memLevel]
|
|
add ecx,6
|
|
xor eax,eax
|
|
inc eax
|
|
shl eax,cl
|
|
mov [edi+deflate_state.lit_bufsize],eax ;16K elements by default
|
|
|
|
ZALLOC ebx, eax, 4 ;sizeof(uint_16)+2
|
|
mov [overlay],eax
|
|
mov [edi+deflate_state.pending_buf],eax
|
|
mov eax,[edi+deflate_state.lit_bufsize]
|
|
imul eax,4 ;sizeof(uint_16)+2
|
|
mov [edi+deflate_state.pending_buf_size],eax
|
|
|
|
cmp dword[edi+deflate_state.window],Z_NULL
|
|
je .end3
|
|
cmp dword[edi+deflate_state.prev],Z_NULL
|
|
je .end3
|
|
cmp dword[edi+deflate_state.head],Z_NULL
|
|
je .end3
|
|
cmp dword[edi+deflate_state.pending_buf],Z_NULL
|
|
je .end3
|
|
jmp @f
|
|
.end3: ;if (..==0 || ..==0 || ..==0 || ..==0)
|
|
mov dword[edi+deflate_state.status],FINISH_STATE
|
|
ERR_MSG Z_MEM_ERROR
|
|
mov [ebx+z_stream.msg],eax
|
|
stdcall deflateEnd, ebx
|
|
mov eax,Z_MEM_ERROR
|
|
jmp .end_f
|
|
@@:
|
|
mov eax,[edi+deflate_state.lit_bufsize]
|
|
shr eax,1 ;/=sizeof(uint_16)
|
|
add eax,[overlay]
|
|
mov [edi+deflate_state.d_buf],eax
|
|
mov eax,[edi+deflate_state.lit_bufsize]
|
|
imul eax,3 ;1+sizeof(uint_16)
|
|
add eax,[edi+deflate_state.pending_buf]
|
|
mov [edi+deflate_state.l_buf],eax
|
|
|
|
mov eax,[level]
|
|
mov [edi+deflate_state.level],ax
|
|
mov eax,[strategy]
|
|
mov [edi+deflate_state.strategy],ax
|
|
mov eax,[method]
|
|
mov [edi+deflate_state.method],al
|
|
|
|
stdcall deflateReset, ebx
|
|
.end_f:
|
|
zlib_debug 'deflateInit2_ strategy = %d',[strategy]
|
|
ret
|
|
endp
|
|
|
|
; =========================================================================
|
|
;int (strm, dictionary, dictLength)
|
|
; z_streamp strm
|
|
; const Bytef *dictionary
|
|
; uInt dictLength
|
|
align 4
|
|
proc deflateSetDictionary uses ebx edi, strm:dword, dictionary:dword, dictLength:dword
|
|
locals
|
|
; deflate_state *s;
|
|
; uInt str, n;
|
|
wrap dd ? ;int
|
|
avail dd ? ;unsigned
|
|
; z_const unsigned char *next;
|
|
endl
|
|
mov ebx,[strm]
|
|
cmp ebx,Z_NULL
|
|
je @f
|
|
mov edi,[ebx+z_stream.state]
|
|
cmp edi,Z_NULL
|
|
je @f
|
|
cmp dword[dictionary],Z_NULL
|
|
je @f ;if (..==0 || ..==0 || ..==0)
|
|
jmp .end0
|
|
@@:
|
|
mov eax,Z_STREAM_ERROR
|
|
jmp .end_f
|
|
.end0:
|
|
|
|
mov eax,[edi+deflate_state.wrap]
|
|
mov [wrap],eax
|
|
; if (wrap == 2 || (wrap == 1 && s->status != INIT_STATE) || s->lookahead)
|
|
; return Z_STREAM_ERROR;
|
|
|
|
; when using zlib wrappers, compute Adler-32 for provided dictionary
|
|
; if (wrap == 1)
|
|
; strm->adler = adler32(strm->adler, dictionary, dictLength);
|
|
; s->wrap = 0; /* avoid computing Adler-32 in read_buf */
|
|
|
|
; if dictionary would fill window, just replace the history
|
|
; if (dictLength >= s->w_size) {
|
|
; if (wrap == 0) { /* already empty otherwise */
|
|
; CLEAR_HASH(s);
|
|
; s->strstart = 0;
|
|
; s->block_start = 0L;
|
|
; s->insert = 0;
|
|
; }
|
|
; dictionary += dictLength - s->w_size; /* use the tail */
|
|
; dictLength = s->w_size;
|
|
; }
|
|
|
|
; insert dictionary into window and hash
|
|
; avail = strm->avail_in;
|
|
; next = strm->next_in;
|
|
; strm->avail_in = dictLength;
|
|
; strm->next_in = (z_const Bytef *)dictionary;
|
|
; fill_window(s);
|
|
; while (s->lookahead >= MIN_MATCH) {
|
|
; str = s->strstart;
|
|
; n = s->lookahead - (MIN_MATCH-1);
|
|
; do {
|
|
; UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]);
|
|
if FASTEST eq 0
|
|
; s->prev[str & s->w_mask] = s->head[s->ins_h];
|
|
end if
|
|
; s->head[s->ins_h] = (Pos)str;
|
|
; str++;
|
|
; } while (--n);
|
|
; s->strstart = str;
|
|
; s->lookahead = MIN_MATCH-1;
|
|
; fill_window(s);
|
|
; }
|
|
; s->strstart += s->lookahead;
|
|
; s->block_start = (long)s->strstart;
|
|
; s->insert = s->lookahead;
|
|
; s->lookahead = 0;
|
|
; s->match_length = s->prev_length = MIN_MATCH-1;
|
|
; s->match_available = 0;
|
|
; strm->next_in = next;
|
|
; strm->avail_in = avail;
|
|
; s->wrap = wrap;
|
|
mov eax,Z_OK
|
|
.end_f:
|
|
ret
|
|
endp
|
|
|
|
; =========================================================================
|
|
;int (strm)
|
|
; z_streamp strm
|
|
align 4
|
|
proc deflateResetKeep uses ebx edi, strm:dword
|
|
; deflate_state *s;
|
|
|
|
mov ebx,[strm]
|
|
cmp ebx,Z_NULL
|
|
je @f
|
|
mov edi,[ebx+z_stream.state]
|
|
cmp edi,Z_NULL
|
|
je @f
|
|
cmp dword[ebx+z_stream.zalloc],0
|
|
je @f
|
|
cmp dword[ebx+z_stream.zfree],0
|
|
je @f ;if (..==0 || ..==0 || ..==0 || ..==0)
|
|
jmp .end0
|
|
@@:
|
|
mov eax,Z_STREAM_ERROR
|
|
jmp .end_f
|
|
.end0:
|
|
|
|
mov dword[ebx+z_stream.total_out],0
|
|
mov dword[ebx+z_stream.total_in],0
|
|
mov dword[ebx+z_stream.msg],Z_NULL ;use zfree if we ever allocate msg dynamically
|
|
mov word[ebx+z_stream.data_type],Z_UNKNOWN
|
|
|
|
mov word[edi+deflate_state.pending],0
|
|
mov eax,[edi+deflate_state.pending_buf]
|
|
mov [edi+deflate_state.pending_out],eax
|
|
|
|
cmp dword[edi+deflate_state.wrap],0
|
|
jge @f ;if (..<0)
|
|
neg dword[edi+deflate_state.wrap]
|
|
inc dword[edi+deflate_state.wrap] ;was made negative by deflate(..., Z_FINISH)
|
|
@@:
|
|
mov eax,BUSY_STATE
|
|
cmp dword[edi+deflate_state.wrap],0
|
|
je @f
|
|
mov eax,INIT_STATE
|
|
@@:
|
|
mov dword[edi+deflate_state.status],eax
|
|
stdcall adler32, 0, Z_NULL, 0
|
|
if GZIP eq 1
|
|
cmp dword[edi+deflate_state.wrap],2
|
|
jne @f
|
|
xor eax,eax ;stdcall calc_crc32, 0, Z_NULL, 0
|
|
@@:
|
|
end if
|
|
mov dword[ebx+z_stream.adler],eax
|
|
mov dword[edi+deflate_state.last_flush],Z_NO_FLUSH
|
|
|
|
stdcall _tr_init, edi
|
|
|
|
mov eax,Z_OK
|
|
.end_f:
|
|
ret
|
|
endp
|
|
|
|
; =========================================================================
|
|
;int (strm)
|
|
; z_streamp strm
|
|
align 4
|
|
proc deflateReset uses ebx, strm:dword
|
|
mov ebx,[strm]
|
|
zlib_debug 'deflateReset'
|
|
stdcall deflateResetKeep, ebx
|
|
cmp eax,0
|
|
jne @f ;if (..==Z_OK)
|
|
stdcall lm_init, [ebx+z_stream.state]
|
|
@@:
|
|
ret
|
|
endp
|
|
|
|
; =========================================================================
|
|
;int (strm, head)
|
|
; z_streamp strm
|
|
; gz_headerp head
|
|
align 4
|
|
proc deflateSetHeader uses ebx, strm:dword, head:dword
|
|
mov ebx,[strm]
|
|
cmp ebx,Z_NULL
|
|
je @f
|
|
mov ebx,[ebx+z_stream.state]
|
|
cmp ebx,Z_NULL
|
|
jne .end0
|
|
@@: ;if (..==0 || ..==0) return ..
|
|
mov eax,Z_STREAM_ERROR
|
|
jmp .end_f
|
|
.end0:
|
|
cmp dword[ebx+deflate_state.wrap],2
|
|
je @f ;if (..!=..) return ..
|
|
mov eax,Z_STREAM_ERROR
|
|
jmp .end_f
|
|
@@:
|
|
mov eax,[head]
|
|
mov [ebx+deflate_state.gzhead],eax
|
|
mov eax,Z_OK
|
|
.end_f:
|
|
ret
|
|
endp
|
|
|
|
; =========================================================================
|
|
;int (strm, pending, bits)
|
|
; unsigned *pending
|
|
; int *bits
|
|
; z_streamp strm
|
|
align 4
|
|
proc deflatePending uses ebx edi, strm:dword, pending:dword, bits:dword
|
|
mov ebx,[strm]
|
|
cmp ebx,Z_NULL
|
|
je @f
|
|
mov edi,[ebx+z_stream.state]
|
|
cmp edi,Z_NULL
|
|
jne .end0
|
|
@@: ;if (..==0 || ..==0) return ..
|
|
mov eax,Z_STREAM_ERROR
|
|
jmp .end_f
|
|
.end0:
|
|
cmp dword[pending],Z_NULL
|
|
je @f ;if (..!=..)
|
|
mov eax,[pending]
|
|
movzx ebx,word[edi+deflate_state.pending]
|
|
mov [eax],ebx
|
|
@@:
|
|
cmp dword[bits],Z_NULL
|
|
je @f ;if (..!=..)
|
|
mov eax,[bits]
|
|
mov ebx,[edi+deflate_state.bi_valid]
|
|
mov [eax],ebx
|
|
@@:
|
|
mov eax,Z_OK
|
|
.end_f:
|
|
ret
|
|
endp
|
|
|
|
; =========================================================================
|
|
;int (strm, bits, value)
|
|
; z_streamp strm
|
|
; int bits
|
|
; int value
|
|
align 4
|
|
proc deflatePrime uses ebx edi, strm:dword, bits:dword, value:dword
|
|
; int put;
|
|
|
|
mov ebx,[strm]
|
|
cmp ebx,Z_NULL
|
|
je @f
|
|
mov edi,[ebx+z_stream.state] ;s = strm.state
|
|
cmp edi,Z_NULL
|
|
jne .end0
|
|
@@: ;if (..==0 || ..==0) return ..
|
|
mov eax,Z_STREAM_ERROR
|
|
jmp .end_f
|
|
.end0:
|
|
; if ((Bytef *)(s->d_buf) < s->pending_out + ((Buf_size + 7) >> 3))
|
|
; return Z_BUF_ERROR;
|
|
; do {
|
|
; put = Buf_size - s->bi_valid;
|
|
; if (put > bits)
|
|
; put = bits;
|
|
; s->bi_buf |= (uint_16)((value & ((1 << put) - 1)) << s->bi_valid);
|
|
; s->bi_valid += put;
|
|
; _tr_flush_bits(s);
|
|
; value >>= put;
|
|
; bits -= put;
|
|
; } while (bits);
|
|
mov eax,Z_OK
|
|
.end_f:
|
|
ret
|
|
endp
|
|
|
|
; =========================================================================
|
|
;int (strm, level, strategy)
|
|
; z_streamp strm
|
|
; int level
|
|
; int strategy
|
|
align 4
|
|
proc deflateParams uses ebx edi, strm:dword, level:dword, strategy:dword
|
|
; compress_func func;
|
|
; int err = Z_OK;
|
|
|
|
mov ebx,[strm]
|
|
cmp ebx,Z_NULL
|
|
je @f
|
|
mov edi,[ebx+z_stream.state] ;s = strm.state
|
|
cmp edi,Z_NULL
|
|
jne .end0
|
|
@@: ;if (..==0 || ..==0) return ..
|
|
mov eax,Z_STREAM_ERROR
|
|
jmp .end_f
|
|
.end0:
|
|
|
|
if FASTEST eq 1
|
|
cmp dword[level],0
|
|
je @f ;if (..!=0)
|
|
mov dword[level],1
|
|
@@:
|
|
else
|
|
cmp dword[level],Z_DEFAULT_COMPRESSION
|
|
jne @f ;if (..==0)
|
|
mov dword[level],6
|
|
@@:
|
|
end if
|
|
; if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) {
|
|
; return Z_STREAM_ERROR;
|
|
; }
|
|
; func = configuration_table[s->level].func;
|
|
|
|
; if ((strategy != s->strategy || func != configuration_table[level].func) &&
|
|
; strm->total_in != 0) {
|
|
; Flush the last buffer:
|
|
; err = deflate(strm, Z_BLOCK);
|
|
; if (err == Z_BUF_ERROR && s->pending == 0)
|
|
; err = Z_OK;
|
|
; }
|
|
; if (s->level != level) {
|
|
; s->level = level;
|
|
; s->max_lazy_match = configuration_table[level].max_lazy;
|
|
; s->good_match = configuration_table[level].good_length;
|
|
; s->nice_match = configuration_table[level].nice_length;
|
|
; s->max_chain_length = configuration_table[level].max_chain;
|
|
; }
|
|
; s->strategy = strategy;
|
|
; return err;
|
|
.end_f:
|
|
ret
|
|
endp
|
|
|
|
; =========================================================================
|
|
;int (strm, good_length, max_lazy, nice_length, max_chain)
|
|
; z_streamp strm
|
|
; int good_length
|
|
; int max_lazy
|
|
; int nice_length
|
|
; int max_chain
|
|
align 4
|
|
proc deflateTune uses ebx, strm:dword, good_length:dword, max_lazy:dword,\
|
|
nice_length:dword, max_chain:dword
|
|
mov ebx,[strm]
|
|
cmp ebx,Z_NULL
|
|
je @f
|
|
cmp dword[ebx+z_stream.state],Z_NULL
|
|
jne .end0
|
|
@@: ;if (..==0 || ..==0) return ..
|
|
mov eax,Z_STREAM_ERROR
|
|
jmp .end_f
|
|
.end0:
|
|
mov ebx,[ebx+z_stream.state] ;s = strm.state
|
|
mov eax,[good_length]
|
|
mov [ebx+deflate_state.good_match],eax
|
|
mov eax,[max_lazy]
|
|
mov [ebx+deflate_state.max_lazy_match],eax
|
|
mov eax,[nice_length]
|
|
mov [ebx+deflate_state.nice_match],eax
|
|
mov eax,[max_chain]
|
|
mov [ebx+deflate_state.max_chain_length],eax
|
|
mov eax,Z_OK
|
|
.end_f:
|
|
ret
|
|
endp
|
|
|
|
; =========================================================================
|
|
; For the default windowBits of 15 and memLevel of 8, this function returns
|
|
; a close to exact, as well as small, upper bound on the compressed size.
|
|
; They are coded as constants here for a reason--if the #define's are
|
|
; changed, then this function needs to be changed as well. The return
|
|
; value for 15 and 8 only works for those exact settings.
|
|
|
|
; For any setting other than those defaults for windowBits and memLevel,
|
|
; the value returned is a conservative worst case for the maximum expansion
|
|
; resulting from using fixed blocks instead of stored blocks, which deflate
|
|
; can emit on compressed data for some combinations of the parameters.
|
|
|
|
; This function could be more sophisticated to provide closer upper bounds for
|
|
; every combination of windowBits and memLevel. But even the conservative
|
|
; upper bound of about 14% expansion does not seem onerous for output buffer
|
|
; allocation.
|
|
|
|
;uLong (strm, sourceLen)
|
|
; z_streamp strm
|
|
; uLong sourceLen
|
|
align 4
|
|
proc deflateBound, strm:dword, sourceLen:dword
|
|
; deflate_state *s;
|
|
; uLong complen, wraplen;
|
|
; Bytef *str;
|
|
zlib_debug 'deflateBound'
|
|
|
|
; conservative upper bound for compressed data
|
|
; complen = sourceLen +
|
|
; ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5;
|
|
|
|
; if can't get parameters, return conservative bound plus zlib wrapper
|
|
; if (strm == Z_NULL || strm->state == Z_NULL)
|
|
; return complen + 6;
|
|
|
|
; compute wrapper length
|
|
; s = strm->state;
|
|
; switch (s->wrap) {
|
|
; case 0: /* raw deflate */
|
|
; wraplen = 0;
|
|
; break;
|
|
; case 1: /* zlib wrapper */
|
|
; wraplen = 6 + (s->strstart ? 4 : 0);
|
|
; break;
|
|
; case 2: /* gzip wrapper */
|
|
; wraplen = 18;
|
|
; if (s->gzhead != Z_NULL) { /* user-supplied gzip header */
|
|
; if (s->gzhead->extra != Z_NULL)
|
|
; wraplen += 2 + s->gzhead->extra_len;
|
|
; str = s->gzhead->name;
|
|
; if (str != Z_NULL)
|
|
; do {
|
|
; wraplen++;
|
|
; } while (*str++);
|
|
; str = s->gzhead->comment;
|
|
; if (str != Z_NULL)
|
|
; do {
|
|
; wraplen++;
|
|
; } while (*str++);
|
|
; if (s->gzhead->hcrc)
|
|
; wraplen += 2;
|
|
; }
|
|
; break;
|
|
; default: /* for compiler happiness */
|
|
; wraplen = 6;
|
|
; }
|
|
|
|
; if not default parameters, return conservative bound
|
|
; if (s->w_bits != 15 || s->hash_bits != 8 + 7)
|
|
; return complen + wraplen;
|
|
|
|
; default settings: return tight bound for that case
|
|
; return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) +
|
|
; (sourceLen >> 25) + 13 - 6 + wraplen;
|
|
.end_f:
|
|
ret
|
|
endp
|
|
|
|
; =========================================================================
|
|
; Put a short in the pending buffer. The 16-bit value is put in MSB order.
|
|
; IN assertion: the stream state is correct and there is enough room in
|
|
; pending_buf.
|
|
|
|
;void (s, b)
|
|
; deflate_state *s
|
|
; uInt b
|
|
align 4
|
|
proc putShortMSB uses ebx ecx, s:dword, b:dword
|
|
mov ebx,[s]
|
|
mov ecx,[b]
|
|
put_byte ebx, ch
|
|
put_byte ebx, cl
|
|
ret
|
|
endp
|
|
|
|
; =========================================================================
|
|
; Flush as much pending output as possible. All deflate() output goes
|
|
; through this function so some applications may wish to modify it
|
|
; to avoid allocating a large strm->next_out buffer and copying into it.
|
|
; (See also read_buf()).
|
|
|
|
;void (strm)
|
|
; z_streamp strm
|
|
align 4
|
|
proc flush_pending uses eax ebx ecx edx, strm:dword
|
|
;ecx - len
|
|
;edx - deflate_state *s
|
|
;ebx - strm
|
|
zlib_debug 'flush_pending'
|
|
mov ebx,[strm]
|
|
mov edx,[ebx+z_stream.state]
|
|
|
|
stdcall _tr_flush_bits, edx
|
|
movzx ecx,word[edx+deflate_state.pending]
|
|
cmp cx,[ebx+z_stream.avail_out]
|
|
jle @f ;if (..>..)
|
|
movzx ecx,word[ebx+z_stream.avail_out]
|
|
@@:
|
|
cmp ecx,0
|
|
je @f
|
|
|
|
stdcall zmemcpy, [ebx+z_stream.next_out], [edx+deflate_state.pending_out], ecx
|
|
add [ebx+z_stream.next_out],ecx
|
|
add [edx+deflate_state.pending_out],ecx
|
|
add [ebx+z_stream.total_out],ecx
|
|
sub [ebx+z_stream.avail_out],cx
|
|
sub [edx+deflate_state.pending],cx
|
|
cmp word[edx+deflate_state.pending],0
|
|
jne @f ;if (..==0)
|
|
mov eax,[edx+deflate_state.pending_buf]
|
|
mov [edx+deflate_state.pending_out],eax
|
|
@@:
|
|
ret
|
|
endp
|
|
|
|
; =========================================================================
|
|
;int (strm, flush)
|
|
; z_streamp strm
|
|
; int flush
|
|
align 4
|
|
proc deflate uses ebx ecx edx edi esi, strm:dword, flush:dword
|
|
locals
|
|
old_flush dd ? ;int ;value of flush param for previous deflate call
|
|
val dd ?
|
|
endl
|
|
mov ebx,[strm]
|
|
zlib_debug 'deflate strm = %d',ebx
|
|
cmp ebx,Z_NULL
|
|
je @f
|
|
mov edi,[ebx+z_stream.state] ;s = strm.state
|
|
cmp edi,Z_NULL
|
|
je @f
|
|
cmp dword[flush],Z_BLOCK
|
|
jg @f
|
|
cmp dword[flush],0
|
|
jl @f ;if (..==0 || ..==0 || ..>.. || ..<0)
|
|
jmp .end10
|
|
@@:
|
|
mov eax,Z_STREAM_ERROR
|
|
jmp .end_f
|
|
.end10:
|
|
cmp dword[ebx+z_stream.next_out],Z_NULL
|
|
je .beg0
|
|
cmp dword[ebx+z_stream.next_in],Z_NULL
|
|
jne @f
|
|
cmp word[ebx+z_stream.avail_in],0
|
|
jne .beg0
|
|
@@:
|
|
cmp dword[edi+deflate_state.status],FINISH_STATE
|
|
jne .end0
|
|
cmp dword[flush],Z_FINISH
|
|
je .end0
|
|
.beg0: ;if (..==0 || (..==0 && ..!=0) || (..=.. && ..!=..))
|
|
ERR_RETURN ebx, Z_STREAM_ERROR
|
|
jmp .end_f
|
|
.end0:
|
|
cmp word[ebx+z_stream.avail_out],0
|
|
jne @f ;if (..==0)
|
|
ERR_RETURN ebx, Z_BUF_ERROR
|
|
jmp .end_f
|
|
@@:
|
|
|
|
mov dword[edi+deflate_state.strm],ebx ;just in case
|
|
mov eax,[edi+deflate_state.last_flush]
|
|
mov [old_flush],eax
|
|
mov eax,[flush]
|
|
mov [edi+deflate_state.last_flush],eax
|
|
|
|
; Write the header
|
|
cmp dword[edi+deflate_state.status],INIT_STATE
|
|
jne .end2 ;if (..==..)
|
|
if GZIP eq 1
|
|
cmp dword[edi+deflate_state.wrap],2
|
|
jne .end1 ;if (..==..)
|
|
xor eax,eax ;stdcall calc_crc32, 0, Z_NULL, 0
|
|
mov [ebx+z_stream.adler],eax
|
|
put_byte edi, 31
|
|
put_byte edi, 139
|
|
put_byte edi, 8
|
|
cmp dword[edi+deflate_state.gzhead],Z_NULL
|
|
jne .end3 ;if (..==0)
|
|
put_byte edi, 0
|
|
put_dword edi, 0
|
|
xor cl,cl
|
|
cmp word[edi+deflate_state.level],2
|
|
jge @f
|
|
mov cl,4
|
|
@@:
|
|
cmp word[edi+deflate_state.strategy],Z_HUFFMAN_ONLY
|
|
jl @f
|
|
mov cl,4
|
|
@@:
|
|
cmp word[edi+deflate_state.level],9
|
|
jne @f
|
|
mov cl,2
|
|
@@: ;..==.. ? 2 : (..>=.. || ..<.. ? 4 : 0)
|
|
put_byte edi, cl
|
|
put_byte edi, OS_CODE
|
|
mov dword[edi+deflate_state.status],BUSY_STATE
|
|
jmp .end2
|
|
.end3: ;else
|
|
mov edx,[edi+deflate_state.gzhead]
|
|
xor cl,cl
|
|
cmp [edx+gz_header.text],0
|
|
je @f
|
|
inc cl
|
|
@@:
|
|
cmp [edx+gz_header.hcrc],0
|
|
je @f
|
|
add cl,2
|
|
@@:
|
|
cmp [edx+gz_header.extra],Z_NULL
|
|
je @f
|
|
add cl,4
|
|
@@:
|
|
cmp [edx+gz_header.name],Z_NULL
|
|
je @f
|
|
add cl,8
|
|
@@:
|
|
cmp [edx+gz_header.comment],Z_NULL
|
|
je @f
|
|
add cl,16
|
|
@@:
|
|
put_byte edi, cl
|
|
mov ecx,[edx+gz_header.time]
|
|
put_dword edi, ecx
|
|
xor cl,cl
|
|
cmp word[edi+deflate_state.level],2
|
|
jge @f
|
|
mov cl,4
|
|
@@:
|
|
cmp word[edi+deflate_state.strategy],Z_HUFFMAN_ONLY
|
|
jl @f
|
|
mov cl,4
|
|
@@:
|
|
cmp word[edi+deflate_state.level],9
|
|
jne @f
|
|
mov cl,2
|
|
@@: ;..==.. ? 2 : (..>=.. || ..<.. ? 4 : 0)
|
|
put_byte edi, cl
|
|
mov ecx,[edx+gz_header.os]
|
|
put_byte edi, cl
|
|
cmp dword[edx+gz_header.extra],Z_NULL
|
|
je @f ;if (..!=0)
|
|
mov ecx,[edx+gz_header.extra_len]
|
|
put_byte edi, cl
|
|
put_byte edi, ch
|
|
@@:
|
|
cmp dword[edx+gz_header.hcrc],0
|
|
je @f ;if (..)
|
|
movzx eax,word[edi+deflate_state.pending]
|
|
stdcall calc_crc32, [ebx+z_stream.adler],\
|
|
[edi+deflate_state.pending_buf], eax
|
|
mov [ebx+z_stream.adler],eax
|
|
@@:
|
|
mov dword[edi+deflate_state.gzindex],0
|
|
mov dword[edi+deflate_state.status],EXTRA_STATE
|
|
jmp .end2
|
|
.end1: ;else
|
|
end if
|
|
mov edx,[edi+deflate_state.w_bits]
|
|
sub edx,8
|
|
shl edx,4
|
|
add edx,Z_DEFLATED
|
|
shl edx,8 ;edx = header
|
|
;esi = level_flags
|
|
|
|
mov esi,3
|
|
cmp word[edi+deflate_state.strategy],Z_HUFFMAN_ONLY
|
|
jl @f
|
|
cmp word[edi+deflate_state.level],2
|
|
jge @f ;if (..>=.. || ..<..)
|
|
xor esi,esi
|
|
jmp .end4
|
|
@@:
|
|
cmp word[edi+deflate_state.level],6
|
|
jge @f ;else if (..<..)
|
|
mov esi,1
|
|
jmp .end4
|
|
@@:
|
|
;;cmp word[edi+deflate_state.level],6
|
|
jne .end4 ;else if (..==..)
|
|
mov esi,2
|
|
.end4:
|
|
shl esi,6
|
|
or edx,esi
|
|
cmp dword[edi+deflate_state.strstart],0
|
|
je @f ;if (..!=0)
|
|
or edx,PRESET_DICT
|
|
@@:
|
|
mov esi,edx
|
|
mov eax,edx
|
|
xor edx,edx
|
|
mov ecx,31
|
|
div ecx
|
|
add esi,31
|
|
sub esi,edx ;esi = header
|
|
|
|
mov dword[edi+deflate_state.status],BUSY_STATE
|
|
stdcall putShortMSB, edi, esi
|
|
|
|
; Save the adler32 of the preset dictionary:
|
|
cmp dword[edi+deflate_state.strstart],0
|
|
je @f ;if (..!=0)
|
|
mov ecx,[ebx+z_stream.adler]
|
|
bswap ecx
|
|
put_dword edi, ecx
|
|
@@:
|
|
xor eax,eax ;stdcall calc_crc32, 0, Z_NULL, 0
|
|
mov [ebx+z_stream.adler],eax
|
|
.end2:
|
|
if GZIP eq 1
|
|
mov edx,[edi+deflate_state.gzhead]
|
|
cmp dword[edi+deflate_state.status],EXTRA_STATE
|
|
jne .end5 ;if (..==..)
|
|
cmp dword[edx+gz_header.extra],Z_NULL
|
|
je .end21 ;if (..!=..)
|
|
movzx esi,word[edi+deflate_state.pending]
|
|
;esi = beg ;start of bytes to update crc
|
|
|
|
movzx ecx,word[edx+gz_header.extra_len]
|
|
.cycle0: ;while (..<..)
|
|
cmp dword[edi+deflate_state.gzindex],ecx
|
|
jge .cycle0end
|
|
movzx eax,word[edi+deflate_state.pending]
|
|
cmp eax,[edi+deflate_state.pending_buf_size]
|
|
jne .end24 ;if (..==..)
|
|
mov dword[edx+gz_header.hcrc],0
|
|
je @f
|
|
cmp [edi+deflate_state.pending],si
|
|
jle @f ;if (.. && ..>..)
|
|
movzx ecx,word[edi+deflate_state.pending]
|
|
sub ecx,esi
|
|
mov eax,[edi+deflate_state.pending_buf]
|
|
add eax,esi
|
|
stdcall calc_crc32, [ebx+z_stream.adler], eax, ecx
|
|
mov [ebx+z_stream.adler],eax
|
|
@@:
|
|
stdcall flush_pending, ebx
|
|
movzx esi,word[edi+deflate_state.pending]
|
|
cmp esi,[edi+deflate_state.pending_buf_size]
|
|
je .cycle0end ;if (..==..) break
|
|
.end24:
|
|
push ebx
|
|
mov ebx,[edi+deflate_state.gzindex]
|
|
add ebx,[edx+gz_header.extra]
|
|
mov bl,[ebx]
|
|
put_byte edi, bl
|
|
pop ebx
|
|
inc dword[edi+deflate_state.gzindex]
|
|
jmp .cycle0
|
|
.cycle0end:
|
|
mov dword[edx+gz_header.hcrc],0
|
|
je @f
|
|
cmp [edi+deflate_state.pending],si
|
|
jle @f ;if (.. && ..>..)
|
|
movzx ecx,word[edi+deflate_state.pending]
|
|
sub ecx,esi
|
|
mov eax,[edi+deflate_state.pending_buf]
|
|
add eax,esi
|
|
stdcall calc_crc32, [ebx+z_stream.adler], eax, ecx
|
|
mov [ebx+z_stream.adler],eax
|
|
@@:
|
|
mov eax,[edx+gz_header.extra_len]
|
|
cmp dword[edi+deflate_state.gzindex],eax
|
|
jne .end5 ;if (..==..)
|
|
mov dword[edi+deflate_state.gzindex],0
|
|
mov dword[edi+deflate_state.status],NAME_STATE
|
|
jmp .end5
|
|
.end21: ;else
|
|
mov dword[edi+deflate_state.status],NAME_STATE
|
|
.end5:
|
|
cmp dword[edi+deflate_state.status],NAME_STATE
|
|
jne .end6 ;if (..==..)
|
|
cmp dword[edx+gz_header.name],Z_NULL
|
|
je .end22 ;if (..!=..)
|
|
movzx esi,word[edi+deflate_state.pending]
|
|
;esi = beg ;start of bytes to update crc
|
|
|
|
.cycle1: ;do
|
|
movzx eax,word[edi+deflate_state.pending]
|
|
cmp eax,[edi+deflate_state.pending_buf_size]
|
|
jne .end25 ;if (..==..)
|
|
mov dword[edx+gz_header.hcrc],0
|
|
je @f
|
|
cmp [edi+deflate_state.pending],si
|
|
jle @f ;if (.. && ..>..)
|
|
movzx ecx,word[edi+deflate_state.pending]
|
|
sub ecx,esi
|
|
mov eax,[edi+deflate_state.pending_buf]
|
|
add eax,esi
|
|
stdcall calc_crc32, [ebx+z_stream.adler], eax, ecx
|
|
mov [ebx+z_stream.adler],eax
|
|
@@:
|
|
stdcall flush_pending, ebx
|
|
movzx esi,word[edi+deflate_state.pending]
|
|
movzx eax,word[edi+deflate_state.pending]
|
|
cmp eax,[edi+deflate_state.pending_buf_size]
|
|
jne .end25 ;if (..==..)
|
|
mov dword[val],1
|
|
jmp .cycle1end
|
|
.end25:
|
|
push ebx
|
|
mov ebx,[edi+deflate_state.gzindex]
|
|
add ebx,[edx+gz_header.name]
|
|
movzx ebx,byte[ebx]
|
|
mov [val],ebx
|
|
inc dword[edi+deflate_state.gzindex]
|
|
put_byte edi, bl
|
|
pop ebx
|
|
cmp dword[val],0
|
|
jne .cycle1 ;while (val != 0)
|
|
.cycle1end:
|
|
mov dword[edx+gz_header.hcrc],0
|
|
je @f
|
|
cmp [edi+deflate_state.pending],si
|
|
jle @f ;if (.. && ..>..)
|
|
movzx ecx,word[edi+deflate_state.pending]
|
|
sub ecx,esi
|
|
mov eax,[edi+deflate_state.pending_buf]
|
|
add eax,esi
|
|
stdcall calc_crc32, [ebx+z_stream.adler], eax, ecx
|
|
mov [ebx+z_stream.adler],eax
|
|
@@:
|
|
cmp dword[val],0
|
|
jne .end6 ;if (val == 0)
|
|
mov dword[edi+deflate_state.gzindex],0
|
|
mov dword[edi+deflate_state.status],COMMENT_STATE
|
|
jmp .end6
|
|
.end22: ;else
|
|
mov dword[edi+deflate_state.status],COMMENT_STATE;
|
|
.end6:
|
|
cmp dword[edi+deflate_state.status],COMMENT_STATE
|
|
jne .end7 ;if (..==..)
|
|
cmp dword[edx+gz_header.comment],Z_NULL
|
|
je .end23 ;if (..!=..)
|
|
movzx esi,word[edi+deflate_state.pending]
|
|
;esi = beg ;start of bytes to update crc
|
|
|
|
.cycle2: ;do
|
|
movzx eax,word[edi+deflate_state.pending]
|
|
cmp eax,[edi+deflate_state.pending_buf_size]
|
|
jne .end26 ;if (..==..)
|
|
mov dword[edx+gz_header.hcrc],0
|
|
je @f
|
|
cmp [edi+deflate_state.pending],si
|
|
jle @f ;if (.. && ..>..)
|
|
movzx ecx,word[edi+deflate_state.pending]
|
|
sub ecx,esi
|
|
mov eax,[edi+deflate_state.pending_buf]
|
|
add eax,esi
|
|
stdcall calc_crc32, [ebx+z_stream.adler], eax, ecx
|
|
mov [ebx+z_stream.adler],eax
|
|
@@:
|
|
stdcall flush_pending, ebx
|
|
movzx esi,word[edi+deflate_state.pending]
|
|
movzx eax,word[edi+deflate_state.pending]
|
|
cmp eax,[edi+deflate_state.pending_buf_size]
|
|
jne .end26 ;if (..==..)
|
|
mov dword[val],1
|
|
jmp .cycle2end
|
|
.end26:
|
|
push ebx
|
|
mov ebx,[edi+deflate_state.gzindex]
|
|
add ebx,[edx+gz_header.comment]
|
|
movzx ebx,byte[ebx]
|
|
mov [val],ebx
|
|
inc dword[edi+deflate_state.gzindex]
|
|
put_byte edi, bl
|
|
pop ebx
|
|
cmp dword[val],0
|
|
jne .cycle2 ;while (val != 0)
|
|
.cycle2end:
|
|
mov dword[edx+gz_header.hcrc],0
|
|
je @f
|
|
cmp [edi+deflate_state.pending],si
|
|
jle @f ;if (.. && ..>..)
|
|
movzx ecx,word[edi+deflate_state.pending]
|
|
sub ecx,esi
|
|
mov eax,[edi+deflate_state.pending_buf]
|
|
add eax,esi
|
|
stdcall calc_crc32, [ebx+z_stream.adler], eax, ecx
|
|
mov [ebx+z_stream.adler],eax
|
|
@@:
|
|
cmp dword[val],0
|
|
jne .end7 ;if (val == 0)
|
|
mov dword[edi+deflate_state.status],HCRC_STATE
|
|
jmp .end7
|
|
.end23: ;else
|
|
mov dword[edi+deflate_state.status],HCRC_STATE
|
|
.end7:
|
|
cmp dword[edi+deflate_state.status],HCRC_STATE
|
|
jne .end8 ;if (..==..)
|
|
cmp dword[edx+gz_header.hcrc],0
|
|
je .end9 ;if (..)
|
|
movzx ecx,word[edi+deflate_state.pending]
|
|
add ecx,2
|
|
cmp ecx,[edi+deflate_state.pending_buf_size]
|
|
jle @f ;if (..>..)
|
|
stdcall flush_pending, ebx
|
|
@@:
|
|
movzx ecx,word[edi+deflate_state.pending]
|
|
add ecx,2
|
|
cmp ecx,[edi+deflate_state.pending_buf_size]
|
|
jg @f ;if (..<=..)
|
|
mov ecx,[ebx+z_stream.adler]
|
|
put_byte edi, cl
|
|
put_byte edi, ch
|
|
xor eax,eax ;stdcall calc_crc32, 0, Z_NULL, 0
|
|
mov [ebx+z_stream.adler],eax
|
|
mov dword[edi+deflate_state.status],BUSY_STATE
|
|
@@:
|
|
jmp .end8
|
|
.end9: ;else
|
|
mov dword[edi+deflate_state.status],BUSY_STATE
|
|
.end8:
|
|
end if
|
|
|
|
; Flush as much pending output as possible
|
|
cmp word[edi+deflate_state.pending],0
|
|
je .end13 ;if (..!=0)
|
|
stdcall flush_pending, ebx
|
|
cmp word[ebx+z_stream.avail_out],0
|
|
jne @f ;if (..==0)
|
|
; Since avail_out is 0, deflate will be called again with
|
|
; more output space, but possibly with both pending and
|
|
; avail_in equal to zero. There won't be anything to do,
|
|
; but this is not an error situation so make sure we
|
|
; return OK instead of BUF_ERROR at next call of deflate:
|
|
|
|
mov dword[edi+deflate_state.last_flush],-1
|
|
mov eax,Z_OK
|
|
jmp .end_f
|
|
@@:
|
|
; Make sure there is something to do and avoid duplicate consecutive
|
|
; flushes. For repeated and useless calls with Z_FINISH, we keep
|
|
; returning Z_STREAM_END instead of Z_BUF_ERROR.
|
|
jmp @f
|
|
.end13:
|
|
cmp word[ebx+z_stream.avail_in],0
|
|
jne @f
|
|
RANK dword[old_flush],esi
|
|
RANK dword[flush],eax
|
|
cmp eax,esi
|
|
jg @f
|
|
cmp dword[flush],Z_FINISH
|
|
je @f ;else if (..==0 && ..<=.. && ..!=..)
|
|
ERR_RETURN ebx, Z_BUF_ERROR
|
|
jmp .end_f
|
|
@@:
|
|
|
|
; User must not provide more input after the first FINISH:
|
|
cmp dword[edi+deflate_state.status],FINISH_STATE
|
|
jne @f
|
|
cmp word[ebx+z_stream.avail_in],0
|
|
je @f ;if (..==.. && ..!=0)
|
|
ERR_RETURN ebx, Z_BUF_ERROR
|
|
jmp .end_f
|
|
@@:
|
|
|
|
; Start a new block or continue the current one.
|
|
|
|
cmp word[ebx+z_stream.avail_in],0
|
|
jne @f
|
|
cmp dword[edi+deflate_state.lookahead],0
|
|
jne @f
|
|
cmp dword[flush],Z_NO_FLUSH
|
|
je .end11
|
|
cmp dword[edi+deflate_state.status],FINISH_STATE
|
|
je .end11
|
|
@@: ;if (..!=0 || ..!=0 || (..!=.. && ..!=..))
|
|
;edx = bstate
|
|
cmp word[edi+deflate_state.strategy],Z_HUFFMAN_ONLY
|
|
jne @f
|
|
stdcall deflate_huff, edi, [flush]
|
|
jmp .end20
|
|
@@:
|
|
cmp word[edi+deflate_state.strategy],Z_RLE
|
|
jne @f
|
|
stdcall deflate_rle, edi, [flush]
|
|
jmp .end20
|
|
@@:
|
|
movzx eax,word[edi+deflate_state.level]
|
|
imul eax,sizeof.config_s
|
|
add eax,configuration_table+config_s.co_func
|
|
stdcall dword[eax], edi, [flush]
|
|
.end20:
|
|
mov edx,eax
|
|
|
|
cmp edx,finish_started
|
|
je @f
|
|
cmp edx,finish_done
|
|
je @f
|
|
jmp .end18
|
|
@@: ;if (..==.. || ..==..)
|
|
mov dword[edi+deflate_state.status],FINISH_STATE
|
|
.end18:
|
|
cmp edx,need_more
|
|
je @f
|
|
cmp edx,finish_started
|
|
je @f
|
|
jmp .end19
|
|
@@: ;if (..==.. || ..==..)
|
|
cmp word[ebx+z_stream.avail_out],0
|
|
jne @f ;if (..==0)
|
|
mov dword[edi+deflate_state.last_flush],-1 ;avoid BUF_ERROR next call, see above
|
|
@@:
|
|
mov eax,Z_OK
|
|
jmp .end_f
|
|
; If flush != Z_NO_FLUSH && avail_out == 0, the next call
|
|
; of deflate should use the same flush parameter to make sure
|
|
; that the flush is complete. So we don't have to output an
|
|
; empty block here, this will be done at next call. This also
|
|
; ensures that for a very small output buffer, we emit at most
|
|
; one empty block.
|
|
|
|
.end19:
|
|
cmp edx,block_done
|
|
jne .end11 ;if (..==..)
|
|
cmp dword[flush],Z_PARTIAL_FLUSH
|
|
jne @f ;if (..==..)
|
|
stdcall _tr_align, edi
|
|
jmp .end16
|
|
@@:
|
|
cmp dword[flush],Z_BLOCK
|
|
je .end16 ;else if (..!=..) ;FULL_FLUSH or SYNC_FLUSH
|
|
stdcall _tr_stored_block, edi, 0, 0, 0
|
|
; For a full flush, this empty block will be recognized
|
|
; as a special marker by inflate_sync().
|
|
|
|
cmp dword[flush],Z_FULL_FLUSH
|
|
jne .end16 ;if (..==..)
|
|
CLEAR_HASH edi ;forget history
|
|
cmp dword[edi+deflate_state.lookahead],0
|
|
jne .end16 ;if (..==0)
|
|
mov dword[edi+deflate_state.strstart],0
|
|
mov dword[edi+deflate_state.block_start],0
|
|
mov dword[edi+deflate_state.insert],0
|
|
.end16:
|
|
stdcall flush_pending, ebx
|
|
cmp word[ebx+z_stream.avail_out],0
|
|
jne .end11 ;if (..==0)
|
|
mov dword[edi+deflate_state.last_flush],-1 ;avoid BUF_ERROR at next call, see above
|
|
mov eax,Z_OK
|
|
jmp .end_f
|
|
.end11:
|
|
cmp word[ebx+z_stream.avail_out],0
|
|
jg @f
|
|
zlib_assert 'bug2' ;Assert(..>0)
|
|
@@:
|
|
|
|
cmp dword[flush],Z_FINISH
|
|
je @f ;if (..!=0)
|
|
mov eax,Z_OK
|
|
jmp .end_f
|
|
@@:
|
|
cmp dword[edi+deflate_state.wrap],0
|
|
jg @f ;if (..<=0)
|
|
mov eax,Z_STREAM_END
|
|
jmp .end_f
|
|
@@:
|
|
|
|
; Write the trailer
|
|
if GZIP eq 1
|
|
cmp dword[edi+deflate_state.wrap],2
|
|
jne @f ;if (..==..)
|
|
mov ecx,[ebx+z_stream.adler]
|
|
put_dword edi, ecx
|
|
mov ecx,[ebx+z_stream.total_in]
|
|
put_dword edi, ecx
|
|
jmp .end17
|
|
@@: ;else
|
|
end if
|
|
mov ecx,[ebx+z_stream.adler]
|
|
bswap ecx
|
|
put_dword edi, ecx
|
|
.end17:
|
|
stdcall flush_pending, ebx
|
|
; If avail_out is zero, the application will call deflate again
|
|
; to flush the rest.
|
|
|
|
cmp word[edi+deflate_state.pending],0
|
|
jle @f ;if (..>0) ;write the trailer only once!
|
|
neg word[edi+deflate_state.pending]
|
|
inc word[edi+deflate_state.pending]
|
|
@@:
|
|
mov eax,Z_OK
|
|
cmp word[edi+deflate_state.pending],0
|
|
je .end_f
|
|
mov eax,Z_STREAM_END
|
|
.end_f:
|
|
zlib_debug ' deflate.ret = %d',eax
|
|
ret
|
|
endp
|
|
|
|
; =========================================================================
|
|
;int (strm)
|
|
; z_streamp strm
|
|
align 4
|
|
proc deflateEnd uses ebx ecx edx, strm:dword
|
|
mov ebx,[strm]
|
|
zlib_debug 'deflateEnd'
|
|
cmp ebx,Z_NULL
|
|
je @f
|
|
mov edx,[ebx+z_stream.state]
|
|
cmp edx,Z_NULL
|
|
jne .end0
|
|
@@: ;if (..==0 || ..==0) return ..
|
|
mov eax,Z_STREAM_ERROR
|
|
jmp .end_f
|
|
.end0:
|
|
|
|
mov ecx,[edx+deflate_state.status]
|
|
cmp ecx,INIT_STATE
|
|
je @f
|
|
cmp ecx,EXTRA_STATE
|
|
je @f
|
|
cmp ecx,NAME_STATE
|
|
je @f
|
|
cmp ecx,COMMENT_STATE
|
|
je @f
|
|
cmp ecx,HCRC_STATE
|
|
je @f
|
|
cmp ecx,BUSY_STATE
|
|
je @f
|
|
cmp ecx,FINISH_STATE
|
|
je @f ;if (..!=.. && ..!=.. && ..!=.. && ..!=.. && ..!=.. && ..!=.. && ..!=..)
|
|
mov eax,Z_STREAM_ERROR
|
|
jmp .end_f
|
|
@@:
|
|
|
|
; Deallocate in reverse order of allocations:
|
|
TRY_FREE ebx, dword[edx+deflate_state.pending_buf]
|
|
TRY_FREE ebx, dword[edx+deflate_state.head]
|
|
TRY_FREE ebx, dword[edx+deflate_state.prev]
|
|
TRY_FREE ebx, dword[edx+deflate_state.window]
|
|
|
|
ZFREE ebx, dword[ebx+z_stream.state]
|
|
mov dword[ebx+z_stream.state],Z_NULL
|
|
|
|
mov eax,Z_DATA_ERROR
|
|
cmp ecx,BUSY_STATE
|
|
je .end_f
|
|
mov eax,Z_OK
|
|
.end_f:
|
|
ret
|
|
endp
|
|
|
|
; =========================================================================
|
|
; Copy the source state to the destination state.
|
|
; To simplify the source, this is not supported for 16-bit MSDOS (which
|
|
; doesn't have enough memory anyway to duplicate compression states).
|
|
|
|
;int (dest, source)
|
|
; z_streamp dest
|
|
; z_streamp source
|
|
align 4
|
|
proc deflateCopy uses ebx edx edi esi, dest:dword, source:dword
|
|
;ebx = overlay ;uint_16p
|
|
;edi = ds ;deflate_state*
|
|
;esi = ss ;deflate_state*
|
|
|
|
mov esi,[source]
|
|
cmp esi,Z_NULL
|
|
je @f
|
|
mov edx,[dest]
|
|
cmp edx,Z_NULL
|
|
je @f
|
|
mov esi,[esi+z_stream.state]
|
|
cmp esi,Z_NULL
|
|
jne .end0
|
|
@@: ;if (..==0 || ..==0 || ..==0)
|
|
mov eax,Z_STREAM_ERROR
|
|
jmp .end_f
|
|
.end0:
|
|
|
|
stdcall zmemcpy, edx, [source], sizeof.z_stream
|
|
|
|
ZALLOC edx, 1, sizeof.deflate_state
|
|
cmp eax,0
|
|
jne @f ;if (..==0) return ..
|
|
mov eax,Z_MEM_ERROR
|
|
jmp .end_f
|
|
@@:
|
|
mov edi,eax
|
|
mov [edx+z_stream.state],eax
|
|
stdcall zmemcpy, edi, esi, sizeof.deflate_state
|
|
mov dword[edi+deflate_state.strm],edx
|
|
|
|
ZALLOC edx, [edi+deflate_state.w_size], 2 ;2*sizeof.db
|
|
mov dword[edi+deflate_state.window],eax
|
|
ZALLOC edx, [edi+deflate_state.w_size], 4 ;sizeof.dd
|
|
mov dword[edi+deflate_state.prev],eax
|
|
ZALLOC edx, [edi+deflate_state.hash_size], 4 ;sizeof.dd
|
|
mov dword[edi+deflate_state.head],eax
|
|
ZALLOC edx, [edi+deflate_state.lit_bufsize], 4 ;sizeof.dw+2
|
|
mov ebx,eax
|
|
mov dword[edi+deflate_state.pending_buf],eax
|
|
|
|
cmp dword[edi+deflate_state.window],Z_NULL
|
|
je @f
|
|
cmp dword[edi+deflate_state.prev],Z_NULL
|
|
je @f
|
|
cmp dword[edi+deflate_state.head],Z_NULL
|
|
je @f
|
|
cmp dword[edi+deflate_state.pending_buf],Z_NULL
|
|
jne .end1
|
|
@@: ;if (..==0 || ..==0 || ..==0 || ..==0)
|
|
stdcall deflateEnd, edx
|
|
mov eax,Z_MEM_ERROR
|
|
jmp .end_f
|
|
.end1:
|
|
|
|
; following zmemcpy do not work for 16-bit MSDOS
|
|
mov eax,[edi+deflate_state.w_size]
|
|
shl eax,1 ;*= 2*sizeof.db
|
|
stdcall zmemcpy, [edi+deflate_state.window], [esi+deflate_state.window], eax
|
|
mov eax,[edi+deflate_state.w_size]
|
|
shl eax,2 ;*= sizeof.dd
|
|
stdcall zmemcpy, [edi+deflate_state.prev], [esi+deflate_state.prev], eax
|
|
mov eax,[edi+deflate_state.hash_size]
|
|
shl eax,2 ;*= sizeof.dd
|
|
stdcall zmemcpy, [edi+deflate_state.head], [esi+deflate_state.head], eax
|
|
stdcall zmemcpy, [edi+deflate_state.pending_buf], [esi+deflate_state.pending_buf], [edi+deflate_state.pending_buf_size]
|
|
|
|
mov eax,[edi+deflate_state.pending_buf]
|
|
add eax,[esi+deflate_state.pending_out]
|
|
sub eax,[esi+deflate_state.pending_buf]
|
|
mov [edi+deflate_state.pending_out],eax
|
|
mov eax,[edi+deflate_state.lit_bufsize]
|
|
shr eax,1 ;/=sizeof.uint_16
|
|
add eax,ebx
|
|
mov [edi+deflate_state.d_buf],eax
|
|
mov eax,[edi+deflate_state.lit_bufsize]
|
|
imul eax,3 ;*=1+sizeof.uint_16
|
|
add eax,[edi+deflate_state.pending_buf]
|
|
mov [edi+deflate_state.l_buf],eax
|
|
|
|
mov eax,edi
|
|
add eax,deflate_state.dyn_ltree
|
|
mov [edi+deflate_state.l_desc.dyn_tree],eax
|
|
add eax,deflate_state.dyn_dtree-deflate_state.dyn_ltree
|
|
mov [edi+deflate_state.d_desc.dyn_tree],eax
|
|
add eax,deflate_state.bl_tree-deflate_state.dyn_dtree
|
|
mov [edi+deflate_state.bl_desc.dyn_tree],eax
|
|
|
|
mov eax,Z_OK
|
|
.end_f:
|
|
ret
|
|
endp
|
|
|
|
; ===========================================================================
|
|
; Read a new buffer from the current input stream, update the adler32
|
|
; and total number of bytes read. All deflate() input goes through
|
|
; this function so some applications may wish to modify it to avoid
|
|
; allocating a large strm->next_in buffer and copying from it.
|
|
; (See also flush_pending()).
|
|
|
|
;int (strm, buf, size)
|
|
; z_streamp strm
|
|
; Bytef *buf
|
|
; unsigned size
|
|
align 4
|
|
proc read_buf uses ebx ecx, strm:dword, buf:dword, size:dword
|
|
mov ebx,[strm]
|
|
movzx eax,word[ebx+z_stream.avail_in]
|
|
|
|
cmp eax,[size]
|
|
jle @f ;if (..>..)
|
|
mov eax,[size]
|
|
@@:
|
|
cmp eax,0
|
|
jg @f
|
|
xor eax,eax
|
|
jmp .end_f ;if (..==0) return 0
|
|
@@:
|
|
|
|
sub [ebx+z_stream.avail_in],ax
|
|
|
|
stdcall zmemcpy, [buf],[ebx+z_stream.next_in],eax
|
|
mov ecx,[ebx+z_stream.state]
|
|
cmp [ecx+deflate_state.wrap],1
|
|
jne @f ;if (..==..)
|
|
push eax
|
|
stdcall adler32, [ebx+z_stream.adler], [buf], eax
|
|
mov [ebx+z_stream.adler],eax
|
|
pop eax
|
|
jmp .end0
|
|
@@:
|
|
if GZIP eq 1
|
|
cmp [ecx+deflate_state.wrap],2
|
|
jne .end0 ;else if (..==..)
|
|
push eax
|
|
stdcall calc_crc32, [ebx+z_stream.adler], [buf], eax
|
|
mov [ebx+z_stream.adler],eax
|
|
pop eax
|
|
end if
|
|
.end0:
|
|
add [ebx+z_stream.next_in],eax
|
|
add [ebx+z_stream.total_in],eax
|
|
|
|
.end_f:
|
|
;zlib_debug ' read_buf.ret = %d',eax
|
|
ret
|
|
endp
|
|
|
|
; ===========================================================================
|
|
; Initialize the "longest match" routines for a new zlib stream
|
|
|
|
;void (s)
|
|
; deflate_state *s
|
|
align 4
|
|
proc lm_init uses eax ebx edi, s:dword
|
|
mov edi,[s]
|
|
mov eax,[edi+deflate_state.w_size]
|
|
shl eax,1
|
|
mov [edi+deflate_state.window_size],eax
|
|
|
|
CLEAR_HASH edi
|
|
|
|
; Set the default configuration parameters:
|
|
|
|
movzx eax,word[edi+deflate_state.level]
|
|
imul eax,sizeof.config_s
|
|
add eax,configuration_table
|
|
movzx ebx,word[eax+config_s.max_lazy]
|
|
mov [edi+deflate_state.max_lazy_match],ebx
|
|
movzx ebx,word[eax+config_s.good_length]
|
|
mov [edi+deflate_state.good_match],ebx
|
|
movzx ebx,word[eax+config_s.nice_length]
|
|
mov [edi+deflate_state.nice_match],ebx
|
|
movzx ebx,word[eax+config_s.max_chain]
|
|
mov [edi+deflate_state.max_chain_length],ebx
|
|
|
|
mov dword[edi+deflate_state.strstart],0
|
|
mov dword[edi+deflate_state.block_start],0
|
|
mov dword[edi+deflate_state.lookahead],0
|
|
mov dword[edi+deflate_state.insert],0
|
|
mov dword[edi+deflate_state.prev_length],MIN_MATCH-1
|
|
mov dword[edi+deflate_state.match_length],MIN_MATCH-1
|
|
mov dword[edi+deflate_state.match_available],0
|
|
mov dword[edi+deflate_state.ins_h],0
|
|
if FASTEST eq 0
|
|
;if ASMV
|
|
; call match_init ;initialize the asm code
|
|
;end if
|
|
end if
|
|
ret
|
|
endp
|
|
|
|
;uInt (s, cur_match)
|
|
; deflate_state *s
|
|
; IPos cur_match ;current match
|
|
align 4
|
|
proc longest_match uses ebx ecx edx edi esi, s:dword, cur_match:dword
|
|
if FASTEST eq 0
|
|
; ===========================================================================
|
|
; Set match_start to the longest match starting at the given string and
|
|
; return its length. Matches shorter or equal to prev_length are discarded,
|
|
; in which case the result is equal to prev_length and match_start is
|
|
; garbage.
|
|
; IN assertions: cur_match is the head of the hash chain for the current
|
|
; string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1
|
|
; OUT assertion: the match length is not greater than s->lookahead.
|
|
|
|
;#ifndef ASMV
|
|
; For 80x86 and 680x0, an optimized version will be provided in match.asm or
|
|
; match.S. The code will be functionally equivalent.
|
|
|
|
; unsigned chain_length = s->max_chain_length;/* max hash chain length */
|
|
; register Bytef *scan = s->window + s->strstart; /* current string */
|
|
; register Bytef *match; /* matched string */
|
|
; register int len; /* length of current match */
|
|
; int best_len = s->prev_length; /* best match length so far */
|
|
; int nice_match = s->nice_match; /* stop if match long enough */
|
|
; IPos limit = s->strstart > (IPos)MAX_DIST(s) ?
|
|
; s->strstart - (IPos)MAX_DIST(s) : NIL;
|
|
; Stop when cur_match becomes <= limit. To simplify the code,
|
|
; we prevent matches with the string of window index 0.
|
|
|
|
; Posf *prev = s->prev;
|
|
; uInt wmask = s->w_mask;
|
|
|
|
; register Bytef *strend = s->window + s->strstart + MAX_MATCH;
|
|
; register Byte scan_end1 = scan[best_len-1];
|
|
; register Byte scan_end = scan[best_len];
|
|
|
|
; The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
|
|
; It is easy to get rid of this optimization if necessary.
|
|
|
|
; Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
|
|
|
|
; Do not waste too much time if we already have a good match:
|
|
; if (s->prev_length >= s->good_match) {
|
|
; chain_length >>= 2;
|
|
; }
|
|
; Do not look for matches beyond the end of the input. This is necessary
|
|
; to make deflate deterministic.
|
|
|
|
; if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead;
|
|
|
|
; Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
|
|
|
|
; do {
|
|
; Assert(cur_match < s->strstart, "no future");
|
|
; match = s->window + cur_match;
|
|
|
|
; Skip to next match if the match length cannot increase
|
|
; or if the match length is less than 2. Note that the checks below
|
|
; for insufficient lookahead only occur occasionally for performance
|
|
; reasons. Therefore uninitialized memory will be accessed, and
|
|
; conditional jumps will be made that depend on those values.
|
|
; However the length of the match is limited to the lookahead, so
|
|
; the output of deflate is not affected by the uninitialized values.
|
|
|
|
; if (match[best_len] != scan_end ||
|
|
; match[best_len-1] != scan_end1 ||
|
|
; *match != *scan ||
|
|
; *++match != scan[1]) continue;
|
|
|
|
; The check at best_len-1 can be removed because it will be made
|
|
; again later. (This heuristic is not always a win.)
|
|
; It is not necessary to compare scan[2] and match[2] since they
|
|
; are always equal when the other bytes match, given that
|
|
; the hash keys are equal and that HASH_BITS >= 8.
|
|
|
|
; scan += 2, match++;
|
|
; Assert(*scan == *match, "match[2]?");
|
|
|
|
; We check for insufficient lookahead only every 8th comparison;
|
|
; the 256th check will be made at strstart+258.
|
|
|
|
; do {
|
|
; } while (*++scan == *++match && *++scan == *++match &&
|
|
; *++scan == *++match && *++scan == *++match &&
|
|
; *++scan == *++match && *++scan == *++match &&
|
|
; *++scan == *++match && *++scan == *++match &&
|
|
; scan < strend);
|
|
|
|
; Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
|
|
|
|
; len = MAX_MATCH - (int)(strend - scan);
|
|
; scan = strend - MAX_MATCH;
|
|
|
|
; if (len > best_len) {
|
|
; s->match_start = cur_match;
|
|
; best_len = len;
|
|
; if (len >= nice_match) break;
|
|
; scan_end1 = scan[best_len-1];
|
|
; scan_end = scan[best_len];
|
|
; }
|
|
; } while ((cur_match = prev[cur_match & wmask]) > limit
|
|
; && --chain_length != 0);
|
|
|
|
; if ((uInt)best_len <= s->lookahead) return (uInt)best_len;
|
|
; return s->lookahead;
|
|
;end if /* ASMV */
|
|
|
|
else ;FASTEST
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; Optimized version for FASTEST only
|
|
mov edx,[s]
|
|
zlib_debug 'longest_match'
|
|
|
|
; The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
|
|
; It is easy to get rid of this optimization if necessary.
|
|
|
|
if MAX_MATCH <> 258
|
|
cmp dword[edx+deflate_state.hash_bits],8
|
|
jge @f
|
|
zlib_assert 'Code too clever' ;Assert(..>=.. && ..==..)
|
|
@@:
|
|
end if
|
|
mov eax,[edx+deflate_state.window_size]
|
|
sub eax,MIN_LOOKAHEAD
|
|
cmp [edx+deflate_state.strstart],eax
|
|
jle @f
|
|
zlib_assert 'need lookahead' ;Assert(..<=..)
|
|
@@:
|
|
mov eax,[edx+deflate_state.strstart]
|
|
cmp [cur_match],eax
|
|
jl @f
|
|
zlib_assert 'no future' ;Assert(..<..)
|
|
@@:
|
|
|
|
mov esi,[edx+deflate_state.window]
|
|
mov edi,esi
|
|
add esi,[cur_match]
|
|
add edi,[edx+deflate_state.strstart]
|
|
;edi = scan
|
|
;esi = match
|
|
|
|
; Return failure if the match length is less than 2:
|
|
|
|
lodsw
|
|
cmp ax,word[edi]
|
|
je @f ;if (word[edi] != word[esi]) return
|
|
mov eax,MIN_MATCH-1
|
|
jmp .end_f
|
|
@@:
|
|
|
|
; The check at best_len-1 can be removed because it will be made
|
|
; again later. (This heuristic is not always a win.)
|
|
; It is not necessary to compare scan[2] and match[2] since they
|
|
; are always equal when the other bytes match, given that
|
|
; the hash keys are equal and that HASH_BITS >= 8.
|
|
|
|
add edi,2
|
|
mov al,byte[edi]
|
|
cmp al,byte[esi]
|
|
je @f
|
|
zlib_assert 'match[2]?' ;Assert(..==..)
|
|
@@:
|
|
|
|
; We check for insufficient lookahead only every 8th comparison;
|
|
; the 256th check will be made at strstart+258.
|
|
|
|
mov ebx,edi
|
|
mov ecx,MAX_MATCH
|
|
align 4
|
|
@@:
|
|
lodsb
|
|
scasb
|
|
loope @b
|
|
|
|
mov eax,[edx+deflate_state.window_size]
|
|
dec eax
|
|
add eax,[edx+deflate_state.window]
|
|
cmp edi,eax
|
|
jle @f
|
|
zlib_assert 'wild scan' ;Assert(..<=..)
|
|
@@:
|
|
sub edi,ebx
|
|
;edi = len
|
|
|
|
cmp edi,MIN_MATCH
|
|
jge @f ;if (..<..)
|
|
mov eax,MIN_MATCH-1
|
|
jmp .end_f
|
|
@@:
|
|
mov eax,[cur_match]
|
|
mov [edx+deflate_state.match_start],eax
|
|
mov eax,[edx+deflate_state.lookahead]
|
|
cmp edi,eax
|
|
jg @f ;if (len <= s.lookahead) ? len : s.lookahead
|
|
mov eax,edi
|
|
@@:
|
|
end if ;FASTEST
|
|
.end_f:
|
|
;zlib_debug ' longest_match.ret = %d',eax
|
|
ret
|
|
endp
|
|
|
|
|
|
; ===========================================================================
|
|
; Check that the match at match_start is indeed a match.
|
|
|
|
;void (s, start, match, length)
|
|
; deflate_state *s
|
|
; IPos start, match
|
|
; int length
|
|
align 4
|
|
proc check_match, s:dword, start:dword, p3match:dword, length:dword
|
|
if DEBUG eq 1
|
|
; check that the match is indeed a match
|
|
; if (zmemcmp(s->window + match,
|
|
; s->window + start, length) != EQUAL) {
|
|
; fprintf(stderr, " start %u, match %u, length %d\n",
|
|
; start, match, length);
|
|
; do {
|
|
; fprintf(stderr, "%c%c", s->window[match++], s->window[start++]);
|
|
; } while (--length != 0);
|
|
; z_error("invalid match");
|
|
; }
|
|
; if (z_verbose > 1) {
|
|
; fprintf(stderr,"\\[%d,%d]", start-match, length);
|
|
; do { putc(s->window[start++], stderr); } while (--length != 0);
|
|
; }
|
|
end if ;DEBUG
|
|
ret
|
|
endp
|
|
|
|
|
|
; ===========================================================================
|
|
; Fill the window when the lookahead becomes insufficient.
|
|
; Updates strstart and lookahead.
|
|
|
|
; IN assertion: lookahead < MIN_LOOKAHEAD
|
|
; OUT assertions: strstart <= window_size-MIN_LOOKAHEAD
|
|
; At least one byte has been read, or avail_in == 0; reads are
|
|
; performed for at least two bytes (required for the zip translate_eol
|
|
; option -- not supported here).
|
|
|
|
;void (s)
|
|
; deflate_state *s
|
|
align 4
|
|
proc fill_window, s:dword
|
|
pushad
|
|
;esi = p, str, curr
|
|
;ebx = more ;Amount of free space at the end of the window.
|
|
;Объем свободного пространства в конце окна.
|
|
;ecx = wsize ;uInt
|
|
;edx = s.strm
|
|
zlib_debug 'fill_window'
|
|
mov edi,[s]
|
|
cmp dword[edi+deflate_state.lookahead],MIN_LOOKAHEAD
|
|
jl @f
|
|
zlib_assert 'already enough lookahead' ;Assert(..<..)
|
|
@@:
|
|
|
|
mov ecx,[edi+deflate_state.w_size]
|
|
mov edx,[edi+deflate_state.strm]
|
|
.cycle0: ;do
|
|
zlib_debug 'do'
|
|
mov ebx,[edi+deflate_state.window_size]
|
|
sub ebx,[edi+deflate_state.lookahead]
|
|
sub ebx,[edi+deflate_state.strstart]
|
|
|
|
; If the window is almost full and there is insufficient lookahead,
|
|
; move the upper half to the lower one to make room in the upper half.
|
|
|
|
MAX_DIST edi
|
|
add eax,ecx
|
|
cmp [edi+deflate_state.strstart],eax
|
|
jl .end0 ;if (..>=..)
|
|
push ecx
|
|
mov eax,[edi+deflate_state.window]
|
|
add eax,ecx
|
|
stdcall zmemcpy, [edi+deflate_state.window], eax
|
|
sub [edi+deflate_state.match_start],ecx
|
|
sub [edi+deflate_state.strstart],ecx ;we now have strstart >= MAX_DIST
|
|
sub [edi+deflate_state.block_start],ecx
|
|
|
|
; Slide the hash table (could be avoided with 32 bit values
|
|
; at the expense of memory usage). We slide even when level == 0
|
|
; to keep the hash table consistent if we switch back to level > 0
|
|
; later. (Using level 0 permanently is not an optimal usage of
|
|
; zlib, so we don't care about this pathological case.)
|
|
|
|
push ebx ecx
|
|
;ebx = wsize
|
|
;ecx = n
|
|
mov ebx,ecx
|
|
mov ecx,[edi+deflate_state.hash_size]
|
|
mov esi,ecx
|
|
shl esi,2
|
|
add esi,[edi+deflate_state.head]
|
|
.cycle1: ;do
|
|
sub esi,4
|
|
mov eax,[esi]
|
|
mov dword[esi],NIL
|
|
cmp eax,ebx
|
|
jl @f
|
|
sub eax,ebx
|
|
mov dword[esi],eax
|
|
@@:
|
|
loop .cycle1 ;while (..)
|
|
|
|
mov ecx,ebx
|
|
if FASTEST eq 0
|
|
mov esi,ecx
|
|
shl esi,2
|
|
add esi,[edi+deflate_state.prev]
|
|
.cycle2: ;do
|
|
sub esi,4
|
|
mov eax,[esi]
|
|
mov dword[esi],NIL
|
|
cmp eax,ebx
|
|
jl @f
|
|
sub eax,ebx
|
|
mov dword[esi],eax
|
|
@@:
|
|
; If n is not on any hash chain, prev[n] is garbage but
|
|
; its value will never be used.
|
|
|
|
loop .cycle2 ;while (..)
|
|
end if
|
|
pop ecx ebx
|
|
add ebx,ecx
|
|
.end0:
|
|
cmp word[edx+z_stream.avail_in],0
|
|
je .cycle0end ;if (..==0) break
|
|
|
|
; If there was no sliding:
|
|
; strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&
|
|
; more == window_size - lookahead - strstart
|
|
; => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)
|
|
; => more >= window_size - 2*WSIZE + 2
|
|
; In the BIG_MEM or MMAP case (not yet supported),
|
|
; window_size == input_size + MIN_LOOKAHEAD &&
|
|
; strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.
|
|
; Otherwise, window_size == 2*WSIZE so more >= 2.
|
|
; If there was sliding, more >= WSIZE. So in all cases, more >= 2.
|
|
|
|
cmp ebx,2
|
|
jge @f
|
|
zlib_assert 'more < 2' ;Assert(..>=..)
|
|
@@:
|
|
mov eax,[edi+deflate_state.window]
|
|
add eax,[edi+deflate_state.strstart]
|
|
add eax,[edi+deflate_state.lookahead]
|
|
stdcall read_buf, edx, eax, ebx
|
|
add [edi+deflate_state.lookahead],eax
|
|
|
|
; Initialize the hash value now that we have some input:
|
|
mov eax,[edi+deflate_state.lookahead]
|
|
add eax,[edi+deflate_state.insert]
|
|
cmp eax,MIN_MATCH
|
|
jl .end1 ;if (..>=..)
|
|
mov esi,[edi+deflate_state.strstart]
|
|
sub esi,[edi+deflate_state.insert]
|
|
;esi = str
|
|
mov eax,[edi+deflate_state.window]
|
|
add eax,esi
|
|
mov [edi+deflate_state.ins_h],eax
|
|
inc eax
|
|
movzx eax,byte[eax]
|
|
UPDATE_HASH edi, [edi+deflate_state.ins_h], eax
|
|
if MIN_MATCH <> 3
|
|
; Call UPDATE_HASH() MIN_MATCH-3 more times
|
|
end if
|
|
.cycle3: ;while (..)
|
|
cmp dword[edi+deflate_state.insert],0
|
|
je .end1
|
|
mov eax,esi
|
|
add eax,MIN_MATCH-1
|
|
add eax,[edi+deflate_state.window]
|
|
movzx eax,byte[eax]
|
|
UPDATE_HASH edi, [edi+deflate_state.ins_h], eax
|
|
if FASTEST eq 0
|
|
mov eax,[edi+deflate_state.ins_h]
|
|
shl eax,2
|
|
add eax,[edi+deflate_state.head]
|
|
push ebx
|
|
mov ebx,[edi+deflate_state.w_mask]
|
|
and ebx,esi
|
|
shl ebx,2
|
|
add ebx,[edi+deflate_state.prev]
|
|
mov eax,[eax]
|
|
mov [ebx],eax
|
|
pop ebx
|
|
end if
|
|
mov eax,[edi+deflate_state.ins_h]
|
|
shl eax,2
|
|
add eax,[edi+deflate_state.head]
|
|
mov [eax],esi
|
|
inc esi
|
|
dec dword[edi+deflate_state.insert]
|
|
mov eax,[edi+deflate_state.lookahead]
|
|
add eax,[edi+deflate_state.insert]
|
|
cmp eax,MIN_MATCH
|
|
jl .end1 ;if (..<..) break
|
|
jmp .cycle3
|
|
.end1:
|
|
; If the whole input has less than MIN_MATCH bytes, ins_h is garbage,
|
|
; but this is not important since only literal bytes will be emitted.
|
|
|
|
cmp dword[edi+deflate_state.lookahead],MIN_LOOKAHEAD
|
|
jge .cycle0end
|
|
cmp word[edx+z_stream.avail_in],0
|
|
jne .cycle0
|
|
.cycle0end: ;while (..<.. && ..!=..)
|
|
|
|
; If the WIN_INIT bytes after the end of the current data have never been
|
|
; written, then zero those bytes in order to avoid memory check reports of
|
|
; the use of uninitialized (or uninitialised as Julian writes) bytes by
|
|
; the longest match routines. Update the high water mark for the next
|
|
; time through here. WIN_INIT is set to MAX_MATCH since the longest match
|
|
; routines allow scanning to strstart + MAX_MATCH, ignoring lookahead.
|
|
|
|
mov eax,[edi+deflate_state.window_size]
|
|
cmp [edi+deflate_state.high_water],eax
|
|
jge .end2 ;if (..<..)
|
|
mov esi,[edi+deflate_state.lookahead]
|
|
add esi,[edi+deflate_state.strstart]
|
|
;esi = curr
|
|
|
|
cmp [edi+deflate_state.high_water],esi
|
|
jge .end3 ;if (..<..)
|
|
; Previous high water mark below current data -- zero WIN_INIT
|
|
; bytes or up to end of window, whichever is less.
|
|
|
|
mov eax,[edi+deflate_state.window_size]
|
|
sub eax,esi
|
|
cmp eax,WIN_INIT
|
|
jle @f ;if (..>..)
|
|
mov eax,WIN_INIT
|
|
@@:
|
|
mov edx,[edi+deflate_state.window]
|
|
add edx,esi
|
|
stdcall zmemzero, edx, eax
|
|
add eax,esi
|
|
mov [edi+deflate_state.high_water],eax
|
|
jmp .end2
|
|
.end3: ;else if (..<..)
|
|
mov eax,esi
|
|
add eax,WIN_INIT
|
|
cmp [edi+deflate_state.high_water],eax
|
|
jge .end2
|
|
; High water mark at or above current data, but below current data
|
|
; plus WIN_INIT -- zero out to current data plus WIN_INIT, or up
|
|
; to end of window, whichever is less.
|
|
|
|
;eax = esi+WIN_INIT
|
|
sub eax,[edi+deflate_state.high_water]
|
|
mov edx,[edi+deflate_state.window_size]
|
|
sub edx,[edi+deflate_state.high_water]
|
|
cmp eax,edx ;if (..>..)
|
|
jle @f
|
|
mov eax,edx
|
|
@@:
|
|
mov edx,[edi+deflate_state.window]
|
|
add edx,[edi+deflate_state.high_water]
|
|
stdcall zmemzero, edx, eax
|
|
add [edi+deflate_state.high_water],eax
|
|
.end2:
|
|
|
|
mov eax,[edi+deflate_state.window_size]
|
|
sub eax,MIN_LOOKAHEAD
|
|
cmp [edi+deflate_state.strstart],eax
|
|
jle @f
|
|
zlib_assert 'not enough room for search' ;Assert(..<=..)
|
|
@@:
|
|
popad
|
|
ret
|
|
endp
|
|
|
|
; ===========================================================================
|
|
; Flush the current block, with given end-of-file flag.
|
|
; IN assertion: strstart is set to the end of the current match.
|
|
|
|
macro FLUSH_BLOCK_ONLY s, last
|
|
{
|
|
local .end0
|
|
push dword last
|
|
mov eax,[s+deflate_state.strstart]
|
|
sub eax,[s+deflate_state.block_start]
|
|
push eax
|
|
xor eax,eax
|
|
cmp dword[s+deflate_state.block_start],0
|
|
jl .end0
|
|
mov eax,[s+deflate_state.block_start]
|
|
add eax,[s+deflate_state.window]
|
|
.end0:
|
|
stdcall _tr_flush_block, s, eax
|
|
mov eax,[s+deflate_state.strstart]
|
|
mov [s+deflate_state.block_start],eax
|
|
stdcall flush_pending, [s+deflate_state.strm]
|
|
; Tracev((stderr,"[FLUSH]"));
|
|
}
|
|
|
|
; Same but force premature exit if necessary.
|
|
macro FLUSH_BLOCK s, last
|
|
{
|
|
local .end0
|
|
FLUSH_BLOCK_ONLY s, last
|
|
mov eax,[s+deflate_state.strm]
|
|
cmp word[eax+z_stream.avail_out],0
|
|
jne .end0 ;if (..==0)
|
|
if last eq 1
|
|
mov eax,finish_started
|
|
else
|
|
mov eax,need_more
|
|
end if
|
|
jmp .end_f
|
|
.end0:
|
|
}
|
|
|
|
; ===========================================================================
|
|
; Copy without compression as much as possible from the input stream, return
|
|
; the current block state.
|
|
; This function does not insert new strings in the dictionary since
|
|
; uncompressible data is probably not useful. This function is used
|
|
; only for the level=0 compression option.
|
|
; NOTE: this function should be optimized to avoid extra copying from
|
|
; window to pending_buf.
|
|
|
|
;block_state (s, flush)
|
|
; deflate_state *s
|
|
; int flush
|
|
align 4
|
|
proc deflate_stored uses ebx ecx edi, s:dword, flush:dword
|
|
; Stored blocks are limited to 0xffff bytes, pending_buf is limited
|
|
; to pending_buf_size, and each stored block has a 5 byte header:
|
|
mov edi,[s]
|
|
zlib_debug 'deflate_stored'
|
|
|
|
mov ecx,0xffff
|
|
mov eax,[edi+deflate_state.pending_buf_size]
|
|
sub eax,5
|
|
cmp ecx,eax
|
|
jle @f ;if (..>..)
|
|
mov ecx,eax
|
|
@@:
|
|
;ecx = max_block_size
|
|
|
|
; Copy as much as possible from input to output:
|
|
.cycle0: ;for (;;) {
|
|
; Fill the window as much as possible:
|
|
cmp dword[edi+deflate_state.lookahead],1
|
|
jg .end0 ;if (..<=..)
|
|
; Assert(s->strstart < s->w_size+MAX_DIST(s) ||
|
|
; s->block_start >= (long)s->w_size, "slide too late");
|
|
|
|
stdcall fill_window, edi
|
|
cmp dword[edi+deflate_state.lookahead],0
|
|
jne @f
|
|
cmp dword[flush],Z_NO_FLUSH
|
|
jne @f ;if (..==0 && ..==..)
|
|
mov eax,need_more
|
|
jmp .end_f
|
|
@@:
|
|
cmp dword[edi+deflate_state.lookahead],0
|
|
je .cycle0end ;if (..==0) break ;flush the current block
|
|
.end0:
|
|
; Assert(s->block_start >= 0, "block gone");
|
|
|
|
mov eax,[edi+deflate_state.lookahead]
|
|
add [edi+deflate_state.strstart],eax
|
|
mov dword[edi+deflate_state.lookahead],0
|
|
|
|
; Emit a stored block if pending_buf will be full:
|
|
mov ebx,[edi+deflate_state.block_start]
|
|
add ebx,ecx
|
|
cmp dword[edi+deflate_state.strstart],0
|
|
je @f
|
|
cmp [edi+deflate_state.strstart],ebx
|
|
jl .end1
|
|
@@: ;if (..==0 || ..>=..)
|
|
; strstart == 0 is possible when wraparound on 16-bit machine
|
|
mov eax,[edi+deflate_state.strstart]
|
|
sub eax,ebx
|
|
mov [edi+deflate_state.lookahead],eax
|
|
mov [edi+deflate_state.strstart],ebx
|
|
FLUSH_BLOCK edi, 0
|
|
.end1:
|
|
; Flush if we may have to slide, otherwise block_start may become
|
|
; negative and the data will be gone:
|
|
|
|
MAX_DIST edi
|
|
mov ebx,[edi+deflate_state.strstart]
|
|
sub ebx,[edi+deflate_state.block_start]
|
|
cmp ebx,eax
|
|
jl .cycle0 ;if (..>=..)
|
|
FLUSH_BLOCK edi, 0
|
|
jmp .cycle0
|
|
align 4
|
|
.cycle0end:
|
|
mov dword[edi+deflate_state.insert],0
|
|
cmp dword[flush],Z_FINISH
|
|
jne @f ;if (..==..)
|
|
FLUSH_BLOCK edi, 1
|
|
mov eax,finish_done
|
|
jmp .end_f
|
|
@@:
|
|
mov eax,[edi+deflate_state.block_start]
|
|
cmp [edi+deflate_state.strstart],eax
|
|
jle @f ;if (..>..)
|
|
FLUSH_BLOCK edi, 0
|
|
@@:
|
|
mov eax,block_done
|
|
.end_f:
|
|
ret
|
|
endp
|
|
|
|
; ===========================================================================
|
|
; Compress as much as possible from the input stream, return the current
|
|
; block state.
|
|
; This function does not perform lazy evaluation of matches and inserts
|
|
; new strings in the dictionary only for unmatched strings or for short
|
|
; matches. It is used only for the fast compression options.
|
|
|
|
;block_state (s, flush)
|
|
; deflate_state *s
|
|
; int flush
|
|
align 4
|
|
proc deflate_fast uses ebx ecx edi, s:dword, flush:dword
|
|
locals
|
|
bflush dd ? ;int ;set if current block must be flushed
|
|
endl
|
|
;ecx = hash_head ;IPos ;head of the hash chain
|
|
mov edi,[s]
|
|
zlib_debug 'deflate_fast'
|
|
|
|
.cycle0: ;for (..)
|
|
; Make sure that we always have enough lookahead, except
|
|
; at the end of the input file. We need MAX_MATCH bytes
|
|
; for the next match, plus MIN_MATCH bytes to insert the
|
|
; string following the next match.
|
|
|
|
cmp dword[edi+deflate_state.lookahead],MIN_LOOKAHEAD
|
|
jge .end0 ;if (..<..)
|
|
stdcall fill_window, edi
|
|
cmp dword[edi+deflate_state.lookahead],MIN_LOOKAHEAD
|
|
jge @f ;if (..<.. && ..==..)
|
|
cmp dword[flush],Z_NO_FLUSH
|
|
jne @f
|
|
mov eax,need_more
|
|
jmp .end_f
|
|
align 4
|
|
@@:
|
|
cmp dword[edi+deflate_state.lookahead],0
|
|
je .cycle0end ;if (..==0) break ;flush the current block
|
|
align 4
|
|
.end0:
|
|
|
|
; Insert the string window[strstart .. strstart+2] in the
|
|
; dictionary, and set hash_head to the head of the hash chain:
|
|
|
|
mov ecx,NIL
|
|
cmp dword[edi+deflate_state.lookahead],MIN_MATCH
|
|
jl @f ;if (..>=..)
|
|
INSERT_STRING edi, [edi+deflate_state.strstart], ecx
|
|
@@:
|
|
|
|
; Find the longest match, discarding those <= prev_length.
|
|
; At this point we have always match_length < MIN_MATCH
|
|
|
|
cmp ecx,NIL
|
|
je @f
|
|
MAX_DIST edi
|
|
mov ebx,[edi+deflate_state.strstart]
|
|
sub ebx,ecx
|
|
cmp ebx,eax
|
|
jg @f ;if (..!=0 && ..<=..)
|
|
; To simplify the code, we prevent matches with the string
|
|
; of window index 0 (in particular we have to avoid a match
|
|
; of the string with itself at the start of the input file).
|
|
|
|
stdcall longest_match, edi, ecx
|
|
mov [edi+deflate_state.match_length],eax
|
|
; longest_match() sets match_start
|
|
@@:
|
|
cmp dword[edi+deflate_state.match_length],MIN_MATCH
|
|
jl .end1 ;if (..>=..)
|
|
stdcall check_match, edi, [edi+deflate_state.strstart], [edi+deflate_state.match_start], [edi+deflate_state.match_length]
|
|
|
|
mov eax,[edi+deflate_state.strstart]
|
|
sub eax,[edi+deflate_state.match_start]
|
|
mov ebx,[edi+deflate_state.match_length]
|
|
sub ebx,MIN_MATCH
|
|
_tr_tally_dist edi, eax, ebx, [bflush]
|
|
|
|
mov eax,[edi+deflate_state.match_length]
|
|
sub [edi+deflate_state.lookahead],eax
|
|
|
|
; Insert new strings in the hash table only if the match length
|
|
; is not too large. This saves time but degrades compression.
|
|
|
|
if FASTEST eq 0
|
|
;;mov eax,[edi+deflate_state.match_length]
|
|
cmp eax,[edi+deflate_state.max_insert_length]
|
|
jg .end3
|
|
cmp dword[edi+deflate_state.lookahead],MIN_MATCH
|
|
jl .end3 ;if (..<=.. && ..>=..)
|
|
dec dword[edi+deflate_state.match_length] ;string at strstart already in table
|
|
.cycle1: ;do {
|
|
inc dword[edi+deflate_state.strstart]
|
|
INSERT_STRING edi, [edi+deflate_state.strstart], ecx
|
|
; strstart never exceeds WSIZE-MAX_MATCH, so there are
|
|
; always MIN_MATCH bytes ahead.
|
|
|
|
dec dword[edi+deflate_state.match_length]
|
|
cmp dword[edi+deflate_state.match_length],0
|
|
jne .cycle1 ;while (..!=0)
|
|
inc dword[edi+deflate_state.strstart]
|
|
jmp .end2
|
|
.end3: ;else
|
|
end if
|
|
|
|
mov eax,[edi+deflate_state.match_length]
|
|
add [edi+deflate_state.strstart],eax
|
|
mov dword[edi+deflate_state.match_length],0
|
|
mov eax,[edi+deflate_state.window]
|
|
add eax,[edi+deflate_state.strstart]
|
|
mov [edi+deflate_state.ins_h],eax
|
|
inc eax
|
|
movzx eax,byte[eax]
|
|
UPDATE_HASH edi, [edi+deflate_state.ins_h], eax
|
|
if MIN_MATCH <> 3
|
|
; Call UPDATE_HASH() MIN_MATCH-3 more times
|
|
end if
|
|
; If lookahead < MIN_MATCH, ins_h is garbage, but it does not
|
|
; matter since it will be recomputed at next deflate call.
|
|
jmp .end2
|
|
.end1: ;else
|
|
; No match, output a literal byte
|
|
mov eax,[edi+deflate_state.window]
|
|
add eax,[edi+deflate_state.strstart]
|
|
movzx eax,byte[eax]
|
|
Tracevv eax,
|
|
_tr_tally_lit edi, eax, [bflush]
|
|
dec dword[edi+deflate_state.lookahead]
|
|
inc dword[edi+deflate_state.strstart]
|
|
.end2:
|
|
cmp dword[bflush],0
|
|
je .cycle0 ;if (..)
|
|
FLUSH_BLOCK edi, 0
|
|
jmp .cycle0
|
|
align 4
|
|
.cycle0end:
|
|
mov eax,[edi+deflate_state.strstart]
|
|
cmp eax,MIN_MATCH-1
|
|
jl @f
|
|
mov eax,MIN_MATCH-1
|
|
@@:
|
|
mov [edi+deflate_state.insert],eax
|
|
cmp dword[flush],Z_FINISH
|
|
jne @f ;if (..==..)
|
|
FLUSH_BLOCK edi, 1
|
|
mov eax,finish_done
|
|
jmp .end_f
|
|
@@:
|
|
cmp dword[edi+deflate_state.last_lit],0
|
|
je @f ;if (..)
|
|
FLUSH_BLOCK edi, 0
|
|
@@:
|
|
mov eax,block_done
|
|
.end_f:
|
|
ret
|
|
endp
|
|
|
|
; ===========================================================================
|
|
; Same as above, but achieves better compression. We use a lazy
|
|
; evaluation for matches: a match is finally adopted only if there is
|
|
; no better match at the next window position.
|
|
|
|
;block_state (s, flush)
|
|
; deflate_state *s
|
|
; int flush
|
|
align 4
|
|
proc deflate_slow uses ebx ecx edx edi, s:dword, flush:dword
|
|
locals
|
|
bflush dd ? ;int ;set if current block must be flushed
|
|
endl
|
|
;ecx = hash_head ;IPos ;head of the hash chain
|
|
mov edi,[s]
|
|
zlib_debug 'deflate_slow'
|
|
|
|
; Process the input block.
|
|
.cycle0: ;for (;;)
|
|
; Make sure that we always have enough lookahead, except
|
|
; at the end of the input file. We need MAX_MATCH bytes
|
|
; for the next match, plus MIN_MATCH bytes to insert the
|
|
; string following the next match.
|
|
|
|
cmp dword[edi+deflate_state.lookahead],MIN_LOOKAHEAD
|
|
jge .end0 ;if (..<..)
|
|
stdcall fill_window, edi
|
|
cmp dword[edi+deflate_state.lookahead],MIN_LOOKAHEAD
|
|
jge @f ;if (..<.. && ..==..)
|
|
cmp dword[flush],Z_NO_FLUSH
|
|
jne @f
|
|
mov eax,need_more
|
|
jmp .end_f
|
|
align 4
|
|
@@:
|
|
cmp dword[edi+deflate_state.lookahead],0
|
|
je .cycle0end ;if (..==0) break ;flush the current block
|
|
align 4
|
|
.end0:
|
|
|
|
; Insert the string window[strstart .. strstart+2] in the
|
|
; dictionary, and set hash_head to the head of the hash chain:
|
|
|
|
mov ecx,NIL
|
|
cmp dword[edi+deflate_state.lookahead],MIN_MATCH
|
|
jl @f ;if (..>=..)
|
|
INSERT_STRING edi, [edi+deflate_state.strstart], ecx
|
|
@@:
|
|
|
|
; Find the longest match, discarding those <= prev_length.
|
|
|
|
mov eax,[edi+deflate_state.match_length]
|
|
mov [edi+deflate_state.prev_length],eax
|
|
mov eax,[edi+deflate_state.match_start]
|
|
mov [edi+deflate_state.prev_match],eax
|
|
mov dword[edi+deflate_state.match_length],MIN_MATCH-1
|
|
|
|
cmp ecx,NIL
|
|
je @f
|
|
mov eax,[edi+deflate_state.prev_length]
|
|
cmp eax,[edi+deflate_state.max_lazy_match]
|
|
jge @f
|
|
MAX_DIST edi
|
|
mov ebx,[edi+deflate_state.strstart]
|
|
sub ebx,ecx
|
|
cmp ebx,eax
|
|
jg .end1 ;if (..!=0 && ..<.. && ..<=..)
|
|
; To simplify the code, we prevent matches with the string
|
|
; of window index 0 (in particular we have to avoid a match
|
|
; of the string with itself at the start of the input file).
|
|
|
|
stdcall longest_match, edi, ecx
|
|
mov [edi+deflate_state.match_length],eax
|
|
; longest_match() sets match_start
|
|
|
|
cmp dword[edi+deflate_state.match_length],5
|
|
jg .end1
|
|
cmp word[edi+deflate_state.strategy],Z_FILTERED
|
|
jne .end1
|
|
; if (..<=.. && (..==..
|
|
;#if TOO_FAR <= 32767
|
|
; || (s->match_length == MIN_MATCH &&
|
|
; s->strstart - s->match_start > TOO_FAR)
|
|
;end if
|
|
; ))
|
|
|
|
; If prev_match is also MIN_MATCH, match_start is garbage
|
|
; but we will ignore the current match anyway.
|
|
|
|
mov dword[edi+deflate_state.match_length],MIN_MATCH-1
|
|
.end1:
|
|
; If there was a match at the previous step and the current
|
|
; match is not better, output the previous match:
|
|
|
|
|
|
mov eax,[edi+deflate_state.prev_length]
|
|
cmp eax,MIN_MATCH
|
|
jl .end2:
|
|
cmp [edi+deflate_state.match_length],eax
|
|
jg .end2: ;if (..>=.. && ..<=..)
|
|
mov edx,[edi+deflate_state.strstart]
|
|
add edx,[edi+deflate_state.lookahead]
|
|
sub edx,MIN_MATCH
|
|
;edx = max_insert
|
|
; Do not insert strings in hash table beyond this.
|
|
|
|
mov eax,[edi+deflate_state.strstart]
|
|
dec eax
|
|
stdcall check_match, edi, eax, [edi+deflate_state.prev_match], [edi+deflate_state.prev_length]
|
|
|
|
mov eax,[edi+deflate_state.strstart]
|
|
dec eax
|
|
sub eax,[edi+deflate_state.prev_match]
|
|
mov ebx,[edi+deflate_state.prev_length]
|
|
sub ebx,MIN_MATCH
|
|
_tr_tally_dist edi, eax, ebx, [bflush]
|
|
|
|
; Insert in hash table all strings up to the end of the match.
|
|
; strstart-1 and strstart are already inserted. If there is not
|
|
; enough lookahead, the last two strings are not inserted in
|
|
; the hash table.
|
|
|
|
mov eax,[edi+deflate_state.prev_length]
|
|
dec eax
|
|
sub [edi+deflate_state.lookahead],eax
|
|
sub dword[edi+deflate_state.prev_length],2
|
|
.cycle1: ;do
|
|
inc dword[edi+deflate_state.strstart]
|
|
cmp [edi+deflate_state.strstart],edx
|
|
jg @f ;if (..<=..)
|
|
INSERT_STRING edi, [edi+deflate_state.strstart], ecx
|
|
@@:
|
|
dec dword[edi+deflate_state.prev_length]
|
|
cmp dword[edi+deflate_state.prev_length],0
|
|
jne .cycle1 ;while (..!=0)
|
|
mov dword[edi+deflate_state.match_available],0
|
|
mov dword[edi+deflate_state.match_length],MIN_MATCH-1
|
|
inc dword[edi+deflate_state.strstart]
|
|
|
|
cmp dword[bflush],0
|
|
je .cycle0 ;if (..)
|
|
FLUSH_BLOCK edi, 0
|
|
jmp .cycle0
|
|
.end2: ;else if (..)
|
|
cmp dword[edi+deflate_state.match_available],0
|
|
je .end3
|
|
; If there was no match at the previous position, output a
|
|
; single literal. If there was a match but the current match
|
|
; is longer, truncate the previous match to a single literal.
|
|
|
|
mov eax,[edi+deflate_state.strstart]
|
|
dec eax
|
|
add eax,[edi+deflate_state.window]
|
|
movzx eax,byte[eax]
|
|
Tracevv eax,
|
|
_tr_tally_lit edi, eax, [bflush]
|
|
cmp dword[bflush],0
|
|
je @f ;if (..)
|
|
FLUSH_BLOCK_ONLY edi, 0
|
|
@@:
|
|
inc dword[edi+deflate_state.strstart]
|
|
dec dword[edi+deflate_state.lookahead]
|
|
mov eax,[edi+deflate_state.strm]
|
|
cmp word[eax+z_stream.avail_out],0
|
|
jne .cycle0 ;if (..==0) return ..
|
|
mov eax,need_more
|
|
jmp .end_f
|
|
jmp .cycle0 ;.end4
|
|
.end3: ;else
|
|
; There is no previous match to compare with, wait for
|
|
; the next step to decide.
|
|
|
|
mov dword[edi+deflate_state.match_available],1
|
|
inc dword[edi+deflate_state.strstart]
|
|
dec dword[edi+deflate_state.lookahead]
|
|
;.end4:
|
|
jmp .cycle0
|
|
.cycle0end:
|
|
cmp dword[flush],Z_NO_FLUSH
|
|
jne @f
|
|
zlib_assert 'no flush?' ;Assert (..!=..)
|
|
@@:
|
|
cmp dword[edi+deflate_state.match_available],0
|
|
je @f ;if (..)
|
|
mov eax,[edi+deflate_state.strstart]
|
|
dec eax
|
|
add eax,[edi+deflate_state.window]
|
|
movzx eax,byte[eax]
|
|
Tracevv eax,
|
|
_tr_tally_lit edi, eax, [bflush]
|
|
mov dword[edi+deflate_state.match_available],0
|
|
@@:
|
|
mov eax,[edi+deflate_state.strstart]
|
|
cmp eax,MIN_MATCH-1
|
|
jl @f
|
|
mov eax,MIN_MATCH-1
|
|
@@:
|
|
mov [edi+deflate_state.insert],eax
|
|
cmp dword[flush],Z_FINISH
|
|
jne @f ;if (..==..)
|
|
FLUSH_BLOCK edi, 1
|
|
mov eax,finish_done
|
|
jmp .end_f
|
|
@@:
|
|
cmp dword[edi+deflate_state.last_lit],0
|
|
je @f ;if (..)
|
|
FLUSH_BLOCK edi, 0
|
|
@@:
|
|
mov eax,block_done
|
|
.end_f:
|
|
ret
|
|
endp
|
|
|
|
; ===========================================================================
|
|
; For Z_RLE, simply look for runs of bytes, generate matches only of distance
|
|
; one. Do not maintain a hash table. (It will be regenerated if this run of
|
|
; deflate switches away from Z_RLE.)
|
|
|
|
;block_state (s, flush)
|
|
; deflate_state *s
|
|
; int flush
|
|
align 4
|
|
proc deflate_rle uses ecx edx edi esi, s:dword, flush:dword
|
|
locals
|
|
bflush dd ? ;int ;set if current block must be flushed
|
|
; uInt prev; /* byte at distance one to match */
|
|
; Bytef *scan, *strend; /* scan goes up to strend for length of run */
|
|
endl
|
|
mov edx,[s]
|
|
zlib_debug 'deflate_rle'
|
|
.cycle0: ;for (;;)
|
|
; Make sure that we always have enough lookahead, except
|
|
; at the end of the input file. We need MAX_MATCH bytes
|
|
; for the longest run, plus one for the unrolled loop.
|
|
cmp dword[edx+deflate_state.lookahead],MAX_MATCH
|
|
jg .end0 ;if (..<=..)
|
|
stdcall fill_window, edx
|
|
cmp dword[edx+deflate_state.lookahead],MAX_MATCH
|
|
jg @f
|
|
cmp dword[flush],Z_NO_FLUSH
|
|
jne @f ;if (..<=.. && ..==..)
|
|
mov eax,need_more
|
|
jmp .end_f
|
|
align 4
|
|
@@:
|
|
cmp dword[edx+deflate_state.lookahead],0
|
|
je .cycle0end ;flush the current block
|
|
align 4
|
|
.end0:
|
|
|
|
; See how many times the previous byte repeats
|
|
mov dword[edx+deflate_state.match_length],0
|
|
cmp dword[edx+deflate_state.lookahead],MIN_MATCH
|
|
jl .end1
|
|
cmp dword[edx+deflate_state.strstart],0
|
|
jle .end1 ;if (..>=.. && ..>..)
|
|
mov esi,[edx+deflate_state.window]
|
|
add esi,[edx+deflate_state.strstart]
|
|
dec esi
|
|
lodsb
|
|
mov edi,esi
|
|
scasb
|
|
jnz .end2
|
|
scasb
|
|
jnz .end2
|
|
scasb
|
|
jnz .end2 ;if (..==.. && ..==.. && ..==..)
|
|
;edi = scan
|
|
; al = prev
|
|
;ecx = strend-scan
|
|
mov ecx,MAX_MATCH-2
|
|
repz scasb
|
|
sub edi,[edx+deflate_state.window]
|
|
sub edi,[edx+deflate_state.strstart]
|
|
mov [edx+deflate_state.match_length],edi
|
|
mov eax,[edx+deflate_state.lookahead]
|
|
cmp [edx+deflate_state.match_length],eax
|
|
jle .end2
|
|
mov [edx+deflate_state.match_length],eax
|
|
.end2:
|
|
mov eax,[edx+deflate_state.window_size]
|
|
dec eax
|
|
add eax,[edx+deflate_state.window]
|
|
cmp edi,eax
|
|
jle .end1
|
|
zlib_assert 'wild scan' ;Assert(..<=..)
|
|
.end1:
|
|
|
|
; Emit match if have run of MIN_MATCH or longer, else emit literal
|
|
cmp dword[edx+deflate_state.match_length],MIN_MATCH
|
|
jl @f ;if (..>=..)
|
|
push dword[edx+deflate_state.match_length]
|
|
mov eax,[edx+deflate_state.strstart]
|
|
dec eax
|
|
stdcall check_match, edx, [edx+deflate_state.strstart], eax
|
|
|
|
mov eax,[edx+deflate_state.match_length]
|
|
sub eax,MIN_MATCH
|
|
_tr_tally_dist edx, 1, eax, [bflush]
|
|
|
|
mov eax,[edx+deflate_state.match_length]
|
|
sub [edx+deflate_state.lookahead],eax
|
|
add [edx+deflate_state.strstart],eax
|
|
mov dword[edx+deflate_state.match_length],0
|
|
jmp .end3
|
|
@@: ;else
|
|
; No match, output a literal byte
|
|
mov eax,[edx+deflate_state.strstart]
|
|
add eax,[edx+deflate_state.window]
|
|
movzx eax,byte[eax]
|
|
Tracevv eax,
|
|
_tr_tally_lit edx, eax, [bflush]
|
|
dec dword[edx+deflate_state.lookahead]
|
|
inc dword[edx+deflate_state.strstart]
|
|
.end3:
|
|
cmp dword[bflush],0
|
|
je .cycle0 ;if (..)
|
|
FLUSH_BLOCK edx, 0
|
|
jmp .cycle0
|
|
align 4
|
|
.cycle0end:
|
|
mov dword[edx+deflate_state.insert],0
|
|
cmp dword[flush],Z_FINISH
|
|
jne @f ;if (..==..)
|
|
FLUSH_BLOCK edx, 1
|
|
mov eax,finish_done
|
|
jmp .end_f
|
|
@@:
|
|
cmp dword[edx+deflate_state.last_lit],0
|
|
je @f ;if (..)
|
|
FLUSH_BLOCK edx, 0
|
|
@@:
|
|
mov eax,block_done
|
|
.end_f:
|
|
ret
|
|
endp
|
|
|
|
; ===========================================================================
|
|
; For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table.
|
|
; (It will be regenerated if this run of deflate switches away from Huffman.)
|
|
|
|
;block_state (s, flush)
|
|
; deflate_state *s
|
|
; int flush
|
|
align 4
|
|
proc deflate_huff uses ebx edi, s:dword, flush:dword
|
|
locals
|
|
bflush dd ? ;int ;set if current block must be flushed
|
|
endl
|
|
mov edi,[s]
|
|
zlib_debug 'deflate_huff'
|
|
.cycle0: ;for (;;)
|
|
; Make sure that we have a literal to write.
|
|
cmp dword[edi+deflate_state.lookahead],0
|
|
jne .end0 ;if (..==0)
|
|
stdcall fill_window, edi
|
|
cmp dword[edi+deflate_state.lookahead],0
|
|
jne .end0 ;if (..==0)
|
|
cmp dword[flush],Z_NO_FLUSH
|
|
jne @f ;if (..==..)
|
|
mov eax,need_more
|
|
jmp .end_f
|
|
align 4
|
|
@@:
|
|
jmp .cycle0end ;flush the current block
|
|
align 4
|
|
.end0:
|
|
|
|
; Output a literal byte
|
|
mov dword[edi+deflate_state.match_length],0
|
|
mov eax,[edi+deflate_state.strstart]
|
|
add eax,[edi+deflate_state.window]
|
|
movzx eax,byte[eax]
|
|
Tracevv eax,
|
|
_tr_tally_lit edi, eax, [bflush]
|
|
dec dword[edi+deflate_state.lookahead]
|
|
inc dword[edi+deflate_state.strstart]
|
|
cmp dword[bflush],0
|
|
je @f ;if (..)
|
|
FLUSH_BLOCK edi, 0
|
|
@@:
|
|
jmp .cycle0
|
|
align 4
|
|
.cycle0end:
|
|
mov dword[edi+deflate_state.insert],0
|
|
cmp dword[flush],Z_FINISH
|
|
jne @f ;if (..==..)
|
|
FLUSH_BLOCK edi, 1
|
|
mov eax,finish_done
|
|
jmp .end_f
|
|
@@:
|
|
cmp dword[edi+deflate_state.last_lit],0
|
|
je @f ;if (..)
|
|
FLUSH_BLOCK edi, 0
|
|
@@:
|
|
mov eax,block_done
|
|
.end_f:
|
|
ret
|
|
endp
|