xref: /titanic_51/usr/src/contrib/zlib/inffast.c (revision 64c3d15931c5518f89221f1e36d3015dbb54b9bd)
1ab9e68a2SToomas Soome /* inffast.c -- fast decoding
2ab9e68a2SToomas Soome  * Copyright (C) 1995-2017 Mark Adler
3ab9e68a2SToomas Soome  * For conditions of distribution and use, see copyright notice in zlib.h
4ab9e68a2SToomas Soome  */
5ab9e68a2SToomas Soome 
6ab9e68a2SToomas Soome #include "zutil.h"
7ab9e68a2SToomas Soome #include "inftrees.h"
8ab9e68a2SToomas Soome #include "inflate.h"
9ab9e68a2SToomas Soome #include "inffast.h"
10ab9e68a2SToomas Soome 
11ab9e68a2SToomas Soome #ifdef ASMINF
12ab9e68a2SToomas Soome #  pragma message("Assembler code may have bugs -- use at your own risk")
13ab9e68a2SToomas Soome #else
14ab9e68a2SToomas Soome 
15ab9e68a2SToomas Soome /*
16ab9e68a2SToomas Soome    Decode literal, length, and distance codes and write out the resulting
17ab9e68a2SToomas Soome    literal and match bytes until either not enough input or output is
18ab9e68a2SToomas Soome    available, an end-of-block is encountered, or a data error is encountered.
19ab9e68a2SToomas Soome    When large enough input and output buffers are supplied to inflate(), for
20ab9e68a2SToomas Soome    example, a 16K input buffer and a 64K output buffer, more than 95% of the
21ab9e68a2SToomas Soome    inflate execution time is spent in this routine.
22ab9e68a2SToomas Soome 
23ab9e68a2SToomas Soome    Entry assumptions:
24ab9e68a2SToomas Soome 
25ab9e68a2SToomas Soome         state->mode == LEN
26ab9e68a2SToomas Soome         strm->avail_in >= 6
27ab9e68a2SToomas Soome         strm->avail_out >= 258
28ab9e68a2SToomas Soome         start >= strm->avail_out
29ab9e68a2SToomas Soome         state->bits < 8
30ab9e68a2SToomas Soome 
31ab9e68a2SToomas Soome    On return, state->mode is one of:
32ab9e68a2SToomas Soome 
33ab9e68a2SToomas Soome         LEN -- ran out of enough output space or enough available input
34ab9e68a2SToomas Soome         TYPE -- reached end of block code, inflate() to interpret next block
35ab9e68a2SToomas Soome         BAD -- error in block data
36ab9e68a2SToomas Soome 
37ab9e68a2SToomas Soome    Notes:
38ab9e68a2SToomas Soome 
39ab9e68a2SToomas Soome     - The maximum input bits used by a length/distance pair is 15 bits for the
40ab9e68a2SToomas Soome       length code, 5 bits for the length extra, 15 bits for the distance code,
41ab9e68a2SToomas Soome       and 13 bits for the distance extra.  This totals 48 bits, or six bytes.
42ab9e68a2SToomas Soome       Therefore if strm->avail_in >= 6, then there is enough input to avoid
43ab9e68a2SToomas Soome       checking for available input while decoding.
44ab9e68a2SToomas Soome 
45ab9e68a2SToomas Soome     - The maximum bytes that a single length/distance pair can output is 258
46ab9e68a2SToomas Soome       bytes, which is the maximum length that can be coded.  inflate_fast()
47ab9e68a2SToomas Soome       requires strm->avail_out >= 258 for each loop to avoid checking for
48ab9e68a2SToomas Soome       output space.
49ab9e68a2SToomas Soome  */
50ab9e68a2SToomas Soome void ZLIB_INTERNAL inflate_fast(z_streamp strm, unsigned start)
51ab9e68a2SToomas Soome {
52ab9e68a2SToomas Soome     struct inflate_state FAR *state;
53ab9e68a2SToomas Soome     z_const unsigned char FAR *in;      /* local strm->next_in */
54ab9e68a2SToomas Soome     z_const unsigned char FAR *last;    /* have enough input while in < last */
55ab9e68a2SToomas Soome     unsigned char FAR *out;     /* local strm->next_out */
56ab9e68a2SToomas Soome     unsigned char FAR *beg;     /* inflate()'s initial strm->next_out */
57ab9e68a2SToomas Soome     unsigned char FAR *end;     /* while out < end, enough space available */
58ab9e68a2SToomas Soome #ifdef INFLATE_STRICT
59ab9e68a2SToomas Soome     unsigned dmax;              /* maximum distance from zlib header */
60ab9e68a2SToomas Soome #endif
61ab9e68a2SToomas Soome     unsigned wsize;             /* window size or zero if not using window */
62ab9e68a2SToomas Soome     unsigned whave;             /* valid bytes in the window */
63ab9e68a2SToomas Soome     unsigned wnext;             /* window write index */
64ab9e68a2SToomas Soome     unsigned char FAR *window;  /* allocated sliding window, if wsize != 0 */
65ab9e68a2SToomas Soome     unsigned long hold;         /* local strm->hold */
66ab9e68a2SToomas Soome     unsigned bits;              /* local strm->bits */
67ab9e68a2SToomas Soome     code const FAR *lcode;      /* local strm->lencode */
68ab9e68a2SToomas Soome     code const FAR *dcode;      /* local strm->distcode */
69ab9e68a2SToomas Soome     unsigned lmask;             /* mask for first level of length codes */
70ab9e68a2SToomas Soome     unsigned dmask;             /* mask for first level of distance codes */
71*64c3d159SToomas Soome     code const *here;           /* retrieved table entry */
72ab9e68a2SToomas Soome     unsigned op;                /* code bits, operation, extra bits, or */
73ab9e68a2SToomas Soome                                 /*  window position, window bytes to copy */
74ab9e68a2SToomas Soome     unsigned len;               /* match length, unused bytes */
75ab9e68a2SToomas Soome     unsigned dist;              /* match distance */
76ab9e68a2SToomas Soome     unsigned char FAR *from;    /* where to copy match from */
77ab9e68a2SToomas Soome 
78ab9e68a2SToomas Soome     /* copy state to local variables */
79ab9e68a2SToomas Soome     state = (struct inflate_state FAR *)strm->state;
80ab9e68a2SToomas Soome     in = strm->next_in;
81ab9e68a2SToomas Soome     last = in + (strm->avail_in - 5);
82ab9e68a2SToomas Soome     out = strm->next_out;
83ab9e68a2SToomas Soome     beg = out - (start - strm->avail_out);
84ab9e68a2SToomas Soome     end = out + (strm->avail_out - 257);
85ab9e68a2SToomas Soome #ifdef INFLATE_STRICT
86ab9e68a2SToomas Soome     dmax = state->dmax;
87ab9e68a2SToomas Soome #endif
88ab9e68a2SToomas Soome     wsize = state->wsize;
89ab9e68a2SToomas Soome     whave = state->whave;
90ab9e68a2SToomas Soome     wnext = state->wnext;
91ab9e68a2SToomas Soome     window = state->window;
92ab9e68a2SToomas Soome     hold = state->hold;
93ab9e68a2SToomas Soome     bits = state->bits;
94ab9e68a2SToomas Soome     lcode = state->lencode;
95ab9e68a2SToomas Soome     dcode = state->distcode;
96ab9e68a2SToomas Soome     lmask = (1U << state->lenbits) - 1;
97ab9e68a2SToomas Soome     dmask = (1U << state->distbits) - 1;
98ab9e68a2SToomas Soome 
99ab9e68a2SToomas Soome     /* decode literals and length/distances until end-of-block or not enough
100ab9e68a2SToomas Soome        input data or output space */
101ab9e68a2SToomas Soome     do {
102ab9e68a2SToomas Soome         if (bits < 15) {
103ab9e68a2SToomas Soome             hold += (unsigned long)(*in++) << bits;
104ab9e68a2SToomas Soome             bits += 8;
105ab9e68a2SToomas Soome             hold += (unsigned long)(*in++) << bits;
106ab9e68a2SToomas Soome             bits += 8;
107ab9e68a2SToomas Soome         }
108*64c3d159SToomas Soome         here = lcode + (hold & lmask);
109ab9e68a2SToomas Soome       dolen:
110*64c3d159SToomas Soome         op = (unsigned)(here->bits);
111ab9e68a2SToomas Soome         hold >>= op;
112ab9e68a2SToomas Soome         bits -= op;
113*64c3d159SToomas Soome         op = (unsigned)(here->op);
114ab9e68a2SToomas Soome         if (op == 0) {                          /* literal */
115*64c3d159SToomas Soome             Tracevv((stderr, here->val >= 0x20 && here->val < 0x7f ?
116ab9e68a2SToomas Soome                     "inflate:         literal '%c'\n" :
117*64c3d159SToomas Soome                     "inflate:         literal 0x%02x\n", here->val));
118*64c3d159SToomas Soome             *out++ = (unsigned char)(here->val);
119ab9e68a2SToomas Soome         }
120ab9e68a2SToomas Soome         else if (op & 16) {                     /* length base */
121*64c3d159SToomas Soome             len = (unsigned)(here->val);
122ab9e68a2SToomas Soome             op &= 15;                           /* number of extra bits */
123ab9e68a2SToomas Soome             if (op) {
124ab9e68a2SToomas Soome                 if (bits < op) {
125ab9e68a2SToomas Soome                     hold += (unsigned long)(*in++) << bits;
126ab9e68a2SToomas Soome                     bits += 8;
127ab9e68a2SToomas Soome                 }
128ab9e68a2SToomas Soome                 len += (unsigned)hold & ((1U << op) - 1);
129ab9e68a2SToomas Soome                 hold >>= op;
130ab9e68a2SToomas Soome                 bits -= op;
131ab9e68a2SToomas Soome             }
132ab9e68a2SToomas Soome             Tracevv((stderr, "inflate:         length %u\n", len));
133ab9e68a2SToomas Soome             if (bits < 15) {
134ab9e68a2SToomas Soome                 hold += (unsigned long)(*in++) << bits;
135ab9e68a2SToomas Soome                 bits += 8;
136ab9e68a2SToomas Soome                 hold += (unsigned long)(*in++) << bits;
137ab9e68a2SToomas Soome                 bits += 8;
138ab9e68a2SToomas Soome             }
139*64c3d159SToomas Soome             here = dcode + (hold & dmask);
140ab9e68a2SToomas Soome           dodist:
141*64c3d159SToomas Soome             op = (unsigned)(here->bits);
142ab9e68a2SToomas Soome             hold >>= op;
143ab9e68a2SToomas Soome             bits -= op;
144*64c3d159SToomas Soome             op = (unsigned)(here->op);
145ab9e68a2SToomas Soome             if (op & 16) {                      /* distance base */
146*64c3d159SToomas Soome                 dist = (unsigned)(here->val);
147ab9e68a2SToomas Soome                 op &= 15;                       /* number of extra bits */
148ab9e68a2SToomas Soome                 if (bits < op) {
149ab9e68a2SToomas Soome                     hold += (unsigned long)(*in++) << bits;
150ab9e68a2SToomas Soome                     bits += 8;
151ab9e68a2SToomas Soome                     if (bits < op) {
152ab9e68a2SToomas Soome                         hold += (unsigned long)(*in++) << bits;
153ab9e68a2SToomas Soome                         bits += 8;
154ab9e68a2SToomas Soome                     }
155ab9e68a2SToomas Soome                 }
156ab9e68a2SToomas Soome                 dist += (unsigned)hold & ((1U << op) - 1);
157ab9e68a2SToomas Soome #ifdef INFLATE_STRICT
158ab9e68a2SToomas Soome                 if (dist > dmax) {
159ab9e68a2SToomas Soome                     strm->msg = (char *)"invalid distance too far back";
160ab9e68a2SToomas Soome                     state->mode = BAD;
161ab9e68a2SToomas Soome                     break;
162ab9e68a2SToomas Soome                 }
163ab9e68a2SToomas Soome #endif
164ab9e68a2SToomas Soome                 hold >>= op;
165ab9e68a2SToomas Soome                 bits -= op;
166ab9e68a2SToomas Soome                 Tracevv((stderr, "inflate:         distance %u\n", dist));
167ab9e68a2SToomas Soome                 op = (unsigned)(out - beg);     /* max distance in output */
168ab9e68a2SToomas Soome                 if (dist > op) {                /* see if copy from window */
169ab9e68a2SToomas Soome                     op = dist - op;             /* distance back in window */
170ab9e68a2SToomas Soome                     if (op > whave) {
171ab9e68a2SToomas Soome                         if (state->sane) {
172ab9e68a2SToomas Soome                             strm->msg =
173ab9e68a2SToomas Soome                                 (char *)"invalid distance too far back";
174ab9e68a2SToomas Soome                             state->mode = BAD;
175ab9e68a2SToomas Soome                             break;
176ab9e68a2SToomas Soome                         }
177ab9e68a2SToomas Soome #ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
178ab9e68a2SToomas Soome                         if (len <= op - whave) {
179ab9e68a2SToomas Soome                             do {
180ab9e68a2SToomas Soome                                 *out++ = 0;
181ab9e68a2SToomas Soome                             } while (--len);
182ab9e68a2SToomas Soome                             continue;
183ab9e68a2SToomas Soome                         }
184ab9e68a2SToomas Soome                         len -= op - whave;
185ab9e68a2SToomas Soome                         do {
186ab9e68a2SToomas Soome                             *out++ = 0;
187ab9e68a2SToomas Soome                         } while (--op > whave);
188ab9e68a2SToomas Soome                         if (op == 0) {
189ab9e68a2SToomas Soome                             from = out - dist;
190ab9e68a2SToomas Soome                             do {
191ab9e68a2SToomas Soome                                 *out++ = *from++;
192ab9e68a2SToomas Soome                             } while (--len);
193ab9e68a2SToomas Soome                             continue;
194ab9e68a2SToomas Soome                         }
195ab9e68a2SToomas Soome #endif
196ab9e68a2SToomas Soome                     }
197ab9e68a2SToomas Soome                     from = window;
198ab9e68a2SToomas Soome                     if (wnext == 0) {           /* very common case */
199ab9e68a2SToomas Soome                         from += wsize - op;
200ab9e68a2SToomas Soome                         if (op < len) {         /* some from window */
201ab9e68a2SToomas Soome                             len -= op;
202ab9e68a2SToomas Soome                             do {
203ab9e68a2SToomas Soome                                 *out++ = *from++;
204ab9e68a2SToomas Soome                             } while (--op);
205ab9e68a2SToomas Soome                             from = out - dist;  /* rest from output */
206ab9e68a2SToomas Soome                         }
207ab9e68a2SToomas Soome                     }
208ab9e68a2SToomas Soome                     else if (wnext < op) {      /* wrap around window */
209ab9e68a2SToomas Soome                         from += wsize + wnext - op;
210ab9e68a2SToomas Soome                         op -= wnext;
211ab9e68a2SToomas Soome                         if (op < len) {         /* some from end of window */
212ab9e68a2SToomas Soome                             len -= op;
213ab9e68a2SToomas Soome                             do {
214ab9e68a2SToomas Soome                                 *out++ = *from++;
215ab9e68a2SToomas Soome                             } while (--op);
216ab9e68a2SToomas Soome                             from = window;
217ab9e68a2SToomas Soome                             if (wnext < len) {  /* some from start of window */
218ab9e68a2SToomas Soome                                 op = wnext;
219ab9e68a2SToomas Soome                                 len -= op;
220ab9e68a2SToomas Soome                                 do {
221ab9e68a2SToomas Soome                                     *out++ = *from++;
222ab9e68a2SToomas Soome                                 } while (--op);
223ab9e68a2SToomas Soome                                 from = out - dist;      /* rest from output */
224ab9e68a2SToomas Soome                             }
225ab9e68a2SToomas Soome                         }
226ab9e68a2SToomas Soome                     }
227ab9e68a2SToomas Soome                     else {                      /* contiguous in window */
228ab9e68a2SToomas Soome                         from += wnext - op;
229ab9e68a2SToomas Soome                         if (op < len) {         /* some from window */
230ab9e68a2SToomas Soome                             len -= op;
231ab9e68a2SToomas Soome                             do {
232ab9e68a2SToomas Soome                                 *out++ = *from++;
233ab9e68a2SToomas Soome                             } while (--op);
234ab9e68a2SToomas Soome                             from = out - dist;  /* rest from output */
235ab9e68a2SToomas Soome                         }
236ab9e68a2SToomas Soome                     }
237ab9e68a2SToomas Soome                     while (len > 2) {
238ab9e68a2SToomas Soome                         *out++ = *from++;
239ab9e68a2SToomas Soome                         *out++ = *from++;
240ab9e68a2SToomas Soome                         *out++ = *from++;
241ab9e68a2SToomas Soome                         len -= 3;
242ab9e68a2SToomas Soome                     }
243ab9e68a2SToomas Soome                     if (len) {
244ab9e68a2SToomas Soome                         *out++ = *from++;
245ab9e68a2SToomas Soome                         if (len > 1)
246ab9e68a2SToomas Soome                             *out++ = *from++;
247ab9e68a2SToomas Soome                     }
248ab9e68a2SToomas Soome                 }
249ab9e68a2SToomas Soome                 else {
250ab9e68a2SToomas Soome                     from = out - dist;          /* copy direct from output */
251ab9e68a2SToomas Soome                     do {                        /* minimum length is three */
252ab9e68a2SToomas Soome                         *out++ = *from++;
253ab9e68a2SToomas Soome                         *out++ = *from++;
254ab9e68a2SToomas Soome                         *out++ = *from++;
255ab9e68a2SToomas Soome                         len -= 3;
256ab9e68a2SToomas Soome                     } while (len > 2);
257ab9e68a2SToomas Soome                     if (len) {
258ab9e68a2SToomas Soome                         *out++ = *from++;
259ab9e68a2SToomas Soome                         if (len > 1)
260ab9e68a2SToomas Soome                             *out++ = *from++;
261ab9e68a2SToomas Soome                     }
262ab9e68a2SToomas Soome                 }
263ab9e68a2SToomas Soome             }
264ab9e68a2SToomas Soome             else if ((op & 64) == 0) {          /* 2nd level distance code */
265*64c3d159SToomas Soome                 here = dcode + here->val + (hold & ((1U << op) - 1));
266ab9e68a2SToomas Soome                 goto dodist;
267ab9e68a2SToomas Soome             }
268ab9e68a2SToomas Soome             else {
269ab9e68a2SToomas Soome                 strm->msg = (char *)"invalid distance code";
270ab9e68a2SToomas Soome                 state->mode = BAD;
271ab9e68a2SToomas Soome                 break;
272ab9e68a2SToomas Soome             }
273ab9e68a2SToomas Soome         }
274ab9e68a2SToomas Soome         else if ((op & 64) == 0) {              /* 2nd level length code */
275*64c3d159SToomas Soome             here = lcode + here->val + (hold & ((1U << op) - 1));
276ab9e68a2SToomas Soome             goto dolen;
277ab9e68a2SToomas Soome         }
278ab9e68a2SToomas Soome         else if (op & 32) {                     /* end-of-block */
279ab9e68a2SToomas Soome             Tracevv((stderr, "inflate:         end of block\n"));
280ab9e68a2SToomas Soome             state->mode = TYPE;
281ab9e68a2SToomas Soome             break;
282ab9e68a2SToomas Soome         }
283ab9e68a2SToomas Soome         else {
284ab9e68a2SToomas Soome             strm->msg = (char *)"invalid literal/length code";
285ab9e68a2SToomas Soome             state->mode = BAD;
286ab9e68a2SToomas Soome             break;
287ab9e68a2SToomas Soome         }
288ab9e68a2SToomas Soome     } while (in < last && out < end);
289ab9e68a2SToomas Soome 
290ab9e68a2SToomas Soome     /* return unused bytes (on entry, bits < 8, so in won't go too far back) */
291ab9e68a2SToomas Soome     len = bits >> 3;
292ab9e68a2SToomas Soome     in -= len;
293ab9e68a2SToomas Soome     bits -= len << 3;
294ab9e68a2SToomas Soome     hold &= (1U << bits) - 1;
295ab9e68a2SToomas Soome 
296ab9e68a2SToomas Soome     /* update state and return */
297ab9e68a2SToomas Soome     strm->next_in = in;
298ab9e68a2SToomas Soome     strm->next_out = out;
299ab9e68a2SToomas Soome     strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last));
300ab9e68a2SToomas Soome     strm->avail_out = (unsigned)(out < end ?
301ab9e68a2SToomas Soome                                  257 + (end - out) : 257 - (out - end));
302ab9e68a2SToomas Soome     state->hold = hold;
303ab9e68a2SToomas Soome     state->bits = bits;
304ab9e68a2SToomas Soome     return;
305ab9e68a2SToomas Soome }
306ab9e68a2SToomas Soome 
307ab9e68a2SToomas Soome /*
308ab9e68a2SToomas Soome    inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe):
309ab9e68a2SToomas Soome    - Using bit fields for code structure
310ab9e68a2SToomas Soome    - Different op definition to avoid & for extra bits (do & for table bits)
311ab9e68a2SToomas Soome    - Three separate decoding do-loops for direct, window, and wnext == 0
312ab9e68a2SToomas Soome    - Special case for distance > 1 copies to do overlapped load and store copy
313ab9e68a2SToomas Soome    - Explicit branch predictions (based on measured branch probabilities)
314ab9e68a2SToomas Soome    - Deferring match copy and interspersed it with decoding subsequent codes
315ab9e68a2SToomas Soome    - Swapping literal/length else
316ab9e68a2SToomas Soome    - Swapping window/direct else
317ab9e68a2SToomas Soome    - Larger unrolled copy loops (three is about right)
318ab9e68a2SToomas Soome    - Moving len -= 3 statement into middle of loop
319ab9e68a2SToomas Soome  */
320ab9e68a2SToomas Soome 
321ab9e68a2SToomas Soome #endif /* !ASMINF */
322