Coverage Report

Created: 2026-09-14 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/c-blosc2/blosc/blosclz.c
Line
Count
Source
1
/*********************************************************************
2
  Blosc - Blocked Shuffling and Compression Library
3
4
  Copyright (c) 2021  Blosc Development Team <blosc@blosc.org>
5
  https://blosc.org
6
  License: BSD 3-Clause (see LICENSE.txt)
7
8
  See LICENSE.txt for details about copyright and rights to use.
9
**********************************************************************/
10
11
/*********************************************************************
12
  The code in this file is heavily based on FastLZ, a lightning-fast
13
  lossless compression library.  See LICENSES/FASTLZ.txt for details.
14
**********************************************************************/
15
16
17
#include "blosclz.h"
18
#include "fastcopy.h"
19
#include "blosc2/blosc2-common.h"
20
21
#include <stdbool.h>
22
#include <stdio.h>
23
#include <stdint.h>
24
#include <string.h>
25
26
/*
27
 * Give hints to the compiler for branch prediction optimization.
28
 * This is not necessary anymore with modern CPUs.
29
 */
30
#if 0 && defined(__GNUC__) && (__GNUC__ > 2)
31
#define BLOSCLZ_LIKELY(c)    (__builtin_expect((c), 1))
32
#define BLOSCLZ_UNLIKELY(c)  (__builtin_expect((c), 0))
33
#else
34
45.4M
#define BLOSCLZ_LIKELY(c)    (c)
35
104M
#define BLOSCLZ_UNLIKELY(c)  (c)
36
#endif
37
38
/*
39
 * Use inlined functions for supported systems.
40
 */
41
#if defined(_MSC_VER) && !defined(__cplusplus)   /* Visual Studio */
42
#define inline __inline  /* Visual C is not C99, but supports some kind of inline */
43
#endif
44
45
1.85M
#define MAX_COPY 32U
46
48.1M
#define MAX_DISTANCE 8191
47
45.2M
#define MAX_FARDISTANCE (65535 + MAX_DISTANCE - 1)
48
49
#ifdef BLOSC_STRICT_ALIGN
50
  #define BLOSCLZ_READU16(p) ((p)[0] | (p)[1]<<8)
51
  #define BLOSCLZ_READU32(p) ((p)[0] | (p)[1]<<8 | (p)[2]<<16 | (p)[3]<<24)
52
#else
53
  #define BLOSCLZ_READU16(p) *((const uint16_t*)(p))
54
97.2M
  #define BLOSCLZ_READU32(p) *((const uint32_t*)(p))
55
#endif
56
57
453k
#define HASH_LOG (14U)
58
59
// This is used in LZ4 and seems to work pretty well here too
60
48.0M
#define HASH_FUNCTION(v, s, h) {      \
61
48.0M
  (v) = ((s) * 2654435761U) >> (32U - (h)); \
62
48.0M
}
63
64
65
#if defined(__AVX2__)
66
static uint8_t *get_run_32(uint8_t *ip, const uint8_t *ip_bound, const uint8_t *ref) {
67
    uint8_t x = ip[-1];
68
69
    while (ip < (ip_bound - (sizeof(__m256i)))) {
70
        __m256i value, value2, cmp;
71
        /* Broadcast the value for every byte in a 256-bit register */
72
        memset(&value, x, sizeof(__m256i));
73
        value2 = _mm256_loadu_si256((__m256i *)ref);
74
        cmp = _mm256_cmpeq_epi64(value, value2);
75
        if ((unsigned)_mm256_movemask_epi8(cmp) != 0xFFFFFFFF) {
76
            /* Return the byte that starts to differ */
77
            while (*ref++ == x) ip++;
78
            return ip;
79
        }
80
        else {
81
            ip += sizeof(__m256i);
82
            ref += sizeof(__m256i);
83
        }
84
    }
85
    /* Look into the remainder */
86
    while ((ip < ip_bound) && (*ref++ == x)) ip++;
87
    return ip;
88
}
89
#endif
90
91
#if defined(__SSE2__)
92
0
uint8_t *get_run_16(uint8_t *ip, const uint8_t *ip_bound, const uint8_t *ref) {
93
0
  uint8_t x = ip[-1];
94
95
0
  while (ip < (ip_bound - sizeof(__m128i))) {
96
0
    __m128i value, value2, cmp;
97
    /* Broadcast the value for every byte in a 128-bit register */
98
0
    memset(&value, x, sizeof(__m128i));
99
0
    value2 = _mm_loadu_si128((__m128i *)ref);
100
0
    cmp = _mm_cmpeq_epi32(value, value2);
101
0
    if (_mm_movemask_epi8(cmp) != 0xFFFF) {
102
      /* Return the byte that starts to differ */
103
0
      while (*ref++ == x) ip++;
104
0
      return ip;
105
0
    }
106
0
    else {
107
0
      ip += sizeof(__m128i);
108
0
      ref += sizeof(__m128i);
109
0
    }
110
0
  }
111
  /* Look into the remainder */
112
0
  while ((ip < ip_bound) && (*ref++ == x)) ip++;
113
0
  return ip;
114
0
}
115
116
#endif
117
118
119
1.16M
static uint8_t *get_run(uint8_t *ip, const uint8_t *ip_bound, const uint8_t *ref) {
120
1.16M
  uint8_t x = ip[-1];
121
1.16M
  int64_t value, value2;
122
  /* Broadcast the value for every byte in a 64-bit register */
123
1.16M
  memset(&value, x, 8);
124
  /* safe because the outer check against ip limit */
125
2.04M
  while (ip < (ip_bound - sizeof(int64_t))) {
126
#if defined(BLOSC_STRICT_ALIGN)
127
    memcpy(&value2, ref, 8);
128
#else
129
2.03M
    value2 = ((int64_t*)ref)[0];
130
2.03M
#endif
131
2.03M
    if (value != value2) {
132
      /* Return the byte that starts to differ */
133
3.58M
      while (*ref++ == x) ip++;
134
1.14M
      return ip;
135
1.14M
    }
136
885k
    else {
137
885k
      ip += 8;
138
885k
      ref += 8;
139
885k
    }
140
2.03M
  }
141
  /* Look into the remainder */
142
62.0k
  while ((ip < ip_bound) && (*ref++ == x)) ip++;
143
15.9k
  return ip;
144
1.16M
}
145
146
147
/* Return the byte that starts to differ */
148
0
uint8_t *get_match(uint8_t *ip, const uint8_t *ip_bound, const uint8_t *ref) {
149
0
#if !defined(BLOSC_STRICT_ALIGN)
150
0
  while (ip < (ip_bound - sizeof(int64_t))) {
151
0
    if (*(int64_t*)ref != *(int64_t*)ip) {
152
      /* Return the byte that starts to differ */
153
0
      while (*ref++ == *ip++) {}
154
0
      return ip;
155
0
    }
156
0
    else {
157
0
      ip += sizeof(int64_t);
158
0
      ref += sizeof(int64_t);
159
0
    }
160
0
  }
161
0
#endif
162
  /* Look into the remainder */
163
0
  while ((ip < ip_bound) && (*ref++ == *ip++)) {}
164
0
  return ip;
165
0
}
166
167
168
#if defined(__SSE2__)
169
8.37M
static uint8_t *get_match_16(uint8_t *ip, const uint8_t *ip_bound, const uint8_t *ref) {
170
8.37M
  __m128i value, value2, cmp;
171
172
11.5M
  while (ip < (ip_bound - sizeof(__m128i))) {
173
11.4M
    value = _mm_loadu_si128((__m128i *) ip);
174
11.4M
    value2 = _mm_loadu_si128((__m128i *) ref);
175
11.4M
    cmp = _mm_cmpeq_epi32(value, value2);
176
11.4M
    if (_mm_movemask_epi8(cmp) != 0xFFFF) {
177
      /* Return the byte that starts to differ */
178
24.3M
      while (*ref++ == *ip++) {}
179
8.29M
      return ip;
180
8.29M
    }
181
3.15M
    else {
182
3.15M
      ip += sizeof(__m128i);
183
3.15M
      ref += sizeof(__m128i);
184
3.15M
    }
185
11.4M
  }
186
  /* Look into the remainder */
187
448k
  while ((ip < ip_bound) && (*ref++ == *ip++)) {}
188
82.5k
  return ip;
189
8.37M
}
190
#endif
191
192
193
#if defined(__AVX2__)
194
static uint8_t *get_match_32(uint8_t *ip, const uint8_t *ip_bound, const uint8_t *ref) {
195
196
  while (ip < (ip_bound - sizeof(__m256i))) {
197
    __m256i value, value2, cmp;
198
    value = _mm256_loadu_si256((__m256i *) ip);
199
    value2 = _mm256_loadu_si256((__m256i *)ref);
200
    cmp = _mm256_cmpeq_epi64(value, value2);
201
    if ((unsigned)_mm256_movemask_epi8(cmp) != 0xFFFFFFFF) {
202
      /* Return the byte that starts to differ */
203
      while (*ref++ == *ip++) {}
204
      return ip;
205
    }
206
    else {
207
      ip += sizeof(__m256i);
208
      ref += sizeof(__m256i);
209
    }
210
  }
211
  /* Look into the remainder */
212
  while ((ip < ip_bound) && (*ref++ == *ip++)) {}
213
  return ip;
214
}
215
#endif
216
217
218
9.54M
static uint8_t* get_run_or_match(uint8_t* ip, uint8_t* ip_bound, const uint8_t* ref, bool run) {
219
9.54M
  if (BLOSCLZ_UNLIKELY(run)) {
220
#if defined(__AVX2__)
221
    // Extensive experiments on AMD Ryzen3 say that regular get_run is faster
222
    // ip = get_run_32(ip, ip_bound, ref);
223
    ip = get_run(ip, ip_bound, ref);
224
#elif defined(__SSE2__)
225
    // Extensive experiments on AMD Ryzen3 say that regular get_run is faster
226
    // ip = get_run_16(ip, ip_bound, ref);
227
1.16M
    ip = get_run(ip, ip_bound, ref);
228
#else
229
    ip = get_run(ip, ip_bound, ref);
230
#endif
231
1.16M
  }
232
8.37M
  else {
233
#if defined(__AVX2__)
234
    // Extensive experiments on AMD Ryzen3 say that regular get_match_16 is faster
235
    // ip = get_match_32(ip, ip_bound, ref);
236
    ip = get_match_16(ip, ip_bound, ref);
237
#elif defined(__SSE2__)
238
    ip = get_match_16(ip, ip_bound, ref);
239
#else
240
    ip = get_match(ip, ip_bound, ref);
241
#endif
242
8.37M
  }
243
244
9.54M
  return ip;
245
9.54M
}
246
247
248
19.0M
#define LITERAL(ip, op, op_limit, anchor, copy) {       \
249
19.0M
  if (BLOSCLZ_UNLIKELY((op_limit) - (op) < 2))          \
250
19.0M
    goto out;                                           \
251
19.0M
  *(op)++ = *(anchor)++;                                \
252
19.0M
  (ip) = (anchor);                                      \
253
19.0M
  (copy)++;                                             \
254
19.0M
  if (BLOSCLZ_UNLIKELY((copy) == MAX_COPY)) {           \
255
349k
    (copy) = 0;                                         \
256
349k
    *(op)++ = MAX_COPY-1;                               \
257
349k
  }                                                     \
258
19.0M
}
259
260
23.9M
#define LITERAL2(ip, anchor, copy) {                    \
261
23.9M
  oc++; (anchor)++;                                     \
262
23.9M
  (ip) = (anchor);                                      \
263
23.9M
  (copy)++;                                             \
264
23.9M
  if (BLOSCLZ_UNLIKELY((copy) == MAX_COPY)) {           \
265
568k
    (copy) = 0;                                         \
266
568k
    oc++;                                               \
267
568k
  }                                                     \
268
23.9M
}
269
270
561k
#define MATCH_SHORT(op, op_limit, len, distance) {        \
271
561k
  if (BLOSCLZ_UNLIKELY((op_limit) - (op) < 2))            \
272
561k
    goto out;                                             \
273
561k
  *(op)++ = (uint8_t)(((len) << 5U) + ((distance) >> 8U));\
274
561k
  *(op)++ = (uint8_t)(((distance) & 255U));               \
275
561k
}
276
277
866k
#define MATCH_LONG(op, op_limit, len, distance) {       \
278
866k
  if (BLOSCLZ_UNLIKELY((op_limit) - (op) < 1))          \
279
866k
    goto out;                                           \
280
866k
  *(op)++ = (uint8_t)((7U << 5U) + ((distance) >> 8U)); \
281
938k
  for ((len) -= 7; (len) >= 255; (len) -= 255) {        \
282
71.1k
    if (BLOSCLZ_UNLIKELY((op_limit) - (op) < 1))        \
283
71.1k
      goto out;                                         \
284
71.1k
    *(op)++ = 255;                                      \
285
71.1k
  }                                                     \
286
866k
  if (BLOSCLZ_UNLIKELY((op_limit) - (op) < 2))          \
287
866k
    goto out;                                           \
288
866k
  *(op)++ = (uint8_t)(len);                             \
289
866k
  *(op)++ = (uint8_t)(((distance) & 255U));             \
290
866k
}
291
292
3.99k
#define MATCH_SHORT_FAR(op, op_limit, len, distance) {      \
293
3.99k
  if (BLOSCLZ_UNLIKELY((op_limit) - (op) < 4))              \
294
3.99k
    goto out;                                               \
295
3.99k
  *(op)++ = (uint8_t)(((len) << 5U) + 31);                  \
296
3.97k
  *(op)++ = 255;                                            \
297
3.97k
  *(op)++ = (uint8_t)((distance) >> 8U);                    \
298
3.97k
  *(op)++ = (uint8_t)((distance) & 255U);                   \
299
3.97k
}
300
301
36.4k
#define MATCH_LONG_FAR(op, op_limit, len, distance) {       \
302
36.4k
  if (BLOSCLZ_UNLIKELY((op_limit) - (op) < 1))              \
303
36.4k
    goto out;                                               \
304
36.4k
  *(op)++ = (7U << 5U) + 31;                                \
305
53.1k
  for ((len) -= 7; (len) >= 255; (len) -= 255) {            \
306
16.6k
    if (BLOSCLZ_UNLIKELY((op_limit) - (op) < 1))            \
307
16.6k
      goto out;                                             \
308
16.6k
    *(op)++ = 255;                                          \
309
16.6k
  }                                                         \
310
36.4k
  if (BLOSCLZ_UNLIKELY((op_limit) - (op) < 4))              \
311
36.4k
    goto out;                                               \
312
36.4k
  *(op)++ = (uint8_t)(len);                                 \
313
36.4k
  *(op)++ = 255;                                            \
314
36.4k
  *(op)++ = (uint8_t)((distance) >> 8U);                    \
315
36.4k
  *(op)++ = (uint8_t)((distance) & 255U);                   \
316
36.4k
}
317
318
319
// Get a guess for the compressed size of a buffer
320
50.4k
static double get_cratio(uint8_t* ibase, int maxlen, int minlen, int ipshift, uint32_t htab[], int8_t hashlog) {
321
50.4k
  uint8_t* ip = ibase;
322
50.4k
  int32_t oc = 0;
323
50.4k
  const uint16_t hashlen = (1U << (uint8_t)hashlog);
324
50.4k
  uint32_t hval;
325
50.4k
  uint32_t seq;
326
50.4k
  uint8_t copy;
327
  // Make a tradeoff between testing too much and too little
328
50.4k
  uint16_t limit = (maxlen > hashlen) ? hashlen : maxlen;
329
50.4k
  uint8_t* ip_bound = ibase + limit - 1;
330
50.4k
  uint8_t* ip_limit = ibase + limit - 12;
331
332
  // Initialize the hash table to distances of 0
333
50.4k
  memset(htab, 0, hashlen * sizeof(uint32_t));
334
335
  /* we start with literal copy */
336
50.4k
  copy = 4;
337
50.4k
  oc += 5;
338
339
  /* main loop */
340
24.9M
  while (BLOSCLZ_LIKELY(ip < ip_limit)) {
341
24.8M
    const uint8_t* ref;
342
24.8M
    unsigned distance;
343
24.8M
    uint8_t* anchor = ip;    /* comparison starting-point */
344
345
    /* find potential match */
346
24.8M
    seq = BLOSCLZ_READU32(ip);
347
24.8M
    HASH_FUNCTION(hval, seq, hashlog)
348
24.8M
    ref = ibase + htab[hval];
349
350
    /* calculate distance to the match */
351
24.8M
    distance = (unsigned int)(anchor - ref);
352
353
    /* update hash table */
354
24.8M
    htab[hval] = (uint32_t) (anchor - ibase);
355
356
24.8M
    if (distance == 0 || (distance >= MAX_FARDISTANCE)) {
357
50.2k
      LITERAL2(ip, anchor, copy)
358
50.2k
      continue;
359
50.2k
    }
360
361
    /* is this a match? check the first 4 bytes */
362
24.8M
    if (BLOSCLZ_READU32(ref) == BLOSCLZ_READU32(ip)) {
363
3.82M
      ref += 4;
364
3.82M
    }
365
20.9M
    else {
366
      /* no luck, copy as a literal */
367
20.9M
      LITERAL2(ip, anchor, copy)
368
20.9M
      continue;
369
20.9M
    }
370
371
    /* last matched byte */
372
3.82M
    ip = anchor + 4;
373
374
    /* distance is biased */
375
3.82M
    distance--;
376
377
    /* get runs or matches; zero distance means a run */
378
3.82M
    ip = get_run_or_match(ip, ip_bound, ref, !distance);
379
380
3.82M
    ip -= ipshift;
381
3.82M
    int len = (int)(ip - anchor);
382
3.82M
    if (len < minlen) {
383
2.96M
      LITERAL2(ip, anchor, copy)
384
2.96M
      continue;
385
2.96M
    }
386
387
    /* if we haven't copied anything, adjust the output counter */
388
859k
    if (!copy)
389
202k
      oc--;
390
    /* reset literal counter */
391
859k
    copy = 0;
392
393
    /* encode the match */
394
859k
    if (distance < MAX_DISTANCE) {
395
854k
      if (len >= 7) {
396
472k
        oc += ((len - 7) / 255) + 1;
397
472k
      }
398
854k
      oc += 2;
399
854k
    }
400
5.47k
    else {
401
      /* far away, but not yet in the another galaxy... */
402
5.47k
      if (len >= 7) {
403
3.52k
        oc += ((len - 7) / 255) + 1;
404
3.52k
      }
405
5.47k
      oc += 4;
406
5.47k
    }
407
408
    /* update the hash at match boundary */
409
859k
    seq = BLOSCLZ_READU32(ip);
410
859k
    HASH_FUNCTION(hval, seq, hashlog)
411
859k
    htab[hval] = (uint32_t)(ip++ - ibase);
412
859k
    ip++;
413
    /* assuming literal copy */
414
859k
    oc++;
415
859k
  }
416
417
50.4k
  double ic = (double)(ip - ibase);
418
50.4k
  return ic / (double)oc;
419
50.4k
}
420
421
422
int blosclz_compress(const int clevel, const void* input, int length,
423
50.4k
                     void* output, int maxout, blosc2_context* ctx) {
424
50.4k
  BLOSC_UNUSED_PARAM(ctx);
425
50.4k
  uint8_t* ibase = (uint8_t*)input;
426
50.4k
  uint32_t htab[1U << (uint8_t)HASH_LOG];
427
428
  /* When we go back in a match (shift), we obtain quite different compression properties.
429
   * It looks like 4 is more useful in combination with bitshuffle and small typesizes
430
   * Fallback to 4 because it provides more consistent results for large cratios.
431
   * UPDATE: new experiments show that using a value of 3 is a bit better, at least for ERA5.
432
   * UPDATE 2: go back to 4, as they seem to provide better cratios in general.
433
   *
434
   * In this block we also check cratios for the beginning of the buffers and
435
   * eventually discard those that are small (take too long to decompress).
436
   * This process is called _entropy probing_.
437
   */
438
50.4k
  unsigned ipshift = 4;
439
  // Minimum lengths for encoding (normally it is good to match the shift value)
440
50.4k
  unsigned minlen = 4;
441
442
50.4k
  uint8_t hashlog_[10] = {0, HASH_LOG - 2, HASH_LOG - 1, HASH_LOG, HASH_LOG,
443
50.4k
                          HASH_LOG, HASH_LOG, HASH_LOG, HASH_LOG, HASH_LOG};
444
50.4k
  uint8_t hashlog = hashlog_[clevel];
445
446
  // Experiments say that checking 1/4 of the buffer is enough to figure out approx cratio
447
  // UPDATE: new experiments with ERA5 datasets (float32) say that checking the whole buffer
448
  // is better (specially when combined with bitshuffle).
449
  // The loss in speed for checking the whole buffer is pretty negligible too.
450
50.4k
  int maxlen = length;
451
50.4k
  if (clevel < 2) {
452
36.4k
    maxlen /= 8;
453
36.4k
  }
454
13.9k
  else if (clevel < 4) {
455
2.33k
    maxlen /= 4;
456
2.33k
  }
457
11.6k
  else if (clevel < 7) {
458
4.03k
    maxlen /= 2;
459
4.03k
  }
460
  // Start probing somewhere inside the buffer
461
50.4k
  int shift = length - maxlen;
462
  // Actual entropy probing!
463
50.4k
  double cratio = get_cratio(ibase + shift, maxlen, minlen, ipshift, htab, hashlog);
464
  // discard probes with small compression ratios (too expensive)
465
50.4k
  double cratio_[10] = {0, 2, 1.5, 1.2, 1.2, 1.2, 1.2, 1.15, 1.1, 1.0};
466
50.4k
  if (cratio < cratio_[clevel]) {
467
16.6k
      goto out;
468
16.6k
  }
469
470
33.7k
  uint8_t* ip = ibase;
471
33.7k
  uint8_t* ip_bound = ibase + length - 1;
472
33.7k
  uint8_t* ip_limit = ibase + length - 12;
473
33.7k
  uint8_t* op = (uint8_t*)output;
474
33.7k
  const uint8_t* op_limit = op + maxout;
475
33.7k
  uint32_t seq;
476
33.7k
  uint8_t copy;
477
33.7k
  uint32_t hval;
478
479
  /* input and output buffer cannot be less than 16 and 66 bytes or we can get into trouble */
480
33.7k
  if (length < 16 || maxout < 66) {
481
187
    return 0;
482
187
  }
483
484
  // Initialize the hash table
485
33.5k
  memset(htab, 0, (1U << hashlog) * sizeof(uint32_t));
486
487
  /* we start with literal copy */
488
33.5k
  copy = 4;
489
33.5k
  *op++ = MAX_COPY - 1;
490
33.5k
  *op++ = *ip++;
491
33.5k
  *op++ = *ip++;
492
33.5k
  *op++ = *ip++;
493
33.5k
  *op++ = *ip++;
494
495
  /* main loop */
496
20.5M
  while (BLOSCLZ_LIKELY(ip < ip_limit)) {
497
20.4M
    const uint8_t* ref;
498
20.4M
    unsigned distance;
499
20.4M
    uint8_t* anchor = ip;    /* comparison starting-point */
500
501
    /* find potential match */
502
20.4M
    seq = BLOSCLZ_READU32(ip);
503
20.4M
    HASH_FUNCTION(hval, seq, hashlog)
504
20.4M
    ref = ibase + htab[hval];
505
506
    /* calculate distance to the match */
507
20.4M
    distance = (unsigned int)(anchor - ref);
508
509
    /* update hash table */
510
20.4M
    htab[hval] = (uint32_t) (anchor - ibase);
511
512
20.4M
    if (distance == 0 || (distance >= MAX_FARDISTANCE)) {
513
52.8k
      LITERAL(ip, op, op_limit, anchor, copy)
514
0
      continue;
515
52.8k
    }
516
517
    /* is this a match? check the first 4 bytes */
518
20.4M
    if (BLOSCLZ_UNLIKELY(BLOSCLZ_READU32(ref) == BLOSCLZ_READU32(ip))) {
519
5.71M
      ref += 4;
520
14.7M
    } else {
521
      /* no luck, copy as a literal */
522
14.7M
      LITERAL(ip, op, op_limit, anchor, copy)
523
0
      continue;
524
14.7M
    }
525
526
    /* last matched byte */
527
5.71M
    ip = anchor + 4;
528
529
    /* distance is biased */
530
5.71M
    distance--;
531
532
    /* get runs or matches; zero distance means a run */
533
5.71M
    ip = get_run_or_match(ip, ip_bound, ref, !distance);
534
535
    /* length is biased, '1' means a match of 3 bytes */
536
5.71M
    ip -= ipshift;
537
538
5.71M
    unsigned len = (int)(ip - anchor);
539
540
    // Encoding short lengths is expensive during decompression
541
5.71M
    if (len < minlen || (len <= 5 && distance >= MAX_DISTANCE)) {
542
4.24M
      LITERAL(ip, op, op_limit, anchor, copy)
543
0
      continue;
544
4.24M
    }
545
546
    /* if we have copied something, adjust the copy count */
547
1.46M
    if (copy)
548
      /* copy is biased, '0' means 1 byte copy */
549
1.03M
      *(op - copy - 1) = (uint8_t)(copy - 1);
550
431k
    else
551
      /* back, to overwrite the copy count */
552
431k
      op--;
553
    /* reset literal counter */
554
1.46M
    copy = 0;
555
556
    /* encode the match */
557
1.46M
    if (distance < MAX_DISTANCE) {
558
1.42M
      if (len < 7) {
559
561k
        MATCH_SHORT(op, op_limit, len, distance)
560
866k
      } else {
561
2.60M
        MATCH_LONG(op, op_limit, len, distance)
562
2.60M
      }
563
1.42M
    } else {
564
      /* far away, but not yet in the another galaxy... */
565
40.4k
      distance -= MAX_DISTANCE;
566
40.4k
      if (len < 7) {
567
3.99k
        MATCH_SHORT_FAR(op, op_limit, len, distance)
568
36.4k
      } else {
569
109k
        MATCH_LONG_FAR(op, op_limit, len, distance)
570
109k
      }
571
40.4k
    }
572
573
    /* update the hash at match boundary */
574
1.46M
    seq = BLOSCLZ_READU32(ip);
575
1.46M
    HASH_FUNCTION(hval, seq, hashlog)
576
1.46M
    htab[hval] = (uint32_t) (ip++ - ibase);
577
1.46M
    if (clevel == 9) {
578
      // In some situations, including a second hash proves to be useful,
579
      // but not in others.  Activating here in max clevel only.
580
441k
      seq >>= 8U;
581
441k
      HASH_FUNCTION(hval, seq, hashlog)
582
441k
      htab[hval] = (uint32_t) (ip++ - ibase);
583
441k
    }
584
1.02M
    else {
585
1.02M
      ip++;
586
1.02M
    }
587
588
1.46M
    if (BLOSCLZ_UNLIKELY(op_limit - op < 1))
589
203
      goto out;
590
591
    /* assuming literal copy */
592
1.46M
    *op++ = MAX_COPY - 1;
593
1.46M
  }
594
595
  /* left-over as literal copy */
596
195k
  while (BLOSCLZ_UNLIKELY(ip <= ip_bound)) {
597
163k
    if (BLOSCLZ_UNLIKELY(op_limit - op < 2)) goto out;
598
163k
    *op++ = *ip++;
599
163k
    copy++;
600
163k
    if (BLOSCLZ_UNLIKELY(copy == MAX_COPY)) {
601
845
      copy = 0;
602
845
      *op++ = MAX_COPY - 1;
603
845
    }
604
163k
  }
605
606
  /* if we have copied something, adjust the copy length */
607
32.2k
  if (copy)
608
31.9k
    *(op - copy - 1) = (uint8_t)(copy - 1);
609
305
  else
610
305
    op--;
611
612
  /* marker for blosclz */
613
32.2k
  *(uint8_t*)output |= (1U << 5U);
614
615
32.2k
  return (int)(op - (uint8_t*)output);
616
617
17.9k
  out:
618
17.9k
  return 0;
619
32.5k
}
620
621
// See https://habr.com/en/company/yandex/blog/457612/
622
#if defined(__AVX2__)
623
624
#if defined(_MSC_VER)
625
#define ALIGNED_(x) __declspec(align(x))
626
#else
627
#if defined(__GNUC__)
628
#define ALIGNED_(x) __attribute__ ((aligned(x)))
629
#endif
630
#endif
631
#define ALIGNED_TYPE_(t, x) t ALIGNED_(x)
632
633
static unsigned char* copy_match_16(unsigned char *op, const unsigned char *match, int32_t len)
634
{
635
  size_t offset = op - match;
636
  while (len >= 16) {
637
638
    static const ALIGNED_TYPE_(uint8_t, 16) masks[] =
639
      {
640
                0,  1,  2,  1,  4,  1,  4,  2,  8,  7,  6,  5,  4,  3,  2,  1, // offset = 0, not used as mask, but for shift
641
                0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // offset = 1
642
                0,  1,  0,  1,  0,  1,  0,  1,  0,  1,  0,  1,  0,  1,  0,  1,
643
                0,  1,  2,  0,  1,  2,  0,  1,  2,  0,  1,  2,  0,  1,  2,  0,
644
                0,  1,  2,  3,  0,  1,  2,  3,  0,  1,  2,  3,  0,  1,  2,  3,
645
                0,  1,  2,  3,  4,  0,  1,  2,  3,  4,  0,  1,  2,  3,  4,  0,
646
                0,  1,  2,  3,  4,  5,  0,  1,  2,  3,  4,  5,  0,  1,  2,  3,
647
                0,  1,  2,  3,  4,  5,  6,  0,  1,  2,  3,  4,  5,  6,  0,  1,
648
                0,  1,  2,  3,  4,  5,  6,  7,  0,  1,  2,  3,  4,  5,  6,  7,
649
                0,  1,  2,  3,  4,  5,  6,  7,  8,  0,  1,  2,  3,  4,  5,  6,
650
                0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  0,  1,  2,  3,  4,  5,
651
                0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10,  0,  1,  2,  3,  4,
652
                0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11,  0,  1,  2,  3,
653
                0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12,  0,  1,  2,
654
                0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13,  0,  1,
655
                0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,  0,
656
                0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,  15, // offset = 16
657
      };
658
659
    _mm_storeu_si128((__m128i *)(op),
660
                     _mm_shuffle_epi8(_mm_loadu_si128((const __m128i *)(match)),
661
                                      _mm_load_si128((const __m128i *)(masks) + offset)));
662
663
    match += masks[offset];
664
665
    op += 16;
666
    len -= 16;
667
  }
668
  // Deal with remainders
669
  for (; len > 0; len--) {
670
    *op++ = *match++;
671
  }
672
  return op;
673
}
674
#endif
675
676
// LZ4 wildCopy which can reach excellent copy bandwidth (even if insecure)
677
503k
static inline void wild_copy(uint8_t *out, const uint8_t* from, uint8_t* end) {
678
503k
  uint8_t* d = out;
679
503k
  const uint8_t* s = from;
680
503k
  uint8_t* const e = end;
681
682
2.66M
  do { memcpy(d,s,8); d+=8; s+=8; } while (d<e);
683
503k
}
684
685
11.0k
int blosclz_decompress(const void* input, int length, void* output, int maxout) {
686
11.0k
  const uint8_t* ip = (const uint8_t*)input;
687
11.0k
  const uint8_t* ip_limit = ip + length;
688
11.0k
  uint8_t* op = (uint8_t*)output;
689
11.0k
  uint32_t ctrl;
690
11.0k
  uint8_t* op_limit = op + maxout;
691
11.0k
  if (BLOSCLZ_UNLIKELY(length == 0)) {
692
0
    return 0;
693
0
  }
694
11.0k
  ctrl = (*ip++) & 31U;
695
696
2.11M
  while (1) {
697
2.11M
    if (ctrl >= 32) {
698
      // match
699
702k
      int32_t len = (int32_t)(ctrl >> 5U) - 1 ;
700
702k
      int32_t ofs = (int32_t)(ctrl & 31U) << 8U;
701
702k
      uint8_t code;
702
702k
      const uint8_t* ref = op - ofs;
703
704
702k
      if (len == 7 - 1) {
705
444k
        do {
706
444k
          if (BLOSCLZ_UNLIKELY(ip + 1 >= ip_limit)) {
707
24
            return 0;
708
24
          }
709
444k
          code = *ip++;
710
444k
          len += code;
711
444k
          if (BLOSCLZ_UNLIKELY(len > maxout)) {
712
30
            return 0;
713
30
          }
714
444k
        } while (code == 255);
715
375k
      }
716
326k
      else {
717
326k
        if (BLOSCLZ_UNLIKELY(ip + 1 >= ip_limit)) {
718
52
          return 0;
719
52
        }
720
326k
      }
721
702k
      code = *ip++;
722
702k
      len += 3;
723
702k
      ref -= code;
724
725
      /* match from 16-bit distance */
726
702k
      if (BLOSCLZ_UNLIKELY(code == 255)) {
727
61.4k
        if (ofs == (31U << 8U)) {
728
28.3k
          if (ip + 1 >= ip_limit) {
729
2
            return 0;
730
2
          }
731
28.3k
          ofs = (*ip++) << 8U;
732
28.3k
          ofs += *ip++;
733
28.3k
          ref = op - ofs - MAX_DISTANCE;
734
28.3k
        }
735
61.4k
      }
736
737
702k
      if (BLOSCLZ_UNLIKELY(len > op_limit - op)) {
738
115
        return 0;
739
115
      }
740
741
702k
      if (BLOSCLZ_UNLIKELY(ref <= (uint8_t*)output)) {
742
161
        return 0;
743
161
      }
744
745
701k
      if (BLOSCLZ_UNLIKELY(ip >= ip_limit)) break;
746
701k
      ctrl = *ip++;
747
748
701k
      ref--;
749
701k
      if (ref == op - 1) {
750
        /* optimized copy for a run */
751
73.6k
        memset(op, *ref, len);
752
73.6k
        op += len;
753
73.6k
      }
754
628k
      else if ((op - ref >= 8) && (op_limit - op >= len + 8)) {
755
        // copy with an overlap not larger than 8
756
503k
        wild_copy(op, ref, op + len);
757
503k
        op += len;
758
503k
      }
759
124k
      else {
760
        // general copy with any overlap
761
#if 0 && defined(__AVX2__)
762
        if (op - ref <= 16) {
763
          // This is not faster on a combination of compilers (clang, gcc, icc) or machines, but
764
          // it is not too slower either.
765
          op = copy_match_16(op, ref, len);
766
        }
767
        else {
768
#endif
769
124k
          op = copy_match(op, ref, (unsigned) len);
770
#if 0 && defined(__AVX2__)
771
        }
772
#endif
773
124k
      }
774
701k
    }
775
1.41M
    else {
776
      // literal
777
1.41M
      ctrl++;
778
1.41M
      if (BLOSCLZ_UNLIKELY(ctrl > op_limit - op)) {
779
81
        return 0;
780
81
      }
781
1.41M
      if (BLOSCLZ_UNLIKELY(ctrl > ip_limit - ip)) {
782
35
        return 0;
783
35
      }
784
785
1.41M
      memcpy(op, ip, ctrl); op += ctrl; ip += ctrl;
786
      // On GCC-6, fastcopy this is still faster than plain memcpy
787
      // However, using recent CLANG/LLVM 9.0, there is almost no difference
788
      // in performance.
789
      // And starting on CLANG/LLVM 10 and GCC 9, memcpy is generally faster.
790
      // op = fastcopy(op, ip, (unsigned) ctrl); ip += ctrl;
791
792
1.41M
      if (BLOSCLZ_UNLIKELY(ip >= ip_limit)) break;
793
1.40M
      ctrl = *ip++;
794
1.40M
    }
795
2.11M
  }
796
797
10.5k
  return (int)(op - (uint8_t*)output);
798
11.0k
}