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