snappy 0.5.1-java → 0.5.2-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -74,7 +74,7 @@
74
74
  #include <cstdint>
75
75
  #include <cstdio>
76
76
  #include <cstring>
77
- #include <functional>
77
+ #include <limits>
78
78
  #include <memory>
79
79
  #include <string>
80
80
  #include <utility>
@@ -195,6 +195,12 @@ inline uint16_t* TableEntry8ByteMatch(uint16_t* table, uint64_t bytes,
195
195
  } // namespace
196
196
 
197
197
  size_t MaxCompressedLength(size_t source_bytes) {
198
+ // Avoid integer overflow that could cause undersized buffer allocations.
199
+ // Return std::numeric_limits<size_t>::max() to force a controlled allocation
200
+ // failure.
201
+ if (source_bytes > (std::numeric_limits<size_t>::max() - 32) / 7 * 6) {
202
+ return std::numeric_limits<size_t>::max();
203
+ }
198
204
  // Compressed data can be defined as:
199
205
  // compressed := item* literal*
200
206
  // item := literal* copy
@@ -281,6 +287,8 @@ inline char* IncrementalCopySlow(const char* src, char* op,
281
287
  // 4, 5, 0, 1, 2, 3, 4, 5, 0, 1}. These byte index sequences are generated by
282
288
  // calling MakePatternMaskBytes(0, 6, index_sequence<16>()) and
283
289
  // MakePatternMaskBytes(16, 6, index_sequence<16>()) respectively.
290
+
291
+
284
292
  template <size_t... indexes>
285
293
  inline constexpr std::array<char, sizeof...(indexes)> MakePatternMaskBytes(
286
294
  int index_offset, int pattern_size, index_sequence<indexes...>) {
@@ -298,7 +306,6 @@ MakePatternMaskBytesTable(int index_offset,
298
306
  MakePatternMaskBytes(index_offset, pattern_sizes_minus_one + 1,
299
307
  make_index_sequence</*indexes=*/sizeof(V128)>())...};
300
308
  }
301
-
302
309
  // This is an array of shuffle control masks that can be used as the source
303
310
  // operand for PSHUFB to permute the contents of the destination XMM register
304
311
  // into a repeating byte pattern.
@@ -494,7 +501,6 @@ inline char* IncrementalCopy(const char* src, char* op, char* const op_limit,
494
501
  LoadPatternAndReshuffleMask(src, pattern_size);
495
502
  V128 pattern = pattern_and_reshuffle_mask.first;
496
503
  V128 reshuffle_mask = pattern_and_reshuffle_mask.second;
497
-
498
504
  // There is at least one, and at most four 16-byte blocks. Writing four
499
505
  // conditionals instead of a loop allows FDO to layout the code with
500
506
  // respect to the actual probabilities of each length.
@@ -521,7 +527,6 @@ inline char* IncrementalCopy(const char* src, char* op, char* const op_limit,
521
527
  LoadPatternAndReshuffleMask(src, pattern_size);
522
528
  V128 pattern = pattern_and_reshuffle_mask.first;
523
529
  V128 reshuffle_mask = pattern_and_reshuffle_mask.second;
524
-
525
530
  // This code path is relatively cold however so we save code size
526
531
  // by avoiding unrolling and vectorizing.
527
532
  //
@@ -752,19 +757,36 @@ uint32_t CalculateTableSize(uint32_t input_size) {
752
757
  } // namespace
753
758
 
754
759
  namespace internal {
755
- WorkingMemory::WorkingMemory(size_t input_size) {
760
+ size_t WorkingMemory::RequiredSize(size_t input_size) {
756
761
  const size_t max_fragment_size = std::min(input_size, kBlockSize);
757
762
  const size_t table_size = CalculateTableSize(max_fragment_size);
758
- size_ = table_size * sizeof(*table_) + max_fragment_size +
759
- MaxCompressedLength(max_fragment_size);
760
- mem_ = std::allocator<char>().allocate(size_);
763
+ return table_size * sizeof(uint16_t) + max_fragment_size +
764
+ MaxCompressedLength(max_fragment_size);
765
+ }
766
+
767
+ WorkingMemory::WorkingMemory(size_t input_size)
768
+ : WorkingMemory(input_size,
769
+ std::allocator<char>().allocate(RequiredSize(input_size))) {
770
+ owns_mem_ = true;
771
+ }
772
+
773
+ WorkingMemory::WorkingMemory(size_t input_size, char* buffer) {
774
+ assert(buffer != nullptr);
775
+ assert(reinterpret_cast<uintptr_t>(buffer) % alignof(uint16_t) == 0);
776
+ const size_t max_fragment_size = std::min(input_size, kBlockSize);
777
+ const size_t table_size = CalculateTableSize(max_fragment_size);
778
+ mem_ = buffer;
779
+ size_ = RequiredSize(input_size);
780
+ owns_mem_ = false;
761
781
  table_ = reinterpret_cast<uint16_t*>(mem_);
762
782
  input_ = mem_ + table_size * sizeof(*table_);
763
783
  output_ = input_ + max_fragment_size;
764
784
  }
765
785
 
766
786
  WorkingMemory::~WorkingMemory() {
767
- std::allocator<char>().deallocate(mem_, size_);
787
+ if (owns_mem_) {
788
+ std::allocator<char>().deallocate(mem_, size_);
789
+ }
768
790
  }
769
791
 
770
792
  uint16_t* WorkingMemory::GetHashTable(size_t fragment_size,
@@ -1227,7 +1249,7 @@ inline bool Copy64BytesWithPatternExtension(ptrdiff_t dst, size_t offset) {
1227
1249
  void MemCopy64(char* dst, const void* src, size_t size) {
1228
1250
  // Always copy this many bytes. If that's below size then copy the full 64.
1229
1251
  constexpr int kShortMemCopy = 32;
1230
-
1252
+ (void)kShortMemCopy;
1231
1253
  assert(size <= 64);
1232
1254
  assert(std::less_equal<const void*>()(static_cast<const char*>(src) + size,
1233
1255
  dst) ||
@@ -1246,6 +1268,27 @@ void MemCopy64(char* dst, const void* src, size_t size) {
1246
1268
  data = _mm256_lddqu_si256(static_cast<const __m256i *>(src) + 1);
1247
1269
  _mm256_storeu_si256(reinterpret_cast<__m256i *>(dst) + 1, data);
1248
1270
  }
1271
+ // RVV acceleration available on RISC-V when compiled with -march=rv64gcv
1272
+ #elif defined(__riscv) && SNAPPY_HAVE_RVV
1273
+ // Cast pointers to the type we will operate on.
1274
+ unsigned char* dst_ptr = reinterpret_cast<unsigned char*>(dst);
1275
+ const unsigned char* src_ptr = reinterpret_cast<const unsigned char*>(src);
1276
+ size_t remaining_bytes = size;
1277
+ // Loop as long as there are bytes remaining to be copied.
1278
+ while (remaining_bytes > 0) {
1279
+ // Set vector configuration: e8 (8-bit elements), m2 (LMUL=2).
1280
+ // Use e8m2 configuration to maximize throughput.
1281
+ size_t vl = VSETVL_E8M2(remaining_bytes);
1282
+ // Load data from the current source pointer.
1283
+ vuint8m2_t vec = VLE8_V_U8M2(src_ptr, vl);
1284
+ // Store data to the current destination pointer.
1285
+ VSE8_V_U8M2(dst_ptr, vec, vl);
1286
+ // Update pointers and the remaining count.
1287
+ src_ptr += vl;
1288
+ dst_ptr += vl;
1289
+ remaining_bytes -= vl;
1290
+ }
1291
+
1249
1292
  #else
1250
1293
  std::memmove(dst, src, kShortMemCopy);
1251
1294
  // Profiling shows that nearly all copies are short.
@@ -1345,19 +1388,51 @@ inline size_t AdvanceToNextTagX86Optimized(const uint8_t** ip_p, size_t* tag) {
1345
1388
  return tag_type;
1346
1389
  }
1347
1390
 
1391
+ SNAPPY_ATTRIBUTE_ALWAYS_INLINE
1392
+ inline size_t AdvanceToNextTagRVOptimized(const uint8_t** ip_p, size_t* tag) {
1393
+ const uint8_t*& ip = *ip_p;
1394
+ // This section is crucial for the throughput of the decompression loop.
1395
+ // The latency of an iteration is fundamentally constrained by the data chain on ip:
1396
+ // ip -> c = *tag -> literal_len = c >> 2, tag_type = c & 3
1397
+ // -> literal_advance = literal_len + 2, copy_advance = tag_type + 1
1398
+ // -> next_ip = ip + literal_advance OR ip + copy_advance (literal vs copy)
1399
+ // -> *tag = byte at (next_ip - 1); ip = next_ip
1400
+ //
1401
+ // Base RISC-V has no x86-style cmov and no AArch64 csinc on the same shape; this
1402
+ // computes both candidate advances and both load offsets, then selects with
1403
+ // (is_literal ? ... : ...). With the Zicond extension (czero.eqz / czero.nez), those
1404
+ // selections typically lower to branchless conditional-zero ops instead of a
1405
+ // hard-to-predict literal/copy branch, which is why this form tends to win there.
1406
+ const size_t literal_len = *tag >> 2;
1407
+ const size_t tag_type = *tag & 3;
1408
+ const bool is_literal = (tag_type == 0);
1409
+ const size_t copy_advance = tag_type + 1;
1410
+ const size_t literal_advance = literal_len + 2;
1411
+ const uint8_t* next_ip = is_literal ? (ip + literal_advance) : (ip + copy_advance);
1412
+ *tag = is_literal ? ip[literal_advance - 1] : ip[copy_advance - 1];
1413
+ ip = next_ip;
1414
+ return tag_type;
1415
+ }
1416
+
1348
1417
  // Extract the offset for copy-1 and copy-2 returns 0 for literals or copy-4.
1349
1418
  inline uint32_t ExtractOffset(uint32_t val, size_t tag_type) {
1350
- // For x86 non-static storage works better. For ARM static storage is better.
1419
+ // For Arm non-static storage works better. For x86 static storage is better.
1351
1420
  // TODO: Once the array is recognized as a register, improve the
1352
1421
  // readability for x86.
1353
1422
  #if defined(__x86_64__)
1354
- constexpr uint64_t kExtractMasksCombined = 0x0000FFFF00FF0000ull;
1423
+ static constexpr uint64_t kExtractMasksCombined = 0x0000FFFF00FF0000ull;
1355
1424
  uint16_t result;
1356
1425
  memcpy(&result,
1357
1426
  reinterpret_cast<const char*>(&kExtractMasksCombined) + 2 * tag_type,
1358
1427
  sizeof(result));
1359
1428
  return val & result;
1360
- #elif defined(__aarch64__)
1429
+ // For AArch64 and RISC-V, use a bit-twiddling trick to extract the mask from a
1430
+ // single combined constant instead of a lookup table. The constant packs multiple
1431
+ // 16-bit masks based on tag_type (see implementation below). The code calculates
1432
+ // the shift amount from tag_type, right-shifts the constant to move the desired
1433
+ // mask to the LSB position, then extracts it with & 0xFFFF. This branchless
1434
+ // approach is often more performant on modern CPUs.
1435
+ #elif defined(__aarch64__) || (defined(__riscv) && (__riscv_xlen == 64))
1361
1436
  constexpr uint64_t kExtractMasksCombined = 0x0000FFFF00FF0000ull;
1362
1437
  return val & static_cast<uint32_t>(
1363
1438
  (kExtractMasksCombined >> (tag_type * 16)) & 0xFFFF);
@@ -1421,6 +1496,11 @@ std::pair<const uint8_t*, ptrdiff_t> DecompressBranchless(
1421
1496
  // We never need more than 16 bits. Doing a Load16 allows the compiler
1422
1497
  // to elide the masking operation in ExtractOffset.
1423
1498
  next = LittleEndian::Load16(old_ip);
1499
+ #elif defined(__riscv)
1500
+ size_t tag_type = AdvanceToNextTagRVOptimized(&ip, &tag);
1501
+ // We never need more than 16 bits. Doing a Load16 allows the compiler
1502
+ // to elide the masking operation in ExtractOffset.
1503
+ next = LittleEndian::Load16(old_ip);
1424
1504
  #else
1425
1505
  size_t tag_type = AdvanceToNextTagX86Optimized(&ip, &tag);
1426
1506
  next = LittleEndian::Load32(old_ip);
@@ -1624,9 +1704,13 @@ class SnappyDecompressor {
1624
1704
  if (SNAPPY_PREDICT_FALSE(literal_length >= 61)) {
1625
1705
  // Long literal.
1626
1706
  const size_t literal_length_length = literal_length - 60;
1707
+ // NOTE: literal_length might be equal 2^32 (i.e. ExtractLowBytes
1708
+ // returns 0xFFFFFFFF); this is implicitly invalid stream (since
1709
+ // uncompressed length is capped with 0xFFFFFFFF); for performance we
1710
+ // do not check for this case here.
1627
1711
  literal_length =
1628
1712
  ExtractLowBytes(LittleEndian::Load32(ip), literal_length_length) +
1629
- 1;
1713
+ size_t{1};
1630
1714
  ip += literal_length_length;
1631
1715
  }
1632
1716
 
@@ -1779,13 +1863,13 @@ static bool InternalUncompressAllTags(SnappyDecompressor* decompressor,
1779
1863
  Writer* writer, uint32_t compressed_len,
1780
1864
  uint32_t uncompressed_len) {
1781
1865
  int token = 0;
1782
- Report(token, "snappy_uncompress", compressed_len, uncompressed_len);
1783
1866
 
1784
1867
  writer->SetExpectedLength(uncompressed_len);
1785
1868
 
1786
1869
  // Process the entire input
1787
1870
  decompressor->DecompressAllTags(writer);
1788
1871
  writer->Flush();
1872
+ Report(token, "snappy_uncompress", compressed_len, uncompressed_len);
1789
1873
  return (decompressor->eof() && writer->CheckLength());
1790
1874
  }
1791
1875
 
@@ -1798,20 +1882,21 @@ size_t Compress(Source* reader, Sink* writer) {
1798
1882
  return Compress(reader, writer, CompressionOptions{});
1799
1883
  }
1800
1884
 
1801
- size_t Compress(Source* reader, Sink* writer, CompressionOptions options) {
1885
+ static size_t InternalCompress(Source* reader, Sink* writer,
1886
+ CompressionOptions options,
1887
+ internal::WorkingMemory* wmem) {
1802
1888
  assert(options.level == 1 || options.level == 2);
1803
- int token = 0;
1804
1889
  size_t written = 0;
1805
1890
  size_t N = reader->Available();
1806
- assert(N <= 0xFFFFFFFFu);
1807
- const size_t uncompressed_size = N;
1891
+ // The uncompressed length is a 32-bit varint in the stream format.
1892
+ if (static_cast<uint64_t>(N) > std::numeric_limits<uint32_t>::max()) {
1893
+ return 0;
1894
+ }
1808
1895
  char ulength[Varint::kMax32];
1809
1896
  char* p = Varint::Encode32(ulength, N);
1810
1897
  writer->Append(ulength, p - ulength);
1811
1898
  written += (p - ulength);
1812
1899
 
1813
- internal::WorkingMemory wmem(N);
1814
-
1815
1900
  while (N > 0) {
1816
1901
  // Get next block to compress (without copying if possible)
1817
1902
  size_t fragment_size;
@@ -1826,7 +1911,7 @@ size_t Compress(Source* reader, Sink* writer, CompressionOptions options) {
1826
1911
  pending_advance = num_to_read;
1827
1912
  fragment_size = num_to_read;
1828
1913
  } else {
1829
- char* scratch = wmem.GetScratchInput();
1914
+ char* scratch = wmem->GetScratchInput();
1830
1915
  std::memcpy(scratch, fragment, bytes_read);
1831
1916
  reader->Skip(bytes_read);
1832
1917
 
@@ -1845,7 +1930,7 @@ size_t Compress(Source* reader, Sink* writer, CompressionOptions options) {
1845
1930
 
1846
1931
  // Get encoding table for compression
1847
1932
  int table_size;
1848
- uint16_t* table = wmem.GetHashTable(num_to_read, &table_size);
1933
+ uint16_t* table = wmem->GetHashTable(num_to_read, &table_size);
1849
1934
 
1850
1935
  // Compress input_fragment and append to dest
1851
1936
  int max_output = MaxCompressedLength(num_to_read);
@@ -1855,27 +1940,89 @@ size_t Compress(Source* reader, Sink* writer, CompressionOptions options) {
1855
1940
  // scratch_output[] region is big enough for this iteration.
1856
1941
  // Need a scratch buffer for the output, in case the byte sink doesn't
1857
1942
  // have room for us directly.
1858
- char* dest = writer->GetAppendBuffer(max_output, wmem.GetScratchOutput());
1943
+ char* dest = writer->GetAppendBuffer(max_output, wmem->GetScratchOutput());
1859
1944
  char* end = nullptr;
1860
- if (options.level == 1) {
1861
- end = internal::CompressFragment(fragment, fragment_size, dest, table,
1862
- table_size);
1863
- } else if (options.level == 2) {
1864
- end = internal::CompressFragmentDoubleHash(
1865
- fragment, fragment_size, dest, table, table_size >> 1,
1866
- table + (table_size >> 1), table_size >> 1);
1867
- }
1945
+ if (options.level == 1) {
1946
+ end = internal::CompressFragment(fragment, fragment_size, dest, table,
1947
+ table_size);
1948
+ } else if (options.level == 2) {
1949
+ end = internal::CompressFragmentDoubleHash(
1950
+ fragment, fragment_size, dest, table, table_size >> 1,
1951
+ table + (table_size >> 1), table_size >> 1);
1952
+ }
1953
+
1868
1954
  writer->Append(dest, end - dest);
1869
1955
  written += (end - dest);
1870
1956
 
1871
1957
  N -= num_to_read;
1872
1958
  reader->Skip(pending_advance);
1873
1959
  }
1874
-
1875
- Report(token, "snappy_compress", written, uncompressed_size);
1876
1960
  return written;
1877
1961
  }
1878
1962
 
1963
+ size_t Compress(Source* reader, Sink* writer, CompressionOptions options) {
1964
+ internal::WorkingMemory wmem(reader->Available());
1965
+ return InternalCompress(reader, writer, options, &wmem);
1966
+ }
1967
+
1968
+ size_t Compress(Source* reader, Sink* writer, CompressionOptions options,
1969
+ CompressionContext* ctx) {
1970
+ assert(ctx != nullptr);
1971
+ assert(ctx->working_memory_ != nullptr);
1972
+ return InternalCompress(reader, writer, options, ctx->working_memory_);
1973
+ }
1974
+
1975
+ CompressionContext::CompressionContext()
1976
+ : working_memory_(new internal::WorkingMemory(kBlockSize)),
1977
+ owns_working_memory_(true) {}
1978
+
1979
+ size_t CompressionContext::WorkspaceSize() {
1980
+ return sizeof(internal::WorkingMemory) +
1981
+ internal::WorkingMemory::RequiredSize(kBlockSize);
1982
+ }
1983
+
1984
+ CompressionContext::CompressionContext(void* workspace, size_t workspace_size)
1985
+ : owns_working_memory_(false) {
1986
+ assert(workspace != nullptr);
1987
+ assert(workspace_size >= WorkspaceSize());
1988
+ assert(reinterpret_cast<uintptr_t>(workspace) %
1989
+ alignof(internal::WorkingMemory) ==
1990
+ 0);
1991
+ (void)workspace_size;
1992
+ char* base = static_cast<char*>(workspace);
1993
+ working_memory_ = new (base) internal::WorkingMemory(
1994
+ kBlockSize, base + sizeof(internal::WorkingMemory));
1995
+ }
1996
+
1997
+ void CompressionContext::Reset() {
1998
+ if (working_memory_ == nullptr) return;
1999
+ if (owns_working_memory_) {
2000
+ delete working_memory_;
2001
+ } else {
2002
+ working_memory_->~WorkingMemory();
2003
+ }
2004
+ working_memory_ = nullptr;
2005
+ }
2006
+
2007
+ CompressionContext::~CompressionContext() { Reset(); }
2008
+
2009
+ CompressionContext::CompressionContext(CompressionContext&& other) noexcept
2010
+ : working_memory_(other.working_memory_),
2011
+ owns_working_memory_(other.owns_working_memory_) {
2012
+ other.working_memory_ = nullptr;
2013
+ }
2014
+
2015
+ CompressionContext& CompressionContext::operator=(
2016
+ CompressionContext&& other) noexcept {
2017
+ if (this != &other) {
2018
+ Reset();
2019
+ working_memory_ = other.working_memory_;
2020
+ owns_working_memory_ = other.owns_working_memory_;
2021
+ other.working_memory_ = nullptr;
2022
+ }
2023
+ return *this;
2024
+ }
2025
+
1879
2026
  // -----------------------------------------------------------------------
1880
2027
  // IOVec interfaces
1881
2028
  // -----------------------------------------------------------------------
@@ -2338,6 +2485,17 @@ void RawCompressFromIOVec(const struct iovec* iov, size_t uncompressed_length,
2338
2485
  *compressed_length = writer.CurrentDestination() - compressed;
2339
2486
  }
2340
2487
 
2488
+ void RawCompress(const char* input, size_t input_length, char* compressed,
2489
+ size_t* compressed_length, CompressionOptions options,
2490
+ CompressionContext* ctx) {
2491
+ ByteArraySource reader(input, input_length);
2492
+ UncheckedByteArraySink writer(compressed);
2493
+ Compress(&reader, &writer, options, ctx);
2494
+
2495
+ // Compute how many bytes were added
2496
+ *compressed_length = (writer.CurrentDestination() - compressed);
2497
+ }
2498
+
2341
2499
  size_t Compress(const char* input, size_t input_length,
2342
2500
  std::string* compressed) {
2343
2501
  return Compress(input, input_length, compressed, CompressionOptions{});
@@ -50,6 +50,10 @@ namespace snappy {
50
50
  class Source;
51
51
  class Sink;
52
52
 
53
+ namespace internal {
54
+ class WorkingMemory;
55
+ } // end namespace internal
56
+
53
57
  struct CompressionOptions {
54
58
  // Compression level.
55
59
  // Level 1 is the fastest
@@ -64,25 +68,78 @@ namespace snappy {
64
68
  // faster decompression speeds than snappy:1 and zstd:-3.
65
69
  int level = DefaultCompressionLevel();
66
70
 
67
- constexpr CompressionOptions() = default;
68
- constexpr CompressionOptions(int compression_level)
69
- : level(compression_level) {}
71
+ constexpr CompressionOptions() = default;
72
+ constexpr explicit CompressionOptions(int compression_level)
73
+ : level(compression_level) {}
74
+
70
75
  static constexpr int MinCompressionLevel() { return 1; }
71
76
  static constexpr int MaxCompressionLevel() { return 2; }
72
77
  static constexpr int DefaultCompressionLevel() { return 1; }
73
78
  };
74
79
 
80
+ // Scratch memory for compression, reusable across compressions. Callers that
81
+ // compress frequently, or that need to avoid large heap allocations can
82
+ // allocate a CompressionContext once and pass it to Compress()/RawCompress()
83
+ // to reuse the working memory across calls.
84
+ //
85
+ // The context is sized for the largest block and works for inputs of any
86
+ // size. A context may be used by any number of sequential compressions, but
87
+ // must not be used from multiple threads concurrently. A moved-from context
88
+ // may only be destroyed or assigned to.
89
+ class CompressionContext {
90
+ public:
91
+ // Allocates the working memory on the heap.
92
+ CompressionContext();
93
+
94
+ // Constructs a context whose working memory is placed in the
95
+ // caller-provided "workspace" instead of being heap-allocated; the
96
+ // library performs no allocation at all.
97
+ //
98
+ // REQUIRES: "workspace" points to at least "workspace_size" bytes with
99
+ // "workspace_size >= WorkspaceSize()", is suitably aligned for any
100
+ // object type (as if returned by malloc), and outlives "*this".
101
+ CompressionContext(void* workspace, size_t workspace_size);
102
+
103
+ ~CompressionContext();
104
+
105
+ CompressionContext(CompressionContext&& other) noexcept;
106
+ CompressionContext& operator=(CompressionContext&& other) noexcept;
107
+
108
+ CompressionContext(const CompressionContext&) = delete;
109
+ CompressionContext& operator=(const CompressionContext&) = delete;
110
+
111
+ // The workspace size required by the non-allocating constructor above.
112
+ static size_t WorkspaceSize();
113
+
114
+ private:
115
+ friend size_t Compress(Source* reader, Sink* writer,
116
+ CompressionOptions options, CompressionContext* ctx);
117
+
118
+ // Destroys the working memory as appropriate for how it was created
119
+ // (delete if heap-allocated, in-place destruction if placement-constructed
120
+ // in a caller-provided workspace).
121
+ void Reset();
122
+
123
+ internal::WorkingMemory* working_memory_;
124
+ bool owns_working_memory_;
125
+ };
126
+
75
127
  // ------------------------------------------------------------------------
76
128
  // Generic compression/decompression routines.
77
129
  // ------------------------------------------------------------------------
78
130
 
79
131
  // Compress the bytes read from "*reader" and append to "*writer". Return the
80
- // number of bytes written.
132
+ // number of bytes written, or zero if "*reader" has 2^32 or more bytes.
81
133
  // First version is to preserve ABI.
82
134
  size_t Compress(Source* reader, Sink* writer);
83
135
  size_t Compress(Source* reader, Sink* writer,
84
136
  CompressionOptions options);
85
137
 
138
+ // Same as the above, but uses the working memory of "*ctx" instead of
139
+ // allocating it internally. See CompressionContext.
140
+ size_t Compress(Source* reader, Sink* writer, CompressionOptions options,
141
+ CompressionContext* ctx);
142
+
86
143
  // Find the uncompressed length of the given stream, as given by the header.
87
144
  // Note that the true length could deviate from this; the stream could e.g.
88
145
  // be truncated.
@@ -97,7 +154,8 @@ namespace snappy {
97
154
  // ------------------------------------------------------------------------
98
155
 
99
156
  // Sets "*compressed" to the compressed version of "input[0..input_length-1]".
100
- // Original contents of *compressed are lost.
157
+ // Original contents of *compressed are lost. Returns zero and writes nothing
158
+ // if "input_length" is 2^32 or more.
101
159
  //
102
160
  // REQUIRES: "input[]" is not an alias of "*compressed".
103
161
  // First version is to preserve ABI.
@@ -109,7 +167,8 @@ namespace snappy {
109
167
  // Same as `Compress` above but taking an `iovec` array as input. Note that
110
168
  // this function preprocesses the inputs to compute the sum of
111
169
  // `iov[0..iov_cnt-1].iov_len` before reading. To avoid this, use
112
- // `RawCompressFromIOVec` below.
170
+ // `RawCompressFromIOVec` below. Returns zero and writes nothing if that sum
171
+ // is 2^32 or more.
113
172
  // First version is to preserve ABI.
114
173
  size_t CompressFromIOVec(const struct iovec* iov, size_t iov_cnt,
115
174
  std::string* compressed);
@@ -150,6 +209,9 @@ namespace snappy {
150
209
  // Takes the data stored in "input[0..input_length]" and stores
151
210
  // it in the array pointed to by "compressed".
152
211
  //
212
+ // "*compressed_length" is set to the length of the compressed output, or to
213
+ // zero, with nothing written, if "input_length" is 2^32 or more.
214
+ //
153
215
  // "*compressed_length" is set to the length of the compressed output.
154
216
  //
155
217
  // Example:
@@ -158,14 +220,23 @@ namespace snappy {
158
220
  // RawCompress(input, input_length, output, &output_length);
159
221
  // ... Process(output, output_length) ...
160
222
  // delete [] output;
223
+ // First version is to preserve ABI.
161
224
  void RawCompress(const char* input, size_t input_length, char* compressed,
162
225
  size_t* compressed_length);
163
226
  void RawCompress(const char* input, size_t input_length, char* compressed,
164
227
  size_t* compressed_length, CompressionOptions options);
228
+ // Same as the above, but uses the working memory of "*ctx" instead of
229
+ // allocating it internally. See CompressionContext.
230
+ void RawCompress(const char* input, size_t input_length, char* compressed,
231
+ size_t* compressed_length, CompressionOptions options,
232
+ CompressionContext* ctx);
165
233
 
166
234
  // Same as `RawCompress` above but taking an `iovec` array as input. Note that
167
235
  // `uncompressed_length` is the total number of bytes to be read from the
168
- // elements of `iov` (_not_ the number of elements in `iov`).
236
+ // elements of `iov` (_not_ the number of elements in `iov`). Sets
237
+ // "*compressed_length" to zero and writes nothing if `uncompressed_length`
238
+ // is 2^32 or more.
239
+ // First version is to preserve ABI.
169
240
  void RawCompressFromIOVec(const struct iovec* iov, size_t uncompressed_length,
170
241
  char* compressed, size_t* compressed_length);
171
242
  void RawCompressFromIOVec(const struct iovec* iov, size_t uncompressed_length,
@@ -31,10 +31,12 @@
31
31
  #include <string>
32
32
  #include <vector>
33
33
 
34
+ #include "snappy-test.h"
35
+
34
36
  #include "benchmark/benchmark.h"
37
+
35
38
  #include "snappy-internal.h"
36
39
  #include "snappy-sinksource.h"
37
- #include "snappy-test.h"
38
40
  #include "snappy.h"
39
41
  #include "snappy_test_data.h"
40
42
 
@@ -192,8 +194,9 @@ void BM_UIOVecSource(benchmark::State& state) {
192
194
  char* dst = new char[snappy::MaxCompressedLength(contents.size())];
193
195
  size_t zsize = 0;
194
196
  for (auto s : state) {
195
- snappy::RawCompressFromIOVec(iov, contents.size(), dst, &zsize,
196
- snappy::CompressionOptions{/*level=*/level});
197
+ snappy::RawCompressFromIOVec(
198
+ iov, contents.size(), dst, &zsize,
199
+ snappy::CompressionOptions{/*level=*/static_cast<int>(level)});
197
200
  benchmark::DoNotOptimize(iov);
198
201
  }
199
202
  state.SetBytesProcessed(static_cast<int64_t>(state.iterations()) *
@@ -226,7 +229,7 @@ void BM_UIOVecSink(benchmark::State& state) {
226
229
  // Uncompress into an iovec containing ten entries.
227
230
  const int kNumEntries = 10;
228
231
  struct iovec iov[kNumEntries];
229
- char* dst = new char[contents.size()];
232
+ char *dst = new char[contents.size()];
230
233
  size_t used_so_far = 0;
231
234
  for (int i = 0; i < kNumEntries; ++i) {
232
235
  iov[i].iov_base = dst + used_so_far;
@@ -304,8 +307,9 @@ void BM_ZFlat(benchmark::State& state) {
304
307
 
305
308
  size_t zsize = 0;
306
309
  for (auto s : state) {
307
- snappy::RawCompress(contents.data(), contents.size(), dst, &zsize,
308
- snappy::CompressionOptions{/*level=*/level});
310
+ snappy::RawCompress(
311
+ contents.data(), contents.size(), dst, &zsize,
312
+ snappy::CompressionOptions{/*level=*/static_cast<int>(level)});
309
313
  benchmark::DoNotOptimize(dst);
310
314
  }
311
315
  state.SetBytesProcessed(static_cast<int64_t>(state.iterations()) *
@@ -340,8 +344,9 @@ void BM_ZFlatAll(benchmark::State& state) {
340
344
  size_t zsize = 0;
341
345
  for (auto s : state) {
342
346
  for (int i = 0; i < num_files; ++i) {
343
- snappy::RawCompress(contents[i].data(), contents[i].size(), dst[i],
344
- &zsize, snappy::CompressionOptions{/*level=*/level});
347
+ snappy::RawCompress(
348
+ contents[i].data(), contents[i].size(), dst[i], &zsize,
349
+ snappy::CompressionOptions{/*level=*/static_cast<int>(level)});
345
350
  benchmark::DoNotOptimize(dst);
346
351
  }
347
352
  }
@@ -377,8 +382,9 @@ void BM_ZFlatIncreasingTableSize(benchmark::State& state) {
377
382
  size_t zsize = 0;
378
383
  for (auto s : state) {
379
384
  for (size_t i = 0; i < contents.size(); ++i) {
380
- snappy::RawCompress(contents[i].data(), contents[i].size(), dst[i],
381
- &zsize, snappy::CompressionOptions{/*level=*/level});
385
+ snappy::RawCompress(
386
+ contents[i].data(), contents[i].size(), dst[i], &zsize,
387
+ snappy::CompressionOptions{/*level=*/static_cast<int>(level)});
382
388
  benchmark::DoNotOptimize(dst);
383
389
  }
384
390
  }