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