Blame view

lib/zlib_inflate/inffast.c 12.7 KB
4f3865fb5   Richard Purdie   [PATCH] zlib_infl...
1
2
3
  /* inffast.c -- fast decoding
   * Copyright (C) 1995-2004 Mark Adler
   * For conditions of distribution and use, see copyright notice in zlib.h
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
4
5
6
7
   */
  
  #include <linux/zutil.h>
  #include "inftrees.h"
4f3865fb5   Richard Purdie   [PATCH] zlib_infl...
8
  #include "inflate.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
9
  #include "inffast.h"
4f3865fb5   Richard Purdie   [PATCH] zlib_infl...
10
11
12
13
14
15
16
17
18
19
20
21
22
  #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)
   */
e69eae655   Joakim Tjernlund   zlib: make new op...
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
  union uu {
  	unsigned short us;
  	unsigned char b[2];
  };
  
  /* Endian independed version */
  static inline unsigned short
  get_unaligned16(const unsigned short *p)
  {
  	union uu  mm;
  	unsigned char *b = (unsigned char *)p;
  
  	mm.b[0] = b[0];
  	mm.b[1] = b[1];
  	return mm.us;
  }
4f3865fb5   Richard Purdie   [PATCH] zlib_infl...
39
40
41
  #ifdef POSTINC
  #  define OFF 0
  #  define PUP(a) *(a)++
e69eae655   Joakim Tjernlund   zlib: make new op...
42
  #  define UP_UNALIGNED(a) get_unaligned16((a)++)
4f3865fb5   Richard Purdie   [PATCH] zlib_infl...
43
44
45
  #else
  #  define OFF 1
  #  define PUP(a) *++(a)
e69eae655   Joakim Tjernlund   zlib: make new op...
46
  #  define UP_UNALIGNED(a) get_unaligned16(++(a))
4f3865fb5   Richard Purdie   [PATCH] zlib_infl...
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
  #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 16K input buffer and a 64K 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
          BAD -- error in block data
  
     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.
b762450e8   Randy Dunlap   [PATCH] zlib infl...
83
84
  
      - @start:	inflate()'s starting value for strm->avail_out
4f3865fb5   Richard Purdie   [PATCH] zlib_infl...
85
   */
b762450e8   Randy Dunlap   [PATCH] zlib infl...
86
  void inflate_fast(z_streamp strm, unsigned start)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
87
  {
4f3865fb5   Richard Purdie   [PATCH] zlib_infl...
88
      struct inflate_state *state;
8336793ba   Denys Vlasenko   [ZLIB]: Move bnx2...
89
90
91
92
93
      const unsigned char *in;    /* local strm->next_in */
      const unsigned char *last;  /* while in < last, enough input available */
      unsigned char *out;         /* local strm->next_out */
      unsigned char *beg;         /* inflate()'s initial strm->next_out */
      unsigned char *end;         /* while out < end, enough space available */
4f3865fb5   Richard Purdie   [PATCH] zlib_infl...
94
95
96
97
98
99
  #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 */
8336793ba   Denys Vlasenko   [ZLIB]: Move bnx2...
100
      unsigned char *window;      /* allocated sliding window, if wsize != 0 */
4f3865fb5   Richard Purdie   [PATCH] zlib_infl...
101
102
      unsigned long hold;         /* local strm->hold */
      unsigned bits;              /* local strm->bits */
8336793ba   Denys Vlasenko   [ZLIB]: Move bnx2...
103
104
      code const *lcode;          /* local strm->lencode */
      code const *dcode;          /* local strm->distcode */
4f3865fb5   Richard Purdie   [PATCH] zlib_infl...
105
106
107
108
109
110
111
      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 */
8336793ba   Denys Vlasenko   [ZLIB]: Move bnx2...
112
      unsigned char *from;        /* where to copy match from */
4f3865fb5   Richard Purdie   [PATCH] zlib_infl...
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
  
      /* copy state to local variables */
      state = (struct inflate_state *)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 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
137
      do {
4f3865fb5   Richard Purdie   [PATCH] zlib_infl...
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
          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 */
              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;
              }
              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";
                      state->mode = BAD;
                      break;
                  }
  #endif
                  hold >>= op;
                  bits -= op;
                  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) {
                          strm->msg = (char *)"invalid distance too far back";
                          state->mode = BAD;
                          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 {
ac4c2a3bb   Joakim Tjernlund   zlib: optimize in...
259
260
  		    unsigned short *sout;
  		    unsigned long loops;
4f3865fb5   Richard Purdie   [PATCH] zlib_infl...
261
                      from = out - dist;          /* copy direct from output */
ac4c2a3bb   Joakim Tjernlund   zlib: optimize in...
262
263
264
265
266
267
268
269
270
271
272
273
274
  		    /* minimum length is three */
  		    /* Align out addr */
  		    if (!((long)(out - 1 + OFF) & 1)) {
  			PUP(out) = PUP(from);
  			len--;
  		    }
  		    sout = (unsigned short *)(out - OFF);
  		    if (dist > 2) {
  			unsigned short *sfrom;
  
  			sfrom = (unsigned short *)(from - OFF);
  			loops = len >> 1;
  			do
e69eae655   Joakim Tjernlund   zlib: make new op...
275
276
277
  #ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
  			    PUP(sout) = PUP(sfrom);
  #else
ac4c2a3bb   Joakim Tjernlund   zlib: optimize in...
278
  			    PUP(sout) = UP_UNALIGNED(sfrom);
e69eae655   Joakim Tjernlund   zlib: make new op...
279
  #endif
ac4c2a3bb   Joakim Tjernlund   zlib: optimize in...
280
281
282
283
284
  			while (--loops);
  			out = (unsigned char *)sout + OFF;
  			from = (unsigned char *)sfrom + OFF;
  		    } else { /* dist == 1 or dist == 2 */
  			unsigned short pat16;
51ea3f6a4   Joakim Tjernlund   inflate_fast: sou...
285
  			pat16 = *(sout-1+OFF);
e69eae655   Joakim Tjernlund   zlib: make new op...
286
287
288
289
290
291
292
  			if (dist == 1) {
  				union uu mm;
  				/* copy one char pattern to both bytes */
  				mm.us = pat16;
  				mm.b[0] = mm.b[1];
  				pat16 = mm.us;
  			}
ac4c2a3bb   Joakim Tjernlund   zlib: optimize in...
293
294
295
296
297
298
299
300
  			loops = len >> 1;
  			do
  			    PUP(sout) = pat16;
  			while (--loops);
  			out = (unsigned char *)sout + OFF;
  		    }
  		    if (len & 1)
  			PUP(out) = PUP(from);
4f3865fb5   Richard Purdie   [PATCH] zlib_infl...
301
302
303
304
305
                  }
              }
              else if ((op & 64) == 0) {          /* 2nd level distance code */
                  this = dcode[this.val + (hold & ((1U << op) - 1))];
                  goto dodist;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
306
              }
4f3865fb5   Richard Purdie   [PATCH] zlib_infl...
307
308
309
310
              else {
                  strm->msg = (char *)"invalid distance code";
                  state->mode = BAD;
                  break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
311
              }
4f3865fb5   Richard Purdie   [PATCH] zlib_infl...
312
313
314
315
316
317
318
          }
          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 */
              state->mode = TYPE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
319
              break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
320
          }
4f3865fb5   Richard Purdie   [PATCH] zlib_infl...
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
          else {
              strm->msg = (char *)"invalid literal/length code";
              state->mode = BAD;
              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;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
343
  }
4f3865fb5   Richard Purdie   [PATCH] zlib_infl...
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
  
  /*
     inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe):
     - 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 */