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