2008-07-30 00:01:23 +04:00
/* inffast.c -- fast decoding
* Copyright ( C ) 1995 - 2004 Mark Adler
* For conditions of distribution and use , see copyright notice in zlib . h
*/
# include "zutil.h"
# include "inftrees.h"
# include "inflate.h"
# include "inffast.h"
# ifndef ASMINF
/* Allow machine dependent optimization for post-increment or pre-increment.
Based on testing to date ,
Pre - increment preferred for :
- PowerPC G3 ( Adler )
- MIPS R5000 ( Randers - Pehrson )
Post - increment preferred for :
- none
No measurable difference :
- Pentium III ( Anderson )
- M68060 ( Nikl )
*/
# ifdef POSTINC
# define OFF 0
# define PUP(a) *(a)++
# else
# define OFF 1
# define PUP(a) *++(a)
# endif
/*
Decode literal , length , and distance codes and write out the resulting
literal and match bytes until either not enough input or output is
available , an end - of - block is encountered , or a data error is encountered .
When large enough input and output buffers are supplied to inflate ( ) , for
example , a 16 K input buffer and a 64 K output buffer , more than 95 % of the
inflate execution time is spent in this routine .
Entry assumptions :
state - > mode = = LEN
strm - > avail_in > = 6
strm - > avail_out > = 258
start > = strm - > avail_out
state - > bits < 8
On return , state - > mode is one of :
LEN - - ran out of enough output space or enough available input
TYPE - - reached end of block code , inflate ( ) to interpret next block
2008-10-20 12:45:42 +04:00
BAD_DATA - - error in block data
2008-07-30 00:01:23 +04:00
Notes :
- The maximum input bits used by a length / distance pair is 15 bits for the
length code , 5 bits for the length extra , 15 bits for the distance code ,
and 13 bits for the distance extra . This totals 48 bits , or six bytes .
Therefore if strm - > avail_in > = 6 , then there is enough input to avoid
checking for available input while decoding .
- The maximum bytes that a single length / distance pair can output is 258
bytes , which is the maximum length that can be coded . inflate_fast ( )
requires strm - > avail_out > = 258 for each loop to avoid checking for
output space .
*/
void inflate_fast ( strm , start )
z_streamp strm ;
unsigned start ; /* inflate()'s starting value for strm->avail_out */
{
struct inflate_state FAR * state ;
2008-08-15 15:32:51 +04:00
unsigned const char FAR * in ; /* local strm->next_in */
unsigned const char FAR * last ; /* while in < last, enough input available */
2008-07-30 00:01:23 +04:00
unsigned char FAR * out ; /* local strm->next_out */
unsigned char FAR * beg ; /* inflate()'s initial strm->next_out */
unsigned char FAR * end ; /* while out < end, enough space available */
# ifdef INFLATE_STRICT
unsigned dmax ; /* maximum distance from zlib header */
# endif
unsigned wsize ; /* window size or zero if not using window */
unsigned whave ; /* valid bytes in the window */
unsigned write ; /* window write index */
unsigned char FAR * window ; /* allocated sliding window, if wsize != 0 */
unsigned long hold ; /* local strm->hold */
unsigned bits ; /* local strm->bits */
code const FAR * lcode ; /* local strm->lencode */
code const FAR * dcode ; /* local strm->distcode */
unsigned lmask ; /* mask for first level of length codes */
unsigned dmask ; /* mask for first level of distance codes */
code this ; /* retrieved table entry */
unsigned op ; /* code bits, operation, extra bits, or */
/* window position, window bytes to copy */
unsigned len ; /* match length, unused bytes */
unsigned dist ; /* match distance */
unsigned char FAR * from ; /* where to copy match from */
/* copy state to local variables */
state = ( struct inflate_state FAR * ) strm - > state ;
in = strm - > next_in - OFF ;
last = in + ( strm - > avail_in - 5 ) ;
out = strm - > next_out - OFF ;
beg = out - ( start - strm - > avail_out ) ;
end = out + ( strm - > avail_out - 257 ) ;
# ifdef INFLATE_STRICT
dmax = state - > dmax ;
# endif
wsize = state - > wsize ;
whave = state - > whave ;
write = state - > write ;
window = state - > window ;
hold = state - > hold ;
bits = state - > bits ;
lcode = state - > lencode ;
dcode = state - > distcode ;
lmask = ( 1U < < state - > lenbits ) - 1 ;
dmask = ( 1U < < state - > distbits ) - 1 ;
/* decode literals and length/distances until end-of-block or not enough
input data or output space */
do {
if ( bits < 15 ) {
hold + = ( unsigned long ) ( PUP ( in ) ) < < bits ;
bits + = 8 ;
hold + = ( unsigned long ) ( PUP ( in ) ) < < bits ;
bits + = 8 ;
}
this = lcode [ hold & lmask ] ;
dolen :
op = ( unsigned ) ( this . bits ) ;
hold > > = op ;
bits - = op ;
op = ( unsigned ) ( this . op ) ;
if ( op = = 0 ) { /* literal */
Tracevv ( ( stderr , this . val > = 0x20 & & this . val < 0x7f ?
" inflate: literal '%c' \n " :
" inflate: literal 0x%02x \n " , this . val ) ) ;
PUP ( out ) = ( unsigned char ) ( this . val ) ;
}
else if ( op & 16 ) { /* length base */
len = ( unsigned ) ( this . val ) ;
op & = 15 ; /* number of extra bits */
if ( op ) {
if ( bits < op ) {
hold + = ( unsigned long ) ( PUP ( in ) ) < < bits ;
bits + = 8 ;
}
len + = ( unsigned ) hold & ( ( 1U < < op ) - 1 ) ;
hold > > = op ;
bits - = op ;
}
Tracevv ( ( stderr , " inflate: length %u \n " , len ) ) ;
if ( bits < 15 ) {
hold + = ( unsigned long ) ( PUP ( in ) ) < < bits ;
bits + = 8 ;
hold + = ( unsigned long ) ( PUP ( in ) ) < < bits ;
bits + = 8 ;
}
this = dcode [ hold & dmask ] ;
dodist :
op = ( unsigned ) ( this . bits ) ;
hold > > = op ;
bits - = op ;
op = ( unsigned ) ( this . op ) ;
if ( op & 16 ) { /* distance base */
dist = ( unsigned ) ( this . val ) ;
op & = 15 ; /* number of extra bits */
if ( bits < op ) {
hold + = ( unsigned long ) ( PUP ( in ) ) < < bits ;
bits + = 8 ;
if ( bits < op ) {
hold + = ( unsigned long ) ( PUP ( in ) ) < < bits ;
bits + = 8 ;
}
}
dist + = ( unsigned ) hold & ( ( 1U < < op ) - 1 ) ;
# ifdef INFLATE_STRICT
if ( dist > dmax ) {
strm - > msg = ( char * ) " invalid distance too far back " ;
2008-10-20 12:45:42 +04:00
state - > mode = BAD_DATA ;
2008-07-30 00:01:23 +04:00
break ;
}
# endif
hold > > = op ;
bits - = op ;
Tracevv ( ( stderr , " inflate: distance %u \n " , dist ) ) ;
op = ( unsigned ) ( out - beg ) ; /* max distance in output */
if ( dist > op ) { /* see if copy from window */
op = dist - op ; /* distance back in window */
if ( op > whave ) {
2008-08-15 15:32:51 +04:00
strm - > msg = " invalid distance too far back " ;
2008-10-20 12:45:42 +04:00
state - > mode = BAD_DATA ;
2008-07-30 00:01:23 +04:00
break ;
}
from = window - OFF ;
if ( write = = 0 ) { /* very common case */
from + = wsize - op ;
if ( op < len ) { /* some from window */
len - = op ;
do {
PUP ( out ) = PUP ( from ) ;
} while ( - - op ) ;
from = out - dist ; /* rest from output */
}
}
else if ( write < op ) { /* wrap around window */
from + = wsize + write - op ;
op - = write ;
if ( op < len ) { /* some from end of window */
len - = op ;
do {
PUP ( out ) = PUP ( from ) ;
} while ( - - op ) ;
from = window - OFF ;
if ( write < len ) { /* some from start of window */
op = write ;
len - = op ;
do {
PUP ( out ) = PUP ( from ) ;
} while ( - - op ) ;
from = out - dist ; /* rest from output */
}
}
}
else { /* contiguous in window */
from + = write - op ;
if ( op < len ) { /* some from window */
len - = op ;
do {
PUP ( out ) = PUP ( from ) ;
} while ( - - op ) ;
from = out - dist ; /* rest from output */
}
}
while ( len > 2 ) {
PUP ( out ) = PUP ( from ) ;
PUP ( out ) = PUP ( from ) ;
PUP ( out ) = PUP ( from ) ;
len - = 3 ;
}
if ( len ) {
PUP ( out ) = PUP ( from ) ;
if ( len > 1 )
PUP ( out ) = PUP ( from ) ;
}
}
else {
from = out - dist ; /* copy direct from output */
do { /* minimum length is three */
PUP ( out ) = PUP ( from ) ;
PUP ( out ) = PUP ( from ) ;
PUP ( out ) = PUP ( from ) ;
len - = 3 ;
} while ( len > 2 ) ;
if ( len ) {
PUP ( out ) = PUP ( from ) ;
if ( len > 1 )
PUP ( out ) = PUP ( from ) ;
}
}
}
else if ( ( op & 64 ) = = 0 ) { /* 2nd level distance code */
this = dcode [ this . val + ( hold & ( ( 1U < < op ) - 1 ) ) ] ;
goto dodist ;
}
else {
2008-08-15 15:32:51 +04:00
strm - > msg = " invalid distance code " ;
2008-10-20 12:45:42 +04:00
state - > mode = BAD_DATA ;
2008-07-30 00:01:23 +04:00
break ;
}
}
else if ( ( op & 64 ) = = 0 ) { /* 2nd level length code */
this = lcode [ this . val + ( hold & ( ( 1U < < op ) - 1 ) ) ] ;
goto dolen ;
}
else if ( op & 32 ) { /* end-of-block */
Tracevv ( ( stderr , " inflate: end of block \n " ) ) ;
state - > mode = TYPE ;
break ;
}
else {
2008-08-15 15:32:51 +04:00
strm - > msg = " invalid literal/length code " ;
2008-10-20 12:45:42 +04:00
state - > mode = BAD_DATA ;
2008-07-30 00:01:23 +04:00
break ;
}
} while ( in < last & & out < end ) ;
/* return unused bytes (on entry, bits < 8, so in won't go too far back) */
len = bits > > 3 ;
in - = len ;
bits - = len < < 3 ;
hold & = ( 1U < < bits ) - 1 ;
/* update state and return */
strm - > next_in = in + OFF ;
strm - > next_out = out + OFF ;
strm - > avail_in = ( unsigned ) ( in < last ? 5 + ( last - in ) : 5 - ( in - last ) ) ;
strm - > avail_out = ( unsigned ) ( out < end ?
257 + ( end - out ) : 257 - ( out - end ) ) ;
state - > hold = hold ;
state - > bits = bits ;
return ;
}
/*
inflate_fast ( ) speedups that turned out slower ( on a PowerPC G3 750 CXe ) :
- Using bit fields for code structure
- Different op definition to avoid & for extra bits ( do & for table bits )
- Three separate decoding do - loops for direct , window , and write = = 0
- Special case for distance > 1 copies to do overlapped load and store copy
- Explicit branch predictions ( based on measured branch probabilities )
- Deferring match copy and interspersed it with decoding subsequent codes
- Swapping literal / length else
- Swapping window / direct else
- Larger unrolled copy loops ( three is about right )
- Moving len - = 3 statement into middle of loop
*/
# endif /* !ASMINF */