|
|
@@ -0,0 +1,857 @@
|
|
|
+// Fast data compression library
|
|
|
+// Copyright (C) 2006-2011 Lasse Mikkel Reinhold
|
|
|
+// lar@quicklz.com
|
|
|
+//
|
|
|
+// QuickLZ can be used for free under the GPL 1, 2 or 3 license (where anything
|
|
|
+// released into public must be open source) or under a commercial license if such
|
|
|
+// has been acquired (see http://www.quicklz.com/order.html). The commercial license
|
|
|
+// does not cover derived or ported versions created by third parties under GPL.
|
|
|
+
|
|
|
+// 1.5.0 final
|
|
|
+
|
|
|
+#include "quicklz.h"
|
|
|
+
|
|
|
+#if QLZ_VERSION_MAJOR != 1 || QLZ_VERSION_MINOR != 5 || QLZ_VERSION_REVISION != 0
|
|
|
+#error quicklz.c and quicklz.h have different versions
|
|
|
+#endif
|
|
|
+
|
|
|
+#if (defined(__X86__) || defined(__i386__) || defined(i386) || defined(_M_IX86) || defined(__386__) || defined(__x86_64__) || defined(_M_X64))
|
|
|
+#define X86X64
|
|
|
+#endif
|
|
|
+
|
|
|
+#define MINOFFSET 2
|
|
|
+#define UNCONDITIONAL_MATCHLEN 6
|
|
|
+#define UNCOMPRESSED_END 4
|
|
|
+#define CWORD_LEN 4
|
|
|
+
|
|
|
+#if QLZ_COMPRESSION_LEVEL == 1 && defined QLZ_PTR_64 && QLZ_STREAMING_BUFFER == 0
|
|
|
+#define OFFSET_BASE source
|
|
|
+#define CAST (ui32)(size_t)
|
|
|
+#else
|
|
|
+#define OFFSET_BASE 0
|
|
|
+#define CAST
|
|
|
+#endif
|
|
|
+
|
|
|
+int qlz_get_setting(int setting)
|
|
|
+{
|
|
|
+ switch(setting)
|
|
|
+ {
|
|
|
+ case 0:
|
|
|
+ return QLZ_COMPRESSION_LEVEL;
|
|
|
+ case 1:
|
|
|
+ return sizeof(qlz_state_compress);
|
|
|
+ case 2:
|
|
|
+ return sizeof(qlz_state_decompress);
|
|
|
+ case 3:
|
|
|
+ return QLZ_STREAMING_BUFFER;
|
|
|
+#ifdef QLZ_MEMORY_SAFE
|
|
|
+ case 6:
|
|
|
+ return 1;
|
|
|
+#else
|
|
|
+ case 6:
|
|
|
+ return 0;
|
|
|
+#endif
|
|
|
+ case 7:
|
|
|
+ return QLZ_VERSION_MAJOR;
|
|
|
+ case 8:
|
|
|
+ return QLZ_VERSION_MINOR;
|
|
|
+ case 9:
|
|
|
+ return QLZ_VERSION_REVISION;
|
|
|
+ }
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
+#if QLZ_COMPRESSION_LEVEL == 1
|
|
|
+static int same(const unsigned char *src, size_t n)
|
|
|
+{
|
|
|
+ while(n > 0 && *(src + n) == *src)
|
|
|
+ n--;
|
|
|
+ return n == 0 ? 1 : 0;
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
+static void reset_table_compress(qlz_state_compress *state)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ for(i = 0; i < QLZ_HASH_VALUES; i++)
|
|
|
+ {
|
|
|
+#if QLZ_COMPRESSION_LEVEL == 1
|
|
|
+ state->hash[i].offset = 0;
|
|
|
+#else
|
|
|
+ state->hash_counter[i] = 0;
|
|
|
+#endif
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void reset_table_decompress(qlz_state_decompress *state)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ (void)state;
|
|
|
+ (void)i;
|
|
|
+#if QLZ_COMPRESSION_LEVEL == 2
|
|
|
+ for(i = 0; i < QLZ_HASH_VALUES; i++)
|
|
|
+ {
|
|
|
+ state->hash_counter[i] = 0;
|
|
|
+ }
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
+static __inline ui32 hash_func(ui32 i)
|
|
|
+{
|
|
|
+#if QLZ_COMPRESSION_LEVEL == 2
|
|
|
+ return ((i >> 9) ^ (i >> 13) ^ i) & (QLZ_HASH_VALUES - 1);
|
|
|
+#else
|
|
|
+ return ((i >> 12) ^ i) & (QLZ_HASH_VALUES - 1);
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
+static __inline ui32 fast_read(void const *src, ui32 bytes)
|
|
|
+{
|
|
|
+#ifndef X86X64
|
|
|
+ unsigned char *p = (unsigned char *)src;
|
|
|
+ switch(bytes)
|
|
|
+ {
|
|
|
+ case 4:
|
|
|
+ return(*p | *(p + 1) << 8 | *(p + 2) << 16 | *(p + 3) << 24);
|
|
|
+ case 3:
|
|
|
+ return(*p | *(p + 1) << 8 | *(p + 2) << 16);
|
|
|
+ case 2:
|
|
|
+ return(*p | *(p + 1) << 8);
|
|
|
+ case 1:
|
|
|
+ return(*p);
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+#else
|
|
|
+ if(bytes >= 1 && bytes <= 4)
|
|
|
+ return *((ui32 *)src);
|
|
|
+ else
|
|
|
+ return 0;
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
+static __inline ui32 hashat(const unsigned char *src)
|
|
|
+{
|
|
|
+ ui32 fetch, hash;
|
|
|
+ fetch = fast_read(src, 3);
|
|
|
+ hash = hash_func(fetch);
|
|
|
+ return hash;
|
|
|
+}
|
|
|
+
|
|
|
+static __inline void fast_write(ui32 f, void *dst, size_t bytes)
|
|
|
+{
|
|
|
+#ifndef X86X64
|
|
|
+ unsigned char *p = (unsigned char *)dst;
|
|
|
+
|
|
|
+ switch(bytes)
|
|
|
+ {
|
|
|
+ case 4:
|
|
|
+ *p = (unsigned char)f;
|
|
|
+ *(p + 1) = (unsigned char)(f >> 8);
|
|
|
+ *(p + 2) = (unsigned char)(f >> 16);
|
|
|
+ *(p + 3) = (unsigned char)(f >> 24);
|
|
|
+ return;
|
|
|
+ case 3:
|
|
|
+ *p = (unsigned char)f;
|
|
|
+ *(p + 1) = (unsigned char)(f >> 8);
|
|
|
+ *(p + 2) = (unsigned char)(f >> 16);
|
|
|
+ return;
|
|
|
+ case 2:
|
|
|
+ *p = (unsigned char)f;
|
|
|
+ *(p + 1) = (unsigned char)(f >> 8);
|
|
|
+ return;
|
|
|
+ case 1:
|
|
|
+ *p = (unsigned char)f;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+#else
|
|
|
+ switch(bytes)
|
|
|
+ {
|
|
|
+ case 4:
|
|
|
+ *((ui32 *)dst) = f;
|
|
|
+ return;
|
|
|
+ case 3:
|
|
|
+ *((ui32 *)dst) = f;
|
|
|
+ return;
|
|
|
+ case 2:
|
|
|
+ *((ui16 *)dst) = (ui16)f;
|
|
|
+ return;
|
|
|
+ case 1:
|
|
|
+ *((unsigned char *)dst) = (unsigned char)f;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+size_t qlz_size_decompressed(const char *source)
|
|
|
+{
|
|
|
+ ui32 n, r;
|
|
|
+ n = (((*source) & 2) == 2) ? 4 : 1;
|
|
|
+ r = fast_read(source + 1 + n, n);
|
|
|
+ r = r & (0xffffffff >> ((4 - n) * 8));
|
|
|
+ return r;
|
|
|
+}
|
|
|
+
|
|
|
+size_t qlz_size_compressed(const char *source)
|
|
|
+{
|
|
|
+ ui32 n, r;
|
|
|
+ n = (((*source) & 2) == 2) ? 4 : 1;
|
|
|
+ r = fast_read(source + 1, n);
|
|
|
+ r = r & (0xffffffff >> ((4 - n) * 8));
|
|
|
+ return r;
|
|
|
+}
|
|
|
+
|
|
|
+size_t qlz_size_header(const char *source)
|
|
|
+{
|
|
|
+ size_t n = 2 * ((((*source) & 2) == 2) ? 4 : 1) + 1;
|
|
|
+ return n;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static __inline void memcpy_up(unsigned char *dst, const unsigned char *src, ui32 n)
|
|
|
+{
|
|
|
+ // Caution if modifying memcpy_up! Overlap of dst and src must be special handled.
|
|
|
+#ifndef X86X64
|
|
|
+ unsigned char *end = dst + n;
|
|
|
+ while(dst < end)
|
|
|
+ {
|
|
|
+ *dst = *src;
|
|
|
+ dst++;
|
|
|
+ src++;
|
|
|
+ }
|
|
|
+#else
|
|
|
+ ui32 f = 0;
|
|
|
+ do
|
|
|
+ {
|
|
|
+ *(ui32 *)(dst + f) = *(ui32 *)(src + f);
|
|
|
+ f += MINOFFSET + 1;
|
|
|
+ }
|
|
|
+ while(f < n);
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
+static __inline void update_hash(qlz_state_decompress *state, const unsigned char *s)
|
|
|
+{
|
|
|
+#if QLZ_COMPRESSION_LEVEL == 1
|
|
|
+ ui32 hash;
|
|
|
+ hash = hashat(s);
|
|
|
+ state->hash[hash].offset = s;
|
|
|
+ state->hash_counter[hash] = 1;
|
|
|
+#elif QLZ_COMPRESSION_LEVEL == 2
|
|
|
+ ui32 hash;
|
|
|
+ unsigned char c;
|
|
|
+ hash = hashat(s);
|
|
|
+ c = state->hash_counter[hash];
|
|
|
+ state->hash[hash].offset[c & (QLZ_POINTERS - 1)] = s;
|
|
|
+ c++;
|
|
|
+ state->hash_counter[hash] = c;
|
|
|
+#endif
|
|
|
+ (void)state;
|
|
|
+ (void)s;
|
|
|
+}
|
|
|
+
|
|
|
+#if QLZ_COMPRESSION_LEVEL <= 2
|
|
|
+static void update_hash_upto(qlz_state_decompress *state, unsigned char **lh, const unsigned char *max)
|
|
|
+{
|
|
|
+ while(*lh < max)
|
|
|
+ {
|
|
|
+ (*lh)++;
|
|
|
+ update_hash(state, *lh);
|
|
|
+ }
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
+static size_t qlz_compress_core(const unsigned char *source, unsigned char *destination, size_t size, qlz_state_compress *state)
|
|
|
+{
|
|
|
+ const unsigned char *last_byte = source + size - 1;
|
|
|
+ const unsigned char *src = source;
|
|
|
+ unsigned char *cword_ptr = destination;
|
|
|
+ unsigned char *dst = destination + CWORD_LEN;
|
|
|
+ ui32 cword_val = 1U << 31;
|
|
|
+ const unsigned char *last_matchstart = last_byte - UNCONDITIONAL_MATCHLEN - UNCOMPRESSED_END;
|
|
|
+ ui32 fetch = 0;
|
|
|
+ unsigned int lits = 0;
|
|
|
+
|
|
|
+ (void) lits;
|
|
|
+
|
|
|
+ if(src <= last_matchstart)
|
|
|
+ fetch = fast_read(src, 3);
|
|
|
+
|
|
|
+ while(src <= last_matchstart)
|
|
|
+ {
|
|
|
+ if((cword_val & 1) == 1)
|
|
|
+ {
|
|
|
+ // store uncompressed if compression ratio is too low
|
|
|
+ if(src > source + (size >> 1) && dst - destination > src - source - ((src - source) >> 5))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ fast_write((cword_val >> 1) | (1U << 31), cword_ptr, CWORD_LEN);
|
|
|
+
|
|
|
+ cword_ptr = dst;
|
|
|
+ dst += CWORD_LEN;
|
|
|
+ cword_val = 1U << 31;
|
|
|
+ fetch = fast_read(src, 3);
|
|
|
+ }
|
|
|
+#if QLZ_COMPRESSION_LEVEL == 1
|
|
|
+ {
|
|
|
+ const unsigned char *o;
|
|
|
+ ui32 hash, cached;
|
|
|
+
|
|
|
+ hash = hash_func(fetch);
|
|
|
+ cached = fetch ^ state->hash[hash].cache;
|
|
|
+ state->hash[hash].cache = fetch;
|
|
|
+
|
|
|
+ o = state->hash[hash].offset + OFFSET_BASE;
|
|
|
+ state->hash[hash].offset = CAST(src - OFFSET_BASE);
|
|
|
+
|
|
|
+#ifdef X86X64
|
|
|
+ if((cached & 0xffffff) == 0 && o != OFFSET_BASE && (src - o > MINOFFSET || (src == o + 1 && lits >= 3 && src > source + 3 && same(src - 3, 6))))
|
|
|
+ {
|
|
|
+ if(cached != 0)
|
|
|
+ {
|
|
|
+#else
|
|
|
+ if(cached == 0 && o != OFFSET_BASE && (src - o > MINOFFSET || (src == o + 1 && lits >= 3 && src > source + 3 && same(src - 3, 6))))
|
|
|
+ {
|
|
|
+ if(*(o + 3) != *(src + 3))
|
|
|
+ {
|
|
|
+#endif
|
|
|
+ hash <<= 4;
|
|
|
+ cword_val = (cword_val >> 1) | (1U << 31);
|
|
|
+ fast_write((3 - 2) | hash, dst, 2);
|
|
|
+ src += 3;
|
|
|
+ dst += 2;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ const unsigned char *old_src = src;
|
|
|
+ size_t matchlen;
|
|
|
+ hash <<= 4;
|
|
|
+
|
|
|
+ cword_val = (cword_val >> 1) | (1U << 31);
|
|
|
+ src += 4;
|
|
|
+
|
|
|
+ if(*(o + (src - old_src)) == *src)
|
|
|
+ {
|
|
|
+ src++;
|
|
|
+ if(*(o + (src - old_src)) == *src)
|
|
|
+ {
|
|
|
+ size_t q = last_byte - UNCOMPRESSED_END - (src - 5) + 1;
|
|
|
+ size_t remaining = q > 255 ? 255 : q;
|
|
|
+ src++;
|
|
|
+ while(*(o + (src - old_src)) == *src && (size_t)(src - old_src) < remaining)
|
|
|
+ src++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ matchlen = src - old_src;
|
|
|
+ if(matchlen < 18)
|
|
|
+ {
|
|
|
+ fast_write((ui32)(matchlen - 2) | hash, dst, 2);
|
|
|
+ dst += 2;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ fast_write((ui32)(matchlen << 16) | hash, dst, 3);
|
|
|
+ dst += 3;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ fetch = fast_read(src, 3);
|
|
|
+ lits = 0;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ lits++;
|
|
|
+ *dst = *src;
|
|
|
+ src++;
|
|
|
+ dst++;
|
|
|
+ cword_val = (cword_val >> 1);
|
|
|
+#ifdef X86X64
|
|
|
+ fetch = fast_read(src, 3);
|
|
|
+#else
|
|
|
+ fetch = (fetch >> 8 & 0xffff) | (*(src + 2) << 16);
|
|
|
+#endif
|
|
|
+ }
|
|
|
+ }
|
|
|
+#elif QLZ_COMPRESSION_LEVEL >= 2
|
|
|
+ {
|
|
|
+ const unsigned char *o, *offset2;
|
|
|
+ ui32 hash, matchlen, k, m, best_k = 0;
|
|
|
+ unsigned char c;
|
|
|
+ size_t remaining = (last_byte - UNCOMPRESSED_END - src + 1) > 255 ? 255 : (last_byte - UNCOMPRESSED_END - src + 1);
|
|
|
+ (void)best_k;
|
|
|
+
|
|
|
+
|
|
|
+ //hash = hashat(src);
|
|
|
+ fetch = fast_read(src, 3);
|
|
|
+ hash = hash_func(fetch);
|
|
|
+
|
|
|
+ c = state->hash_counter[hash];
|
|
|
+
|
|
|
+ offset2 = state->hash[hash].offset[0];
|
|
|
+ if(offset2 < src - MINOFFSET && c > 0 && ((fast_read(offset2, 3) ^ fetch) & 0xffffff) == 0)
|
|
|
+ {
|
|
|
+ matchlen = 3;
|
|
|
+ if(*(offset2 + matchlen) == *(src + matchlen))
|
|
|
+ {
|
|
|
+ matchlen = 4;
|
|
|
+ while(*(offset2 + matchlen) == *(src + matchlen) && matchlen < remaining)
|
|
|
+ matchlen++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ matchlen = 0;
|
|
|
+ for(k = 1; k < QLZ_POINTERS && c > k; k++)
|
|
|
+ {
|
|
|
+ o = state->hash[hash].offset[k];
|
|
|
+#if QLZ_COMPRESSION_LEVEL == 3
|
|
|
+ if(((fast_read(o, 3) ^ fetch) & 0xffffff) == 0 && o < src - MINOFFSET)
|
|
|
+#elif QLZ_COMPRESSION_LEVEL == 2
|
|
|
+ if(*(src + matchlen) == *(o + matchlen) && ((fast_read(o, 3) ^ fetch) & 0xffffff) == 0 && o < src - MINOFFSET)
|
|
|
+#endif
|
|
|
+ {
|
|
|
+ m = 3;
|
|
|
+ while(*(o + m) == *(src + m) && m < remaining)
|
|
|
+ m++;
|
|
|
+#if QLZ_COMPRESSION_LEVEL == 3
|
|
|
+ if((m > matchlen) || (m == matchlen && o > offset2))
|
|
|
+#elif QLZ_COMPRESSION_LEVEL == 2
|
|
|
+ if(m > matchlen)
|
|
|
+#endif
|
|
|
+ {
|
|
|
+ offset2 = o;
|
|
|
+ matchlen = m;
|
|
|
+ best_k = k;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ o = offset2;
|
|
|
+ state->hash[hash].offset[c & (QLZ_POINTERS - 1)] = src;
|
|
|
+ c++;
|
|
|
+ state->hash_counter[hash] = c;
|
|
|
+
|
|
|
+#if QLZ_COMPRESSION_LEVEL == 3
|
|
|
+ if(matchlen > 2 && src - o < 131071)
|
|
|
+ {
|
|
|
+ ui32 u;
|
|
|
+ size_t offset = src - o;
|
|
|
+
|
|
|
+ for(u = 1; u < matchlen; u++)
|
|
|
+ {
|
|
|
+ hash = hashat(src + u);
|
|
|
+ c = state->hash_counter[hash]++;
|
|
|
+ state->hash[hash].offset[c & (QLZ_POINTERS - 1)] = src + u;
|
|
|
+ }
|
|
|
+
|
|
|
+ cword_val = (cword_val >> 1) | (1U << 31);
|
|
|
+ src += matchlen;
|
|
|
+
|
|
|
+ if(matchlen == 3 && offset <= 63)
|
|
|
+ {
|
|
|
+ *dst = (unsigned char)(offset << 2);
|
|
|
+ dst++;
|
|
|
+ }
|
|
|
+ else if(matchlen == 3 && offset <= 16383)
|
|
|
+ {
|
|
|
+ ui32 f = (ui32)((offset << 2) | 1);
|
|
|
+ fast_write(f, dst, 2);
|
|
|
+ dst += 2;
|
|
|
+ }
|
|
|
+ else if(matchlen <= 18 && offset <= 1023)
|
|
|
+ {
|
|
|
+ ui32 f = ((matchlen - 3) << 2) | ((ui32)offset << 6) | 2;
|
|
|
+ fast_write(f, dst, 2);
|
|
|
+ dst += 2;
|
|
|
+ }
|
|
|
+
|
|
|
+ else if(matchlen <= 33)
|
|
|
+ {
|
|
|
+ ui32 f = ((matchlen - 2) << 2) | ((ui32)offset << 7) | 3;
|
|
|
+ fast_write(f, dst, 3);
|
|
|
+ dst += 3;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ ui32 f = ((matchlen - 3) << 7) | ((ui32)offset << 15) | 3;
|
|
|
+ fast_write(f, dst, 4);
|
|
|
+ dst += 4;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ *dst = *src;
|
|
|
+ src++;
|
|
|
+ dst++;
|
|
|
+ cword_val = (cword_val >> 1);
|
|
|
+ }
|
|
|
+#elif QLZ_COMPRESSION_LEVEL == 2
|
|
|
+
|
|
|
+ if(matchlen > 2)
|
|
|
+ {
|
|
|
+ cword_val = (cword_val >> 1) | (1U << 31);
|
|
|
+ src += matchlen;
|
|
|
+
|
|
|
+ if(matchlen < 10)
|
|
|
+ {
|
|
|
+ ui32 f = best_k | ((matchlen - 2) << 2) | (hash << 5);
|
|
|
+ fast_write(f, dst, 2);
|
|
|
+ dst += 2;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ ui32 f = best_k | (matchlen << 16) | (hash << 5);
|
|
|
+ fast_write(f, dst, 3);
|
|
|
+ dst += 3;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ *dst = *src;
|
|
|
+ src++;
|
|
|
+ dst++;
|
|
|
+ cword_val = (cword_val >> 1);
|
|
|
+ }
|
|
|
+#endif
|
|
|
+ }
|
|
|
+#endif
|
|
|
+ }
|
|
|
+ while(src <= last_byte)
|
|
|
+ {
|
|
|
+ if((cword_val & 1) == 1)
|
|
|
+ {
|
|
|
+ fast_write((cword_val >> 1) | (1U << 31), cword_ptr, CWORD_LEN);
|
|
|
+ cword_ptr = dst;
|
|
|
+ dst += CWORD_LEN;
|
|
|
+ cword_val = 1U << 31;
|
|
|
+ }
|
|
|
+#if QLZ_COMPRESSION_LEVEL < 3
|
|
|
+ if(src <= last_byte - 3)
|
|
|
+ {
|
|
|
+#if QLZ_COMPRESSION_LEVEL == 1
|
|
|
+ ui32 hash, fetch;
|
|
|
+ fetch = fast_read(src, 3);
|
|
|
+ hash = hash_func(fetch);
|
|
|
+ state->hash[hash].offset = CAST(src - OFFSET_BASE);
|
|
|
+ state->hash[hash].cache = fetch;
|
|
|
+#elif QLZ_COMPRESSION_LEVEL == 2
|
|
|
+ ui32 hash;
|
|
|
+ unsigned char c;
|
|
|
+ hash = hashat(src);
|
|
|
+ c = state->hash_counter[hash];
|
|
|
+ state->hash[hash].offset[c & (QLZ_POINTERS - 1)] = src;
|
|
|
+ c++;
|
|
|
+ state->hash_counter[hash] = c;
|
|
|
+#endif
|
|
|
+ }
|
|
|
+#endif
|
|
|
+ *dst = *src;
|
|
|
+ src++;
|
|
|
+ dst++;
|
|
|
+ cword_val = (cword_val >> 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ while((cword_val & 1) != 1)
|
|
|
+ cword_val = (cword_val >> 1);
|
|
|
+
|
|
|
+ fast_write((cword_val >> 1) | (1U << 31), cword_ptr, CWORD_LEN);
|
|
|
+
|
|
|
+ // min. size must be 9 bytes so that the qlz_size functions can take 9 bytes as argument
|
|
|
+ return dst - destination < 9 ? 9 : dst - destination;
|
|
|
+}
|
|
|
+
|
|
|
+static size_t qlz_decompress_core(const unsigned char *source, unsigned char *destination, size_t size, qlz_state_decompress *state, const unsigned char *history)
|
|
|
+{
|
|
|
+ const unsigned char *src = source + qlz_size_header((const char *)source);
|
|
|
+ unsigned char *dst = destination;
|
|
|
+ const unsigned char *last_destination_byte = destination + size - 1;
|
|
|
+ ui32 cword_val = 1;
|
|
|
+ const unsigned char *last_matchstart = last_destination_byte - UNCONDITIONAL_MATCHLEN - UNCOMPRESSED_END;
|
|
|
+ unsigned char *last_hashed = destination - 1;
|
|
|
+ const unsigned char *last_source_byte = source + qlz_size_compressed((const char *)source) - 1;
|
|
|
+ static const ui32 bitlut[16] = {4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0};
|
|
|
+
|
|
|
+ (void) last_source_byte;
|
|
|
+ (void) last_hashed;
|
|
|
+ (void) state;
|
|
|
+ (void) history;
|
|
|
+
|
|
|
+ for(;;)
|
|
|
+ {
|
|
|
+ ui32 fetch;
|
|
|
+
|
|
|
+ if(cword_val == 1)
|
|
|
+ {
|
|
|
+#ifdef QLZ_MEMORY_SAFE
|
|
|
+ if(src + CWORD_LEN - 1 > last_source_byte)
|
|
|
+ return 0;
|
|
|
+#endif
|
|
|
+ cword_val = fast_read(src, CWORD_LEN);
|
|
|
+ src += CWORD_LEN;
|
|
|
+ }
|
|
|
+
|
|
|
+#ifdef QLZ_MEMORY_SAFE
|
|
|
+ if(src + 4 - 1 > last_source_byte)
|
|
|
+ return 0;
|
|
|
+#endif
|
|
|
+
|
|
|
+ fetch = fast_read(src, 4);
|
|
|
+
|
|
|
+ if((cword_val & 1) == 1)
|
|
|
+ {
|
|
|
+ ui32 matchlen;
|
|
|
+ const unsigned char *offset2;
|
|
|
+
|
|
|
+#if QLZ_COMPRESSION_LEVEL == 1
|
|
|
+ ui32 hash;
|
|
|
+ cword_val = cword_val >> 1;
|
|
|
+ hash = (fetch >> 4) & 0xfff;
|
|
|
+ offset2 = (const unsigned char *)(size_t)state->hash[hash].offset;
|
|
|
+
|
|
|
+ if((fetch & 0xf) != 0)
|
|
|
+ {
|
|
|
+ matchlen = (fetch & 0xf) + 2;
|
|
|
+ src += 2;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ matchlen = *(src + 2);
|
|
|
+ src += 3;
|
|
|
+ }
|
|
|
+
|
|
|
+#elif QLZ_COMPRESSION_LEVEL == 2
|
|
|
+ ui32 hash;
|
|
|
+ unsigned char c;
|
|
|
+ cword_val = cword_val >> 1;
|
|
|
+ hash = (fetch >> 5) & 0x7ff;
|
|
|
+ c = (unsigned char)(fetch & 0x3);
|
|
|
+ offset2 = state->hash[hash].offset[c];
|
|
|
+
|
|
|
+ if((fetch & (28)) != 0)
|
|
|
+ {
|
|
|
+ matchlen = ((fetch >> 2) & 0x7) + 2;
|
|
|
+ src += 2;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ matchlen = *(src + 2);
|
|
|
+ src += 3;
|
|
|
+ }
|
|
|
+
|
|
|
+#elif QLZ_COMPRESSION_LEVEL == 3
|
|
|
+ ui32 offset;
|
|
|
+ cword_val = cword_val >> 1;
|
|
|
+ if((fetch & 3) == 0)
|
|
|
+ {
|
|
|
+ offset = (fetch & 0xff) >> 2;
|
|
|
+ matchlen = 3;
|
|
|
+ src++;
|
|
|
+ }
|
|
|
+ else if((fetch & 2) == 0)
|
|
|
+ {
|
|
|
+ offset = (fetch & 0xffff) >> 2;
|
|
|
+ matchlen = 3;
|
|
|
+ src += 2;
|
|
|
+ }
|
|
|
+ else if((fetch & 1) == 0)
|
|
|
+ {
|
|
|
+ offset = (fetch & 0xffff) >> 6;
|
|
|
+ matchlen = ((fetch >> 2) & 15) + 3;
|
|
|
+ src += 2;
|
|
|
+ }
|
|
|
+ else if((fetch & 127) != 3)
|
|
|
+ {
|
|
|
+ offset = (fetch >> 7) & 0x1ffff;
|
|
|
+ matchlen = ((fetch >> 2) & 0x1f) + 2;
|
|
|
+ src += 3;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ offset = (fetch >> 15);
|
|
|
+ matchlen = ((fetch >> 7) & 255) + 3;
|
|
|
+ src += 4;
|
|
|
+ }
|
|
|
+
|
|
|
+ offset2 = dst - offset;
|
|
|
+#endif
|
|
|
+
|
|
|
+#ifdef QLZ_MEMORY_SAFE
|
|
|
+ if(offset2 < history || offset2 > dst - MINOFFSET - 1)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if(matchlen > (ui32)(last_destination_byte - dst - UNCOMPRESSED_END + 1))
|
|
|
+ return 0;
|
|
|
+#endif
|
|
|
+
|
|
|
+ memcpy_up(dst, offset2, matchlen);
|
|
|
+ dst += matchlen;
|
|
|
+
|
|
|
+#if QLZ_COMPRESSION_LEVEL <= 2
|
|
|
+ update_hash_upto(state, &last_hashed, dst - matchlen);
|
|
|
+ last_hashed = dst - 1;
|
|
|
+#endif
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if(dst < last_matchstart)
|
|
|
+ {
|
|
|
+ unsigned int n = bitlut[cword_val & 0xf];
|
|
|
+#ifdef X86X64
|
|
|
+ *(ui32 *)dst = *(ui32 *)src;
|
|
|
+#else
|
|
|
+ memcpy_up(dst, src, 4);
|
|
|
+#endif
|
|
|
+ cword_val = cword_val >> n;
|
|
|
+ dst += n;
|
|
|
+ src += n;
|
|
|
+#if QLZ_COMPRESSION_LEVEL <= 2
|
|
|
+ update_hash_upto(state, &last_hashed, dst - 3);
|
|
|
+#endif
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ while(dst <= last_destination_byte)
|
|
|
+ {
|
|
|
+ if(cword_val == 1)
|
|
|
+ {
|
|
|
+ src += CWORD_LEN;
|
|
|
+ cword_val = 1U << 31;
|
|
|
+ }
|
|
|
+#ifdef QLZ_MEMORY_SAFE
|
|
|
+ if(src >= last_source_byte + 1)
|
|
|
+ return 0;
|
|
|
+#endif
|
|
|
+ *dst = *src;
|
|
|
+ dst++;
|
|
|
+ src++;
|
|
|
+ cword_val = cword_val >> 1;
|
|
|
+ }
|
|
|
+
|
|
|
+#if QLZ_COMPRESSION_LEVEL <= 2
|
|
|
+ update_hash_upto(state, &last_hashed, last_destination_byte - 3); // todo, use constant
|
|
|
+#endif
|
|
|
+ return size;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+size_t qlz_compress(const void *source, char *destination, size_t size, qlz_state_compress *state)
|
|
|
+{
|
|
|
+ size_t r;
|
|
|
+ ui32 compressed;
|
|
|
+ size_t base;
|
|
|
+
|
|
|
+ if(size == 0 || size > 0xffffffff - 400)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if(size < 216)
|
|
|
+ base = 3;
|
|
|
+ else
|
|
|
+ base = 9;
|
|
|
+
|
|
|
+#if QLZ_STREAMING_BUFFER > 0
|
|
|
+ if(state->stream_counter + size - 1 >= QLZ_STREAMING_BUFFER)
|
|
|
+#endif
|
|
|
+ {
|
|
|
+ reset_table_compress(state);
|
|
|
+ r = base + qlz_compress_core((const unsigned char *)source, (unsigned char *)destination + base, size, state);
|
|
|
+#if QLZ_STREAMING_BUFFER > 0
|
|
|
+ reset_table_compress(state);
|
|
|
+#endif
|
|
|
+ if(r == base)
|
|
|
+ {
|
|
|
+ memcpy(destination + base, source, size);
|
|
|
+ r = size + base;
|
|
|
+ compressed = 0;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ compressed = 1;
|
|
|
+ }
|
|
|
+ state->stream_counter = 0;
|
|
|
+ }
|
|
|
+#if QLZ_STREAMING_BUFFER > 0
|
|
|
+ else
|
|
|
+ {
|
|
|
+ unsigned char *src = state->stream_buffer + state->stream_counter;
|
|
|
+
|
|
|
+ memcpy(src, source, size);
|
|
|
+ r = base + qlz_compress_core(src, (unsigned char *)destination + base, size, state);
|
|
|
+
|
|
|
+ if(r == base)
|
|
|
+ {
|
|
|
+ memcpy(destination + base, src, size);
|
|
|
+ r = size + base;
|
|
|
+ compressed = 0;
|
|
|
+ reset_table_compress(state);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ compressed = 1;
|
|
|
+ }
|
|
|
+ state->stream_counter += size;
|
|
|
+ }
|
|
|
+#endif
|
|
|
+ if(base == 3)
|
|
|
+ {
|
|
|
+ *destination = (unsigned char)(0 | compressed);
|
|
|
+ *(destination + 1) = (unsigned char)r;
|
|
|
+ *(destination + 2) = (unsigned char)size;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ *destination = (unsigned char)(2 | compressed);
|
|
|
+ fast_write((ui32)r, destination + 1, 4);
|
|
|
+ fast_write((ui32)size, destination + 5, 4);
|
|
|
+ }
|
|
|
+
|
|
|
+ *destination |= (QLZ_COMPRESSION_LEVEL << 2);
|
|
|
+ *destination |= (1 << 6);
|
|
|
+ *destination |= ((QLZ_STREAMING_BUFFER == 0 ? 0 : (QLZ_STREAMING_BUFFER == 100000 ? 1 : (QLZ_STREAMING_BUFFER == 1000000 ? 2 : 3))) << 4);
|
|
|
+
|
|
|
+// 76543210
|
|
|
+// 01SSLLHC
|
|
|
+
|
|
|
+ return r;
|
|
|
+}
|
|
|
+
|
|
|
+size_t qlz_decompress(const char *source, void *destination, qlz_state_decompress *state)
|
|
|
+{
|
|
|
+ size_t dsiz = qlz_size_decompressed(source);
|
|
|
+
|
|
|
+#if QLZ_STREAMING_BUFFER > 0
|
|
|
+ if(state->stream_counter + qlz_size_decompressed(source) - 1 >= QLZ_STREAMING_BUFFER)
|
|
|
+#endif
|
|
|
+ {
|
|
|
+ if((*source & 1) == 1)
|
|
|
+ {
|
|
|
+ reset_table_decompress(state);
|
|
|
+ dsiz = qlz_decompress_core((const unsigned char *)source, (unsigned char *)destination, dsiz, state, (const unsigned char *)destination);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ memcpy(destination, source + qlz_size_header(source), dsiz);
|
|
|
+ }
|
|
|
+ state->stream_counter = 0;
|
|
|
+ reset_table_decompress(state);
|
|
|
+ }
|
|
|
+#if QLZ_STREAMING_BUFFER > 0
|
|
|
+ else
|
|
|
+ {
|
|
|
+ unsigned char *dst = state->stream_buffer + state->stream_counter;
|
|
|
+ if((*source & 1) == 1)
|
|
|
+ {
|
|
|
+ dsiz = qlz_decompress_core((const unsigned char *)source, dst, dsiz, state, (const unsigned char *)state->stream_buffer);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ memcpy(dst, source + qlz_size_header(source), dsiz);
|
|
|
+ reset_table_decompress(state);
|
|
|
+ }
|
|
|
+ memcpy(destination, dst, dsiz);
|
|
|
+ state->stream_counter += dsiz;
|
|
|
+ }
|
|
|
+#endif
|
|
|
+ return dsiz;
|
|
|
+}
|
|
|
+
|