2 FastLZ - lightning-fast lossless compression library
4 Copyright (C) 2007 Ariya Hidayat (ariya@kde.org)
5 Copyright (C) 2006 Ariya Hidayat (ariya@kde.org)
6 Copyright (C) 2005 Ariya Hidayat (ariya@kde.org)
8 Permission is hereby granted, free of charge, to any person obtaining a copy
9 of this software and associated documentation files (the "Software"), to deal
10 in the Software without restriction, including without limitation the rights
11 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 copies of the Software, and to permit persons to whom the Software is
13 furnished to do so, subject to the following conditions:
15 The above copyright notice and this permission notice shall be included in
16 all copies or substantial portions of the Software.
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
32 #if !defined(FASTLZ__COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR)
35 * Always check for bound when decompressing.
36 * Generally it is best to leave it defined.
40 #if defined(WIN32) || defined(__NT__) || defined(_WIN32) || defined(__WIN32__)
41 #if defined(_MSC_VER) || defined(__GNUC__)
42 /* #include <windows.h> */
43 #pragma warning(disable : 4242)
44 #pragma warning(disable : 4244)
49 * Give hints to the compiler for branch prediction optimization.
51 #if defined(__GNUC__) && (__GNUC__ > 2)
52 #define FASTLZ_EXPECT_CONDITIONAL(c) (__builtin_expect((c), 1))
53 #define FASTLZ_UNEXPECT_CONDITIONAL(c) (__builtin_expect((c), 0))
55 #define FASTLZ_EXPECT_CONDITIONAL(c) (c)
56 #define FASTLZ_UNEXPECT_CONDITIONAL(c) (c)
60 * Use inlined functions for supported systems.
62 #if defined(__GNUC__) || defined(__DMC__) || defined(__POCC__) ||\
63 defined(__WATCOMC__) || defined(__SUNPRO_C)
64 #define FASTLZ_INLINE inline
65 #elif defined(__BORLANDC__) || defined(_MSC_VER) || defined(__LCC__)
66 #define FASTLZ_INLINE __inline
72 * Prevent accessing more than 8-bit at once, except on x86 architectures.
74 #if !defined(FASTLZ_STRICT_ALIGN)
75 #define FASTLZ_STRICT_ALIGN
76 #if defined(__i386__) || defined(__386) /* GNU C, Sun Studio */
77 #undef FASTLZ_STRICT_ALIGN
78 #elif defined(__i486__) || defined(__i586__) || defined(__i686__) /* GNU C */
79 #undef FASTLZ_STRICT_ALIGN
80 #elif defined(_M_IX86) /* Intel, MSVC */
81 #undef FASTLZ_STRICT_ALIGN
83 #undef FASTLZ_STRICT_ALIGN
84 #elif defined(_X86_) /* MinGW */
85 #undef FASTLZ_STRICT_ALIGN
86 #elif defined(__I86__) /* Digital Mars */
87 #undef FASTLZ_STRICT_ALIGN
92 * FIXME: use preprocessor magic to set this on different platforms!
96 #define MAX_LEN 264 /* 256 + 8 */
97 #define MAX_DISTANCE 8192
99 #if !defined(FASTLZ_STRICT_ALIGN)
100 #define FASTLZ_READU16(p) (*((const unsigned short *)(p)))
102 #define FASTLZ_READU16(p) ((p)[0] | (p)[1]<<8)
106 #define HASH_SIZE (1 << HASH_LOG)
107 #define HASH_MASK (HASH_SIZE - 1)
108 #define HASH_FUNCTION(v, p) {\
109 v = FASTLZ_READU16(p);\
110 v ^= FASTLZ_READU16(p + 1)^\
111 (v>>(16 - HASH_LOG));\
116 #define FASTLZ_LEVEL 1
118 #undef FASTLZ_COMPRESSOR
119 #undef FASTLZ_DECOMPRESSOR
120 #define FASTLZ_COMPRESSOR fastlz1_compress
121 #define FASTLZ_DECOMPRESSOR fastlz1_decompress
122 static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void *input, int length,
124 static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void *input, int length,
125 void *output, int maxout);
129 #define FASTLZ_LEVEL 2
132 #define MAX_DISTANCE 8191
133 #define MAX_FARDISTANCE (65535 + MAX_DISTANCE - 1)
135 #undef FASTLZ_COMPRESSOR
136 #undef FASTLZ_DECOMPRESSOR
137 #define FASTLZ_COMPRESSOR fastlz2_compress
138 #define FASTLZ_DECOMPRESSOR fastlz2_decompress
139 static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void *input, int length,
141 static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void *input, int length,
142 void *output, int maxout);
145 int fastlz_compress(const void *input, int length, void *output)
147 /* for short block, choose fastlz1 */
149 return fastlz1_compress(input, length, output);
152 return fastlz2_compress(input, length, output);
155 int fastlz_decompress(const void *input, int length, void *output, int maxout)
157 /* magic identifier for compression level */
158 int level = ((*(const unsigned char *)input) >> 5) + 1;
161 return fastlz1_decompress(input, length, output, maxout);
163 return fastlz2_decompress(input, length, output, maxout);
165 /* unknown level, trigger error */
169 int fastlz_compress_level(int level, const void *input, int length,
173 return fastlz1_compress(input, length, output);
175 return fastlz2_compress(input, length, output);
180 #else /* !defined(FASTLZ_COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) */
183 static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void *input, int length,
186 const unsigned char *ip = (const unsigned char *) input;
187 const unsigned char *ip_bound = ip + length - 2;
188 const unsigned char *ip_limit = ip + length - 12;
189 unsigned char *op = (unsigned char *) output;
190 static const unsigned char *g_htab[HASH_SIZE];
192 const unsigned char **htab = g_htab;
193 const unsigned char **hslot;
199 if (FASTLZ_UNEXPECT_CONDITIONAL(length < 4)) {
201 /* create literal copy only */
204 while (ip <= ip_bound)
211 /* initializes hash table */
212 for (hslot = htab; hslot < htab + HASH_SIZE; hslot++)
215 /* we start with literal copy */
217 *op++ = MAX_COPY - 1;
222 while (FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit)) {
223 const unsigned char *ref;
224 unsigned int distance;
226 /* minimum match length */
227 unsigned int len = 3;
229 /* comparison starting-point */
230 const unsigned char *anchor = ip;
232 /* check for a run */
233 #if FASTLZ_LEVEL == 2
234 if (ip[0] == ip[-1] &&
235 FASTLZ_READU16(ip - 1) == FASTLZ_READU16(ip + 1)) {
238 ref = anchor - 1 + 3;
243 /* find potential match */
244 HASH_FUNCTION(hval, ip);
248 /* calculate distance to the match */
249 distance = anchor - ref;
251 /* update hash table */
256 /* is this a match? check the first 3 bytes */
258 #if FASTLZ_LEVEL == 1
259 (distance >= MAX_DISTANCE) ||
261 (distance >= MAX_FARDISTANCE) ||
263 *ref++ != *ip++ || *ref++ != *ip++ ||
267 #if FASTLZ_LEVEL == 2
268 /* far, needs at least 5-byte match */
269 if (distance >= MAX_DISTANCE) {
270 if (*ip++ != *ref++ || *ip++ != *ref++)
278 /* last matched byte */
281 /* distance is biased */
285 /* zero distance means a run */
286 unsigned char x = ip[-1];
287 while (ip < ip_bound)
294 /* safe because the outer check
295 * against ip limit */
312 while (ip < ip_bound)
318 /* if we have copied something, adjust the copy count */
320 /* copy is biased, '0' means 1 byte copy */
321 *(op - copy - 1) = copy - 1;
323 /* back, to overwrite the copy count */
326 /* reset literal counter */
329 /* length is biased, '1' means a match of 3 bytes */
333 /* encode the match */
334 #if FASTLZ_LEVEL == 2
335 if (distance < MAX_DISTANCE) {
337 *op++ = (len << 5) + (distance >> 8);
338 *op++ = (distance & 255);
340 *op++ = (7 << 5) + (distance >> 8);
341 for (len -= 7; len >= 255; len -= 255)
344 *op++ = (distance & 255);
347 /* far away, but not yet in the another galaxy... */
349 distance -= MAX_DISTANCE;
350 *op++ = (len << 5) + 31;
352 *op++ = distance >> 8;
353 *op++ = distance & 255;
355 distance -= MAX_DISTANCE;
356 *op++ = (7 << 5) + 31;
357 for (len -= 7; len >= 255; len -= 255)
361 *op++ = distance >> 8;
362 *op++ = distance & 255;
367 if (FASTLZ_UNEXPECT_CONDITIONAL(len > MAX_LEN - 2))
368 while (len > MAX_LEN - 2) {
369 *op++ = (7 << 5) + (distance >> 8);
370 *op++ = MAX_LEN - 2 - 7 - 2;
371 *op++ = (distance & 255);
376 *op++ = (len << 5) + (distance >> 8);
377 *op++ = (distance & 255);
379 *op++ = (7 << 5) + (distance >> 8);
381 *op++ = (distance & 255);
385 /* update the hash at match boundary */
386 HASH_FUNCTION(hval, ip);
388 HASH_FUNCTION(hval, ip);
391 /* assuming literal copy */
392 *op++ = MAX_COPY - 1;
400 if (FASTLZ_UNEXPECT_CONDITIONAL(copy == MAX_COPY)) {
402 *op++ = MAX_COPY - 1;
406 /* left-over as literal copy */
408 while (ip <= ip_bound) {
411 if (copy == MAX_COPY) {
413 *op++ = MAX_COPY - 1;
417 /* if we have copied something, adjust the copy length */
419 *(op - copy - 1) = copy - 1;
423 #if FASTLZ_LEVEL == 2
424 /* marker for fastlz2 */
425 *(unsigned char *)output |= (1 << 5);
428 return op - (unsigned char *)output;
431 static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void *input, int length,
432 void *output, int maxout)
434 const unsigned char *ip = (const unsigned char *) input;
435 const unsigned char *ip_limit = ip + length;
436 unsigned char *op = (unsigned char *) output;
437 unsigned char *op_limit = op + maxout;
438 unsigned int ctrl = (*ip++) & 31;
442 const unsigned char *ref = op;
443 unsigned int len = ctrl >> 5;
444 unsigned int ofs = (ctrl & 31) << 8;
447 #if FASTLZ_LEVEL == 2
453 #if FASTLZ_LEVEL == 1
460 } while (code == 255);
464 /* match from 16-bit distance */
465 if (FASTLZ_UNEXPECT_CONDITIONAL(code == 255))
466 if (FASTLZ_EXPECT_CONDITIONAL(ofs ==
470 ref = op - ofs - MAX_DISTANCE;
475 if (FASTLZ_UNEXPECT_CONDITIONAL(op + len + 3 >
479 if (FASTLZ_UNEXPECT_CONDITIONAL(ref - 1 <
480 (unsigned char *)output)
485 if (FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit))
491 /* optimize copy for a run */
492 unsigned char b = ref[-1];
499 #if !defined(FASTLZ_STRICT_ALIGN)
500 const unsigned short *p;
503 /* copy from reference */
509 #if !defined(FASTLZ_STRICT_ALIGN)
510 /* copy a byte, so that now it's word aligned */
516 /* copy 16-bit at once */
517 q = (unsigned short *) op;
519 p = (const unsigned short *) ref;
520 for (len >>= 1; len > 4; len -= 4) {
536 if (FASTLZ_UNEXPECT_CONDITIONAL(op + ctrl > op_limit))
538 if (FASTLZ_UNEXPECT_CONDITIONAL(ip + ctrl > ip_limit))
543 for (--ctrl; ctrl; ctrl--)
546 loop = FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit);
550 } while (FASTLZ_EXPECT_CONDITIONAL(loop));
552 return op - (unsigned char *)output;
555 #endif /* !defined(FASTLZ_COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) */