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
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
|
|
29
29
|
#include <algorithm>
|
|
30
30
|
#include <cinttypes>
|
|
31
|
+
#include <cstdint>
|
|
31
32
|
#include <cmath>
|
|
32
33
|
#include <cstdlib>
|
|
33
34
|
#include <random>
|
|
@@ -543,6 +544,174 @@ TEST(Snappy, RandomData) {
|
|
|
543
544
|
}
|
|
544
545
|
}
|
|
545
546
|
|
|
547
|
+
TEST(Snappy, CompressionContext) {
|
|
548
|
+
std::minstd_rand0 rng(snappy::GetFlag(FLAGS_test_random_seed));
|
|
549
|
+
std::uniform_int_distribution<int> uniform_byte(0, 255);
|
|
550
|
+
|
|
551
|
+
// A single context, reused across every compression below.
|
|
552
|
+
CompressionContext ctx;
|
|
553
|
+
|
|
554
|
+
const size_t sizes[] = {0,
|
|
555
|
+
1,
|
|
556
|
+
100,
|
|
557
|
+
kBlockSize - 1,
|
|
558
|
+
kBlockSize,
|
|
559
|
+
kBlockSize + 1,
|
|
560
|
+
2 * kBlockSize,
|
|
561
|
+
(1 << 20) + 17};
|
|
562
|
+
for (int level = CompressionOptions::MinCompressionLevel();
|
|
563
|
+
level <= CompressionOptions::MaxCompressionLevel(); ++level) {
|
|
564
|
+
CompressionOptions options(level);
|
|
565
|
+
for (size_t len : sizes) {
|
|
566
|
+
for (bool compressible : {true, false}) {
|
|
567
|
+
std::string input;
|
|
568
|
+
input.reserve(len);
|
|
569
|
+
while (input.size() < len) {
|
|
570
|
+
input.push_back(compressible
|
|
571
|
+
? static_cast<char>('a' + input.size() % 4)
|
|
572
|
+
: static_cast<char>(uniform_byte(rng)));
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
std::string plain(MaxCompressedLength(len), '\0');
|
|
576
|
+
size_t plain_len = 0;
|
|
577
|
+
RawCompress(input.data(), input.size(), &plain[0], &plain_len, options);
|
|
578
|
+
plain.resize(plain_len);
|
|
579
|
+
|
|
580
|
+
std::string with_context(MaxCompressedLength(len), '\0');
|
|
581
|
+
size_t with_context_len = 0;
|
|
582
|
+
RawCompress(input.data(), input.size(), &with_context[0],
|
|
583
|
+
&with_context_len, options, &ctx);
|
|
584
|
+
with_context.resize(with_context_len);
|
|
585
|
+
|
|
586
|
+
// Compressing with a reused context must produce output identical to
|
|
587
|
+
// the context-free API.
|
|
588
|
+
EXPECT_EQ(plain, with_context) << "level=" << level << " len=" << len
|
|
589
|
+
<< " compressible=" << compressible;
|
|
590
|
+
|
|
591
|
+
std::string uncompressed;
|
|
592
|
+
EXPECT_TRUE(Uncompress(with_context, &uncompressed));
|
|
593
|
+
EXPECT_EQ(input, uncompressed);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
TEST(Snappy, CompressionContextStaticWorkspace) {
|
|
600
|
+
// The library performs no allocation for a context constructed over a
|
|
601
|
+
// caller-provided workspace.
|
|
602
|
+
std::vector<char> workspace(CompressionContext::WorkspaceSize());
|
|
603
|
+
CompressionContext static_ctx(workspace.data(), workspace.size());
|
|
604
|
+
CompressionContext heap_ctx;
|
|
605
|
+
|
|
606
|
+
const size_t sizes[] = {0, 1, kBlockSize - 1, kBlockSize + 1,
|
|
607
|
+
2 * kBlockSize + 17};
|
|
608
|
+
for (size_t len : sizes) {
|
|
609
|
+
std::string input;
|
|
610
|
+
input.reserve(len);
|
|
611
|
+
while (input.size() < len) {
|
|
612
|
+
input.push_back(static_cast<char>('a' + input.size() % 7));
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
std::string with_static(MaxCompressedLength(len), '\0');
|
|
616
|
+
size_t with_static_len = 0;
|
|
617
|
+
RawCompress(input.data(), input.size(), &with_static[0], &with_static_len,
|
|
618
|
+
CompressionOptions{}, &static_ctx);
|
|
619
|
+
with_static.resize(with_static_len);
|
|
620
|
+
|
|
621
|
+
std::string with_heap(MaxCompressedLength(len), '\0');
|
|
622
|
+
size_t with_heap_len = 0;
|
|
623
|
+
RawCompress(input.data(), input.size(), &with_heap[0], &with_heap_len,
|
|
624
|
+
CompressionOptions{}, &heap_ctx);
|
|
625
|
+
with_heap.resize(with_heap_len);
|
|
626
|
+
|
|
627
|
+
EXPECT_EQ(with_static, with_heap) << "len=" << len;
|
|
628
|
+
|
|
629
|
+
std::string uncompressed;
|
|
630
|
+
EXPECT_TRUE(Uncompress(with_static, &uncompressed));
|
|
631
|
+
EXPECT_EQ(input, uncompressed);
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// Both context flavors keep working after being moved.
|
|
635
|
+
CompressionContext moved_static(std::move(static_ctx));
|
|
636
|
+
CompressionContext moved_heap = std::move(heap_ctx);
|
|
637
|
+
const std::string input = "the quick brown fox jumps over the lazy dog";
|
|
638
|
+
std::string a(MaxCompressedLength(input.size()), '\0');
|
|
639
|
+
std::string b(MaxCompressedLength(input.size()), '\0');
|
|
640
|
+
size_t a_len = 0;
|
|
641
|
+
size_t b_len = 0;
|
|
642
|
+
RawCompress(input.data(), input.size(), &a[0], &a_len, CompressionOptions{},
|
|
643
|
+
&moved_static);
|
|
644
|
+
RawCompress(input.data(), input.size(), &b[0], &b_len, CompressionOptions{},
|
|
645
|
+
&moved_heap);
|
|
646
|
+
a.resize(a_len);
|
|
647
|
+
b.resize(b_len);
|
|
648
|
+
EXPECT_EQ(a, b);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// An input of 2^32 bytes or more cannot be expressed by the stream format and
|
|
652
|
+
// must be refused rather than compressed under a truncated length.
|
|
653
|
+
#if SIZE_MAX > 0xFFFFFFFFu
|
|
654
|
+
|
|
655
|
+
// Reports an arbitrary number of bytes available without materializing them.
|
|
656
|
+
class OversizedSource : public Source {
|
|
657
|
+
public:
|
|
658
|
+
explicit OversizedSource(uint64_t total)
|
|
659
|
+
: left_(total), buf_(1 << 16, 'a') {}
|
|
660
|
+
size_t Available() const override { return static_cast<size_t>(left_); }
|
|
661
|
+
const char* Peek(size_t* len) override {
|
|
662
|
+
*len = static_cast<size_t>(std::min<uint64_t>(left_, buf_.size()));
|
|
663
|
+
return buf_.data();
|
|
664
|
+
}
|
|
665
|
+
void Skip(size_t n) override { left_ -= n; }
|
|
666
|
+
|
|
667
|
+
private:
|
|
668
|
+
uint64_t left_;
|
|
669
|
+
std::string buf_;
|
|
670
|
+
};
|
|
671
|
+
|
|
672
|
+
// Counts every appended byte and keeps the first few, which is where the
|
|
673
|
+
// uncompressed-length varint lives.
|
|
674
|
+
class CountingSink : public Sink {
|
|
675
|
+
public:
|
|
676
|
+
void Append(const char* data, size_t n) override {
|
|
677
|
+
for (size_t i = 0; i < n && head_.size() < 8; ++i) head_.push_back(data[i]);
|
|
678
|
+
total_ += n;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
std::string head_;
|
|
682
|
+
uint64_t total_ = 0;
|
|
683
|
+
};
|
|
684
|
+
|
|
685
|
+
// Decodes the uncompressed-length varint a compressed stream starts with.
|
|
686
|
+
uint32_t DeclaredLength(const std::string& stream) {
|
|
687
|
+
uint32_t result = 0;
|
|
688
|
+
int shift = 0;
|
|
689
|
+
for (size_t i = 0; i < stream.size(); ++i) {
|
|
690
|
+
const unsigned char c = static_cast<unsigned char>(stream[i]);
|
|
691
|
+
result |= static_cast<uint32_t>(c & 0x7f) << shift;
|
|
692
|
+
if (c < 128) break;
|
|
693
|
+
shift += 7;
|
|
694
|
+
}
|
|
695
|
+
return result;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
TEST(Snappy, RefusesInputLongerThanTheFormatCanExpress) {
|
|
699
|
+
OversizedSource too_big(uint64_t{1} << 32);
|
|
700
|
+
CountingSink refused;
|
|
701
|
+
EXPECT_EQ(0u, Compress(&too_big, &refused));
|
|
702
|
+
EXPECT_EQ(0u, refused.total_);
|
|
703
|
+
EXPECT_TRUE(refused.head_.empty());
|
|
704
|
+
|
|
705
|
+
// One byte below that is the largest input the format can express, and it
|
|
706
|
+
// still compresses to a stream whose header names its real length.
|
|
707
|
+
OversizedSource largest((uint64_t{1} << 32) - 1);
|
|
708
|
+
CountingSink accepted;
|
|
709
|
+
EXPECT_GT(Compress(&largest, &accepted), 0u);
|
|
710
|
+
EXPECT_EQ(0xFFFFFFFFu, DeclaredLength(accepted.head_));
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
#endif // SIZE_MAX > 0xFFFFFFFFu
|
|
714
|
+
|
|
546
715
|
TEST(Snappy, FourByteOffset) {
|
|
547
716
|
// The new compressor cannot generate four-byte offsets since
|
|
548
717
|
// it chops up the input into 32KB pieces. So we hand-emit the
|
|
@@ -790,6 +959,61 @@ TEST(Snappy, ZeroOffsetCopyValidation) {
|
|
|
790
959
|
EXPECT_FALSE(snappy::IsValidCompressedBuffer(compressed, 4));
|
|
791
960
|
}
|
|
792
961
|
|
|
962
|
+
// A 4-byte extended literal length of 0xffffffff decodes as length =
|
|
963
|
+
// 0xffffffff + 1. In correct (64-bit or overflow-checked) arithmetic this is
|
|
964
|
+
// 4294967296, which exceeds the source buffer and must be rejected. In
|
|
965
|
+
// vulnerable 32-bit unsigned arithmetic the +1 wraps to 0, causing the
|
|
966
|
+
// decoder to skip the literal entirely and continue, silently producing
|
|
967
|
+
// wrong output that happens to match the preamble length (65536).
|
|
968
|
+
//
|
|
969
|
+
// Payload structure:
|
|
970
|
+
// Bytes 0-2: Varint 0x80 0x80 0x04 → expected length 65536
|
|
971
|
+
// Bytes 3-4: Literal tag 0x00 + data 0x44 → 1-byte literal
|
|
972
|
+
// Bytes 5-784: 260× copy2 (0xfe 0x01 0x00) → 64 bytes from offset 1
|
|
973
|
+
// Bytes 785-786: Literal tag 0x00 + data 0x46 → 1-byte literal
|
|
974
|
+
// Bytes 787: 0xfc → literal tag with 4-byte length prefix (m=63)
|
|
975
|
+
// Bytes 788-791: 0xff 0xff 0xff 0xff → length-1 = 0xffffffff → +1 wraps
|
|
976
|
+
// Bytes 792+: 763× copy2 + 1× copy2(len=62) to fill remaining output
|
|
977
|
+
TEST(Snappy, LiteralLengthU32Overflow) {
|
|
978
|
+
std::string compressed;
|
|
979
|
+
// Varint: expected output length 65536
|
|
980
|
+
compressed.push_back('\x80');
|
|
981
|
+
compressed.push_back('\x80');
|
|
982
|
+
compressed.push_back('\x04');
|
|
983
|
+
// 1-byte literal (0x44)
|
|
984
|
+
AppendLiteral(&compressed, "D");
|
|
985
|
+
// 260 copy2 elements: each copies 64 bytes from offset 1
|
|
986
|
+
// Output after this section: 1 + 260*64 = 16641
|
|
987
|
+
for (int i = 0; i < 260; i++) {
|
|
988
|
+
AppendCopy(&compressed, 1, 64);
|
|
989
|
+
}
|
|
990
|
+
// 1-byte literal (0x46)
|
|
991
|
+
AppendLiteral(&compressed, "F");
|
|
992
|
+
// Output so far (if wrapping): 16642
|
|
993
|
+
|
|
994
|
+
// Poison literal: tag 0xfc (m=63 → 4-byte extended length), length bytes
|
|
995
|
+
// 0xffffffff. Correct length = 0xffffffff + 1 = 4294967296.
|
|
996
|
+
// Wrapping length = 0.
|
|
997
|
+
compressed.push_back('\xfc'); // literal tag, 4 extra length bytes
|
|
998
|
+
compressed.push_back('\xff');
|
|
999
|
+
compressed.push_back('\xff');
|
|
1000
|
+
compressed.push_back('\xff');
|
|
1001
|
+
compressed.push_back('\xff');
|
|
1002
|
+
|
|
1003
|
+
// Remaining copies to fill output to 65536 if the literal is skipped:
|
|
1004
|
+
// 65536 - 16642 = 48894 = 763*64 + 62
|
|
1005
|
+
for (int i = 0; i < 763; i++) {
|
|
1006
|
+
AppendCopy(&compressed, 1, 64);
|
|
1007
|
+
}
|
|
1008
|
+
AppendCopy(&compressed, 1, 62);
|
|
1009
|
+
|
|
1010
|
+
std::string uncompressed;
|
|
1011
|
+
EXPECT_FALSE(snappy::Uncompress(compressed.data(), compressed.size(),
|
|
1012
|
+
&uncompressed));
|
|
1013
|
+
EXPECT_FALSE(snappy::IsValidCompressedBuffer(compressed.data(),
|
|
1014
|
+
compressed.size()));
|
|
1015
|
+
}
|
|
1016
|
+
|
|
793
1017
|
int TestFindMatchLength(const char* s1, const char *s2, unsigned length) {
|
|
794
1018
|
uint64_t data;
|
|
795
1019
|
std::pair<size_t, bool> p =
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: snappy
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.5.
|
|
4
|
+
version: 0.5.2
|
|
5
5
|
platform: java
|
|
6
6
|
authors:
|
|
7
7
|
- miyucy
|
|
@@ -67,11 +67,11 @@ files:
|
|
|
67
67
|
- vendor/snappy/CMakeLists.txt
|
|
68
68
|
- vendor/snappy/CONTRIBUTING.md
|
|
69
69
|
- vendor/snappy/COPYING
|
|
70
|
+
- vendor/snappy/COPYING.notestdata
|
|
70
71
|
- vendor/snappy/MODULE.bazel
|
|
71
72
|
- vendor/snappy/NEWS
|
|
72
73
|
- vendor/snappy/README.md
|
|
73
74
|
- vendor/snappy/WORKSPACE
|
|
74
|
-
- vendor/snappy/WORKSPACE.bzlmod
|
|
75
75
|
- vendor/snappy/cmake/SnappyConfig.cmake.in
|
|
76
76
|
- vendor/snappy/cmake/config.h.in
|
|
77
77
|
- vendor/snappy/docs/README.md
|
|
@@ -116,7 +116,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
116
116
|
- !ruby/object:Gem::Version
|
|
117
117
|
version: '0'
|
|
118
118
|
requirements: []
|
|
119
|
-
rubygems_version:
|
|
119
|
+
rubygems_version: 4.0.21
|
|
120
120
|
specification_version: 4
|
|
121
121
|
summary: libsnappy binding for Ruby
|
|
122
122
|
test_files:
|
|
File without changes
|