Coverage Report

Created: 2026-05-19 06:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/duckdb/third_party/snappy/snappy-internal.h
Line
Count
Source
1
// Copyright 2008 Google Inc. All Rights Reserved.
2
//
3
// Redistribution and use in source and binary forms, with or without
4
// modification, are permitted provided that the following conditions are
5
// met:
6
//
7
//     * Redistributions of source code must retain the above copyright
8
// notice, this list of conditions and the following disclaimer.
9
//     * Redistributions in binary form must reproduce the above
10
// copyright notice, this list of conditions and the following disclaimer
11
// in the documentation and/or other materials provided with the
12
// distribution.
13
//     * Neither the name of Google Inc. nor the names of its
14
// contributors may be used to endorse or promote products derived from
15
// this software without specific prior written permission.
16
//
17
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
//
29
// Internals shared between the Snappy implementation and its unittest.
30
31
#ifndef THIRD_PARTY_SNAPPY_SNAPPY_INTERNAL_H_
32
#define THIRD_PARTY_SNAPPY_SNAPPY_INTERNAL_H_
33
34
#include "snappy_version.hpp"
35
36
#if SNAPPY_NEW_VERSION
37
38
#include <utility>
39
40
#include "snappy-stubs-internal.h"
41
42
#if SNAPPY_HAVE_SSSE3
43
// Please do not replace with <x86intrin.h> or with headers that assume more
44
// advanced SSE versions without checking with all the OWNERS.
45
#include <emmintrin.h>
46
#include <tmmintrin.h>
47
#endif
48
49
#if SNAPPY_HAVE_NEON
50
#include <arm_neon.h>
51
#endif
52
53
#if SNAPPY_HAVE_SSSE3 || SNAPPY_HAVE_NEON
54
#define SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE 1
55
#else
56
#define SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE 0
57
#endif
58
59
namespace duckdb_snappy {
60
namespace internal {
61
62
#if SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE
63
#if SNAPPY_HAVE_SSSE3
64
using V128 = __m128i;
65
#elif SNAPPY_HAVE_NEON
66
using V128 = uint8x16_t;
67
#endif
68
69
// Load 128 bits of integer data. `src` must be 16-byte aligned.
70
inline V128 V128_Load(const V128* src);
71
72
// Load 128 bits of integer data. `src` does not need to be aligned.
73
inline V128 V128_LoadU(const V128* src);
74
75
// Store 128 bits of integer data. `dst` does not need to be aligned.
76
inline void V128_StoreU(V128* dst, V128 val);
77
78
// Shuffle packed 8-bit integers using a shuffle mask.
79
// Each packed integer in the shuffle mask must be in [0,16).
80
inline V128 V128_Shuffle(V128 input, V128 shuffle_mask);
81
82
// Constructs V128 with 16 chars |c|.
83
inline V128 V128_DupChar(char c);
84
85
#if SNAPPY_HAVE_SSSE3
86
inline V128 V128_Load(const V128* src) { return _mm_load_si128(src); }
87
88
inline V128 V128_LoadU(const V128* src) { return _mm_loadu_si128(src); }
89
90
inline void V128_StoreU(V128* dst, V128 val) { _mm_storeu_si128(dst, val); }
91
92
inline V128 V128_Shuffle(V128 input, V128 shuffle_mask) {
93
  return _mm_shuffle_epi8(input, shuffle_mask);
94
}
95
96
inline V128 V128_DupChar(char c) { return _mm_set1_epi8(c); }
97
98
#elif SNAPPY_HAVE_NEON
99
inline V128 V128_Load(const V128* src) {
100
  return vld1q_u8(reinterpret_cast<const uint8_t*>(src));
101
}
102
103
inline V128 V128_LoadU(const V128* src) {
104
  return vld1q_u8(reinterpret_cast<const uint8_t*>(src));
105
}
106
107
inline void V128_StoreU(V128* dst, V128 val) {
108
  vst1q_u8(reinterpret_cast<uint8_t*>(dst), val);
109
}
110
111
inline V128 V128_Shuffle(V128 input, V128 shuffle_mask) {
112
  assert(vminvq_u8(shuffle_mask) >= 0 && vmaxvq_u8(shuffle_mask) <= 15);
113
  return vqtbl1q_u8(input, shuffle_mask);
114
}
115
116
inline V128 V128_DupChar(char c) { return vdupq_n_u8(c); }
117
#endif
118
#endif  // SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE
119
120
// Working memory performs a single allocation to hold all scratch space
121
// required for compression.
122
class WorkingMemory {
123
 public:
124
  explicit WorkingMemory(size_t input_size);
125
  ~WorkingMemory();
126
127
  // Allocates and clears a hash table using memory in "*this",
128
  // stores the number of buckets in "*table_size" and returns a pointer to
129
  // the base of the hash table.
130
  uint16_t* GetHashTable(size_t fragment_size, int* table_size) const;
131
0
  char* GetScratchInput() const { return input_; }
132
0
  char* GetScratchOutput() const { return output_; }
133
134
 private:
135
  char* mem_;        // the allocated memory, never nullptr
136
  size_t size_;      // the size of the allocated memory, never 0
137
  uint16_t* table_;  // the pointer to the hashtable
138
  char* input_;      // the pointer to the input scratch buffer
139
  char* output_;     // the pointer to the output scratch buffer
140
141
  // No copying
142
  WorkingMemory(const WorkingMemory&);
143
  void operator=(const WorkingMemory&);
144
};
145
146
// Flat array compression that does not emit the "uncompressed length"
147
// prefix. Compresses "input" string to the "*op" buffer.
148
//
149
// REQUIRES: "input_length <= kBlockSize"
150
// REQUIRES: "op" points to an array of memory that is at least
151
// "MaxCompressedLength(input_length)" in size.
152
// REQUIRES: All elements in "table[0..table_size-1]" are initialized to zero.
153
// REQUIRES: "table_size" is a power of two
154
//
155
// Returns an "end" pointer into "op" buffer.
156
// "end - op" is the compressed size of "input".
157
char* CompressFragment(const char* input,
158
                       size_t input_length,
159
                       char* op,
160
                       uint16_t* table,
161
                       const int table_size);
162
163
// Find the largest n such that
164
//
165
//   s1[0,n-1] == s2[0,n-1]
166
//   and n <= (s2_limit - s2).
167
//
168
// Return make_pair(n, n < 8).
169
// Does not read *s2_limit or beyond.
170
// Does not read *(s1 + (s2_limit - s2)) or beyond.
171
// Requires that s2_limit >= s2.
172
//
173
// In addition populate *data with the next 5 bytes from the end of the match.
174
// This is only done if 8 bytes are available (s2_limit - s2 >= 8). The point is
175
// that on some arch's this can be done faster in this routine than subsequent
176
// loading from s2 + n.
177
//
178
// Separate implementation for 64-bit, little-endian cpus.
179
#if !SNAPPY_IS_BIG_ENDIAN && \
180
    (defined(__x86_64__) || defined(_M_X64) || defined(ARCH_PPC) || \
181
     defined(ARCH_ARM))
182
static inline std::pair<size_t, bool> FindMatchLength(const char* s1,
183
                                                      const char* s2,
184
                                                      const char* s2_limit,
185
0
                                                      uint64_t* data) {
186
0
  assert(s2_limit >= s2);
187
0
  size_t matched = 0;
188
189
  // This block isn't necessary for correctness; we could just start looping
190
  // immediately.  As an optimization though, it is useful.  It creates some not
191
  // uncommon code paths that determine, without extra effort, whether the match
192
  // length is less than 8.  In short, we are hoping to avoid a conditional
193
  // branch, and perhaps get better code layout from the C++ compiler.
194
0
  if (SNAPPY_PREDICT_TRUE(s2 <= s2_limit - 16)) {
195
0
    uint64_t a1 = UNALIGNED_LOAD64(s1);
196
0
    uint64_t a2 = UNALIGNED_LOAD64(s2);
197
0
    if (SNAPPY_PREDICT_TRUE(a1 != a2)) {
198
      // This code is critical for performance. The reason is that it determines
199
      // how much to advance `ip` (s2). This obviously depends on both the loads
200
      // from the `candidate` (s1) and `ip`. Furthermore the next `candidate`
201
      // depends on the advanced `ip` calculated here through a load, hash and
202
      // new candidate hash lookup (a lot of cycles). This makes s1 (ie.
203
      // `candidate`) the variable that limits throughput. This is the reason we
204
      // go through hoops to have this function update `data` for the next iter.
205
      // The straightforward code would use *data, given by
206
      //
207
      // *data = UNALIGNED_LOAD64(s2 + matched_bytes) (Latency of 5 cycles),
208
      //
209
      // as input for the hash table lookup to find next candidate. However
210
      // this forces the load on the data dependency chain of s1, because
211
      // matched_bytes directly depends on s1. However matched_bytes is 0..7, so
212
      // we can also calculate *data by
213
      //
214
      // *data = AlignRight(UNALIGNED_LOAD64(s2), UNALIGNED_LOAD64(s2 + 8),
215
      //                    matched_bytes);
216
      //
217
      // The loads do not depend on s1 anymore and are thus off the bottleneck.
218
      // The straightforward implementation on x86_64 would be to use
219
      //
220
      // shrd rax, rdx, cl  (cl being matched_bytes * 8)
221
      //
222
      // unfortunately shrd with a variable shift has a 4 cycle latency. So this
223
      // only wins 1 cycle. The BMI2 shrx instruction is a 1 cycle variable
224
      // shift instruction but can only shift 64 bits. If we focus on just
225
      // obtaining the least significant 4 bytes, we can obtain this by
226
      //
227
      // *data = ConditionalMove(matched_bytes < 4, UNALIGNED_LOAD64(s2),
228
      //     UNALIGNED_LOAD64(s2 + 4) >> ((matched_bytes & 3) * 8);
229
      //
230
      // Writen like above this is not a big win, the conditional move would be
231
      // a cmp followed by a cmov (2 cycles) followed by a shift (1 cycle).
232
      // However matched_bytes < 4 is equal to
233
      // static_cast<uint32_t>(xorval) != 0. Writen that way, the conditional
234
      // move (2 cycles) can execute in parallel with FindLSBSetNonZero64
235
      // (tzcnt), which takes 3 cycles.
236
0
      uint64_t xorval = a1 ^ a2;
237
0
      int shift = Bits::FindLSBSetNonZero64(xorval);
238
0
      size_t matched_bytes = shift >> 3;
239
0
      uint64_t a3 = UNALIGNED_LOAD64(s2 + 4);
240
#ifndef __x86_64__
241
      a2 = static_cast<uint32_t>(xorval) == 0 ? a3 : a2;
242
#else
243
      // Ideally this would just be
244
      //
245
      // a2 = static_cast<uint32_t>(xorval) == 0 ? a3 : a2;
246
      //
247
      // However clang correctly infers that the above statement participates on
248
      // a critical data dependency chain and thus, unfortunately, refuses to
249
      // use a conditional move (it's tuned to cut data dependencies). In this
250
      // case there is a longer parallel chain anyway AND this will be fairly
251
      // unpredictable.
252
0
      asm("testl %k2, %k2\n\t"
253
0
          "cmovzq %1, %0\n\t"
254
0
          : "+r"(a2)
255
0
          : "r"(a3), "r"(xorval)
256
0
          : "cc");
257
0
#endif
258
0
      *data = a2 >> (shift & (3 * 8));
259
0
      return std::pair<size_t, bool>(matched_bytes, true);
260
0
    } else {
261
0
      matched = 8;
262
0
      s2 += 8;
263
0
    }
264
0
  }
265
0
  SNAPPY_PREFETCH(s1 + 64);
266
0
  SNAPPY_PREFETCH(s2 + 64);
267
268
  // Find out how long the match is. We loop over the data 64 bits at a
269
  // time until we find a 64-bit block that doesn't match; then we find
270
  // the first non-matching bit and use that to calculate the total
271
  // length of the match.
272
0
  while (SNAPPY_PREDICT_TRUE(s2 <= s2_limit - 16)) {
273
0
    uint64_t a1 = UNALIGNED_LOAD64(s1 + matched);
274
0
    uint64_t a2 = UNALIGNED_LOAD64(s2);
275
0
    if (a1 == a2) {
276
0
      s2 += 8;
277
0
      matched += 8;
278
0
    } else {
279
0
      uint64_t xorval = a1 ^ a2;
280
0
      int shift = Bits::FindLSBSetNonZero64(xorval);
281
0
      size_t matched_bytes = shift >> 3;
282
0
      uint64_t a3 = UNALIGNED_LOAD64(s2 + 4);
283
#ifndef __x86_64__
284
      a2 = static_cast<uint32_t>(xorval) == 0 ? a3 : a2;
285
#else
286
0
      asm("testl %k2, %k2\n\t"
287
0
          "cmovzq %1, %0\n\t"
288
0
          : "+r"(a2)
289
0
          : "r"(a3), "r"(xorval)
290
0
          : "cc");
291
0
#endif
292
0
      *data = a2 >> (shift & (3 * 8));
293
0
      matched += matched_bytes;
294
0
      assert(matched >= 8);
295
0
      return std::pair<size_t, bool>(matched, false);
296
0
    }
297
0
  }
298
0
  while (SNAPPY_PREDICT_TRUE(s2 < s2_limit)) {
299
0
    if (s1[matched] == *s2) {
300
0
      ++s2;
301
0
      ++matched;
302
0
    } else {
303
0
      if (s2 <= s2_limit - 8) {
304
0
        *data = UNALIGNED_LOAD64(s2);
305
0
      }
306
0
      return std::pair<size_t, bool>(matched, matched < 8);
307
0
    }
308
0
  }
309
0
  return std::pair<size_t, bool>(matched, matched < 8);
310
0
}
311
#else
312
static inline std::pair<size_t, bool> FindMatchLength(const char* s1,
313
                                                      const char* s2,
314
                                                      const char* s2_limit,
315
                                                      uint64_t* data) {
316
  // Implementation based on the x86-64 version, above.
317
  assert(s2_limit >= s2);
318
  int matched = 0;
319
320
  while (s2 <= s2_limit - 4 &&
321
         UNALIGNED_LOAD32(s2) == UNALIGNED_LOAD32(s1 + matched)) {
322
    s2 += 4;
323
    matched += 4;
324
  }
325
  if (LittleEndian::IsLittleEndian() && s2 <= s2_limit - 4) {
326
    uint32_t x = UNALIGNED_LOAD32(s2) ^ UNALIGNED_LOAD32(s1 + matched);
327
    int matching_bits = Bits::FindLSBSetNonZero(x);
328
    matched += matching_bits >> 3;
329
    s2 += matching_bits >> 3;
330
  } else {
331
    while ((s2 < s2_limit) && (s1[matched] == *s2)) {
332
      ++s2;
333
      ++matched;
334
    }
335
  }
336
  if (s2 <= s2_limit - 8) *data = LittleEndian::Load64(s2);
337
  return std::pair<size_t, bool>(matched, matched < 8);
338
}
339
#endif
340
341
static inline size_t FindMatchLengthPlain(const char* s1, const char* s2,
342
0
                                          const char* s2_limit) {
343
  // Implementation based on the x86-64 version, above.
344
0
  assert(s2_limit >= s2);
345
0
  int matched = 0;
346
347
0
  while (s2 <= s2_limit - 8 &&
348
0
         UNALIGNED_LOAD64(s2) == UNALIGNED_LOAD64(s1 + matched)) {
349
0
    s2 += 8;
350
0
    matched += 8;
351
0
  }
352
0
  if (LittleEndian::IsLittleEndian() && s2 <= s2_limit - 8) {
353
0
    uint64_t x = UNALIGNED_LOAD64(s2) ^ UNALIGNED_LOAD64(s1 + matched);
354
0
    int matching_bits = Bits::FindLSBSetNonZero64(x);
355
0
    matched += matching_bits >> 3;
356
0
    s2 += matching_bits >> 3;
357
0
  } else {
358
0
    while ((s2 < s2_limit) && (s1[matched] == *s2)) {
359
0
      ++s2;
360
0
      ++matched;
361
0
    }
362
0
  }
363
0
  return matched;
364
0
}
365
366
// Lookup tables for decompression code.  Give --snappy_dump_decompression_table
367
// to the unit test to recompute char_table.
368
369
enum {
370
  LITERAL = 0,
371
  COPY_1_BYTE_OFFSET = 1,  // 3 bit length + 3 bits of offset in opcode
372
  COPY_2_BYTE_OFFSET = 2,
373
  COPY_4_BYTE_OFFSET = 3
374
};
375
static const int kMaximumTagLength = 5;  // COPY_4_BYTE_OFFSET plus the actual offset.
376
377
// Data stored per entry in lookup table:
378
//      Range   Bits-used       Description
379
//      ------------------------------------
380
//      1..64   0..7            Literal/copy length encoded in opcode byte
381
//      0..7    8..10           Copy offset encoded in opcode byte / 256
382
//      0..4    11..13          Extra bytes after opcode
383
//
384
// We use eight bits for the length even though 7 would have sufficed
385
// because of efficiency reasons:
386
//      (1) Extracting a byte is faster than a bit-field
387
//      (2) It properly aligns copy offset so we do not need a <<8
388
static constexpr uint16_t char_table[256] = {
389
    // clang-format off
390
  0x0001, 0x0804, 0x1001, 0x2001, 0x0002, 0x0805, 0x1002, 0x2002,
391
  0x0003, 0x0806, 0x1003, 0x2003, 0x0004, 0x0807, 0x1004, 0x2004,
392
  0x0005, 0x0808, 0x1005, 0x2005, 0x0006, 0x0809, 0x1006, 0x2006,
393
  0x0007, 0x080a, 0x1007, 0x2007, 0x0008, 0x080b, 0x1008, 0x2008,
394
  0x0009, 0x0904, 0x1009, 0x2009, 0x000a, 0x0905, 0x100a, 0x200a,
395
  0x000b, 0x0906, 0x100b, 0x200b, 0x000c, 0x0907, 0x100c, 0x200c,
396
  0x000d, 0x0908, 0x100d, 0x200d, 0x000e, 0x0909, 0x100e, 0x200e,
397
  0x000f, 0x090a, 0x100f, 0x200f, 0x0010, 0x090b, 0x1010, 0x2010,
398
  0x0011, 0x0a04, 0x1011, 0x2011, 0x0012, 0x0a05, 0x1012, 0x2012,
399
  0x0013, 0x0a06, 0x1013, 0x2013, 0x0014, 0x0a07, 0x1014, 0x2014,
400
  0x0015, 0x0a08, 0x1015, 0x2015, 0x0016, 0x0a09, 0x1016, 0x2016,
401
  0x0017, 0x0a0a, 0x1017, 0x2017, 0x0018, 0x0a0b, 0x1018, 0x2018,
402
  0x0019, 0x0b04, 0x1019, 0x2019, 0x001a, 0x0b05, 0x101a, 0x201a,
403
  0x001b, 0x0b06, 0x101b, 0x201b, 0x001c, 0x0b07, 0x101c, 0x201c,
404
  0x001d, 0x0b08, 0x101d, 0x201d, 0x001e, 0x0b09, 0x101e, 0x201e,
405
  0x001f, 0x0b0a, 0x101f, 0x201f, 0x0020, 0x0b0b, 0x1020, 0x2020,
406
  0x0021, 0x0c04, 0x1021, 0x2021, 0x0022, 0x0c05, 0x1022, 0x2022,
407
  0x0023, 0x0c06, 0x1023, 0x2023, 0x0024, 0x0c07, 0x1024, 0x2024,
408
  0x0025, 0x0c08, 0x1025, 0x2025, 0x0026, 0x0c09, 0x1026, 0x2026,
409
  0x0027, 0x0c0a, 0x1027, 0x2027, 0x0028, 0x0c0b, 0x1028, 0x2028,
410
  0x0029, 0x0d04, 0x1029, 0x2029, 0x002a, 0x0d05, 0x102a, 0x202a,
411
  0x002b, 0x0d06, 0x102b, 0x202b, 0x002c, 0x0d07, 0x102c, 0x202c,
412
  0x002d, 0x0d08, 0x102d, 0x202d, 0x002e, 0x0d09, 0x102e, 0x202e,
413
  0x002f, 0x0d0a, 0x102f, 0x202f, 0x0030, 0x0d0b, 0x1030, 0x2030,
414
  0x0031, 0x0e04, 0x1031, 0x2031, 0x0032, 0x0e05, 0x1032, 0x2032,
415
  0x0033, 0x0e06, 0x1033, 0x2033, 0x0034, 0x0e07, 0x1034, 0x2034,
416
  0x0035, 0x0e08, 0x1035, 0x2035, 0x0036, 0x0e09, 0x1036, 0x2036,
417
  0x0037, 0x0e0a, 0x1037, 0x2037, 0x0038, 0x0e0b, 0x1038, 0x2038,
418
  0x0039, 0x0f04, 0x1039, 0x2039, 0x003a, 0x0f05, 0x103a, 0x203a,
419
  0x003b, 0x0f06, 0x103b, 0x203b, 0x003c, 0x0f07, 0x103c, 0x203c,
420
  0x0801, 0x0f08, 0x103d, 0x203d, 0x1001, 0x0f09, 0x103e, 0x203e,
421
  0x1801, 0x0f0a, 0x103f, 0x203f, 0x2001, 0x0f0b, 0x1040, 0x2040,
422
    // clang-format on
423
};
424
425
}  // end namespace internal
426
}  // end namespace duckdb_snappy
427
428
#else // #if SNAPPY_NEW_VERSION
429
430
#include "snappy-stubs-internal.h"
431
432
namespace duckdb_snappy {
433
namespace internal {
434
435
// Working memory performs a single allocation to hold all scratch space
436
// required for compression.
437
class WorkingMemory {
438
 public:
439
  explicit WorkingMemory(size_t input_size);
440
  ~WorkingMemory();
441
442
  // Allocates and clears a hash table using memory in "*this",
443
  // stores the number of buckets in "*table_size" and returns a pointer to
444
  // the base of the hash table.
445
  uint16* GetHashTable(size_t fragment_size, int* table_size) const;
446
  char* GetScratchInput() const { return input_; }
447
  char* GetScratchOutput() const { return output_; }
448
449
 private:
450
  char* mem_;      // the allocated memory, never nullptr
451
  size_t size_;    // the size of the allocated memory, never 0
452
  uint16* table_;  // the pointer to the hashtable
453
  char* input_;    // the pointer to the input scratch buffer
454
  char* output_;   // the pointer to the output scratch buffer
455
456
  // No copying
457
  WorkingMemory(const WorkingMemory&);
458
  void operator=(const WorkingMemory&);
459
};
460
461
// Flat array compression that does not emit the "uncompressed length"
462
// prefix. Compresses "input" string to the "*op" buffer.
463
//
464
// REQUIRES: "input_length <= kBlockSize"
465
// REQUIRES: "op" points to an array of memory that is at least
466
// "MaxCompressedLength(input_length)" in size.
467
// REQUIRES: All elements in "table[0..table_size-1]" are initialized to zero.
468
// REQUIRES: "table_size" is a power of two
469
//
470
// Returns an "end" pointer into "op" buffer.
471
// "end - op" is the compressed size of "input".
472
char* CompressFragment(const char* input,
473
                       size_t input_length,
474
                       char* op,
475
                       uint16* table,
476
                       const int table_size);
477
478
// Find the largest n such that
479
//
480
//   s1[0,n-1] == s2[0,n-1]
481
//   and n <= (s2_limit - s2).
482
//
483
// Return make_pair(n, n < 8).
484
// Does not read *s2_limit or beyond.
485
// Does not read *(s1 + (s2_limit - s2)) or beyond.
486
// Requires that s2_limit >= s2.
487
//
488
// Separate implementation for 64-bit, little-endian cpus.
489
#if !defined(SNAPPY_IS_BIG_ENDIAN) && \
490
    (defined(ARCH_K8) || defined(ARCH_PPC) || defined(ARCH_ARM))
491
static inline std::pair<size_t, bool> FindMatchLength(const char* s1,
492
                                                      const char* s2,
493
                                                      const char* s2_limit) {
494
  assert(s2_limit >= s2);
495
  size_t matched = 0;
496
497
  // This block isn't necessary for correctness; we could just start looping
498
  // immediately.  As an optimization though, it is useful.  It creates some not
499
  // uncommon code paths that determine, without extra effort, whether the match
500
  // length is less than 8.  In short, we are hoping to avoid a conditional
501
  // branch, and perhaps get better code layout from the C++ compiler.
502
  if (SNAPPY_PREDICT_TRUE(s2 <= s2_limit - 8)) {
503
    uint64 a1 = UNALIGNED_LOAD64(s1);
504
    uint64 a2 = UNALIGNED_LOAD64(s2);
505
    if (a1 != a2) {
506
      return std::pair<size_t, bool>(Bits::FindLSBSetNonZero64(a1 ^ a2) >> 3,
507
                                     true);
508
    } else {
509
      matched = 8;
510
      s2 += 8;
511
    }
512
  }
513
514
  // Find out how long the match is. We loop over the data 64 bits at a
515
  // time until we find a 64-bit block that doesn't match; then we find
516
  // the first non-matching bit and use that to calculate the total
517
  // length of the match.
518
  while (SNAPPY_PREDICT_TRUE(s2 <= s2_limit - 8)) {
519
    if (UNALIGNED_LOAD64(s2) == UNALIGNED_LOAD64(s1 + matched)) {
520
      s2 += 8;
521
      matched += 8;
522
    } else {
523
      uint64 x = UNALIGNED_LOAD64(s2) ^ UNALIGNED_LOAD64(s1 + matched);
524
      int matching_bits = Bits::FindLSBSetNonZero64(x);
525
      matched += matching_bits >> 3;
526
      assert(matched >= 8);
527
      return std::pair<size_t, bool>(matched, false);
528
    }
529
  }
530
  while (SNAPPY_PREDICT_TRUE(s2 < s2_limit)) {
531
    if (s1[matched] == *s2) {
532
      ++s2;
533
      ++matched;
534
    } else {
535
      return std::pair<size_t, bool>(matched, matched < 8);
536
    }
537
  }
538
  return std::pair<size_t, bool>(matched, matched < 8);
539
}
540
#else
541
static inline std::pair<size_t, bool> FindMatchLength(const char* s1,
542
                                                      const char* s2,
543
                                                      const char* s2_limit) {
544
  // Implementation based on the x86-64 version, above.
545
  assert(s2_limit >= s2);
546
  int matched = 0;
547
548
  while (s2 <= s2_limit - 4 &&
549
         UNALIGNED_LOAD32(s2) == UNALIGNED_LOAD32(s1 + matched)) {
550
    s2 += 4;
551
    matched += 4;
552
  }
553
  if (LittleEndian::IsLittleEndian() && s2 <= s2_limit - 4) {
554
    uint32 x = UNALIGNED_LOAD32(s2) ^ UNALIGNED_LOAD32(s1 + matched);
555
    int matching_bits = Bits::FindLSBSetNonZero(x);
556
    matched += matching_bits >> 3;
557
  } else {
558
    while ((s2 < s2_limit) && (s1[matched] == *s2)) {
559
      ++s2;
560
      ++matched;
561
    }
562
  }
563
  return std::pair<size_t, bool>(matched, matched < 8);
564
}
565
#endif
566
567
// Lookup tables for decompression code.  Give --snappy_dump_decompression_table
568
// to the unit test to recompute char_table.
569
570
enum {
571
  LITERAL = 0,
572
  COPY_1_BYTE_OFFSET = 1,  // 3 bit length + 3 bits of offset in opcode
573
  COPY_2_BYTE_OFFSET = 2,
574
  COPY_4_BYTE_OFFSET = 3
575
};
576
static const int kMaximumTagLength = 5;  // COPY_4_BYTE_OFFSET plus the actual offset.
577
578
// Data stored per entry in lookup table:
579
//      Range   Bits-used       Description
580
//      ------------------------------------
581
//      1..64   0..7            Literal/copy length encoded in opcode byte
582
//      0..7    8..10           Copy offset encoded in opcode byte / 256
583
//      0..4    11..13          Extra bytes after opcode
584
//
585
// We use eight bits for the length even though 7 would have sufficed
586
// because of efficiency reasons:
587
//      (1) Extracting a byte is faster than a bit-field
588
//      (2) It properly aligns copy offset so we do not need a <<8
589
static const uint16 char_table[256] = {
590
  0x0001, 0x0804, 0x1001, 0x2001, 0x0002, 0x0805, 0x1002, 0x2002,
591
  0x0003, 0x0806, 0x1003, 0x2003, 0x0004, 0x0807, 0x1004, 0x2004,
592
  0x0005, 0x0808, 0x1005, 0x2005, 0x0006, 0x0809, 0x1006, 0x2006,
593
  0x0007, 0x080a, 0x1007, 0x2007, 0x0008, 0x080b, 0x1008, 0x2008,
594
  0x0009, 0x0904, 0x1009, 0x2009, 0x000a, 0x0905, 0x100a, 0x200a,
595
  0x000b, 0x0906, 0x100b, 0x200b, 0x000c, 0x0907, 0x100c, 0x200c,
596
  0x000d, 0x0908, 0x100d, 0x200d, 0x000e, 0x0909, 0x100e, 0x200e,
597
  0x000f, 0x090a, 0x100f, 0x200f, 0x0010, 0x090b, 0x1010, 0x2010,
598
  0x0011, 0x0a04, 0x1011, 0x2011, 0x0012, 0x0a05, 0x1012, 0x2012,
599
  0x0013, 0x0a06, 0x1013, 0x2013, 0x0014, 0x0a07, 0x1014, 0x2014,
600
  0x0015, 0x0a08, 0x1015, 0x2015, 0x0016, 0x0a09, 0x1016, 0x2016,
601
  0x0017, 0x0a0a, 0x1017, 0x2017, 0x0018, 0x0a0b, 0x1018, 0x2018,
602
  0x0019, 0x0b04, 0x1019, 0x2019, 0x001a, 0x0b05, 0x101a, 0x201a,
603
  0x001b, 0x0b06, 0x101b, 0x201b, 0x001c, 0x0b07, 0x101c, 0x201c,
604
  0x001d, 0x0b08, 0x101d, 0x201d, 0x001e, 0x0b09, 0x101e, 0x201e,
605
  0x001f, 0x0b0a, 0x101f, 0x201f, 0x0020, 0x0b0b, 0x1020, 0x2020,
606
  0x0021, 0x0c04, 0x1021, 0x2021, 0x0022, 0x0c05, 0x1022, 0x2022,
607
  0x0023, 0x0c06, 0x1023, 0x2023, 0x0024, 0x0c07, 0x1024, 0x2024,
608
  0x0025, 0x0c08, 0x1025, 0x2025, 0x0026, 0x0c09, 0x1026, 0x2026,
609
  0x0027, 0x0c0a, 0x1027, 0x2027, 0x0028, 0x0c0b, 0x1028, 0x2028,
610
  0x0029, 0x0d04, 0x1029, 0x2029, 0x002a, 0x0d05, 0x102a, 0x202a,
611
  0x002b, 0x0d06, 0x102b, 0x202b, 0x002c, 0x0d07, 0x102c, 0x202c,
612
  0x002d, 0x0d08, 0x102d, 0x202d, 0x002e, 0x0d09, 0x102e, 0x202e,
613
  0x002f, 0x0d0a, 0x102f, 0x202f, 0x0030, 0x0d0b, 0x1030, 0x2030,
614
  0x0031, 0x0e04, 0x1031, 0x2031, 0x0032, 0x0e05, 0x1032, 0x2032,
615
  0x0033, 0x0e06, 0x1033, 0x2033, 0x0034, 0x0e07, 0x1034, 0x2034,
616
  0x0035, 0x0e08, 0x1035, 0x2035, 0x0036, 0x0e09, 0x1036, 0x2036,
617
  0x0037, 0x0e0a, 0x1037, 0x2037, 0x0038, 0x0e0b, 0x1038, 0x2038,
618
  0x0039, 0x0f04, 0x1039, 0x2039, 0x003a, 0x0f05, 0x103a, 0x203a,
619
  0x003b, 0x0f06, 0x103b, 0x203b, 0x003c, 0x0f07, 0x103c, 0x203c,
620
  0x0801, 0x0f08, 0x103d, 0x203d, 0x1001, 0x0f09, 0x103e, 0x203e,
621
  0x1801, 0x0f0a, 0x103f, 0x203f, 0x2001, 0x0f0b, 0x1040, 0x2040
622
};
623
624
}  // end namespace internal
625
626
627
// The size of a compression block. Note that many parts of the compression
628
// code assumes that kBlockSize <= 65536; in particular, the hash table
629
// can only store 16-bit offsets, and EmitCopy() also assumes the offset
630
// is 65535 bytes or less. Note also that if you change this, it will
631
// affect the framing format (see framing_format.txt).
632
//
633
// Note that there might be older data around that is compressed with larger
634
// block sizes, so the decompression code should not rely on the
635
// non-existence of long backreferences.
636
static const int kBlockLog = 16;
637
static const size_t kBlockSize = 1 << kBlockLog;
638
639
static const int kMaxHashTableBits = 14;
640
static const size_t kMaxHashTableSize = 1 << kMaxHashTableBits;
641
642
643
}  // end namespace duckdb_snappy
644
645
#endif  // #if SNAPPY_NEW_VERSION # else
646
647
#endif  // THIRD_PARTY_SNAPPY_SNAPPY_INTERNAL_H_