other-licenses/snappy/src/snappy_unittest.cc

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 // Copyright 2005 and onwards Google Inc.
     2 //
     3 // Redistribution and use in source and binary forms, with or without
     4 // modification, are permitted provided that the following conditions are
     5 // met:
     6 //
     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
    12 // distribution.
    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.
    16 //
    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.
    29 #include <math.h>
    30 #include <stdlib.h>
    33 #include <algorithm>
    34 #include <string>
    35 #include <vector>
    37 #include "snappy.h"
    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");
    68 namespace snappy {
    71 #ifdef HAVE_FUNC_MMAP
    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 {
    80  public:
    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);
    93     data_ = dst;
    94     size_ = size;
    95     // Make guard page unreadable.
    96     CHECK_EQ(0, mprotect(protected_page_, page_size, PROT_NONE));
    97   }
    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_));
   103   }
   105   const char* data() const { return data_; }
   106   size_t size() const { return size_; }
   108  private:
   109   size_t alloc_size_;
   110   void* mem_;
   111   char* protected_page_;
   112   const char* data_;
   113   size_t size_;
   114 };
   116 #else  // HAVE_FUNC_MMAP
   118 // Fallback for systems without mmap.
   119 typedef string DataEndingAtUnreadablePage;
   121 #endif
   123 enum CompressorType {
   124   ZLIB, LZO, LIBLZF, QUICKLZ, FASTLZ, SNAPPY
   125 };
   127 const char* names[] = {
   128   "ZLIB", "LZO", "LIBLZF", "QUICKLZ", "FASTLZ", "SNAPPY"
   129 };
   131 static size_t MinimumRequiredOutputSpace(size_t input_size,
   132                                          CompressorType comp) {
   133   switch (comp) {
   134 #ifdef ZLIB_VERSION
   135     case ZLIB:
   136       return ZLib::MinCompressbufSize(input_size);
   137 #endif  // ZLIB_VERSION
   139 #ifdef LZO_VERSION
   140     case LZO:
   141       return input_size + input_size/64 + 16 + 3;
   142 #endif  // LZO_VERSION
   144 #ifdef LZF_VERSION
   145     case LIBLZF:
   146       return input_size;
   147 #endif  // LZF_VERSION
   149 #ifdef QLZ_VERSION_MAJOR
   150     case QUICKLZ:
   151       return input_size + 36000;  // 36000 is used for scratch.
   152 #endif  // QLZ_VERSION_MAJOR
   154 #ifdef FASTLZ_VERSION
   155     case FASTLZ:
   156       return max(static_cast<int>(ceil(input_size * 1.05)), 66);
   157 #endif  // FASTLZ_VERSION
   159     case SNAPPY:
   160       return snappy::MaxCompressedLength(input_size);
   162     default:
   163       LOG(FATAL) << "Unknown compression type number " << comp;
   164   }
   165 }
   167 // Returns true if we successfully compressed, false otherwise.
   168 //
   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));
   178   }
   180   switch (comp) {
   181 #ifdef ZLIB_VERSION
   182     case ZLIB: {
   183       ZLib zlib;
   184       uLongf destlen = compressed->size();
   185       int ret = zlib.Compress(
   186           reinterpret_cast<Bytef*>(string_as_array(compressed)),
   187           &destlen,
   188           reinterpret_cast<const Bytef*>(input),
   189           input_size);
   190       CHECK_EQ(Z_OK, ret);
   191       if (!compressed_is_preallocated) {
   192         compressed->resize(destlen);
   193       }
   194       return true;
   195     }
   196 #endif  // ZLIB_VERSION
   198 #ifdef LZO_VERSION
   199     case LZO: {
   200       unsigned char* mem = new unsigned char[LZO1X_1_15_MEM_COMPRESS];
   201       lzo_uint destlen;
   202       int ret = lzo1x_1_15_compress(
   203           reinterpret_cast<const uint8*>(input),
   204           input_size,
   205           reinterpret_cast<uint8*>(string_as_array(compressed)),
   206           &destlen,
   207           mem);
   208       CHECK_EQ(LZO_E_OK, ret);
   209       delete[] mem;
   210       if (!compressed_is_preallocated) {
   211         compressed->resize(destlen);
   212       }
   213       break;
   214     }
   215 #endif  // LZO_VERSION
   217 #ifdef LZF_VERSION
   218     case LIBLZF: {
   219       int destlen = lzf_compress(input,
   220                                  input_size,
   221                                  string_as_array(compressed),
   222                                  input_size);
   223       if (destlen == 0) {
   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;
   229       }
   230       if (!compressed_is_preallocated) {
   231         compressed->resize(destlen);
   232       }
   233       break;
   234     }
   235 #endif  // LZF_VERSION
   237 #ifdef QLZ_VERSION_MAJOR
   238     case QUICKLZ: {
   239       qlz_state_compress *state_compress = new qlz_state_compress;
   240       int destlen = qlz_compress(input,
   241                                  string_as_array(compressed),
   242                                  input_size,
   243                                  state_compress);
   244       delete state_compress;
   245       CHECK_NE(0, destlen);
   246       if (!compressed_is_preallocated) {
   247         compressed->resize(destlen);
   248       }
   249       break;
   250     }
   251 #endif  // QLZ_VERSION_MAJOR
   253 #ifdef FASTLZ_VERSION
   254     case FASTLZ: {
   255       // Use level 1 compression since we mostly care about speed.
   256       int destlen = fastlz_compress_level(
   257           1,
   258           input,
   259           input_size,
   260           string_as_array(compressed));
   261       if (!compressed_is_preallocated) {
   262         compressed->resize(destlen);
   263       }
   264       CHECK_NE(destlen, 0);
   265       break;
   266     }
   267 #endif  // FASTLZ_VERSION
   269     case SNAPPY: {
   270       size_t destlen;
   271       snappy::RawCompress(input, input_size,
   272                           string_as_array(compressed),
   273                           &destlen);
   274       CHECK_LE(destlen, snappy::MaxCompressedLength(input_size));
   275       if (!compressed_is_preallocated) {
   276         compressed->resize(destlen);
   277       }
   278       break;
   279     }
   282     default: {
   283       return false;     // the asked-for library wasn't compiled in
   284     }
   285   }
   286   return true;
   287 }
   289 static bool Uncompress(const string& compressed, CompressorType comp,
   290                        int size, string* output) {
   291   switch (comp) {
   292 #ifdef ZLIB_VERSION
   293     case ZLIB: {
   294       output->resize(size);
   295       ZLib zlib;
   296       uLongf destlen = output->size();
   297       int ret = zlib.Uncompress(
   298           reinterpret_cast<Bytef*>(string_as_array(output)),
   299           &destlen,
   300           reinterpret_cast<const Bytef*>(compressed.data()),
   301           compressed.size());
   302       CHECK_EQ(Z_OK, ret);
   303       CHECK_EQ(static_cast<uLongf>(size), destlen);
   304       break;
   305     }
   306 #endif  // ZLIB_VERSION
   308 #ifdef LZO_VERSION
   309     case LZO: {
   310       output->resize(size);
   311       lzo_uint destlen;
   312       int ret = lzo1x_decompress(
   313           reinterpret_cast<const uint8*>(compressed.data()),
   314           compressed.size(),
   315           reinterpret_cast<uint8*>(string_as_array(output)),
   316           &destlen,
   317           NULL);
   318       CHECK_EQ(LZO_E_OK, ret);
   319       CHECK_EQ(static_cast<lzo_uint>(size), destlen);
   320       break;
   321     }
   322 #endif  // LZO_VERSION
   324 #ifdef LZF_VERSION
   325     case LIBLZF: {
   326       output->resize(size);
   327       int destlen = lzf_decompress(compressed.data(),
   328                                    compressed.size(),
   329                                    string_as_array(output),
   330                                    output->size());
   331       if (destlen == 0) {
   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();
   336       }
   337       CHECK_EQ(destlen, size);
   338       break;
   339     }
   340 #endif  // LZF_VERSION
   342 #ifdef QLZ_VERSION_MAJOR
   343     case QUICKLZ: {
   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),
   348                                    state_decompress);
   349       delete state_decompress;
   350       CHECK_EQ(destlen, size);
   351       break;
   352     }
   353 #endif  // QLZ_VERSION_MAJOR
   355 #ifdef FASTLZ_VERSION
   356     case FASTLZ: {
   357       output->resize(size);
   358       int destlen = fastlz_decompress(compressed.data(),
   359                                       compressed.length(),
   360                                       string_as_array(output),
   361                                       size);
   362       CHECK_EQ(destlen, size);
   363       break;
   364     }
   365 #endif  // FASTLZ_VERSION
   367     case SNAPPY: {
   368       snappy::RawUncompress(compressed.data(), compressed.size(),
   369                             string_as_array(output));
   370       break;
   371     }
   374     default: {
   375       return false;     // the asked-for library wasn't compiled in
   376     }
   377   }
   378   return true;
   379 }
   381 static void Measure(const char* data,
   382                     size_t length,
   383                     CompressorType comp,
   384                     int repeats,
   385                     int block_size) {
   386   // Run tests a few time and pick median running times
   387   static const int kRuns = 5;
   388   double ctime[kRuns];
   389   double utime[kRuns];
   390   int compressed_size = 0;
   392   {
   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));
   407     }
   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";
   413       return;
   414     }
   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));
   422       }
   424       ctimer.Start();
   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);
   428       ctimer.Stop();
   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);
   434       }
   436       for (int b = 0; b < num_blocks; b++) {
   437         output[b].resize(input_length[b]);
   438       }
   440       utimer.Start();
   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]);
   444       utimer.Stop();
   446       ctime[run] = ctimer.Get();
   447       utime[run] = utimer.Get();
   448     }
   450     compressed_size = 0;
   451     for (int i = 0; i < compressed.size(); i++) {
   452       compressed_size += compressed[i].size();
   453     }
   454   }
   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];
   463   x += ":";
   464   string urate = (uncomp_rate >= 0)
   465                  ? StringPrintf("%.1f", uncomp_rate)
   466                  : string("?");
   467   printf("%-7s [b %dM] bytes %6d -> %6d %4.1f%%  "
   468          "comp %5.1f MB/s  uncomp %5s MB/s\n",
   469          x.c_str(),
   470          block_size/(1<<20),
   471          static_cast<int>(length), static_cast<uint32>(compressed_size),
   472          (compressed_size * 100.0) / max<int>(1, length),
   473          comp_rate,
   474          urate.c_str());
   475 }
   478 static int VerifyString(const string& input) {
   479   string compressed;
   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()));
   487   string uncompressed;
   488   DataEndingAtUnreadablePage c(compressed);
   489   CHECK(snappy::Uncompress(c.data(), c.size(), &uncompressed));
   490   CHECK_EQ(uncompressed, input);
   491   return uncompressed.size();
   492 }
   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.
   500     return;
   501   }
   503   string prefix;
   504   Varint::Append32(&prefix, input.size());
   506   // Setup compression table
   507   snappy::internal::WorkingMemory wmem;
   508   int table_size;
   509   uint16* table = wmem.GetHashTable(input.size(), &table_size);
   511   // Compress entire input in one shot
   512   string compressed;
   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
   521   string uncomp_str;
   522   CHECK(snappy::Uncompress(compressed.data(), compressed.size(), &uncomp_str));
   523   CHECK_EQ(uncomp_str, input);
   525 }
   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;
   530   string data = input;
   531   while (data.size() < K * snappy::kBlockSize) {
   532     data += input;
   533   }
   534   return data;
   535 }
   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));
   547   }
   550   return result;
   551 }
   553 // This test checks to ensure that snappy doesn't coredump if it gets
   554 // corrupted data.
   556 static bool IsValidCompressedBuffer(const string& c) {
   557   return snappy::IsValidCompressedBuffer(c.data(), c.size());
   558 }
   559 static bool Uncompress(const string& c, string* u) {
   560   return snappy::Uncompress(c.data(), c.size(), u);
   561 }
   563 TYPED_TEST(CorruptedTest, VerifyCorrupted) {
   564   string source = "making sure we don't crash with corrupted input";
   565   VLOG(1) << source;
   566   string dest;
   567   TypeParam uncmp;
   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);
   573   dest[1]--;
   574   dest[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) {
   583     source[i] = 'A';
   584   }
   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
   592     // over-allocation.
   593     // Currently this is done only for 32-bit builds.  On 64-bit builds,
   594     // where 3GBytes might be an acceptable allocation size, Uncompress()
   595     // attempts to decompress, and sometimes causes the test to run out of
   596     // memory.
   597     dest[0] = dest[1] = dest[2] = dest[3] = 0xff;
   598     // This decodes to a really large size, i.e., 3221225471 bytes
   599     dest[4] = 'k';
   600     CHECK(!IsValidCompressedBuffer(TypeParam(dest)));
   601     CHECK(!Uncompress(TypeParam(dest), &uncmp));
   602     dest[0] = dest[1] = dest[2] = 0xff;
   603     dest[3] = 0x7f;
   604     CHECK(!IsValidCompressedBuffer(TypeParam(dest)));
   605     CHECK(!Uncompress(TypeParam(dest), &uncmp));
   606   } else {
   607     LOG(WARNING) << "Crazy decompression lengths not checked on 64-bit build";
   608   }
   610   // try reading stuff in from a bad file.
   611   for (int i = 1; i <= 3; ++i) {
   612     string data = ReadTestDataFile(StringPrintf("baddata%d.snappy", i).c_str());
   613     string uncmp;
   614     // check that we don't return a crazy length
   615     size_t ulen;
   616     CHECK(!snappy::GetUncompressedLength(data.data(), data.size(), &ulen)
   617           || (ulen < (1<<20)));
   618     uint32 ulen2;
   619     snappy::ByteArraySource source(data.data(), data.size());
   620     CHECK(!snappy::GetUncompressedLength(&source, &ulen2) ||
   621           (ulen2 < (1<<20)));
   622     CHECK(!IsValidCompressedBuffer(TypeParam(data)));
   623     CHECK(!Uncompress(TypeParam(data), &uncmp));
   624   }
   625 }
   627 // Helper routines to construct arbitrary compressed strings.
   628 // These mirror the compression code in snappy.cc, but are copied
   629 // here so that we can bypass some limitations in the how snappy.cc
   630 // invokes these routines.
   631 static void AppendLiteral(string* dst, const string& literal) {
   632   if (literal.empty()) return;
   633   int n = literal.size() - 1;
   634   if (n < 60) {
   635     // Fit length in tag byte
   636     dst->push_back(0 | (n << 2));
   637   } else {
   638     // Encode in upcoming bytes
   639     char number[4];
   640     int count = 0;
   641     while (n > 0) {
   642       number[count++] = n & 0xff;
   643       n >>= 8;
   644     }
   645     dst->push_back(0 | ((59+count) << 2));
   646     *dst += string(number, count);
   647   }
   648   *dst += literal;
   649 }
   651 static void AppendCopy(string* dst, int offset, int length) {
   652   while (length > 0) {
   653     // Figure out how much to copy in one shot
   654     int to_copy;
   655     if (length >= 68) {
   656       to_copy = 64;
   657     } else if (length > 64) {
   658       to_copy = 60;
   659     } else {
   660       to_copy = length;
   661     }
   662     length -= to_copy;
   664     if ((to_copy < 12) && (offset < 2048)) {
   665       assert(to_copy-4 < 8);            // Must fit in 3 bits
   666       dst->push_back(1 | ((to_copy-4) << 2) | ((offset >> 8) << 5));
   667       dst->push_back(offset & 0xff);
   668     } else if (offset < 65536) {
   669       dst->push_back(2 | ((to_copy-1) << 2));
   670       dst->push_back(offset & 0xff);
   671       dst->push_back(offset >> 8);
   672     } else {
   673       dst->push_back(3 | ((to_copy-1) << 2));
   674       dst->push_back(offset & 0xff);
   675       dst->push_back((offset >> 8) & 0xff);
   676       dst->push_back((offset >> 16) & 0xff);
   677       dst->push_back((offset >> 24) & 0xff);
   678     }
   679   }
   680 }
   682 TEST(Snappy, SimpleTests) {
   683   Verify("");
   684   Verify("a");
   685   Verify("ab");
   686   Verify("abc");
   688   Verify("aaaaaaa" + string(16, 'b') + string("aaaaa") + "abc");
   689   Verify("aaaaaaa" + string(256, 'b') + string("aaaaa") + "abc");
   690   Verify("aaaaaaa" + string(2047, 'b') + string("aaaaa") + "abc");
   691   Verify("aaaaaaa" + string(65536, 'b') + string("aaaaa") + "abc");
   692   Verify("abcaaaaaaa" + string(65536, 'b') + string("aaaaa") + "abc");
   693 }
   695 // Verify max blowup (lots of four-byte copies)
   696 TEST(Snappy, MaxBlowup) {
   697   string input;
   698   for (int i = 0; i < 20000; i++) {
   699     ACMRandom rnd(i);
   700     uint32 bytes = static_cast<uint32>(rnd.Next());
   701     input.append(reinterpret_cast<char*>(&bytes), sizeof(bytes));
   702   }
   703   for (int i = 19999; i >= 0; i--) {
   704     ACMRandom rnd(i);
   705     uint32 bytes = static_cast<uint32>(rnd.Next());
   706     input.append(reinterpret_cast<char*>(&bytes), sizeof(bytes));
   707   }
   708   Verify(input);
   709 }
   711 TEST(Snappy, RandomData) {
   712   ACMRandom rnd(FLAGS_test_random_seed);
   714   const int num_ops = 20000;
   715   for (int i = 0; i < num_ops; i++) {
   716     if ((i % 1000) == 0) {
   717       VLOG(0) << "Random op " << i << " of " << num_ops;
   718     }
   720     string x;
   721     int len = rnd.Uniform(4096);
   722     if (i < 100) {
   723       len = 65536 + rnd.Uniform(65536);
   724     }
   725     while (x.size() < len) {
   726       int run_len = 1;
   727       if (rnd.OneIn(10)) {
   728         run_len = rnd.Skewed(8);
   729       }
   730       char c = (i < 100) ? rnd.Uniform(256) : rnd.Skewed(3);
   731       while (run_len-- > 0 && x.size() < len) {
   732         x += c;
   733       }
   734     }
   736     Verify(x);
   737   }
   738 }
   740 TEST(Snappy, FourByteOffset) {
   741   // The new compressor cannot generate four-byte offsets since
   742   // it chops up the input into 32KB pieces.  So we hand-emit the
   743   // copy manually.
   745   // The two fragments that make up the input string.
   746   string fragment1 = "012345689abcdefghijklmnopqrstuvwxyz";
   747   string fragment2 = "some other string";
   749   // How many times each fragment is emitted.
   750   const int n1 = 2;
   751   const int n2 = 100000 / fragment2.size();
   752   const int length = n1 * fragment1.size() + n2 * fragment2.size();
   754   string compressed;
   755   Varint::Append32(&compressed, length);
   757   AppendLiteral(&compressed, fragment1);
   758   string src = fragment1;
   759   for (int i = 0; i < n2; i++) {
   760     AppendLiteral(&compressed, fragment2);
   761     src += fragment2;
   762   }
   763   AppendCopy(&compressed, src.size(), fragment1.size());
   764   src += fragment1;
   765   CHECK_EQ(length, src.size());
   767   string uncompressed;
   768   CHECK(snappy::IsValidCompressedBuffer(compressed.data(), compressed.size()));
   769   CHECK(snappy::Uncompress(compressed.data(), compressed.size(), &uncompressed));
   770   CHECK_EQ(uncompressed, src);
   771 }
   774 static bool CheckUncompressedLength(const string& compressed,
   775                                     size_t* ulength) {
   776   const bool result1 = snappy::GetUncompressedLength(compressed.data(),
   777                                                      compressed.size(),
   778                                                      ulength);
   780   snappy::ByteArraySource source(compressed.data(), compressed.size());
   781   uint32 length;
   782   const bool result2 = snappy::GetUncompressedLength(&source, &length);
   783   CHECK_EQ(result1, result2);
   784   return result1;
   785 }
   787 TEST(SnappyCorruption, TruncatedVarint) {
   788   string compressed, uncompressed;
   789   size_t ulength;
   790   compressed.push_back('\xf0');
   791   CHECK(!CheckUncompressedLength(compressed, &ulength));
   792   CHECK(!snappy::IsValidCompressedBuffer(compressed.data(), compressed.size()));
   793   CHECK(!snappy::Uncompress(compressed.data(), compressed.size(),
   794                             &uncompressed));
   795 }
   797 TEST(SnappyCorruption, UnterminatedVarint) {
   798   string compressed, uncompressed;
   799   size_t ulength;
   800   compressed.push_back(128);
   801   compressed.push_back(128);
   802   compressed.push_back(128);
   803   compressed.push_back(128);
   804   compressed.push_back(128);
   805   compressed.push_back(10);
   806   CHECK(!CheckUncompressedLength(compressed, &ulength));
   807   CHECK(!snappy::IsValidCompressedBuffer(compressed.data(), compressed.size()));
   808   CHECK(!snappy::Uncompress(compressed.data(), compressed.size(),
   809                             &uncompressed));
   810 }
   812 TEST(Snappy, ReadPastEndOfBuffer) {
   813   // Check that we do not read past end of input
   815   // Make a compressed string that ends with a single-byte literal
   816   string compressed;
   817   Varint::Append32(&compressed, 1);
   818   AppendLiteral(&compressed, "x");
   820   string uncompressed;
   821   DataEndingAtUnreadablePage c(compressed);
   822   CHECK(snappy::Uncompress(c.data(), c.size(), &uncompressed));
   823   CHECK_EQ(uncompressed, string("x"));
   824 }
   826 // Check for an infinite loop caused by a copy with offset==0
   827 TEST(Snappy, ZeroOffsetCopy) {
   828   const char* compressed = "\x40\x12\x00\x00";
   829   //  \x40              Length (must be > kMaxIncrementCopyOverflow)
   830   //  \x12\x00\x00      Copy with offset==0, length==5
   831   char uncompressed[100];
   832   EXPECT_FALSE(snappy::RawUncompress(compressed, 4, uncompressed));
   833 }
   835 TEST(Snappy, ZeroOffsetCopyValidation) {
   836   const char* compressed = "\x05\x12\x00\x00";
   837   //  \x05              Length
   838   //  \x12\x00\x00      Copy with offset==0, length==5
   839   EXPECT_FALSE(snappy::IsValidCompressedBuffer(compressed, 4));
   840 }
   843 namespace {
   845 int TestFindMatchLength(const char* s1, const char *s2, unsigned length) {
   846   return snappy::internal::FindMatchLength(s1, s2, s2 + length);
   847 }
   849 }  // namespace
   851 TEST(Snappy, FindMatchLength) {
   852   // Exercise all different code paths through the function.
   853   // 64-bit version:
   855   // Hit s1_limit in 64-bit loop, hit s1_limit in single-character loop.
   856   EXPECT_EQ(6, TestFindMatchLength("012345", "012345", 6));
   857   EXPECT_EQ(11, TestFindMatchLength("01234567abc", "01234567abc", 11));
   859   // Hit s1_limit in 64-bit loop, find a non-match in single-character loop.
   860   EXPECT_EQ(9, TestFindMatchLength("01234567abc", "01234567axc", 9));
   862   // Same, but edge cases.
   863   EXPECT_EQ(11, TestFindMatchLength("01234567abc!", "01234567abc!", 11));
   864   EXPECT_EQ(11, TestFindMatchLength("01234567abc!", "01234567abc?", 11));
   866   // Find non-match at once in first loop.
   867   EXPECT_EQ(0, TestFindMatchLength("01234567xxxxxxxx", "?1234567xxxxxxxx", 16));
   868   EXPECT_EQ(1, TestFindMatchLength("01234567xxxxxxxx", "0?234567xxxxxxxx", 16));
   869   EXPECT_EQ(4, TestFindMatchLength("01234567xxxxxxxx", "01237654xxxxxxxx", 16));
   870   EXPECT_EQ(7, TestFindMatchLength("01234567xxxxxxxx", "0123456?xxxxxxxx", 16));
   872   // Find non-match in first loop after one block.
   873   EXPECT_EQ(8, TestFindMatchLength("abcdefgh01234567xxxxxxxx",
   874                                    "abcdefgh?1234567xxxxxxxx", 24));
   875   EXPECT_EQ(9, TestFindMatchLength("abcdefgh01234567xxxxxxxx",
   876                                    "abcdefgh0?234567xxxxxxxx", 24));
   877   EXPECT_EQ(12, TestFindMatchLength("abcdefgh01234567xxxxxxxx",
   878                                     "abcdefgh01237654xxxxxxxx", 24));
   879   EXPECT_EQ(15, TestFindMatchLength("abcdefgh01234567xxxxxxxx",
   880                                     "abcdefgh0123456?xxxxxxxx", 24));
   882   // 32-bit version:
   884   // Short matches.
   885   EXPECT_EQ(0, TestFindMatchLength("01234567", "?1234567", 8));
   886   EXPECT_EQ(1, TestFindMatchLength("01234567", "0?234567", 8));
   887   EXPECT_EQ(2, TestFindMatchLength("01234567", "01?34567", 8));
   888   EXPECT_EQ(3, TestFindMatchLength("01234567", "012?4567", 8));
   889   EXPECT_EQ(4, TestFindMatchLength("01234567", "0123?567", 8));
   890   EXPECT_EQ(5, TestFindMatchLength("01234567", "01234?67", 8));
   891   EXPECT_EQ(6, TestFindMatchLength("01234567", "012345?7", 8));
   892   EXPECT_EQ(7, TestFindMatchLength("01234567", "0123456?", 8));
   893   EXPECT_EQ(7, TestFindMatchLength("01234567", "0123456?", 7));
   894   EXPECT_EQ(7, TestFindMatchLength("01234567!", "0123456??", 7));
   896   // Hit s1_limit in 32-bit loop, hit s1_limit in single-character loop.
   897   EXPECT_EQ(10, TestFindMatchLength("xxxxxxabcd", "xxxxxxabcd", 10));
   898   EXPECT_EQ(10, TestFindMatchLength("xxxxxxabcd?", "xxxxxxabcd?", 10));
   899   EXPECT_EQ(13, TestFindMatchLength("xxxxxxabcdef", "xxxxxxabcdef", 13));
   901   // Same, but edge cases.
   902   EXPECT_EQ(12, TestFindMatchLength("xxxxxx0123abc!", "xxxxxx0123abc!", 12));
   903   EXPECT_EQ(12, TestFindMatchLength("xxxxxx0123abc!", "xxxxxx0123abc?", 12));
   905   // Hit s1_limit in 32-bit loop, find a non-match in single-character loop.
   906   EXPECT_EQ(11, TestFindMatchLength("xxxxxx0123abc", "xxxxxx0123axc", 13));
   908   // Find non-match at once in first loop.
   909   EXPECT_EQ(6, TestFindMatchLength("xxxxxx0123xxxxxxxx",
   910                                    "xxxxxx?123xxxxxxxx", 18));
   911   EXPECT_EQ(7, TestFindMatchLength("xxxxxx0123xxxxxxxx",
   912                                    "xxxxxx0?23xxxxxxxx", 18));
   913   EXPECT_EQ(8, TestFindMatchLength("xxxxxx0123xxxxxxxx",
   914                                    "xxxxxx0132xxxxxxxx", 18));
   915   EXPECT_EQ(9, TestFindMatchLength("xxxxxx0123xxxxxxxx",
   916                                    "xxxxxx012?xxxxxxxx", 18));
   918   // Same, but edge cases.
   919   EXPECT_EQ(6, TestFindMatchLength("xxxxxx0123", "xxxxxx?123", 10));
   920   EXPECT_EQ(7, TestFindMatchLength("xxxxxx0123", "xxxxxx0?23", 10));
   921   EXPECT_EQ(8, TestFindMatchLength("xxxxxx0123", "xxxxxx0132", 10));
   922   EXPECT_EQ(9, TestFindMatchLength("xxxxxx0123", "xxxxxx012?", 10));
   924   // Find non-match in first loop after one block.
   925   EXPECT_EQ(10, TestFindMatchLength("xxxxxxabcd0123xx",
   926                                     "xxxxxxabcd?123xx", 16));
   927   EXPECT_EQ(11, TestFindMatchLength("xxxxxxabcd0123xx",
   928                                     "xxxxxxabcd0?23xx", 16));
   929   EXPECT_EQ(12, TestFindMatchLength("xxxxxxabcd0123xx",
   930                                     "xxxxxxabcd0132xx", 16));
   931   EXPECT_EQ(13, TestFindMatchLength("xxxxxxabcd0123xx",
   932                                     "xxxxxxabcd012?xx", 16));
   934   // Same, but edge cases.
   935   EXPECT_EQ(10, TestFindMatchLength("xxxxxxabcd0123", "xxxxxxabcd?123", 14));
   936   EXPECT_EQ(11, TestFindMatchLength("xxxxxxabcd0123", "xxxxxxabcd0?23", 14));
   937   EXPECT_EQ(12, TestFindMatchLength("xxxxxxabcd0123", "xxxxxxabcd0132", 14));
   938   EXPECT_EQ(13, TestFindMatchLength("xxxxxxabcd0123", "xxxxxxabcd012?", 14));
   939 }
   941 TEST(Snappy, FindMatchLengthRandom) {
   942   const int kNumTrials = 10000;
   943   const int kTypicalLength = 10;
   944   ACMRandom rnd(FLAGS_test_random_seed);
   946   for (int i = 0; i < kNumTrials; i++) {
   947     string s, t;
   948     char a = rnd.Rand8();
   949     char b = rnd.Rand8();
   950     while (!rnd.OneIn(kTypicalLength)) {
   951       s.push_back(rnd.OneIn(2) ? a : b);
   952       t.push_back(rnd.OneIn(2) ? a : b);
   953     }
   954     DataEndingAtUnreadablePage u(s);
   955     DataEndingAtUnreadablePage v(t);
   956     int matched = snappy::internal::FindMatchLength(
   957         u.data(), v.data(), v.data() + t.size());
   958     if (matched == t.size()) {
   959       EXPECT_EQ(s, t);
   960     } else {
   961       EXPECT_NE(s[matched], t[matched]);
   962       for (int j = 0; j < matched; j++) {
   963         EXPECT_EQ(s[j], t[j]);
   964       }
   965     }
   966   }
   967 }
   970 static void CompressFile(const char* fname) {
   971   string fullinput;
   972   File::ReadFileToStringOrDie(fname, &fullinput);
   974   string compressed;
   975   Compress(fullinput.data(), fullinput.size(), SNAPPY, &compressed, false);
   977   File::WriteStringToFileOrDie(compressed,
   978                                string(fname).append(".comp").c_str());
   979 }
   981 static void UncompressFile(const char* fname) {
   982   string fullinput;
   983   File::ReadFileToStringOrDie(fname, &fullinput);
   985   size_t uncompLength;
   986   CHECK(CheckUncompressedLength(fullinput, &uncompLength));
   988   string uncompressed;
   989   uncompressed.resize(uncompLength);
   990   CHECK(snappy::Uncompress(fullinput.data(), fullinput.size(), &uncompressed));
   992   File::WriteStringToFileOrDie(uncompressed,
   993                                string(fname).append(".uncomp").c_str());
   994 }
   996 static void MeasureFile(const char* fname) {
   997   string fullinput;
   998   File::ReadFileToStringOrDie(fname, &fullinput);
   999   printf("%-40s :\n", fname);
  1001   int start_len = (FLAGS_start_len < 0) ? fullinput.size() : FLAGS_start_len;
  1002   int end_len = fullinput.size();
  1003   if (FLAGS_end_len >= 0) {
  1004     end_len = min<int>(fullinput.size(), FLAGS_end_len);
  1006   for (int len = start_len; len <= end_len; len++) {
  1007     const char* const input = fullinput.data();
  1008     int repeats = (FLAGS_bytes + len) / (len + 1);
  1009     if (FLAGS_zlib)     Measure(input, len, ZLIB, repeats, 1024<<10);
  1010     if (FLAGS_lzo)      Measure(input, len, LZO, repeats, 1024<<10);
  1011     if (FLAGS_liblzf)   Measure(input, len, LIBLZF, repeats, 1024<<10);
  1012     if (FLAGS_quicklz)  Measure(input, len, QUICKLZ, repeats, 1024<<10);
  1013     if (FLAGS_fastlz)   Measure(input, len, FASTLZ, repeats, 1024<<10);
  1014     if (FLAGS_snappy)    Measure(input, len, SNAPPY, repeats, 4096<<10);
  1016     // For block-size based measurements
  1017     if (0 && FLAGS_snappy) {
  1018       Measure(input, len, SNAPPY, repeats, 8<<10);
  1019       Measure(input, len, SNAPPY, repeats, 16<<10);
  1020       Measure(input, len, SNAPPY, repeats, 32<<10);
  1021       Measure(input, len, SNAPPY, repeats, 64<<10);
  1022       Measure(input, len, SNAPPY, repeats, 256<<10);
  1023       Measure(input, len, SNAPPY, repeats, 1024<<10);
  1028 static struct {
  1029   const char* label;
  1030   const char* filename;
  1031 } files[] = {
  1032   { "html", "html" },
  1033   { "urls", "urls.10K" },
  1034   { "jpg", "house.jpg" },
  1035   { "pdf", "mapreduce-osdi-1.pdf" },
  1036   { "html4", "html_x_4" },
  1037   { "cp", "cp.html" },
  1038   { "c", "fields.c" },
  1039   { "lsp", "grammar.lsp" },
  1040   { "xls", "kennedy.xls" },
  1041   { "txt1", "alice29.txt" },
  1042   { "txt2", "asyoulik.txt" },
  1043   { "txt3", "lcet10.txt" },
  1044   { "txt4", "plrabn12.txt" },
  1045   { "bin", "ptt5" },
  1046   { "sum", "sum" },
  1047   { "man", "xargs.1" },
  1048   { "pb", "geo.protodata" },
  1049   { "gaviota", "kppkn.gtb" },
  1050 };
  1052 static void BM_UFlat(int iters, int arg) {
  1053   StopBenchmarkTiming();
  1055   // Pick file to process based on "arg"
  1056   CHECK_GE(arg, 0);
  1057   CHECK_LT(arg, ARRAYSIZE(files));
  1058   string contents = ReadTestDataFile(files[arg].filename);
  1060   string zcontents;
  1061   snappy::Compress(contents.data(), contents.size(), &zcontents);
  1062   char* dst = new char[contents.size()];
  1064   SetBenchmarkBytesProcessed(static_cast<int64>(iters) *
  1065                              static_cast<int64>(contents.size()));
  1066   SetBenchmarkLabel(files[arg].label);
  1067   StartBenchmarkTiming();
  1068   while (iters-- > 0) {
  1069     CHECK(snappy::RawUncompress(zcontents.data(), zcontents.size(), dst));
  1071   StopBenchmarkTiming();
  1073   delete[] dst;
  1075 BENCHMARK(BM_UFlat)->DenseRange(0, 17);
  1077 static void BM_UValidate(int iters, int arg) {
  1078   StopBenchmarkTiming();
  1080   // Pick file to process based on "arg"
  1081   CHECK_GE(arg, 0);
  1082   CHECK_LT(arg, ARRAYSIZE(files));
  1083   string contents = ReadTestDataFile(files[arg].filename);
  1085   string zcontents;
  1086   snappy::Compress(contents.data(), contents.size(), &zcontents);
  1088   SetBenchmarkBytesProcessed(static_cast<int64>(iters) *
  1089                              static_cast<int64>(contents.size()));
  1090   SetBenchmarkLabel(files[arg].label);
  1091   StartBenchmarkTiming();
  1092   while (iters-- > 0) {
  1093     CHECK(snappy::IsValidCompressedBuffer(zcontents.data(), zcontents.size()));
  1095   StopBenchmarkTiming();
  1097 BENCHMARK(BM_UValidate)->DenseRange(0, 4);
  1100 static void BM_ZFlat(int iters, int arg) {
  1101   StopBenchmarkTiming();
  1103   // Pick file to process based on "arg"
  1104   CHECK_GE(arg, 0);
  1105   CHECK_LT(arg, ARRAYSIZE(files));
  1106   string contents = ReadTestDataFile(files[arg].filename);
  1108   char* dst = new char[snappy::MaxCompressedLength(contents.size())];
  1110   SetBenchmarkBytesProcessed(static_cast<int64>(iters) *
  1111                              static_cast<int64>(contents.size()));
  1112   StartBenchmarkTiming();
  1114   size_t zsize = 0;
  1115   while (iters-- > 0) {
  1116     snappy::RawCompress(contents.data(), contents.size(), dst, &zsize);
  1118   StopBenchmarkTiming();
  1119   const double compression_ratio =
  1120       static_cast<double>(zsize) / std::max<size_t>(1, contents.size());
  1121   SetBenchmarkLabel(StringPrintf("%s (%.2f %%)",
  1122                                  files[arg].label, 100.0 * compression_ratio));
  1123   VLOG(0) << StringPrintf("compression for %s: %zd -> %zd bytes",
  1124                           files[arg].label, contents.size(), zsize);
  1125   delete[] dst;
  1127 BENCHMARK(BM_ZFlat)->DenseRange(0, 17);
  1130 }  // namespace snappy
  1133 int main(int argc, char** argv) {
  1134   InitGoogle(argv[0], &argc, &argv, true);
  1135   File::Init();
  1136   RunSpecifiedBenchmarks();
  1139   if (argc >= 2) {
  1140     for (int arg = 1; arg < argc; arg++) {
  1141       if (FLAGS_write_compressed) {
  1142         CompressFile(argv[arg]);
  1143       } else if (FLAGS_write_uncompressed) {
  1144         UncompressFile(argv[arg]);
  1145       } else {
  1146         MeasureFile(argv[arg]);
  1149     return 0;
  1152   return RUN_ALL_TESTS();

mercurial