1 | /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */ |
2 | // vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4: |
3 | #ident "$Id$" |
4 | /*====== |
5 | This file is part of PerconaFT. |
6 | |
7 | |
8 | Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved. |
9 | |
10 | PerconaFT is free software: you can redistribute it and/or modify |
11 | it under the terms of the GNU General Public License, version 2, |
12 | as published by the Free Software Foundation. |
13 | |
14 | PerconaFT is distributed in the hope that it will be useful, |
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | GNU General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU General Public License |
20 | along with PerconaFT. If not, see <http://www.gnu.org/licenses/>. |
21 | |
22 | ---------------------------------------- |
23 | |
24 | PerconaFT is free software: you can redistribute it and/or modify |
25 | it under the terms of the GNU Affero General Public License, version 3, |
26 | as published by the Free Software Foundation. |
27 | |
28 | PerconaFT is distributed in the hope that it will be useful, |
29 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
30 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
31 | GNU Affero General Public License for more details. |
32 | |
33 | You should have received a copy of the GNU Affero General Public License |
34 | along with PerconaFT. If not, see <http://www.gnu.org/licenses/>. |
35 | ======= */ |
36 | |
37 | #ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved." |
38 | |
39 | // Fast data compression library |
40 | // Copyright (C) 2006-2011 Lasse Mikkel Reinhold |
41 | // lar@quicklz.com |
42 | // |
43 | // QuickLZ can be used for free under the GPL 1, 2 or 3 license (where anything |
44 | // released into public must be open source) or under a commercial license if such |
45 | // has been acquired (see http://www.quicklz.com/order.html). The commercial license |
46 | // does not cover derived or ported versions created by third parties under GPL. |
47 | |
48 | // 1.5.0 final |
49 | |
50 | #include "quicklz.h" |
51 | |
52 | #if QLZ_VERSION_MAJOR != 1 || QLZ_VERSION_MINOR != 5 || QLZ_VERSION_REVISION != 0 |
53 | #error quicklz.c and quicklz.h have different versions |
54 | #endif |
55 | |
56 | #if (defined(__X86__) || defined(__i386__) || defined(i386) || defined(_M_IX86) || defined(__386__) || defined(__x86_64__) || defined(_M_X64)) |
57 | #define X86X64 |
58 | #endif |
59 | |
60 | #define MINOFFSET 2 |
61 | #define UNCONDITIONAL_MATCHLEN 6 |
62 | #define UNCOMPRESSED_END 4 |
63 | #define CWORD_LEN 4 |
64 | |
65 | #if QLZ_COMPRESSION_LEVEL == 1 && defined QLZ_PTR_64 && QLZ_STREAMING_BUFFER == 0 |
66 | #define OFFSET_BASE source |
67 | #define CAST (ui32)(size_t) |
68 | #else |
69 | #define OFFSET_BASE 0 |
70 | #define CAST |
71 | #endif |
72 | |
73 | int qlz_get_setting(int setting) |
74 | { |
75 | switch (setting) |
76 | { |
77 | case 0: return QLZ_COMPRESSION_LEVEL; |
78 | case 1: return sizeof(qlz_state_compress); |
79 | case 2: return sizeof(qlz_state_decompress); |
80 | case 3: return QLZ_STREAMING_BUFFER; |
81 | #ifdef QLZ_MEMORY_SAFE |
82 | case 6: return 1; |
83 | #else |
84 | case 6: return 0; |
85 | #endif |
86 | case 7: return QLZ_VERSION_MAJOR; |
87 | case 8: return QLZ_VERSION_MINOR; |
88 | case 9: return QLZ_VERSION_REVISION; |
89 | } |
90 | return -1; |
91 | } |
92 | |
93 | #if QLZ_COMPRESSION_LEVEL == 1 |
94 | static int same(const unsigned char *src, size_t n) |
95 | { |
96 | while(n > 0 && *(src + n) == *src) |
97 | n--; |
98 | return n == 0 ? 1 : 0; |
99 | } |
100 | #endif |
101 | |
102 | static void reset_table_compress(qlz_state_compress *state) |
103 | { |
104 | int i; |
105 | for(i = 0; i < QLZ_HASH_VALUES; i++) |
106 | { |
107 | #if QLZ_COMPRESSION_LEVEL == 1 |
108 | state->hash[i].offset = 0; |
109 | #else |
110 | state->hash_counter[i] = 0; |
111 | state->hash[i].offset[0] = 0; |
112 | #endif |
113 | } |
114 | } |
115 | |
116 | static void reset_table_decompress(qlz_state_decompress *state) |
117 | { |
118 | (void)state; |
119 | #if QLZ_COMPRESSION_LEVEL == 2 |
120 | for(int i = 0; i < QLZ_HASH_VALUES; i++) |
121 | { |
122 | state->hash_counter[i] = 0; |
123 | } |
124 | #endif |
125 | } |
126 | |
127 | static __inline ui32 hash_func(ui32 i) |
128 | { |
129 | #if QLZ_COMPRESSION_LEVEL == 2 |
130 | return ((i >> 9) ^ (i >> 13) ^ i) & (QLZ_HASH_VALUES - 1); |
131 | #else |
132 | return ((i >> 12) ^ i) & (QLZ_HASH_VALUES - 1); |
133 | #endif |
134 | } |
135 | |
136 | static __inline ui32 fast_read(void const *src, ui32 bytes) |
137 | { |
138 | #ifndef X86X64 |
139 | unsigned char *p = (unsigned char*)src; |
140 | switch (bytes) |
141 | { |
142 | case 4: |
143 | return(*p | *(p + 1) << 8 | *(p + 2) << 16 | *(p + 3) << 24); |
144 | case 3: |
145 | return(*p | *(p + 1) << 8 | *(p + 2) << 16); |
146 | case 2: |
147 | return(*p | *(p + 1) << 8); |
148 | case 1: |
149 | return(*p); |
150 | } |
151 | return 0; |
152 | #else |
153 | if (bytes >= 1 && bytes <= 4) |
154 | return *((ui32*)src); |
155 | else |
156 | return 0; |
157 | #endif |
158 | } |
159 | |
160 | static __inline ui32 hashat(const unsigned char *src) |
161 | { |
162 | ui32 fetch, hash; |
163 | fetch = fast_read(src, 3); |
164 | hash = hash_func(fetch); |
165 | return hash; |
166 | } |
167 | |
168 | static __inline void fast_write(ui32 f, void *dst, size_t bytes) |
169 | { |
170 | #ifndef X86X64 |
171 | unsigned char *p = (unsigned char*)dst; |
172 | |
173 | switch (bytes) |
174 | { |
175 | case 4: |
176 | *p = (unsigned char)f; |
177 | *(p + 1) = (unsigned char)(f >> 8); |
178 | *(p + 2) = (unsigned char)(f >> 16); |
179 | *(p + 3) = (unsigned char)(f >> 24); |
180 | return; |
181 | case 3: |
182 | *p = (unsigned char)f; |
183 | *(p + 1) = (unsigned char)(f >> 8); |
184 | *(p + 2) = (unsigned char)(f >> 16); |
185 | return; |
186 | case 2: |
187 | *p = (unsigned char)f; |
188 | *(p + 1) = (unsigned char)(f >> 8); |
189 | return; |
190 | case 1: |
191 | *p = (unsigned char)f; |
192 | return; |
193 | } |
194 | #else |
195 | switch (bytes) |
196 | { |
197 | case 4: |
198 | *((ui32*)dst) = f; |
199 | return; |
200 | case 3: |
201 | *((ui32*)dst) = f; |
202 | return; |
203 | case 2: |
204 | *((ui16 *)dst) = (ui16)f; |
205 | return; |
206 | case 1: |
207 | *((unsigned char*)dst) = (unsigned char)f; |
208 | return; |
209 | } |
210 | #endif |
211 | } |
212 | |
213 | |
214 | size_t qlz_size_decompressed(const char *source) |
215 | { |
216 | ui32 n, r; |
217 | n = (((*source) & 2) == 2) ? 4 : 1; |
218 | r = fast_read(source + 1 + n, n); |
219 | r = r & (0xffffffff >> ((4 - n)*8)); |
220 | return r; |
221 | } |
222 | |
223 | size_t qlz_size_compressed(const char *source) |
224 | { |
225 | ui32 n, r; |
226 | n = (((*source) & 2) == 2) ? 4 : 1; |
227 | r = fast_read(source + 1, n); |
228 | r = r & (0xffffffff >> ((4 - n)*8)); |
229 | return r; |
230 | } |
231 | |
232 | static |
233 | size_t (const char *source) |
234 | { |
235 | size_t n = 2*((((*source) & 2) == 2) ? 4 : 1) + 1; |
236 | return n; |
237 | } |
238 | |
239 | |
240 | static __inline void memcpy_up(unsigned char *dst, const unsigned char *src, ui32 n) |
241 | { |
242 | // Caution if modifying memcpy_up! Overlap of dst and src must be special handled. |
243 | #ifndef X86X64 |
244 | unsigned char *end = dst + n; |
245 | while(dst < end) |
246 | { |
247 | *dst = *src; |
248 | dst++; |
249 | src++; |
250 | } |
251 | #else |
252 | ui32 f = 0; |
253 | do |
254 | { |
255 | *(ui32 *)(dst + f) = *(ui32 *)(src + f); |
256 | f += MINOFFSET + 1; |
257 | } |
258 | while (f < n); |
259 | #endif |
260 | } |
261 | |
262 | __attribute__((unused)) |
263 | static __inline void update_hash(qlz_state_decompress *state, const unsigned char *s) |
264 | { |
265 | #if QLZ_COMPRESSION_LEVEL == 1 |
266 | ui32 hash; |
267 | hash = hashat(s); |
268 | state->hash[hash].offset = s; |
269 | state->hash_counter[hash] = 1; |
270 | #elif QLZ_COMPRESSION_LEVEL == 2 |
271 | ui32 hash; |
272 | unsigned char c; |
273 | hash = hashat(s); |
274 | c = state->hash_counter[hash]; |
275 | state->hash[hash].offset[c & (QLZ_POINTERS - 1)] = s; |
276 | c++; |
277 | state->hash_counter[hash] = c; |
278 | #endif |
279 | (void)state; |
280 | (void)s; |
281 | } |
282 | |
283 | #if QLZ_COMPRESSION_LEVEL <= 2 |
284 | static void update_hash_upto(qlz_state_decompress *state, unsigned char **lh, const unsigned char *max) |
285 | { |
286 | while(*lh < max) |
287 | { |
288 | (*lh)++; |
289 | update_hash(state, *lh); |
290 | } |
291 | } |
292 | #endif |
293 | |
294 | static size_t qlz_compress_core(const unsigned char *source, unsigned char *destination, size_t size, qlz_state_compress *state) |
295 | { |
296 | const unsigned char *last_byte = source + size - 1; |
297 | const unsigned char *src = source; |
298 | unsigned char *cword_ptr = destination; |
299 | unsigned char *dst = destination + CWORD_LEN; |
300 | ui32 cword_val = 1U << 31; |
301 | const unsigned char *last_matchstart = last_byte - UNCONDITIONAL_MATCHLEN - UNCOMPRESSED_END; |
302 | ui32 fetch = 0; |
303 | unsigned int lits = 0; |
304 | |
305 | (void) lits; |
306 | |
307 | if(src <= last_matchstart) |
308 | fetch = fast_read(src, 3); |
309 | |
310 | while(src <= last_matchstart) |
311 | { |
312 | if ((cword_val & 1) == 1) |
313 | { |
314 | // store uncompressed if compression ratio is too low |
315 | if (src > source + (size >> 1) && dst - destination > src - source - ((src - source) >> 5)) |
316 | return 0; |
317 | |
318 | fast_write((cword_val >> 1) | (1U << 31), cword_ptr, CWORD_LEN); |
319 | |
320 | cword_ptr = dst; |
321 | dst += CWORD_LEN; |
322 | cword_val = 1U << 31; |
323 | fetch = fast_read(src, 3); |
324 | } |
325 | #if QLZ_COMPRESSION_LEVEL == 1 |
326 | { |
327 | const unsigned char *o; |
328 | ui32 hash, cached; |
329 | |
330 | hash = hash_func(fetch); |
331 | cached = fetch ^ state->hash[hash].cache; |
332 | state->hash[hash].cache = fetch; |
333 | |
334 | o = state->hash[hash].offset + OFFSET_BASE; |
335 | state->hash[hash].offset = CAST(src - OFFSET_BASE); |
336 | |
337 | #ifdef X86X64 |
338 | if ((cached & 0xffffff) == 0 && o != OFFSET_BASE && (src - o > MINOFFSET || (src == o + 1 && lits >= 3 && src > source + 3 && same(src - 3, 6)))) |
339 | { |
340 | if(cached != 0) |
341 | { |
342 | #else |
343 | if (cached == 0 && o != OFFSET_BASE && (src - o > MINOFFSET || (src == o + 1 && lits >= 3 && src > source + 3 && same(src - 3, 6)))) |
344 | { |
345 | if (*(o + 3) != *(src + 3)) |
346 | { |
347 | #endif |
348 | hash <<= 4; |
349 | cword_val = (cword_val >> 1) | (1U << 31); |
350 | fast_write((3 - 2) | hash, dst, 2); |
351 | src += 3; |
352 | dst += 2; |
353 | } |
354 | else |
355 | { |
356 | const unsigned char *old_src = src; |
357 | size_t matchlen; |
358 | hash <<= 4; |
359 | |
360 | cword_val = (cword_val >> 1) | (1U << 31); |
361 | src += 4; |
362 | |
363 | if(*(o + (src - old_src)) == *src) |
364 | { |
365 | src++; |
366 | if(*(o + (src - old_src)) == *src) |
367 | { |
368 | size_t q = last_byte - UNCOMPRESSED_END - (src - 5) + 1; |
369 | size_t remaining = q > 255 ? 255 : q; |
370 | src++; |
371 | while(*(o + (src - old_src)) == *src && (size_t)(src - old_src) < remaining) |
372 | src++; |
373 | } |
374 | } |
375 | |
376 | matchlen = src - old_src; |
377 | if (matchlen < 18) |
378 | { |
379 | fast_write((ui32)(matchlen - 2) | hash, dst, 2); |
380 | dst += 2; |
381 | } |
382 | else |
383 | { |
384 | fast_write((ui32)(matchlen << 16) | hash, dst, 3); |
385 | dst += 3; |
386 | } |
387 | } |
388 | fetch = fast_read(src, 3); |
389 | lits = 0; |
390 | } |
391 | else |
392 | { |
393 | lits++; |
394 | *dst = *src; |
395 | src++; |
396 | dst++; |
397 | cword_val = (cword_val >> 1); |
398 | #ifdef X86X64 |
399 | fetch = fast_read(src, 3); |
400 | #else |
401 | fetch = (fetch >> 8 & 0xffff) | (*(src + 2) << 16); |
402 | #endif |
403 | } |
404 | } |
405 | #elif QLZ_COMPRESSION_LEVEL >= 2 |
406 | { |
407 | const unsigned char *o, *offset2; |
408 | ui32 hash, matchlen, k, m, best_k = 0; |
409 | unsigned char c; |
410 | size_t remaining = (last_byte - UNCOMPRESSED_END - src + 1) > 255 ? 255 : (last_byte - UNCOMPRESSED_END - src + 1); |
411 | (void)best_k; |
412 | |
413 | |
414 | //hash = hashat(src); |
415 | fetch = fast_read(src, 3); |
416 | hash = hash_func(fetch); |
417 | |
418 | c = state->hash_counter[hash]; |
419 | |
420 | offset2 = state->hash[hash].offset[0]; |
421 | if(offset2 < src - MINOFFSET && c > 0 && ((fast_read(offset2, 3) ^ fetch) & 0xffffff) == 0) |
422 | { |
423 | matchlen = 3; |
424 | if(*(offset2 + matchlen) == *(src + matchlen)) |
425 | { |
426 | matchlen = 4; |
427 | while(*(offset2 + matchlen) == *(src + matchlen) && matchlen < remaining) |
428 | matchlen++; |
429 | } |
430 | } |
431 | else |
432 | matchlen = 0; |
433 | for(k = 1; k < QLZ_POINTERS && c > k; k++) |
434 | { |
435 | o = state->hash[hash].offset[k]; |
436 | #if QLZ_COMPRESSION_LEVEL == 3 |
437 | if(((fast_read(o, 3) ^ fetch) & 0xffffff) == 0 && o < src - MINOFFSET) |
438 | #elif QLZ_COMPRESSION_LEVEL == 2 |
439 | if(*(src + matchlen) == *(o + matchlen) && ((fast_read(o, 3) ^ fetch) & 0xffffff) == 0 && o < src - MINOFFSET) |
440 | #endif |
441 | { |
442 | m = 3; |
443 | while(*(o + m) == *(src + m) && m < remaining) |
444 | m++; |
445 | #if QLZ_COMPRESSION_LEVEL == 3 |
446 | if ((m > matchlen) || (m == matchlen && o > offset2)) |
447 | #elif QLZ_COMPRESSION_LEVEL == 2 |
448 | if (m > matchlen) |
449 | #endif |
450 | { |
451 | offset2 = o; |
452 | matchlen = m; |
453 | best_k = k; |
454 | } |
455 | } |
456 | } |
457 | o = offset2; |
458 | state->hash[hash].offset[c & (QLZ_POINTERS - 1)] = src; |
459 | c++; |
460 | state->hash_counter[hash] = c; |
461 | |
462 | #if QLZ_COMPRESSION_LEVEL == 3 |
463 | if(matchlen > 2 && src - o < 131071) |
464 | { |
465 | ui32 u; |
466 | size_t offset = src - o; |
467 | |
468 | for(u = 1; u < matchlen; u++) |
469 | { |
470 | hash = hashat(src + u); |
471 | c = state->hash_counter[hash]++; |
472 | state->hash[hash].offset[c & (QLZ_POINTERS - 1)] = src + u; |
473 | } |
474 | |
475 | cword_val = (cword_val >> 1) | (1U << 31); |
476 | src += matchlen; |
477 | |
478 | if(matchlen == 3 && offset <= 63) |
479 | { |
480 | *dst = (unsigned char)(offset << 2); |
481 | dst++; |
482 | } |
483 | else if (matchlen == 3 && offset <= 16383) |
484 | { |
485 | ui32 f = (ui32)((offset << 2) | 1); |
486 | fast_write(f, dst, 2); |
487 | dst += 2; |
488 | } |
489 | else if (matchlen <= 18 && offset <= 1023) |
490 | { |
491 | ui32 f = ((matchlen - 3) << 2) | ((ui32)offset << 6) | 2; |
492 | fast_write(f, dst, 2); |
493 | dst += 2; |
494 | } |
495 | |
496 | else if(matchlen <= 33) |
497 | { |
498 | ui32 f = ((matchlen - 2) << 2) | ((ui32)offset << 7) | 3; |
499 | fast_write(f, dst, 3); |
500 | dst += 3; |
501 | } |
502 | else |
503 | { |
504 | ui32 f = ((matchlen - 3) << 7) | ((ui32)offset << 15) | 3; |
505 | fast_write(f, dst, 4); |
506 | dst += 4; |
507 | } |
508 | } |
509 | else |
510 | { |
511 | *dst = *src; |
512 | src++; |
513 | dst++; |
514 | cword_val = (cword_val >> 1); |
515 | } |
516 | #elif QLZ_COMPRESSION_LEVEL == 2 |
517 | |
518 | if(matchlen > 2) |
519 | { |
520 | cword_val = (cword_val >> 1) | (1U << 31); |
521 | src += matchlen; |
522 | |
523 | if (matchlen < 10) |
524 | { |
525 | ui32 f = best_k | ((matchlen - 2) << 2) | (hash << 5); |
526 | fast_write(f, dst, 2); |
527 | dst += 2; |
528 | } |
529 | else |
530 | { |
531 | ui32 f = best_k | (matchlen << 16) | (hash << 5); |
532 | fast_write(f, dst, 3); |
533 | dst += 3; |
534 | } |
535 | } |
536 | else |
537 | { |
538 | *dst = *src; |
539 | src++; |
540 | dst++; |
541 | cword_val = (cword_val >> 1); |
542 | } |
543 | #endif |
544 | } |
545 | #endif |
546 | } |
547 | while (src <= last_byte) |
548 | { |
549 | if ((cword_val & 1) == 1) |
550 | { |
551 | fast_write((cword_val >> 1) | (1U << 31), cword_ptr, CWORD_LEN); |
552 | cword_ptr = dst; |
553 | dst += CWORD_LEN; |
554 | cword_val = 1U << 31; |
555 | } |
556 | #if QLZ_COMPRESSION_LEVEL < 3 |
557 | if (src <= last_byte - 3) |
558 | { |
559 | #if QLZ_COMPRESSION_LEVEL == 1 |
560 | ui32 hash, fetchv; |
561 | fetchv = fast_read(src, 3); |
562 | hash = hash_func(fetch); |
563 | state->hash[hash].offset = CAST(src - OFFSET_BASE); |
564 | state->hash[hash].cache = fetchv; |
565 | #elif QLZ_COMPRESSION_LEVEL == 2 |
566 | ui32 hash; |
567 | unsigned char c; |
568 | hash = hashat(src); |
569 | c = state->hash_counter[hash]; |
570 | state->hash[hash].offset[c & (QLZ_POINTERS - 1)] = src; |
571 | c++; |
572 | state->hash_counter[hash] = c; |
573 | #endif |
574 | } |
575 | #endif |
576 | *dst = *src; |
577 | src++; |
578 | dst++; |
579 | cword_val = (cword_val >> 1); |
580 | } |
581 | |
582 | while((cword_val & 1) != 1) |
583 | cword_val = (cword_val >> 1); |
584 | |
585 | fast_write((cword_val >> 1) | (1U << 31), cword_ptr, CWORD_LEN); |
586 | |
587 | // min. size must be 9 bytes so that the qlz_size functions can take 9 bytes as argument |
588 | return dst - destination < 9 ? 9 : dst - destination; |
589 | } |
590 | |
591 | static size_t qlz_decompress_core(const unsigned char *source, unsigned char *destination, size_t size, qlz_state_decompress *state, const unsigned char *history) |
592 | { |
593 | const unsigned char *src = source + qlz_size_header((const char *)source); |
594 | unsigned char *dst = destination; |
595 | const unsigned char *last_destination_byte = destination + size - 1; |
596 | ui32 cword_val = 1; |
597 | const unsigned char *last_matchstart = last_destination_byte - UNCONDITIONAL_MATCHLEN - UNCOMPRESSED_END; |
598 | unsigned char *last_hashed = destination - 1; |
599 | const unsigned char *last_source_byte = source + qlz_size_compressed((const char *)source) - 1; |
600 | static const ui32 bitlut[16] = {4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0}; |
601 | |
602 | (void) last_source_byte; |
603 | (void) last_hashed; |
604 | (void) state; |
605 | (void) history; |
606 | |
607 | for(;;) |
608 | { |
609 | ui32 fetch; |
610 | |
611 | if (cword_val == 1) |
612 | { |
613 | #ifdef QLZ_MEMORY_SAFE |
614 | if(src + CWORD_LEN - 1 > last_source_byte) |
615 | return 0; |
616 | #endif |
617 | cword_val = fast_read(src, CWORD_LEN); |
618 | src += CWORD_LEN; |
619 | } |
620 | |
621 | #ifdef QLZ_MEMORY_SAFE |
622 | if(src + 4 - 1 > last_source_byte) |
623 | return 0; |
624 | #endif |
625 | |
626 | fetch = fast_read(src, 4); |
627 | |
628 | if ((cword_val & 1) == 1) |
629 | { |
630 | ui32 matchlen; |
631 | const unsigned char *offset2; |
632 | |
633 | #if QLZ_COMPRESSION_LEVEL == 1 |
634 | ui32 hash; |
635 | cword_val = cword_val >> 1; |
636 | hash = (fetch >> 4) & 0xfff; |
637 | offset2 = (const unsigned char *)(size_t)state->hash[hash].offset; |
638 | |
639 | if((fetch & 0xf) != 0) |
640 | { |
641 | matchlen = (fetch & 0xf) + 2; |
642 | src += 2; |
643 | } |
644 | else |
645 | { |
646 | matchlen = *(src + 2); |
647 | src += 3; |
648 | } |
649 | |
650 | #elif QLZ_COMPRESSION_LEVEL == 2 |
651 | ui32 hash; |
652 | unsigned char c; |
653 | cword_val = cword_val >> 1; |
654 | hash = (fetch >> 5) & 0x7ff; |
655 | c = (unsigned char)(fetch & 0x3); |
656 | offset2 = state->hash[hash].offset[c]; |
657 | |
658 | if((fetch & (28)) != 0) |
659 | { |
660 | matchlen = ((fetch >> 2) & 0x7) + 2; |
661 | src += 2; |
662 | } |
663 | else |
664 | { |
665 | matchlen = *(src + 2); |
666 | src += 3; |
667 | } |
668 | |
669 | #elif QLZ_COMPRESSION_LEVEL == 3 |
670 | ui32 offset; |
671 | cword_val = cword_val >> 1; |
672 | if ((fetch & 3) == 0) |
673 | { |
674 | offset = (fetch & 0xff) >> 2; |
675 | matchlen = 3; |
676 | src++; |
677 | } |
678 | else if ((fetch & 2) == 0) |
679 | { |
680 | offset = (fetch & 0xffff) >> 2; |
681 | matchlen = 3; |
682 | src += 2; |
683 | } |
684 | else if ((fetch & 1) == 0) |
685 | { |
686 | offset = (fetch & 0xffff) >> 6; |
687 | matchlen = ((fetch >> 2) & 15) + 3; |
688 | src += 2; |
689 | } |
690 | else if ((fetch & 127) != 3) |
691 | { |
692 | offset = (fetch >> 7) & 0x1ffff; |
693 | matchlen = ((fetch >> 2) & 0x1f) + 2; |
694 | src += 3; |
695 | } |
696 | else |
697 | { |
698 | offset = (fetch >> 15); |
699 | matchlen = ((fetch >> 7) & 255) + 3; |
700 | src += 4; |
701 | } |
702 | |
703 | offset2 = dst - offset; |
704 | #endif |
705 | |
706 | #ifdef QLZ_MEMORY_SAFE |
707 | if(offset2 < history || offset2 > dst - MINOFFSET - 1) |
708 | return 0; |
709 | |
710 | if(matchlen > (ui32)(last_destination_byte - dst - UNCOMPRESSED_END + 1)) |
711 | return 0; |
712 | #endif |
713 | |
714 | memcpy_up(dst, offset2, matchlen); |
715 | dst += matchlen; |
716 | |
717 | #if QLZ_COMPRESSION_LEVEL <= 2 |
718 | update_hash_upto(state, &last_hashed, dst - matchlen); |
719 | last_hashed = dst - 1; |
720 | #endif |
721 | } |
722 | else |
723 | { |
724 | if (dst < last_matchstart) |
725 | { |
726 | unsigned int n = bitlut[cword_val & 0xf]; |
727 | #ifdef X86X64 |
728 | *(ui32 *)dst = *(ui32 *)src; |
729 | #else |
730 | memcpy_up(dst, src, 4); |
731 | #endif |
732 | cword_val = cword_val >> n; |
733 | dst += n; |
734 | src += n; |
735 | #if QLZ_COMPRESSION_LEVEL <= 2 |
736 | update_hash_upto(state, &last_hashed, dst - 3); |
737 | #endif |
738 | } |
739 | else |
740 | { |
741 | while(dst <= last_destination_byte) |
742 | { |
743 | if (cword_val == 1) |
744 | { |
745 | src += CWORD_LEN; |
746 | cword_val = 1U << 31; |
747 | } |
748 | #ifdef QLZ_MEMORY_SAFE |
749 | if(src >= last_source_byte + 1) |
750 | return 0; |
751 | #endif |
752 | *dst = *src; |
753 | dst++; |
754 | src++; |
755 | cword_val = cword_val >> 1; |
756 | } |
757 | |
758 | #if QLZ_COMPRESSION_LEVEL <= 2 |
759 | update_hash_upto(state, &last_hashed, last_destination_byte - 3); // todo, use constant |
760 | #endif |
761 | return size; |
762 | } |
763 | |
764 | } |
765 | } |
766 | } |
767 | |
768 | size_t qlz_compress(const void *source, char *destination, size_t size, qlz_state_compress *state) |
769 | { |
770 | size_t r; |
771 | ui32 compressed; |
772 | size_t base; |
773 | |
774 | if(size == 0 || size > 0xffffffff - 400) |
775 | return 0; |
776 | |
777 | if(size < 216) |
778 | base = 3; |
779 | else |
780 | base = 9; |
781 | |
782 | #if QLZ_STREAMING_BUFFER > 0 |
783 | if (state->stream_counter + size - 1 >= QLZ_STREAMING_BUFFER) |
784 | #endif |
785 | { |
786 | reset_table_compress(state); |
787 | r = base + qlz_compress_core((const unsigned char *)source, (unsigned char*)destination + base, size, state); |
788 | #if QLZ_STREAMING_BUFFER > 0 |
789 | reset_table_compress(state); |
790 | #endif |
791 | if(r == base) |
792 | { |
793 | memcpy(destination + base, source, size); |
794 | r = size + base; |
795 | compressed = 0; |
796 | } |
797 | else |
798 | { |
799 | compressed = 1; |
800 | } |
801 | state->stream_counter = 0; |
802 | } |
803 | #if QLZ_STREAMING_BUFFER > 0 |
804 | else |
805 | { |
806 | unsigned char *src = state->stream_buffer + state->stream_counter; |
807 | |
808 | memcpy(src, source, size); |
809 | r = base + qlz_compress_core(src, (unsigned char*)destination + base, size, state); |
810 | |
811 | if(r == base) |
812 | { |
813 | memcpy(destination + base, src, size); |
814 | r = size + base; |
815 | compressed = 0; |
816 | reset_table_compress(state); |
817 | } |
818 | else |
819 | { |
820 | compressed = 1; |
821 | } |
822 | state->stream_counter += size; |
823 | } |
824 | #endif |
825 | if(base == 3) |
826 | { |
827 | *destination = (unsigned char)(0 | compressed); |
828 | *(destination + 1) = (unsigned char)r; |
829 | *(destination + 2) = (unsigned char)size; |
830 | } |
831 | else |
832 | { |
833 | *destination = (unsigned char)(2 | compressed); |
834 | fast_write((ui32)r, destination + 1, 4); |
835 | fast_write((ui32)size, destination + 5, 4); |
836 | } |
837 | |
838 | *destination |= (QLZ_COMPRESSION_LEVEL << 2); |
839 | *destination |= (1 << 6); |
840 | *destination |= ((QLZ_STREAMING_BUFFER == 0 ? 0 : (QLZ_STREAMING_BUFFER == 100000 ? 1 : (QLZ_STREAMING_BUFFER == 1000000 ? 2 : 3))) << 4); |
841 | |
842 | // 76543210 |
843 | // 01SSLLHC |
844 | |
845 | return r; |
846 | } |
847 | |
848 | size_t qlz_decompress(const char *source, void *destination, qlz_state_decompress *state) |
849 | { |
850 | size_t dsiz = qlz_size_decompressed(source); |
851 | |
852 | #if QLZ_STREAMING_BUFFER > 0 |
853 | if (state->stream_counter + qlz_size_decompressed(source) - 1 >= QLZ_STREAMING_BUFFER) |
854 | #endif |
855 | { |
856 | if((*source & 1) == 1) |
857 | { |
858 | reset_table_decompress(state); |
859 | dsiz = qlz_decompress_core((const unsigned char *)source, (unsigned char *)destination, dsiz, state, (const unsigned char *)destination); |
860 | } |
861 | else |
862 | { |
863 | memcpy(destination, source + qlz_size_header(source), dsiz); |
864 | } |
865 | state->stream_counter = 0; |
866 | reset_table_decompress(state); |
867 | } |
868 | #if QLZ_STREAMING_BUFFER > 0 |
869 | else |
870 | { |
871 | unsigned char *dst = state->stream_buffer + state->stream_counter; |
872 | if((*source & 1) == 1) |
873 | { |
874 | dsiz = qlz_decompress_core((const unsigned char *)source, dst, dsiz, state, (const unsigned char *)state->stream_buffer); |
875 | } |
876 | else |
877 | { |
878 | memcpy(dst, source + qlz_size_header(source), dsiz); |
879 | reset_table_decompress(state); |
880 | } |
881 | memcpy(destination, dst, dsiz); |
882 | state->stream_counter += dsiz; |
883 | } |
884 | #endif |
885 | return dsiz; |
886 | } |
887 | |
888 | |