kolibrios/programs/fs/kfar/trunk/zlib/deflate.asm

2963 lines
82 KiB
NASM
Raw Normal View History

; 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
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 (..==..)
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
@@:
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
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_debug '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 edx edi esi, dest:dword, source:dword
locals
overlay dd ? ;uint_16p
endl
;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 [overlay],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
; zmemcpy((voidpf)ds->prev, (voidpf)ss->prev, ds->w_size * sizeof(Pos));
; zmemcpy((voidpf)ds->head, (voidpf)ss->head, ds->hash_size * sizeof(Pos));
; zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size);
; ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf);
; ds->d_buf = overlay + ds->lit_bufsize/sizeof(uint_16);
; ds->l_buf = ds->pending_buf + (1+sizeof(uint_16))*ds->lit_bufsize;
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;
if UNALIGNED_OK eq 1
; Compare two bytes at a time. Note: this is not always beneficial.
; Try with and without -DUNALIGNED_OK to check.
; register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1;
; register uint_16 scan_start = *(uint_16p*)scan;
; register uint_16 scan_end = *(uint_16p*)(scan+best_len-1);
else
; register Bytef *strend = s->window + s->strstart + MAX_MATCH;
; register Byte scan_end1 = scan[best_len-1];
; register Byte scan_end = scan[best_len];
end if
; 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 ((UNALIGNED_OK eq 1) & MAX_MATCH == 258)
; This code assumes sizeof(unsigned short) == 2. Do not use
; UNALIGNED_OK if your compiler uses a different size.
; if (*(uint_16p*)(match+best_len-1) != scan_end ||
; *(uint_16p*)match != scan_start) continue;
; 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. Compare 2 bytes at a time at
; strstart+3, +5, ... up to strstart+257. We check for insufficient
; lookahead only every 4th comparison; the 128th check will be made
; at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is
; necessary to put more guard bytes at the end of the window, or
; to check more often for insufficient lookahead.
; Assert(scan[2] == match[2], "scan[2]?");
; scan++, match++;
; do {
; } while (*(uint_16p*)(scan+=2) == *(uint_16p*)(match+=2) &&
; *(uint_16p*)(scan+=2) == *(uint_16p*)(match+=2) &&
; *(uint_16p*)(scan+=2) == *(uint_16p*)(match+=2) &&
; *(uint_16p*)(scan+=2) == *(uint_16p*)(match+=2) &&
; scan < strend);
; The funny "do {}" generates better code on most compilers
; Here, scan <= window+strstart+257
; Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
; if (*scan == *match) scan++;
; len = (MAX_MATCH - 1) - (int)(strend-scan);
; scan = strend - (MAX_MATCH-1);
else ;UNALIGNED_OK
; 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;
end if ;UNALIGNED_OK
; if (len > best_len) {
; s->match_start = cur_match;
; best_len = len;
; if (len >= nice_match) break;
if UNALIGNED_OK eq 1
; scan_end = *(uint_16p*)(scan+best_len-1);
else
; scan_end1 = scan[best_len-1];
; scan_end = scan[best_len];
end if
; }
; } 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_debug '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_debug 'need lookahead' ;Assert(..<=..)
@@:
mov eax,[edx+deflate_state.strstart]
cmp [cur_match],eax
jl @f
zlib_debug '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_debug '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_debug '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_debug '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_debug '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_debug '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_debug '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_debug '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