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.
- checksums.yaml +4 -4
- data/ext/api.c +9 -0
- data/lib/snappy/version.rb +1 -1
- data/lib/snappy_ext.jar +0 -0
- data/snappy.gemspec +2 -1
- data/test/snappy_test.rb +17 -0
- data/vendor/snappy/BUILD.bazel +30 -18
- data/vendor/snappy/CMakeLists.txt +67 -12
- data/vendor/snappy/COPYING.notestdata +28 -0
- data/vendor/snappy/MODULE.bazel +7 -4
- data/vendor/snappy/NEWS +12 -0
- data/vendor/snappy/README.md +8 -4
- data/vendor/snappy/cmake/config.h.in +6 -0
- data/vendor/snappy/format_description.txt +2 -1
- data/vendor/snappy/snappy-c.cc +22 -0
- data/vendor/snappy/snappy-c.h +19 -5
- data/vendor/snappy/snappy-internal.h +26 -1
- data/vendor/snappy/snappy-stubs-internal.h +2 -0
- data/vendor/snappy/snappy-test.cc +2 -1
- data/vendor/snappy/snappy.cc +192 -34
- data/vendor/snappy/snappy.h +78 -7
- data/vendor/snappy/snappy_benchmark.cc +16 -10
- data/vendor/snappy/snappy_unittest.cc +224 -0
- metadata +3 -3
- data/vendor/snappy/WORKSPACE.bzlmod +0 -0
data/vendor/snappy/snappy.cc
CHANGED
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
#include <cstdint>
|
|
75
75
|
#include <cstdio>
|
|
76
76
|
#include <cstring>
|
|
77
|
-
#include <
|
|
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::
|
|
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
|
-
|
|
759
|
-
|
|
760
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
1807
|
-
|
|
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
|
|
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
|
|
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
|
|
1943
|
+
char* dest = writer->GetAppendBuffer(max_output, wmem->GetScratchOutput());
|
|
1859
1944
|
char* end = nullptr;
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
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{});
|
data/vendor/snappy/snappy.h
CHANGED
|
@@ -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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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(
|
|
196
|
-
|
|
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*
|
|
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(
|
|
308
|
-
|
|
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(
|
|
344
|
-
|
|
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(
|
|
381
|
-
|
|
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
|
}
|