1 // Copyright 2005 and onwards Google Inc.
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are
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
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.
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.
38 #include "snappy-internal.h"
39 #include "snappy-test.h"
40 #include "snappy-sinksource.h"
42 DEFINE_int32(start_len, -1,
43 "Starting prefix size for testing (-1: just full file contents)");
44 DEFINE_int32(end_len, -1,
45 "Starting prefix size for testing (-1: just full file contents)");
46 DEFINE_int32(bytes, 10485760,
47 "How many bytes to compress/uncompress per file for timing");
49 DEFINE_bool(zlib, false,
50 "Run zlib compression (http://www.zlib.net)");
51 DEFINE_bool(lzo, false,
52 "Run LZO compression (http://www.oberhumer.com/opensource/lzo/)");
53 DEFINE_bool(quicklz, false,
54 "Run quickLZ compression (http://www.quicklz.com/)");
55 DEFINE_bool(liblzf, false,
56 "Run libLZF compression "
57 "(http://www.goof.com/pcg/marc/liblzf.html)");
58 DEFINE_bool(fastlz, false,
59 "Run FastLZ compression (http://www.fastlz.org/");
60 DEFINE_bool(snappy, true, "Run snappy compression");
63 DEFINE_bool(write_compressed, false,
64 "Write compressed versions of each file to <file>.comp");
65 DEFINE_bool(write_uncompressed, false,
66 "Write uncompressed versions of each file to <file>.uncomp");
73 // To test against code that reads beyond its input, this class copies a
74 // string to a newly allocated group of pages, the last of which
75 // is made unreadable via mprotect. Note that we need to allocate the
76 // memory with mmap(), as POSIX allows mprotect() only on memory allocated
77 // with mmap(), and some malloc/posix_memalign implementations expect to
78 // be able to read previously allocated memory while doing heap allocations.
79 class DataEndingAtUnreadablePage {
81 explicit DataEndingAtUnreadablePage(const string& s) {
82 const size_t page_size = getpagesize();
83 const size_t size = s.size();
84 // Round up space for string to a multiple of page_size.
85 size_t space_for_string = (size + page_size - 1) & ~(page_size - 1);
86 alloc_size_ = space_for_string + page_size;
87 mem_ = mmap(NULL, alloc_size_,
88 PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
89 CHECK_NE(MAP_FAILED, mem_);
90 protected_page_ = reinterpret_cast<char*>(mem_) + space_for_string;
91 char* dst = protected_page_ - size;
92 memcpy(dst, s.data(), size);
95 // Make guard page unreadable.
96 CHECK_EQ(0, mprotect(protected_page_, page_size, PROT_NONE));
99 ~DataEndingAtUnreadablePage() {
100 // Undo the mprotect.
101 CHECK_EQ(0, mprotect(protected_page_, getpagesize(), PROT_READ|PROT_WRITE));
102 CHECK_EQ(0, munmap(mem_, alloc_size_));
105 const char* data() const { return data_; }
106 size_t size() const { return size_; }
111 char* protected_page_;
116 #else // HAVE_FUNC_MMAP
118 // Fallback for systems without mmap.
119 typedef string DataEndingAtUnreadablePage;
123 enum CompressorType {
124 ZLIB, LZO, LIBLZF, QUICKLZ, FASTLZ, SNAPPY
127 const char* names[] = {
128 "ZLIB", "LZO", "LIBLZF", "QUICKLZ", "FASTLZ", "SNAPPY"
131 static size_t MinimumRequiredOutputSpace(size_t input_size,
132 CompressorType comp) {
136 return ZLib::MinCompressbufSize(input_size);
137 #endif // ZLIB_VERSION
141 return input_size + input_size/64 + 16 + 3;
142 #endif // LZO_VERSION
147 #endif // LZF_VERSION
149 #ifdef QLZ_VERSION_MAJOR
151 return input_size + 36000; // 36000 is used for scratch.
152 #endif // QLZ_VERSION_MAJOR
154 #ifdef FASTLZ_VERSION
156 return max(static_cast<int>(ceil(input_size * 1.05)), 66);
157 #endif // FASTLZ_VERSION
160 return snappy::MaxCompressedLength(input_size);
163 LOG(FATAL) << "Unknown compression type number " << comp;
167 // Returns true if we successfully compressed, false otherwise.
169 // If compressed_is_preallocated is set, do not resize the compressed buffer.
170 // This is typically what you want for a benchmark, in order to not spend
171 // time in the memory allocator. If you do set this flag, however,
172 // "compressed" must be preinitialized to at least MinCompressbufSize(comp)
173 // number of bytes, and may contain junk bytes at the end after return.
174 static bool Compress(const char* input, size_t input_size, CompressorType comp,
175 string* compressed, bool compressed_is_preallocated) {
176 if (!compressed_is_preallocated) {
177 compressed->resize(MinimumRequiredOutputSpace(input_size, comp));
184 uLongf destlen = compressed->size();
185 int ret = zlib.Compress(
186 reinterpret_cast<Bytef*>(string_as_array(compressed)),
188 reinterpret_cast<const Bytef*>(input),
191 if (!compressed_is_preallocated) {
192 compressed->resize(destlen);
196 #endif // ZLIB_VERSION
200 unsigned char* mem = new unsigned char[LZO1X_1_15_MEM_COMPRESS];
202 int ret = lzo1x_1_15_compress(
203 reinterpret_cast<const uint8*>(input),
205 reinterpret_cast<uint8*>(string_as_array(compressed)),
208 CHECK_EQ(LZO_E_OK, ret);
210 if (!compressed_is_preallocated) {
211 compressed->resize(destlen);
215 #endif // LZO_VERSION
219 int destlen = lzf_compress(input,
221 string_as_array(compressed),
224 // lzf *can* cause lots of blowup when compressing, so they
225 // recommend to limit outsize to insize, and just not compress
226 // if it's bigger. Ideally, we'd just swap input and output.
227 compressed->assign(input, input_size);
228 destlen = input_size;
230 if (!compressed_is_preallocated) {
231 compressed->resize(destlen);
235 #endif // LZF_VERSION
237 #ifdef QLZ_VERSION_MAJOR
239 qlz_state_compress *state_compress = new qlz_state_compress;
240 int destlen = qlz_compress(input,
241 string_as_array(compressed),
244 delete state_compress;
245 CHECK_NE(0, destlen);
246 if (!compressed_is_preallocated) {
247 compressed->resize(destlen);
251 #endif // QLZ_VERSION_MAJOR
253 #ifdef FASTLZ_VERSION
255 // Use level 1 compression since we mostly care about speed.
256 int destlen = fastlz_compress_level(
260 string_as_array(compressed));
261 if (!compressed_is_preallocated) {
262 compressed->resize(destlen);
264 CHECK_NE(destlen, 0);
267 #endif // FASTLZ_VERSION
271 snappy::RawCompress(input, input_size,
272 string_as_array(compressed),
274 CHECK_LE(destlen, snappy::MaxCompressedLength(input_size));
275 if (!compressed_is_preallocated) {
276 compressed->resize(destlen);
283 return false; // the asked-for library wasn't compiled in
289 static bool Uncompress(const string& compressed, CompressorType comp,
290 int size, string* output) {
294 output->resize(size);
296 uLongf destlen = output->size();
297 int ret = zlib.Uncompress(
298 reinterpret_cast<Bytef*>(string_as_array(output)),
300 reinterpret_cast<const Bytef*>(compressed.data()),
303 CHECK_EQ(static_cast<uLongf>(size), destlen);
306 #endif // ZLIB_VERSION
310 output->resize(size);
312 int ret = lzo1x_decompress(
313 reinterpret_cast<const uint8*>(compressed.data()),
315 reinterpret_cast<uint8*>(string_as_array(output)),
318 CHECK_EQ(LZO_E_OK, ret);
319 CHECK_EQ(static_cast<lzo_uint>(size), destlen);
322 #endif // LZO_VERSION
326 output->resize(size);
327 int destlen = lzf_decompress(compressed.data(),
329 string_as_array(output),
332 // This error probably means we had decided not to compress,
333 // and thus have stored input in output directly.
334 output->assign(compressed.data(), compressed.size());
335 destlen = compressed.size();
337 CHECK_EQ(destlen, size);
340 #endif // LZF_VERSION
342 #ifdef QLZ_VERSION_MAJOR
344 output->resize(size);
345 qlz_state_decompress *state_decompress = new qlz_state_decompress;
346 int destlen = qlz_decompress(compressed.data(),
347 string_as_array(output),
349 delete state_decompress;
350 CHECK_EQ(destlen, size);
353 #endif // QLZ_VERSION_MAJOR
355 #ifdef FASTLZ_VERSION
357 output->resize(size);
358 int destlen = fastlz_decompress(compressed.data(),
360 string_as_array(output),
362 CHECK_EQ(destlen, size);
365 #endif // FASTLZ_VERSION
368 snappy::RawUncompress(compressed.data(), compressed.size(),
369 string_as_array(output));
375 return false; // the asked-for library wasn't compiled in
381 static void Measure(const char* data,
386 // Run tests a few time and pick median running times
387 static const int kRuns = 5;
390 int compressed_size = 0;
393 // Chop the input into blocks
394 int num_blocks = (length + block_size - 1) / block_size;
395 vector<const char*> input(num_blocks);
396 vector<size_t> input_length(num_blocks);
397 vector<string> compressed(num_blocks);
398 vector<string> output(num_blocks);
399 for (int b = 0; b < num_blocks; b++) {
400 int input_start = b * block_size;
401 int input_limit = min<int>((b+1)*block_size, length);
402 input[b] = data+input_start;
403 input_length[b] = input_limit-input_start;
405 // Pre-grow the output buffer so we don't measure string append time.
406 compressed[b].resize(MinimumRequiredOutputSpace(block_size, comp));
409 // First, try one trial compression to make sure the code is compiled in
410 if (!Compress(input[0], input_length[0], comp, &compressed[0], true)) {
411 LOG(WARNING) << "Skipping " << names[comp] << ": "
412 << "library not compiled in";
416 for (int run = 0; run < kRuns; run++) {
417 CycleTimer ctimer, utimer;
419 for (int b = 0; b < num_blocks; b++) {
420 // Pre-grow the output buffer so we don't measure string append time.
421 compressed[b].resize(MinimumRequiredOutputSpace(block_size, comp));
425 for (int b = 0; b < num_blocks; b++)
426 for (int i = 0; i < repeats; i++)
427 Compress(input[b], input_length[b], comp, &compressed[b], true);
430 // Compress once more, with resizing, so we don't leave junk
431 // at the end that will confuse the decompressor.
432 for (int b = 0; b < num_blocks; b++) {
433 Compress(input[b], input_length[b], comp, &compressed[b], false);
436 for (int b = 0; b < num_blocks; b++) {
437 output[b].resize(input_length[b]);
441 for (int i = 0; i < repeats; i++)
442 for (int b = 0; b < num_blocks; b++)
443 Uncompress(compressed[b], comp, input_length[b], &output[b]);
446 ctime[run] = ctimer.Get();
447 utime[run] = utimer.Get();
451 for (int i = 0; i < compressed.size(); i++) {
452 compressed_size += compressed[i].size();
456 sort(ctime, ctime + kRuns);
457 sort(utime, utime + kRuns);
458 const int med = kRuns/2;
460 float comp_rate = (length / ctime[med]) * repeats / 1048576.0;
461 float uncomp_rate = (length / utime[med]) * repeats / 1048576.0;
462 string x = names[comp];
464 string urate = (uncomp_rate >= 0)
465 ? StringPrintf("%.1f", uncomp_rate)
467 printf("%-7s [b %dM] bytes %6d -> %6d %4.1f%% "
468 "comp %5.1f MB/s uncomp %5s MB/s\n",
471 static_cast<int>(length), static_cast<uint32>(compressed_size),
472 (compressed_size * 100.0) / max<int>(1, length),
478 static int VerifyString(const string& input) {
480 DataEndingAtUnreadablePage i(input);
481 const size_t written = snappy::Compress(i.data(), i.size(), &compressed);
482 CHECK_EQ(written, compressed.size());
483 CHECK_LE(compressed.size(),
484 snappy::MaxCompressedLength(input.size()));
485 CHECK(snappy::IsValidCompressedBuffer(compressed.data(), compressed.size()));
488 DataEndingAtUnreadablePage c(compressed);
489 CHECK(snappy::Uncompress(c.data(), c.size(), &uncompressed));
490 CHECK_EQ(uncompressed, input);
491 return uncompressed.size();
495 // Test that data compressed by a compressor that does not
496 // obey block sizes is uncompressed properly.
497 static void VerifyNonBlockedCompression(const string& input) {
498 if (input.length() > snappy::kBlockSize) {
499 // We cannot test larger blocks than the maximum block size, obviously.
504 Varint::Append32(&prefix, input.size());
506 // Setup compression table
507 snappy::internal::WorkingMemory wmem;
509 uint16* table = wmem.GetHashTable(input.size(), &table_size);
511 // Compress entire input in one shot
513 compressed += prefix;
514 compressed.resize(prefix.size()+snappy::MaxCompressedLength(input.size()));
515 char* dest = string_as_array(&compressed) + prefix.size();
516 char* end = snappy::internal::CompressFragment(input.data(), input.size(),
517 dest, table, table_size);
518 compressed.resize(end - compressed.data());
520 // Uncompress into string
522 CHECK(snappy::Uncompress(compressed.data(), compressed.size(), &uncomp_str));
523 CHECK_EQ(uncomp_str, input);
527 // Expand the input so that it is at least K times as big as block size
528 static string Expand(const string& input) {
529 static const int K = 3;
531 while (data.size() < K * snappy::kBlockSize) {
537 static int Verify(const string& input) {
538 VLOG(1) << "Verifying input of size " << input.size();
540 // Compress using string based routines
541 const int result = VerifyString(input);
544 VerifyNonBlockedCompression(input);
545 if (!input.empty()) {
546 VerifyNonBlockedCompression(Expand(input));
553 // This test checks to ensure that snappy doesn't coredump if it gets
556 static bool IsValidCompressedBuffer(const string& c) {
557 return snappy::IsValidCompressedBuffer(c.data(), c.size());
559 static bool Uncompress(const string& c, string* u) {
560 return snappy::Uncompress(c.data(), c.size(), u);
563 TYPED_TEST(CorruptedTest, VerifyCorrupted) {
564 string source = "making sure we don't crash with corrupted input";
568 snappy::Compress(source.data(), source.size(), &dest);
570 // Mess around with the data. It's hard to simulate all possible
571 // corruptions; this is just one example ...
572 CHECK_GT(dest.size(), 3);
575 // this really ought to fail.
576 CHECK(!IsValidCompressedBuffer(TypeParam(dest)));
577 CHECK(!Uncompress(TypeParam(dest), &uncmp));
579 // This is testing for a security bug - a buffer that decompresses to 100k
580 // but we lie in the snappy header and only reserve 0 bytes of memory :)
581 source.resize(100000);
582 for (int i = 0; i < source.length(); ++i) {
585 snappy::Compress(source.data(), source.size(), &dest);
586 dest[0] = dest[1] = dest[2] = dest[3] = 0;
587 CHECK(!IsValidCompressedBuffer(TypeParam(dest)));
588 CHECK(!Uncompress(TypeParam(dest), &uncmp));
590 if (sizeof(void *) == 4) {
591 // Another security check; check a crazy big length can't DoS us with an
593 // Currently this is done only for 32-bit builds. On 64-bit builds,
594 // where 3 GB might be an acceptable allocation size, Uncompress()
595 // attempts to decompress, and sometimes causes the test to run out of
597 dest[0] = dest[1] = dest[2] = dest[3] = 0xff;
598 // This decodes to a really large size, i.e., about 3 GB.
600 CHECK(!IsValidCompressedBuffer(TypeParam(dest)));
601 CHECK(!Uncompress(TypeParam(dest), &uncmp));
603 LOG(WARNING) << "Crazy decompression lengths not checked on 64-bit build";
606 // This decodes to about 2 MB; much smaller, but should still fail.
607 dest[0] = dest[1] = dest[2] = 0xff;
609 CHECK(!IsValidCompressedBuffer(TypeParam(dest)));
610 CHECK(!Uncompress(TypeParam(dest), &uncmp));
612 // try reading stuff in from a bad file.
613 for (int i = 1; i <= 3; ++i) {
614 string data = ReadTestDataFile(StringPrintf("baddata%d.snappy", i).c_str());
616 // check that we don't return a crazy length
618 CHECK(!snappy::GetUncompressedLength(data.data(), data.size(), &ulen)
619 || (ulen < (1<<20)));
621 snappy::ByteArraySource source(data.data(), data.size());
622 CHECK(!snappy::GetUncompressedLength(&source, &ulen2) ||
624 CHECK(!IsValidCompressedBuffer(TypeParam(data)));
625 CHECK(!Uncompress(TypeParam(data), &uncmp));
629 // Helper routines to construct arbitrary compressed strings.
630 // These mirror the compression code in snappy.cc, but are copied
631 // here so that we can bypass some limitations in the how snappy.cc
632 // invokes these routines.
633 static void AppendLiteral(string* dst, const string& literal) {
634 if (literal.empty()) return;
635 int n = literal.size() - 1;
637 // Fit length in tag byte
638 dst->push_back(0 | (n << 2));
640 // Encode in upcoming bytes
644 number[count++] = n & 0xff;
647 dst->push_back(0 | ((59+count) << 2));
648 *dst += string(number, count);
653 static void AppendCopy(string* dst, int offset, int length) {
655 // Figure out how much to copy in one shot
659 } else if (length > 64) {
666 if ((to_copy < 12) && (offset < 2048)) {
667 assert(to_copy-4 < 8); // Must fit in 3 bits
668 dst->push_back(1 | ((to_copy-4) << 2) | ((offset >> 8) << 5));
669 dst->push_back(offset & 0xff);
670 } else if (offset < 65536) {
671 dst->push_back(2 | ((to_copy-1) << 2));
672 dst->push_back(offset & 0xff);
673 dst->push_back(offset >> 8);
675 dst->push_back(3 | ((to_copy-1) << 2));
676 dst->push_back(offset & 0xff);
677 dst->push_back((offset >> 8) & 0xff);
678 dst->push_back((offset >> 16) & 0xff);
679 dst->push_back((offset >> 24) & 0xff);
684 TEST(Snappy, SimpleTests) {
690 Verify("aaaaaaa" + string(16, 'b') + string("aaaaa") + "abc");
691 Verify("aaaaaaa" + string(256, 'b') + string("aaaaa") + "abc");
692 Verify("aaaaaaa" + string(2047, 'b') + string("aaaaa") + "abc");
693 Verify("aaaaaaa" + string(65536, 'b') + string("aaaaa") + "abc");
694 Verify("abcaaaaaaa" + string(65536, 'b') + string("aaaaa") + "abc");
697 // Verify max blowup (lots of four-byte copies)
698 TEST(Snappy, MaxBlowup) {
700 for (int i = 0; i < 20000; i++) {
702 uint32 bytes = static_cast<uint32>(rnd.Next());
703 input.append(reinterpret_cast<char*>(&bytes), sizeof(bytes));
705 for (int i = 19999; i >= 0; i--) {
707 uint32 bytes = static_cast<uint32>(rnd.Next());
708 input.append(reinterpret_cast<char*>(&bytes), sizeof(bytes));
713 TEST(Snappy, RandomData) {
714 ACMRandom rnd(FLAGS_test_random_seed);
716 const int num_ops = 20000;
717 for (int i = 0; i < num_ops; i++) {
718 if ((i % 1000) == 0) {
719 VLOG(0) << "Random op " << i << " of " << num_ops;
723 int len = rnd.Uniform(4096);
725 len = 65536 + rnd.Uniform(65536);
727 while (x.size() < len) {
730 run_len = rnd.Skewed(8);
732 char c = (i < 100) ? rnd.Uniform(256) : rnd.Skewed(3);
733 while (run_len-- > 0 && x.size() < len) {
742 TEST(Snappy, FourByteOffset) {
743 // The new compressor cannot generate four-byte offsets since
744 // it chops up the input into 32KB pieces. So we hand-emit the
747 // The two fragments that make up the input string.
748 string fragment1 = "012345689abcdefghijklmnopqrstuvwxyz";
749 string fragment2 = "some other string";
751 // How many times each fragment is emitted.
753 const int n2 = 100000 / fragment2.size();
754 const int length = n1 * fragment1.size() + n2 * fragment2.size();
757 Varint::Append32(&compressed, length);
759 AppendLiteral(&compressed, fragment1);
760 string src = fragment1;
761 for (int i = 0; i < n2; i++) {
762 AppendLiteral(&compressed, fragment2);
765 AppendCopy(&compressed, src.size(), fragment1.size());
767 CHECK_EQ(length, src.size());
770 CHECK(snappy::IsValidCompressedBuffer(compressed.data(), compressed.size()));
771 CHECK(snappy::Uncompress(compressed.data(), compressed.size(), &uncompressed));
772 CHECK_EQ(uncompressed, src);
776 static bool CheckUncompressedLength(const string& compressed,
778 const bool result1 = snappy::GetUncompressedLength(compressed.data(),
782 snappy::ByteArraySource source(compressed.data(), compressed.size());
784 const bool result2 = snappy::GetUncompressedLength(&source, &length);
785 CHECK_EQ(result1, result2);
789 TEST(SnappyCorruption, TruncatedVarint) {
790 string compressed, uncompressed;
792 compressed.push_back('\xf0');
793 CHECK(!CheckUncompressedLength(compressed, &ulength));
794 CHECK(!snappy::IsValidCompressedBuffer(compressed.data(), compressed.size()));
795 CHECK(!snappy::Uncompress(compressed.data(), compressed.size(),
799 TEST(SnappyCorruption, UnterminatedVarint) {
800 string compressed, uncompressed;
802 compressed.push_back(128);
803 compressed.push_back(128);
804 compressed.push_back(128);
805 compressed.push_back(128);
806 compressed.push_back(128);
807 compressed.push_back(10);
808 CHECK(!CheckUncompressedLength(compressed, &ulength));
809 CHECK(!snappy::IsValidCompressedBuffer(compressed.data(), compressed.size()));
810 CHECK(!snappy::Uncompress(compressed.data(), compressed.size(),
814 TEST(Snappy, ReadPastEndOfBuffer) {
815 // Check that we do not read past end of input
817 // Make a compressed string that ends with a single-byte literal
819 Varint::Append32(&compressed, 1);
820 AppendLiteral(&compressed, "x");
823 DataEndingAtUnreadablePage c(compressed);
824 CHECK(snappy::Uncompress(c.data(), c.size(), &uncompressed));
825 CHECK_EQ(uncompressed, string("x"));
828 // Check for an infinite loop caused by a copy with offset==0
829 TEST(Snappy, ZeroOffsetCopy) {
830 const char* compressed = "\x40\x12\x00\x00";
831 // \x40 Length (must be > kMaxIncrementCopyOverflow)
832 // \x12\x00\x00 Copy with offset==0, length==5
833 char uncompressed[100];
834 EXPECT_FALSE(snappy::RawUncompress(compressed, 4, uncompressed));
837 TEST(Snappy, ZeroOffsetCopyValidation) {
838 const char* compressed = "\x05\x12\x00\x00";
840 // \x12\x00\x00 Copy with offset==0, length==5
841 EXPECT_FALSE(snappy::IsValidCompressedBuffer(compressed, 4));
847 int TestFindMatchLength(const char* s1, const char *s2, unsigned length) {
848 return snappy::internal::FindMatchLength(s1, s2, s2 + length);
853 TEST(Snappy, FindMatchLength) {
854 // Exercise all different code paths through the function.
857 // Hit s1_limit in 64-bit loop, hit s1_limit in single-character loop.
858 EXPECT_EQ(6, TestFindMatchLength("012345", "012345", 6));
859 EXPECT_EQ(11, TestFindMatchLength("01234567abc", "01234567abc", 11));
861 // Hit s1_limit in 64-bit loop, find a non-match in single-character loop.
862 EXPECT_EQ(9, TestFindMatchLength("01234567abc", "01234567axc", 9));
864 // Same, but edge cases.
865 EXPECT_EQ(11, TestFindMatchLength("01234567abc!", "01234567abc!", 11));
866 EXPECT_EQ(11, TestFindMatchLength("01234567abc!", "01234567abc?", 11));
868 // Find non-match at once in first loop.
869 EXPECT_EQ(0, TestFindMatchLength("01234567xxxxxxxx", "?1234567xxxxxxxx", 16));
870 EXPECT_EQ(1, TestFindMatchLength("01234567xxxxxxxx", "0?234567xxxxxxxx", 16));
871 EXPECT_EQ(4, TestFindMatchLength("01234567xxxxxxxx", "01237654xxxxxxxx", 16));
872 EXPECT_EQ(7, TestFindMatchLength("01234567xxxxxxxx", "0123456?xxxxxxxx", 16));
874 // Find non-match in first loop after one block.
875 EXPECT_EQ(8, TestFindMatchLength("abcdefgh01234567xxxxxxxx",
876 "abcdefgh?1234567xxxxxxxx", 24));
877 EXPECT_EQ(9, TestFindMatchLength("abcdefgh01234567xxxxxxxx",
878 "abcdefgh0?234567xxxxxxxx", 24));
879 EXPECT_EQ(12, TestFindMatchLength("abcdefgh01234567xxxxxxxx",
880 "abcdefgh01237654xxxxxxxx", 24));
881 EXPECT_EQ(15, TestFindMatchLength("abcdefgh01234567xxxxxxxx",
882 "abcdefgh0123456?xxxxxxxx", 24));
887 EXPECT_EQ(0, TestFindMatchLength("01234567", "?1234567", 8));
888 EXPECT_EQ(1, TestFindMatchLength("01234567", "0?234567", 8));
889 EXPECT_EQ(2, TestFindMatchLength("01234567", "01?34567", 8));
890 EXPECT_EQ(3, TestFindMatchLength("01234567", "012?4567", 8));
891 EXPECT_EQ(4, TestFindMatchLength("01234567", "0123?567", 8));
892 EXPECT_EQ(5, TestFindMatchLength("01234567", "01234?67", 8));
893 EXPECT_EQ(6, TestFindMatchLength("01234567", "012345?7", 8));
894 EXPECT_EQ(7, TestFindMatchLength("01234567", "0123456?", 8));
895 EXPECT_EQ(7, TestFindMatchLength("01234567", "0123456?", 7));
896 EXPECT_EQ(7, TestFindMatchLength("01234567!", "0123456??", 7));
898 // Hit s1_limit in 32-bit loop, hit s1_limit in single-character loop.
899 EXPECT_EQ(10, TestFindMatchLength("xxxxxxabcd", "xxxxxxabcd", 10));
900 EXPECT_EQ(10, TestFindMatchLength("xxxxxxabcd?", "xxxxxxabcd?", 10));
901 EXPECT_EQ(13, TestFindMatchLength("xxxxxxabcdef", "xxxxxxabcdef", 13));
903 // Same, but edge cases.
904 EXPECT_EQ(12, TestFindMatchLength("xxxxxx0123abc!", "xxxxxx0123abc!", 12));
905 EXPECT_EQ(12, TestFindMatchLength("xxxxxx0123abc!", "xxxxxx0123abc?", 12));
907 // Hit s1_limit in 32-bit loop, find a non-match in single-character loop.
908 EXPECT_EQ(11, TestFindMatchLength("xxxxxx0123abc", "xxxxxx0123axc", 13));
910 // Find non-match at once in first loop.
911 EXPECT_EQ(6, TestFindMatchLength("xxxxxx0123xxxxxxxx",
912 "xxxxxx?123xxxxxxxx", 18));
913 EXPECT_EQ(7, TestFindMatchLength("xxxxxx0123xxxxxxxx",
914 "xxxxxx0?23xxxxxxxx", 18));
915 EXPECT_EQ(8, TestFindMatchLength("xxxxxx0123xxxxxxxx",
916 "xxxxxx0132xxxxxxxx", 18));
917 EXPECT_EQ(9, TestFindMatchLength("xxxxxx0123xxxxxxxx",
918 "xxxxxx012?xxxxxxxx", 18));
920 // Same, but edge cases.
921 EXPECT_EQ(6, TestFindMatchLength("xxxxxx0123", "xxxxxx?123", 10));
922 EXPECT_EQ(7, TestFindMatchLength("xxxxxx0123", "xxxxxx0?23", 10));
923 EXPECT_EQ(8, TestFindMatchLength("xxxxxx0123", "xxxxxx0132", 10));
924 EXPECT_EQ(9, TestFindMatchLength("xxxxxx0123", "xxxxxx012?", 10));
926 // Find non-match in first loop after one block.
927 EXPECT_EQ(10, TestFindMatchLength("xxxxxxabcd0123xx",
928 "xxxxxxabcd?123xx", 16));
929 EXPECT_EQ(11, TestFindMatchLength("xxxxxxabcd0123xx",
930 "xxxxxxabcd0?23xx", 16));
931 EXPECT_EQ(12, TestFindMatchLength("xxxxxxabcd0123xx",
932 "xxxxxxabcd0132xx", 16));
933 EXPECT_EQ(13, TestFindMatchLength("xxxxxxabcd0123xx",
934 "xxxxxxabcd012?xx", 16));
936 // Same, but edge cases.
937 EXPECT_EQ(10, TestFindMatchLength("xxxxxxabcd0123", "xxxxxxabcd?123", 14));
938 EXPECT_EQ(11, TestFindMatchLength("xxxxxxabcd0123", "xxxxxxabcd0?23", 14));
939 EXPECT_EQ(12, TestFindMatchLength("xxxxxxabcd0123", "xxxxxxabcd0132", 14));
940 EXPECT_EQ(13, TestFindMatchLength("xxxxxxabcd0123", "xxxxxxabcd012?", 14));
943 TEST(Snappy, FindMatchLengthRandom) {
944 const int kNumTrials = 10000;
945 const int kTypicalLength = 10;
946 ACMRandom rnd(FLAGS_test_random_seed);
948 for (int i = 0; i < kNumTrials; i++) {
950 char a = rnd.Rand8();
951 char b = rnd.Rand8();
952 while (!rnd.OneIn(kTypicalLength)) {
953 s.push_back(rnd.OneIn(2) ? a : b);
954 t.push_back(rnd.OneIn(2) ? a : b);
956 DataEndingAtUnreadablePage u(s);
957 DataEndingAtUnreadablePage v(t);
958 int matched = snappy::internal::FindMatchLength(
959 u.data(), v.data(), v.data() + t.size());
960 if (matched == t.size()) {
963 EXPECT_NE(s[matched], t[matched]);
964 for (int j = 0; j < matched; j++) {
965 EXPECT_EQ(s[j], t[j]);
972 static void CompressFile(const char* fname) {
974 File::ReadFileToStringOrDie(fname, &fullinput);
977 Compress(fullinput.data(), fullinput.size(), SNAPPY, &compressed, false);
979 File::WriteStringToFileOrDie(compressed,
980 string(fname).append(".comp").c_str());
983 static void UncompressFile(const char* fname) {
985 File::ReadFileToStringOrDie(fname, &fullinput);
988 CHECK(CheckUncompressedLength(fullinput, &uncompLength));
991 uncompressed.resize(uncompLength);
992 CHECK(snappy::Uncompress(fullinput.data(), fullinput.size(), &uncompressed));
994 File::WriteStringToFileOrDie(uncompressed,
995 string(fname).append(".uncomp").c_str());
998 static void MeasureFile(const char* fname) {
1000 File::ReadFileToStringOrDie(fname, &fullinput);
1001 printf("%-40s :\n", fname);
1003 int start_len = (FLAGS_start_len < 0) ? fullinput.size() : FLAGS_start_len;
1004 int end_len = fullinput.size();
1005 if (FLAGS_end_len >= 0) {
1006 end_len = min<int>(fullinput.size(), FLAGS_end_len);
1008 for (int len = start_len; len <= end_len; len++) {
1009 const char* const input = fullinput.data();
1010 int repeats = (FLAGS_bytes + len) / (len + 1);
1011 if (FLAGS_zlib) Measure(input, len, ZLIB, repeats, 1024<<10);
1012 if (FLAGS_lzo) Measure(input, len, LZO, repeats, 1024<<10);
1013 if (FLAGS_liblzf) Measure(input, len, LIBLZF, repeats, 1024<<10);
1014 if (FLAGS_quicklz) Measure(input, len, QUICKLZ, repeats, 1024<<10);
1015 if (FLAGS_fastlz) Measure(input, len, FASTLZ, repeats, 1024<<10);
1016 if (FLAGS_snappy) Measure(input, len, SNAPPY, repeats, 4096<<10);
1018 // For block-size based measurements
1019 if (0 && FLAGS_snappy) {
1020 Measure(input, len, SNAPPY, repeats, 8<<10);
1021 Measure(input, len, SNAPPY, repeats, 16<<10);
1022 Measure(input, len, SNAPPY, repeats, 32<<10);
1023 Measure(input, len, SNAPPY, repeats, 64<<10);
1024 Measure(input, len, SNAPPY, repeats, 256<<10);
1025 Measure(input, len, SNAPPY, repeats, 1024<<10);
1032 const char* filename;
1035 { "urls", "urls.10K" },
1036 { "jpg", "house.jpg" },
1037 { "pdf", "mapreduce-osdi-1.pdf" },
1038 { "html4", "html_x_4" },
1039 { "cp", "cp.html" },
1040 { "c", "fields.c" },
1041 { "lsp", "grammar.lsp" },
1042 { "xls", "kennedy.xls" },
1043 { "txt1", "alice29.txt" },
1044 { "txt2", "asyoulik.txt" },
1045 { "txt3", "lcet10.txt" },
1046 { "txt4", "plrabn12.txt" },
1049 { "man", "xargs.1" },
1050 { "pb", "geo.protodata" },
1051 { "gaviota", "kppkn.gtb" },
1054 static void BM_UFlat(int iters, int arg) {
1055 StopBenchmarkTiming();
1057 // Pick file to process based on "arg"
1059 CHECK_LT(arg, ARRAYSIZE(files));
1060 string contents = ReadTestDataFile(files[arg].filename);
1063 snappy::Compress(contents.data(), contents.size(), &zcontents);
1064 char* dst = new char[contents.size()];
1066 SetBenchmarkBytesProcessed(static_cast<int64>(iters) *
1067 static_cast<int64>(contents.size()));
1068 SetBenchmarkLabel(files[arg].label);
1069 StartBenchmarkTiming();
1070 while (iters-- > 0) {
1071 CHECK(snappy::RawUncompress(zcontents.data(), zcontents.size(), dst));
1073 StopBenchmarkTiming();
1077 BENCHMARK(BM_UFlat)->DenseRange(0, 17);
1079 static void BM_UValidate(int iters, int arg) {
1080 StopBenchmarkTiming();
1082 // Pick file to process based on "arg"
1084 CHECK_LT(arg, ARRAYSIZE(files));
1085 string contents = ReadTestDataFile(files[arg].filename);
1088 snappy::Compress(contents.data(), contents.size(), &zcontents);
1090 SetBenchmarkBytesProcessed(static_cast<int64>(iters) *
1091 static_cast<int64>(contents.size()));
1092 SetBenchmarkLabel(files[arg].label);
1093 StartBenchmarkTiming();
1094 while (iters-- > 0) {
1095 CHECK(snappy::IsValidCompressedBuffer(zcontents.data(), zcontents.size()));
1097 StopBenchmarkTiming();
1099 BENCHMARK(BM_UValidate)->DenseRange(0, 4);
1102 static void BM_ZFlat(int iters, int arg) {
1103 StopBenchmarkTiming();
1105 // Pick file to process based on "arg"
1107 CHECK_LT(arg, ARRAYSIZE(files));
1108 string contents = ReadTestDataFile(files[arg].filename);
1110 char* dst = new char[snappy::MaxCompressedLength(contents.size())];
1112 SetBenchmarkBytesProcessed(static_cast<int64>(iters) *
1113 static_cast<int64>(contents.size()));
1114 StartBenchmarkTiming();
1117 while (iters-- > 0) {
1118 snappy::RawCompress(contents.data(), contents.size(), dst, &zsize);
1120 StopBenchmarkTiming();
1121 const double compression_ratio =
1122 static_cast<double>(zsize) / std::max<size_t>(1, contents.size());
1123 SetBenchmarkLabel(StringPrintf("%s (%.2f %%)",
1124 files[arg].label, 100.0 * compression_ratio));
1125 VLOG(0) << StringPrintf("compression for %s: %zd -> %zd bytes",
1126 files[arg].label, contents.size(), zsize);
1129 BENCHMARK(BM_ZFlat)->DenseRange(0, 17);
1132 } // namespace snappy
1135 int main(int argc, char** argv) {
1136 InitGoogle(argv[0], &argc, &argv, true);
1138 RunSpecifiedBenchmarks();
1142 for (int arg = 1; arg < argc; arg++) {
1143 if (FLAGS_write_compressed) {
1144 CompressFile(argv[arg]);
1145 } else if (FLAGS_write_uncompressed) {
1146 UncompressFile(argv[arg]);
1148 MeasureFile(argv[arg]);
1154 return RUN_ALL_TESTS();