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.
@@ -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.1
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: 3.7.2
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