lzo: update to 2.09
[tomato.git] / release / src / router / lzo / src / lzo1a.c
blob1af4dba1ad9e759052ad63790d0d58f2cff4c5bc
1 /* lzo1a.c -- implementation of the LZO1A algorithm
3 This file is part of the LZO real-time data compression library.
5 Copyright (C) 1996-2015 Markus Franz Xaver Johannes Oberhumer
6 All Rights Reserved.
8 The LZO library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2 of
11 the License, or (at your option) any later version.
13 The LZO library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with the LZO library; see the file COPYING.
20 If not, write to the Free Software Foundation, Inc.,
21 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 Markus F.X.J. Oberhumer
24 <markus@oberhumer.com>
25 http://www.oberhumer.com/opensource/lzo/
29 #include "lzo_conf.h"
30 #include <lzo/lzo1a.h>
33 /***********************************************************************
34 // The next two defines can be changed to customize LZO1A.
35 // The default version is LZO1A-5/1.
36 ************************************************************************/
38 /* run bits (3 - 5) - the compressor and the decompressor
39 * must use the same value. */
40 #if !defined(RBITS)
41 # define RBITS 5
42 #endif
44 /* compression level (1 - 9) - this only affects the compressor.
45 * 1 is fastest, 9 is best compression ratio
47 #if !defined(CLEVEL)
48 # define CLEVEL 1 /* fastest by default */
49 #endif
52 /* Collect statistics */
53 #if 0 && !defined(LZO_COLLECT_STATS)
54 # define LZO_COLLECT_STATS 1
55 #endif
58 /***********************************************************************
59 // You should not have to change anything below this line.
60 ************************************************************************/
62 /* check configuration */
63 #if (RBITS < 3 || RBITS > 5)
64 # error "invalid RBITS"
65 #endif
66 #if (CLEVEL < 1 || CLEVEL > 9)
67 # error "invalid CLEVEL"
68 #endif
71 /***********************************************************************
72 // internal configuration
73 // all of these affect compression only
74 ************************************************************************/
76 /* choose the hashing strategy */
77 #ifndef LZO_HASH
78 #define LZO_HASH LZO_HASH_LZO_INCREMENTAL_A
79 #endif
80 #define D_INDEX1(d,p) d = DM(DMUL(0x21,DX2(p,5,5)) >> 5)
81 #define D_INDEX2(d,p) d = d ^ D_MASK
83 #include "lzo1a_de.h"
84 #include "stats1a.h"
87 /* check other constants */
88 #if (LBITS < 5 || LBITS > 8)
89 # error "invalid LBITS"
90 #endif
93 #if (LZO_COLLECT_STATS)
94 static lzo1a_stats_t lzo_statistics;
95 lzo1a_stats_t *lzo1a_stats = &lzo_statistics;
96 # define lzo_stats lzo1a_stats
97 #endif
100 /***********************************************************************
101 // get algorithm info, return memory required for compression
102 ************************************************************************/
104 LZO_EXTERN(lzo_uint) lzo1a_info ( int *rbits, int *clevel );
106 LZO_PUBLIC(lzo_uint)
107 lzo1a_info ( int *rbits, int *clevel )
109 if (rbits)
110 *rbits = RBITS;
111 if (clevel)
112 *clevel = CLEVEL;
113 return D_SIZE * lzo_sizeof(lzo_bytep);
117 /***********************************************************************
118 // LZO1A decompress a block of data.
120 // Could be easily translated into assembly code.
121 ************************************************************************/
123 LZO_PUBLIC(int)
124 lzo1a_decompress ( const lzo_bytep in , lzo_uint in_len,
125 lzo_bytep out, lzo_uintp out_len,
126 lzo_voidp wrkmem )
128 lzo_bytep op;
129 const lzo_bytep ip;
130 lzo_uint t;
131 const lzo_bytep m_pos;
132 const lzo_bytep const ip_end = in + in_len;
134 LZO_UNUSED(wrkmem);
136 op = out;
137 ip = in;
138 while (ip < ip_end)
140 t = *ip++; /* get marker */
141 LZO_STATS(lzo_stats->marker[t]++);
143 if (t == 0) /* a R0 literal run */
145 t = *ip++;
146 if (t >= R0FAST - R0MIN) /* a long R0 run */
148 t -= R0FAST - R0MIN;
149 if (t == 0)
150 t = R0FAST;
151 else
153 #if 0
154 t = 256u << ((unsigned) t);
155 #else
156 /* help the optimizer */
157 lzo_uint tt = 256;
158 do tt <<= 1; while (--t > 0);
159 t = tt;
160 #endif
162 MEMCPY8_DS(op,ip,t);
163 continue;
165 t += R0MIN;
166 goto literal;
168 else if (t < R0MIN) /* a short literal run */
170 literal:
171 MEMCPY_DS(op,ip,t);
173 /* after a literal a match must follow */
174 while (ip < ip_end)
176 t = *ip++; /* get R1 marker */
177 if (t >= R0MIN)
178 goto match;
180 /* R1 match - a context sensitive 3 byte match + 1 byte literal */
181 assert((t & OMASK) == t);
182 m_pos = op - MIN_OFFSET;
183 m_pos -= t | (((lzo_uint) *ip++) << OBITS);
184 assert(m_pos >= out); assert(m_pos < op);
185 *op++ = m_pos[0];
186 *op++ = m_pos[1];
187 *op++ = m_pos[2];
188 *op++ = *ip++;
191 else /* a match */
193 match:
194 /* get match offset */
195 m_pos = op - MIN_OFFSET;
196 m_pos -= (t & OMASK) | (((lzo_uint) *ip++) << OBITS);
197 assert(m_pos >= out); assert(m_pos < op);
199 /* get match len */
200 if (t < ((MSIZE - 1) << OBITS)) /* a short match */
202 t >>= OBITS;
203 *op++ = *m_pos++;
204 *op++ = *m_pos++;
205 MEMCPY_DS(op,m_pos,t);
207 else /* a long match */
209 #if (LBITS < 8)
210 t = (MIN_MATCH_LONG - THRESHOLD) + ((lzo_uint)(*ip++) & LMASK);
211 #else
212 t = (MIN_MATCH_LONG - THRESHOLD) + (lzo_uint)(*ip++);
213 #endif
214 *op++ = *m_pos++;
215 *op++ = *m_pos++;
216 MEMCPY_DS(op,m_pos,t);
217 #if (LBITS < 8)
218 /* a very short literal following a long match */
219 t = ip[-1] >> LBITS;
220 if (t) do
221 *op++ = *ip++;
222 while (--t);
223 #endif
228 *out_len = pd(op, out);
230 /* the next line is the only check in the decompressor */
231 return (ip == ip_end ? LZO_E_OK :
232 (ip < ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN));
237 /***********************************************************************
238 // LZO1A compress a block of data.
240 // I apologize for the spaghetti code, but it really helps the optimizer.
241 ************************************************************************/
243 #include "lzo1a_cr.ch"
245 static int
246 do_compress ( const lzo_bytep in , lzo_uint in_len,
247 lzo_bytep out, lzo_uintp out_len,
248 lzo_voidp wrkmem )
250 const lzo_bytep ip;
251 #if defined(__LZO_HASH_INCREMENTAL)
252 lzo_xint dv;
253 #endif
254 const lzo_bytep m_pos;
255 lzo_bytep op;
256 const lzo_bytep const ip_end = in+in_len - DVAL_LEN - MIN_MATCH_LONG;
257 const lzo_bytep const in_end = in+in_len - DVAL_LEN;
258 const lzo_bytep ii;
259 lzo_dict_p const dict = (lzo_dict_p) wrkmem;
260 const lzo_bytep r1 = ip_end; /* pointer for R1 match (none yet) */
261 #if (LBITS < 8)
262 const lzo_bytep im = ip_end; /* pointer to last match start */
263 #endif
265 #if !defined(NDEBUG)
266 const lzo_bytep m_pos_sav;
267 #endif
269 op = out;
270 ip = in;
271 ii = ip; /* point to start of current literal run */
273 /* init dictionary */
274 #if (LZO_DETERMINISTIC)
275 BZERO8_PTR(wrkmem,sizeof(lzo_dict_t),D_SIZE);
276 #endif
278 DVAL_FIRST(dv,ip); UPDATE_D(dict,0,dv,ip,in); ip++;
279 DVAL_NEXT(dv,ip);
281 do {
282 LZO_DEFINE_UNINITIALIZED_VAR(lzo_uint, m_off, 0);
283 lzo_uint dindex;
285 DINDEX1(dindex,ip);
286 GINDEX(m_pos,m_off,dict,dindex,in);
287 if (LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,MAX_OFFSET))
288 goto literal;
289 if (m_pos[0] == ip[0] && m_pos[1] == ip[1] && m_pos[2] == ip[2])
290 goto match;
291 DINDEX2(dindex,ip);
292 GINDEX(m_pos,m_off,dict,dindex,in);
293 if (LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,MAX_OFFSET))
294 goto literal;
295 if (m_pos[0] == ip[0] && m_pos[1] == ip[1] && m_pos[2] == ip[2])
296 goto match;
297 goto literal;
299 literal:
300 UPDATE_I(dict,0,dindex,ip,in);
301 if (++ip >= ip_end)
302 break;
303 continue;
305 match:
306 UPDATE_I(dict,0,dindex,ip,in);
307 #if !defined(NDEBUG) && (LZO_DICT_USE_PTR)
308 assert(m_pos == NULL || m_pos >= in);
309 m_pos_sav = m_pos;
310 #endif
311 m_pos += 3;
313 /* we have found a match (of at least length 3) */
315 #if !defined(NDEBUG) && !(LZO_DICT_USE_PTR)
316 assert((m_pos_sav = ip - m_off) == (m_pos - 3));
317 #endif
319 assert(m_pos >= in);
320 assert(ip < ip_end);
322 /* 1) store the current literal run */
323 if (pd(ip,ii) > 0)
325 lzo_uint t = pd(ip,ii);
327 if (ip - r1 == MIN_MATCH + 1)
329 /* Code a context sensitive R1 match.
330 * This is tricky and somewhat difficult to explain:
331 * multiplex a literal run of length 1 into the previous
332 * short match of length MIN_MATCH.
333 * The key idea is:
334 * - after a short run a match MUST follow
335 * - therefore the value m = 000 in the mmmooooo marker is free
336 * - use 000ooooo to indicate a MIN_MATCH match (this
337 * is already coded) plus a 1 byte literal
339 assert(t == 1);
340 /* modify marker byte */
341 assert((op[-2] >> OBITS) == (MIN_MATCH - THRESHOLD));
342 op[-2] &= OMASK;
343 assert((op[-2] >> OBITS) == 0);
344 /* copy 1 literal */
345 *op++ = *ii;
346 LZO_STATS(lzo_stats->r1_matches++);
347 r1 = ip; /* set new R1 pointer */
349 else if (t < R0MIN)
351 /* inline the copying of a short run */
352 #if (LBITS < 8)
353 if (t < (1 << (8-LBITS)) && ii - im >= MIN_MATCH_LONG)
355 /* Code a very short literal run into the
356 * previous long match length byte.
358 LZO_STATS(lzo_stats->lit_runs_after_long_match++);
359 LZO_STATS(lzo_stats->lit_run_after_long_match[t]++);
360 assert(ii - im <= MAX_MATCH_LONG);
361 assert((op[-1] >> LBITS) == 0);
362 op[-1] = LZO_BYTE(op[-1] | (t << LBITS));
363 MEMCPY_DS(op, ii, t);
365 else
366 #endif
368 LZO_STATS(lzo_stats->lit_runs++);
369 LZO_STATS(lzo_stats->lit_run[t]++);
370 *op++ = LZO_BYTE(t);
371 MEMCPY_DS(op, ii, t);
372 r1 = ip; /* set new R1 pointer */
375 else if (t < R0FAST)
377 /* inline the copying of a short R0 run */
378 LZO_STATS(lzo_stats->r0short_runs++);
379 *op++ = 0; *op++ = LZO_BYTE(t - R0MIN);
380 MEMCPY_DS(op, ii, t);
381 r1 = ip; /* set new R1 pointer */
383 else
384 op = store_run(op,ii,t);
386 #if (LBITS < 8)
387 im = ip;
388 #endif
391 /* 2) compute match len */
392 ii = ip; /* point to start of current match */
394 /* we already matched MIN_MATCH bytes,
395 * m_pos also already advanced MIN_MATCH bytes */
396 ip += MIN_MATCH;
397 assert(m_pos < ip);
399 /* try to match another MIN_MATCH_LONG - MIN_MATCH bytes
400 * to see if we get a long match */
402 #define PS *m_pos++ != *ip++
404 #if (MIN_MATCH_LONG - MIN_MATCH == 2) /* MBITS == 2 */
405 if (PS || PS)
406 #elif (MIN_MATCH_LONG - MIN_MATCH == 6) /* MBITS == 3 */
407 if (PS || PS || PS || PS || PS || PS)
408 #elif (MIN_MATCH_LONG - MIN_MATCH == 14) /* MBITS == 4 */
409 if (PS || PS || PS || PS || PS || PS || PS ||
410 PS || PS || PS || PS || PS || PS || PS)
411 #elif (MIN_MATCH_LONG - MIN_MATCH == 30) /* MBITS == 5 */
412 if (PS || PS || PS || PS || PS || PS || PS || PS ||
413 PS || PS || PS || PS || PS || PS || PS || PS ||
414 PS || PS || PS || PS || PS || PS || PS || PS ||
415 PS || PS || PS || PS || PS || PS)
416 #else
417 # error "MBITS not yet implemented"
418 #endif
420 /* we've found a short match */
421 lzo_uint m_len;
423 /* 2a) compute match parameters */
424 assert(ip-m_pos == (int)m_off);
425 --ip; /* ran one too far, point back to non-match */
426 m_len = pd(ip, ii);
427 assert(m_len >= MIN_MATCH_SHORT);
428 assert(m_len <= MAX_MATCH_SHORT);
429 assert(m_off >= MIN_OFFSET);
430 assert(m_off <= MAX_OFFSET);
431 assert(ii-m_off == m_pos_sav);
432 assert(lzo_memcmp(m_pos_sav,ii,m_len) == 0);
433 m_off -= MIN_OFFSET;
435 /* 2b) code a short match */
436 /* code short match len + low offset bits */
437 *op++ = LZO_BYTE(((m_len - THRESHOLD) << OBITS) |
438 (m_off & OMASK));
439 /* code high offset bits */
440 *op++ = LZO_BYTE(m_off >> OBITS);
443 #if (LZO_COLLECT_STATS)
444 lzo_stats->short_matches++;
445 lzo_stats->short_match[m_len]++;
446 if (m_off < OSIZE)
447 lzo_stats->short_match_offset_osize[m_len]++;
448 if (m_off < 256)
449 lzo_stats->short_match_offset_256[m_len]++;
450 if (m_off < 1024)
451 lzo_stats->short_match_offset_1024[m_len]++;
452 #endif
455 /* 2c) Insert phrases (beginning with ii+1) into the dictionary. */
457 #define SI /* nothing */
458 #define DI ++ii; DVAL_NEXT(dv,ii); UPDATE_D(dict,0,dv,ii,in);
459 #define XI assert(ii < ip); ii = ip; DVAL_FIRST(dv,(ip));
461 #if (CLEVEL == 9) || (CLEVEL >= 7 && MBITS <= 4) || (CLEVEL >= 5 && MBITS <= 3)
462 /* Insert the whole match (ii+1)..(ip-1) into dictionary. */
463 ++ii;
464 do {
465 DVAL_NEXT(dv,ii);
466 UPDATE_D(dict,0,dv,ii,in);
467 } while (++ii < ip);
468 DVAL_NEXT(dv,ii);
469 assert(ii == ip);
470 DVAL_ASSERT(dv,ip);
471 #elif (CLEVEL >= 3)
472 SI DI DI XI
473 #elif (CLEVEL >= 2)
474 SI DI XI
475 #else
477 #endif
480 else
482 /* we've found a long match - see how far we can still go */
483 const lzo_bytep end;
484 lzo_uint m_len;
486 assert(ip <= in_end);
487 assert(ii == ip - MIN_MATCH_LONG);
489 if (pd(in_end,ip) <= (MAX_MATCH_LONG - MIN_MATCH_LONG))
490 end = in_end;
491 else
493 end = ip + (MAX_MATCH_LONG - MIN_MATCH_LONG);
494 assert(end < in_end);
497 while (ip < end && *m_pos == *ip)
498 m_pos++, ip++;
499 assert(ip <= in_end);
501 /* 2a) compute match parameters */
502 m_len = pd(ip, ii);
503 assert(m_len >= MIN_MATCH_LONG);
504 assert(m_len <= MAX_MATCH_LONG);
505 assert(m_off >= MIN_OFFSET);
506 assert(m_off <= MAX_OFFSET);
507 assert(ii-m_off == m_pos_sav);
508 assert(lzo_memcmp(m_pos_sav,ii,m_len) == 0);
509 assert(pd(ip,m_pos) == m_off);
510 m_off -= MIN_OFFSET;
512 /* 2b) code the long match */
513 /* code long match flag + low offset bits */
514 *op++ = LZO_BYTE(((MSIZE - 1) << OBITS) | (m_off & OMASK));
515 /* code high offset bits */
516 *op++ = LZO_BYTE(m_off >> OBITS);
517 /* code match len */
518 *op++ = LZO_BYTE(m_len - MIN_MATCH_LONG);
521 #if (LZO_COLLECT_STATS)
522 lzo_stats->long_matches++;
523 lzo_stats->long_match[m_len]++;
524 #endif
527 /* 2c) Insert phrases (beginning with ii+1) into the dictionary. */
528 #if (CLEVEL == 9)
529 /* Insert the whole match (ii+1)..(ip-1) into dictionary. */
530 /* This is not recommended because it is slow. */
531 ++ii;
532 do {
533 DVAL_NEXT(dv,ii);
534 UPDATE_D(dict,0,dv,ii,in);
535 } while (++ii < ip);
536 DVAL_NEXT(dv,ii);
537 assert(ii == ip);
538 DVAL_ASSERT(dv,ip);
539 #elif (CLEVEL >= 8)
540 SI DI DI DI DI DI DI DI DI XI
541 #elif (CLEVEL >= 7)
542 SI DI DI DI DI DI DI DI XI
543 #elif (CLEVEL >= 6)
544 SI DI DI DI DI DI DI XI
545 #elif (CLEVEL >= 5)
546 SI DI DI DI DI XI
547 #elif (CLEVEL >= 4)
548 SI DI DI DI XI
549 #elif (CLEVEL >= 3)
550 SI DI DI XI
551 #elif (CLEVEL >= 2)
552 SI DI XI
553 #else
555 #endif
558 /* ii now points to the start of the next literal run */
559 assert(ii == ip);
562 } while (ip < ip_end);
564 assert(ip <= in_end);
567 #if defined(LZO_RETURN_IF_NOT_COMPRESSIBLE)
568 /* return -1 if op == out to indicate that we
569 * couldn't compress and didn't copy anything.
571 if (op == out)
573 *out_len = 0;
574 return LZO_E_NOT_COMPRESSIBLE;
576 #endif
578 /* store the final literal run */
579 if (pd(in_end+DVAL_LEN,ii) > 0)
580 op = store_run(op,ii,pd(in_end+DVAL_LEN,ii));
582 *out_len = pd(op, out);
583 return 0; /* compression went ok */
587 /***********************************************************************
588 // LZO1A compress public entry point.
589 ************************************************************************/
591 LZO_PUBLIC(int)
592 lzo1a_compress ( const lzo_bytep in , lzo_uint in_len,
593 lzo_bytep out, lzo_uintp out_len,
594 lzo_voidp wrkmem )
596 int r = LZO_E_OK;
599 #if (LZO_COLLECT_STATS)
600 lzo_memset(lzo_stats,0,sizeof(*lzo_stats));
601 lzo_stats->rbits = RBITS;
602 lzo_stats->clevel = CLEVEL;
603 lzo_stats->dbits = DBITS;
604 lzo_stats->lbits = LBITS;
605 lzo_stats->min_match_short = MIN_MATCH_SHORT;
606 lzo_stats->max_match_short = MAX_MATCH_SHORT;
607 lzo_stats->min_match_long = MIN_MATCH_LONG;
608 lzo_stats->max_match_long = MAX_MATCH_LONG;
609 lzo_stats->min_offset = MIN_OFFSET;
610 lzo_stats->max_offset = MAX_OFFSET;
611 lzo_stats->r0min = R0MIN;
612 lzo_stats->r0fast = R0FAST;
613 lzo_stats->r0max = R0MAX;
614 lzo_stats->in_len = in_len;
615 #endif
618 /* don't try to compress a block that's too short */
619 if (in_len == 0)
620 *out_len = 0;
621 else if (in_len <= MIN_MATCH_LONG + DVAL_LEN + 1)
623 #if defined(LZO_RETURN_IF_NOT_COMPRESSIBLE)
624 r = LZO_E_NOT_COMPRESSIBLE;
625 #else
626 *out_len = pd(store_run(out,in,in_len), out);
627 #endif
629 else
630 r = do_compress(in,in_len,out,out_len,wrkmem);
633 #if (LZO_COLLECT_STATS)
634 lzo_stats->short_matches -= lzo_stats->r1_matches;
635 lzo_stats->short_match[MIN_MATCH] -= lzo_stats->r1_matches;
636 lzo_stats->out_len = *out_len;
637 #endif
639 return r;
643 /* vim:set ts=4 sw=4 et: */