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.
28 
29 #include <math.h>
30 #include <stdlib.h>
31 
32 
33 #include <algorithm>
34 #include <string>
35 #include <vector>
36 
37 #include "snappy.h"
38 #include "snappy-internal.h"
39 #include "snappy-test.h"
40 #include "snappy-sinksource.h"
41 
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");
48 
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");
61 
62 DEFINE_bool(write_compressed, false,
63             "Write compressed versions of each file to <file>.comp");
64 DEFINE_bool(write_uncompressed, false,
65             "Write uncompressed versions of each file to <file>.uncomp");
66 
67 namespace snappy {
68 
69 
70 #ifdef HAVE_FUNC_MMAP
71 
72 // To test against code that reads beyond its input, this class copies a
73 // string to a newly allocated group of pages, the last of which
74 // is made unreadable via mprotect. Note that we need to allocate the
75 // memory with mmap(), as POSIX allows mprotect() only on memory allocated
76 // with mmap(), and some malloc/posix_memalign implementations expect to
77 // be able to read previously allocated memory while doing heap allocations.
78 class DataEndingAtUnreadablePage {
79  public:
DataEndingAtUnreadablePage(const string & s)80   explicit DataEndingAtUnreadablePage(const string& s) {
81     const size_t page_size = getpagesize();
82     const size_t size = s.size();
83     // Round up space for string to a multiple of page_size.
84     size_t space_for_string = (size + page_size - 1) & ~(page_size - 1);
85     alloc_size_ = space_for_string + page_size;
86     mem_ = mmap(NULL, alloc_size_,
87                 PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
88     CHECK_NE(MAP_FAILED, mem_);
89     protected_page_ = reinterpret_cast<char*>(mem_) + space_for_string;
90     char* dst = protected_page_ - size;
91     memcpy(dst, s.data(), size);
92     data_ = dst;
93     size_ = size;
94     // Make guard page unreadable.
95     CHECK_EQ(0, mprotect(protected_page_, page_size, PROT_NONE));
96   }
97 
~DataEndingAtUnreadablePage()98   ~DataEndingAtUnreadablePage() {
99     // Undo the mprotect.
100     CHECK_EQ(0, mprotect(protected_page_, getpagesize(), PROT_READ|PROT_WRITE));
101     CHECK_EQ(0, munmap(mem_, alloc_size_));
102   }
103 
data() const104   const char* data() const { return data_; }
size() const105   size_t size() const { return size_; }
106 
107  private:
108   size_t alloc_size_;
109   void* mem_;
110   char* protected_page_;
111   const char* data_;
112   size_t size_;
113 };
114 
115 #else  // HAVE_FUNC_MMAP
116 
117 // Fallback for systems without mmap.
118 typedef string DataEndingAtUnreadablePage;
119 
120 #endif
121 
122 enum CompressorType {
123   ZLIB, LZO, LIBLZF, QUICKLZ, FASTLZ, SNAPPY
124 };
125 
126 const char* names[] = {
127   "ZLIB", "LZO", "LIBLZF", "QUICKLZ", "FASTLZ", "SNAPPY"
128 };
129 
MinimumRequiredOutputSpace(size_t input_size,CompressorType comp)130 static size_t MinimumRequiredOutputSpace(size_t input_size,
131                                          CompressorType comp) {
132   switch (comp) {
133 #ifdef ZLIB_VERSION
134     case ZLIB:
135       return ZLib::MinCompressbufSize(input_size);
136 #endif  // ZLIB_VERSION
137 
138 #ifdef LZO_VERSION
139     case LZO:
140       return input_size + input_size/64 + 16 + 3;
141 #endif  // LZO_VERSION
142 
143 #ifdef LZF_VERSION
144     case LIBLZF:
145       return input_size;
146 #endif  // LZF_VERSION
147 
148 #ifdef QLZ_VERSION_MAJOR
149     case QUICKLZ:
150       return input_size + 36000;  // 36000 is used for scratch.
151 #endif  // QLZ_VERSION_MAJOR
152 
153 #ifdef FASTLZ_VERSION
154     case FASTLZ:
155       return max(static_cast<int>(ceil(input_size * 1.05)), 66);
156 #endif  // FASTLZ_VERSION
157 
158     case SNAPPY:
159       return snappy::MaxCompressedLength(input_size);
160 
161     default:
162       LOG(FATAL) << "Unknown compression type number " << comp;
163       return 0;
164   }
165 }
166 
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.
Compress(const char * input,size_t input_size,CompressorType comp,string * compressed,bool compressed_is_preallocated)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   }
179 
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
197 
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
216 
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
236 
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
252 
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
268 
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     }
280 
281     default: {
282       return false;     // the asked-for library wasn't compiled in
283     }
284   }
285   return true;
286 }
287 
Uncompress(const string & compressed,CompressorType comp,int size,string * output)288 static bool Uncompress(const string& compressed, CompressorType comp,
289                        int size, string* output) {
290   switch (comp) {
291 #ifdef ZLIB_VERSION
292     case ZLIB: {
293       output->resize(size);
294       ZLib zlib;
295       uLongf destlen = output->size();
296       int ret = zlib.Uncompress(
297           reinterpret_cast<Bytef*>(string_as_array(output)),
298           &destlen,
299           reinterpret_cast<const Bytef*>(compressed.data()),
300           compressed.size());
301       CHECK_EQ(Z_OK, ret);
302       CHECK_EQ(static_cast<uLongf>(size), destlen);
303       break;
304     }
305 #endif  // ZLIB_VERSION
306 
307 #ifdef LZO_VERSION
308     case LZO: {
309       output->resize(size);
310       lzo_uint destlen;
311       int ret = lzo1x_decompress(
312           reinterpret_cast<const uint8*>(compressed.data()),
313           compressed.size(),
314           reinterpret_cast<uint8*>(string_as_array(output)),
315           &destlen,
316           NULL);
317       CHECK_EQ(LZO_E_OK, ret);
318       CHECK_EQ(static_cast<lzo_uint>(size), destlen);
319       break;
320     }
321 #endif  // LZO_VERSION
322 
323 #ifdef LZF_VERSION
324     case LIBLZF: {
325       output->resize(size);
326       int destlen = lzf_decompress(compressed.data(),
327                                    compressed.size(),
328                                    string_as_array(output),
329                                    output->size());
330       if (destlen == 0) {
331         // This error probably means we had decided not to compress,
332         // and thus have stored input in output directly.
333         output->assign(compressed.data(), compressed.size());
334         destlen = compressed.size();
335       }
336       CHECK_EQ(destlen, size);
337       break;
338     }
339 #endif  // LZF_VERSION
340 
341 #ifdef QLZ_VERSION_MAJOR
342     case QUICKLZ: {
343       output->resize(size);
344       qlz_state_decompress *state_decompress = new qlz_state_decompress;
345       int destlen = qlz_decompress(compressed.data(),
346                                    string_as_array(output),
347                                    state_decompress);
348       delete state_decompress;
349       CHECK_EQ(destlen, size);
350       break;
351     }
352 #endif  // QLZ_VERSION_MAJOR
353 
354 #ifdef FASTLZ_VERSION
355     case FASTLZ: {
356       output->resize(size);
357       int destlen = fastlz_decompress(compressed.data(),
358                                       compressed.length(),
359                                       string_as_array(output),
360                                       size);
361       CHECK_EQ(destlen, size);
362       break;
363     }
364 #endif  // FASTLZ_VERSION
365 
366     case SNAPPY: {
367       snappy::RawUncompress(compressed.data(), compressed.size(),
368                             string_as_array(output));
369       break;
370     }
371 
372     default: {
373       return false;     // the asked-for library wasn't compiled in
374     }
375   }
376   return true;
377 }
378 
Measure(const char * data,size_t length,CompressorType comp,int repeats,int block_size)379 static void Measure(const char* data,
380                     size_t length,
381                     CompressorType comp,
382                     int repeats,
383                     int block_size) {
384   // Run tests a few time and pick median running times
385   static const int kRuns = 5;
386   double ctime[kRuns];
387   double utime[kRuns];
388   int compressed_size = 0;
389 
390   {
391     // Chop the input into blocks
392     int num_blocks = (length + block_size - 1) / block_size;
393     vector<const char*> input(num_blocks);
394     vector<size_t> input_length(num_blocks);
395     vector<string> compressed(num_blocks);
396     vector<string> output(num_blocks);
397     for (int b = 0; b < num_blocks; b++) {
398       int input_start = b * block_size;
399       int input_limit = min<int>((b+1)*block_size, length);
400       input[b] = data+input_start;
401       input_length[b] = input_limit-input_start;
402 
403       // Pre-grow the output buffer so we don't measure string append time.
404       compressed[b].resize(MinimumRequiredOutputSpace(block_size, comp));
405     }
406 
407     // First, try one trial compression to make sure the code is compiled in
408     if (!Compress(input[0], input_length[0], comp, &compressed[0], true)) {
409       LOG(WARNING) << "Skipping " << names[comp] << ": "
410                    << "library not compiled in";
411       return;
412     }
413 
414     for (int run = 0; run < kRuns; run++) {
415       CycleTimer ctimer, utimer;
416 
417       for (int b = 0; b < num_blocks; b++) {
418         // Pre-grow the output buffer so we don't measure string append time.
419         compressed[b].resize(MinimumRequiredOutputSpace(block_size, comp));
420       }
421 
422       ctimer.Start();
423       for (int b = 0; b < num_blocks; b++)
424         for (int i = 0; i < repeats; i++)
425           Compress(input[b], input_length[b], comp, &compressed[b], true);
426       ctimer.Stop();
427 
428       // Compress once more, with resizing, so we don't leave junk
429       // at the end that will confuse the decompressor.
430       for (int b = 0; b < num_blocks; b++) {
431         Compress(input[b], input_length[b], comp, &compressed[b], false);
432       }
433 
434       for (int b = 0; b < num_blocks; b++) {
435         output[b].resize(input_length[b]);
436       }
437 
438       utimer.Start();
439       for (int i = 0; i < repeats; i++)
440         for (int b = 0; b < num_blocks; b++)
441           Uncompress(compressed[b], comp, input_length[b], &output[b]);
442       utimer.Stop();
443 
444       ctime[run] = ctimer.Get();
445       utime[run] = utimer.Get();
446     }
447 
448     compressed_size = 0;
449     for (size_t i = 0; i < compressed.size(); i++) {
450       compressed_size += compressed[i].size();
451     }
452   }
453 
454   sort(ctime, ctime + kRuns);
455   sort(utime, utime + kRuns);
456   const int med = kRuns/2;
457 
458   float comp_rate = (length / ctime[med]) * repeats / 1048576.0;
459   float uncomp_rate = (length / utime[med]) * repeats / 1048576.0;
460   string x = names[comp];
461   x += ":";
462   string urate = (uncomp_rate >= 0)
463                  ? StringPrintf("%.1f", uncomp_rate)
464                  : string("?");
465   printf("%-7s [b %dM] bytes %6d -> %6d %4.1f%%  "
466          "comp %5.1f MB/s  uncomp %5s MB/s\n",
467          x.c_str(),
468          block_size/(1<<20),
469          static_cast<int>(length), static_cast<uint32>(compressed_size),
470          (compressed_size * 100.0) / max<int>(1, length),
471          comp_rate,
472          urate.c_str());
473 }
474 
VerifyString(const string & input)475 static int VerifyString(const string& input) {
476   string compressed;
477   DataEndingAtUnreadablePage i(input);
478   const size_t written = snappy::Compress(i.data(), i.size(), &compressed);
479   CHECK_EQ(written, compressed.size());
480   CHECK_LE(compressed.size(),
481            snappy::MaxCompressedLength(input.size()));
482   CHECK(snappy::IsValidCompressedBuffer(compressed.data(), compressed.size()));
483 
484   string uncompressed;
485   DataEndingAtUnreadablePage c(compressed);
486   CHECK(snappy::Uncompress(c.data(), c.size(), &uncompressed));
487   CHECK_EQ(uncompressed, input);
488   return uncompressed.size();
489 }
490 
VerifyStringSink(const string & input)491 static void VerifyStringSink(const string& input) {
492   string compressed;
493   DataEndingAtUnreadablePage i(input);
494   const size_t written = snappy::Compress(i.data(), i.size(), &compressed);
495   CHECK_EQ(written, compressed.size());
496   CHECK_LE(compressed.size(),
497            snappy::MaxCompressedLength(input.size()));
498   CHECK(snappy::IsValidCompressedBuffer(compressed.data(), compressed.size()));
499 
500   string uncompressed;
501   uncompressed.resize(input.size());
502   snappy::UncheckedByteArraySink sink(string_as_array(&uncompressed));
503   DataEndingAtUnreadablePage c(compressed);
504   snappy::ByteArraySource source(c.data(), c.size());
505   CHECK(snappy::Uncompress(&source, &sink));
506   CHECK_EQ(uncompressed, input);
507 }
508 
VerifyIOVec(const string & input)509 static void VerifyIOVec(const string& input) {
510   string compressed;
511   DataEndingAtUnreadablePage i(input);
512   const size_t written = snappy::Compress(i.data(), i.size(), &compressed);
513   CHECK_EQ(written, compressed.size());
514   CHECK_LE(compressed.size(),
515            snappy::MaxCompressedLength(input.size()));
516   CHECK(snappy::IsValidCompressedBuffer(compressed.data(), compressed.size()));
517 
518   // Try uncompressing into an iovec containing a random number of entries
519   // ranging from 1 to 10.
520   char* buf = new char[input.size()];
521   ACMRandom rnd(input.size());
522   size_t num = rnd.Next() % 10 + 1;
523   if (input.size() < num) {
524     num = input.size();
525   }
526   struct iovec* iov = new iovec[num];
527   int used_so_far = 0;
528   for (size_t i = 0; i < num; ++i) {
529     iov[i].iov_base = buf + used_so_far;
530     if (i == num - 1) {
531       iov[i].iov_len = input.size() - used_so_far;
532     } else {
533       // Randomly choose to insert a 0 byte entry.
534       if (rnd.OneIn(5)) {
535         iov[i].iov_len = 0;
536       } else {
537         iov[i].iov_len = rnd.Uniform(input.size());
538       }
539     }
540     used_so_far += iov[i].iov_len;
541   }
542   CHECK(snappy::RawUncompressToIOVec(
543       compressed.data(), compressed.size(), iov, num));
544   CHECK(!memcmp(buf, input.data(), input.size()));
545   delete[] iov;
546   delete[] buf;
547 }
548 
549 // Test that data compressed by a compressor that does not
550 // obey block sizes is uncompressed properly.
VerifyNonBlockedCompression(const string & input)551 static void VerifyNonBlockedCompression(const string& input) {
552   if (input.length() > snappy::kBlockSize) {
553     // We cannot test larger blocks than the maximum block size, obviously.
554     return;
555   }
556 
557   string prefix;
558   Varint::Append32(&prefix, input.size());
559 
560   // Setup compression table
561   snappy::internal::WorkingMemory wmem;
562   int table_size;
563   uint16* table = wmem.GetHashTable(input.size(), &table_size);
564 
565   // Compress entire input in one shot
566   string compressed;
567   compressed += prefix;
568   compressed.resize(prefix.size()+snappy::MaxCompressedLength(input.size()));
569   char* dest = string_as_array(&compressed) + prefix.size();
570   char* end = snappy::internal::CompressFragment(input.data(), input.size(),
571                                                 dest, table, table_size);
572   compressed.resize(end - compressed.data());
573 
574   // Uncompress into string
575   string uncomp_str;
576   CHECK(snappy::Uncompress(compressed.data(), compressed.size(), &uncomp_str));
577   CHECK_EQ(uncomp_str, input);
578 
579   // Uncompress using source/sink
580   string uncomp_str2;
581   uncomp_str2.resize(input.size());
582   snappy::UncheckedByteArraySink sink(string_as_array(&uncomp_str2));
583   snappy::ByteArraySource source(compressed.data(), compressed.size());
584   CHECK(snappy::Uncompress(&source, &sink));
585   CHECK_EQ(uncomp_str2, input);
586 
587   // Uncompress into iovec
588   {
589     static const int kNumBlocks = 10;
590     struct iovec vec[kNumBlocks];
591     const int block_size = 1 + input.size() / kNumBlocks;
592     string iovec_data(block_size * kNumBlocks, 'x');
593     for (int i = 0; i < kNumBlocks; i++) {
594       vec[i].iov_base = string_as_array(&iovec_data) + i * block_size;
595       vec[i].iov_len = block_size;
596     }
597     CHECK(snappy::RawUncompressToIOVec(compressed.data(), compressed.size(),
598                                        vec, kNumBlocks));
599     CHECK_EQ(string(iovec_data.data(), input.size()), input);
600   }
601 }
602 
603 // Expand the input so that it is at least K times as big as block size
Expand(const string & input)604 static string Expand(const string& input) {
605   static const int K = 3;
606   string data = input;
607   while (data.size() < K * snappy::kBlockSize) {
608     data += input;
609   }
610   return data;
611 }
612 
Verify(const string & input)613 static int Verify(const string& input) {
614   VLOG(1) << "Verifying input of size " << input.size();
615 
616   // Compress using string based routines
617   const int result = VerifyString(input);
618 
619   // Verify using sink based routines
620   VerifyStringSink(input);
621 
622   VerifyNonBlockedCompression(input);
623   VerifyIOVec(input);
624   if (!input.empty()) {
625     const string expanded = Expand(input);
626     VerifyNonBlockedCompression(expanded);
627     VerifyIOVec(input);
628   }
629 
630   return result;
631 }
632 
633 
IsValidCompressedBuffer(const string & c)634 static bool IsValidCompressedBuffer(const string& c) {
635   return snappy::IsValidCompressedBuffer(c.data(), c.size());
636 }
Uncompress(const string & c,string * u)637 static bool Uncompress(const string& c, string* u) {
638   return snappy::Uncompress(c.data(), c.size(), u);
639 }
640 
641 // This test checks to ensure that snappy doesn't coredump if it gets
642 // corrupted data.
TEST(CorruptedTest,VerifyCorrupted)643 TEST(CorruptedTest, VerifyCorrupted) {
644   string source = "making sure we don't crash with corrupted input";
645   VLOG(1) << source;
646   string dest;
647   string uncmp;
648   snappy::Compress(source.data(), source.size(), &dest);
649 
650   // Mess around with the data. It's hard to simulate all possible
651   // corruptions; this is just one example ...
652   CHECK_GT(dest.size(), 3);
653   dest[1]--;
654   dest[3]++;
655   // this really ought to fail.
656   CHECK(!IsValidCompressedBuffer(dest));
657   CHECK(!Uncompress(dest, &uncmp));
658 
659   // This is testing for a security bug - a buffer that decompresses to 100k
660   // but we lie in the snappy header and only reserve 0 bytes of memory :)
661   source.resize(100000);
662   for (size_t i = 0; i < source.length(); ++i) {
663     source[i] = 'A';
664   }
665   snappy::Compress(source.data(), source.size(), &dest);
666   dest[0] = dest[1] = dest[2] = dest[3] = 0;
667   CHECK(!IsValidCompressedBuffer(dest));
668   CHECK(!Uncompress(dest, &uncmp));
669 
670   if (sizeof(void *) == 4) {
671     // Another security check; check a crazy big length can't DoS us with an
672     // over-allocation.
673     // Currently this is done only for 32-bit builds.  On 64-bit builds,
674     // where 3 GB might be an acceptable allocation size, Uncompress()
675     // attempts to decompress, and sometimes causes the test to run out of
676     // memory.
677     dest[0] = dest[1] = dest[2] = dest[3] = '\xff';
678     // This decodes to a really large size, i.e., about 3 GB.
679     dest[4] = 'k';
680     CHECK(!IsValidCompressedBuffer(dest));
681     CHECK(!Uncompress(dest, &uncmp));
682   } else {
683     LOG(WARNING) << "Crazy decompression lengths not checked on 64-bit build";
684   }
685 
686   // This decodes to about 2 MB; much smaller, but should still fail.
687   dest[0] = dest[1] = dest[2] = '\xff';
688   dest[3] = 0x00;
689   CHECK(!IsValidCompressedBuffer(dest));
690   CHECK(!Uncompress(dest, &uncmp));
691 
692   // try reading stuff in from a bad file.
693   for (int i = 1; i <= 3; ++i) {
694     string data = ReadTestDataFile(StringPrintf("baddata%d.snappy", i).c_str(),
695                                    0);
696     string uncmp;
697     // check that we don't return a crazy length
698     size_t ulen;
699     CHECK(!snappy::GetUncompressedLength(data.data(), data.size(), &ulen)
700           || (ulen < (1<<20)));
701     uint32 ulen2;
702     snappy::ByteArraySource source(data.data(), data.size());
703     CHECK(!snappy::GetUncompressedLength(&source, &ulen2) ||
704           (ulen2 < (1<<20)));
705     CHECK(!IsValidCompressedBuffer(data));
706     CHECK(!Uncompress(data, &uncmp));
707   }
708 }
709 
710 // Helper routines to construct arbitrary compressed strings.
711 // These mirror the compression code in snappy.cc, but are copied
712 // here so that we can bypass some limitations in the how snappy.cc
713 // invokes these routines.
AppendLiteral(string * dst,const string & literal)714 static void AppendLiteral(string* dst, const string& literal) {
715   if (literal.empty()) return;
716   int n = literal.size() - 1;
717   if (n < 60) {
718     // Fit length in tag byte
719     dst->push_back(0 | (n << 2));
720   } else {
721     // Encode in upcoming bytes
722     char number[4];
723     int count = 0;
724     while (n > 0) {
725       number[count++] = n & 0xff;
726       n >>= 8;
727     }
728     dst->push_back(0 | ((59+count) << 2));
729     *dst += string(number, count);
730   }
731   *dst += literal;
732 }
733 
AppendCopy(string * dst,int offset,int length)734 static void AppendCopy(string* dst, int offset, int length) {
735   while (length > 0) {
736     // Figure out how much to copy in one shot
737     int to_copy;
738     if (length >= 68) {
739       to_copy = 64;
740     } else if (length > 64) {
741       to_copy = 60;
742     } else {
743       to_copy = length;
744     }
745     length -= to_copy;
746 
747     if ((to_copy >= 4) && (to_copy < 12) && (offset < 2048)) {
748       assert(to_copy-4 < 8);            // Must fit in 3 bits
749       dst->push_back(1 | ((to_copy-4) << 2) | ((offset >> 8) << 5));
750       dst->push_back(offset & 0xff);
751     } else if (offset < 65536) {
752       dst->push_back(2 | ((to_copy-1) << 2));
753       dst->push_back(offset & 0xff);
754       dst->push_back(offset >> 8);
755     } else {
756       dst->push_back(3 | ((to_copy-1) << 2));
757       dst->push_back(offset & 0xff);
758       dst->push_back((offset >> 8) & 0xff);
759       dst->push_back((offset >> 16) & 0xff);
760       dst->push_back((offset >> 24) & 0xff);
761     }
762   }
763 }
764 
TEST(Snappy,SimpleTests)765 TEST(Snappy, SimpleTests) {
766   Verify("");
767   Verify("a");
768   Verify("ab");
769   Verify("abc");
770 
771   Verify("aaaaaaa" + string(16, 'b') + string("aaaaa") + "abc");
772   Verify("aaaaaaa" + string(256, 'b') + string("aaaaa") + "abc");
773   Verify("aaaaaaa" + string(2047, 'b') + string("aaaaa") + "abc");
774   Verify("aaaaaaa" + string(65536, 'b') + string("aaaaa") + "abc");
775   Verify("abcaaaaaaa" + string(65536, 'b') + string("aaaaa") + "abc");
776 }
777 
778 // Verify max blowup (lots of four-byte copies)
TEST(Snappy,MaxBlowup)779 TEST(Snappy, MaxBlowup) {
780   string input;
781   for (int i = 0; i < 20000; i++) {
782     ACMRandom rnd(i);
783     uint32 bytes = static_cast<uint32>(rnd.Next());
784     input.append(reinterpret_cast<char*>(&bytes), sizeof(bytes));
785   }
786   for (int i = 19999; i >= 0; i--) {
787     ACMRandom rnd(i);
788     uint32 bytes = static_cast<uint32>(rnd.Next());
789     input.append(reinterpret_cast<char*>(&bytes), sizeof(bytes));
790   }
791   Verify(input);
792 }
793 
TEST(Snappy,RandomData)794 TEST(Snappy, RandomData) {
795   ACMRandom rnd(FLAGS_test_random_seed);
796 
797   const int num_ops = 20000;
798   for (int i = 0; i < num_ops; i++) {
799     if ((i % 1000) == 0) {
800       VLOG(0) << "Random op " << i << " of " << num_ops;
801     }
802 
803     string x;
804     size_t len = rnd.Uniform(4096);
805     if (i < 100) {
806       len = 65536 + rnd.Uniform(65536);
807     }
808     while (x.size() < len) {
809       int run_len = 1;
810       if (rnd.OneIn(10)) {
811         run_len = rnd.Skewed(8);
812       }
813       char c = (i < 100) ? rnd.Uniform(256) : rnd.Skewed(3);
814       while (run_len-- > 0 && x.size() < len) {
815         x += c;
816       }
817     }
818 
819     Verify(x);
820   }
821 }
822 
TEST(Snappy,FourByteOffset)823 TEST(Snappy, FourByteOffset) {
824   // The new compressor cannot generate four-byte offsets since
825   // it chops up the input into 32KB pieces.  So we hand-emit the
826   // copy manually.
827 
828   // The two fragments that make up the input string.
829   string fragment1 = "012345689abcdefghijklmnopqrstuvwxyz";
830   string fragment2 = "some other string";
831 
832   // How many times each fragment is emitted.
833   const int n1 = 2;
834   const int n2 = 100000 / fragment2.size();
835   const int length = n1 * fragment1.size() + n2 * fragment2.size();
836 
837   string compressed;
838   Varint::Append32(&compressed, length);
839 
840   AppendLiteral(&compressed, fragment1);
841   string src = fragment1;
842   for (int i = 0; i < n2; i++) {
843     AppendLiteral(&compressed, fragment2);
844     src += fragment2;
845   }
846   AppendCopy(&compressed, src.size(), fragment1.size());
847   src += fragment1;
848   CHECK_EQ(length, src.size());
849 
850   string uncompressed;
851   CHECK(snappy::IsValidCompressedBuffer(compressed.data(), compressed.size()));
852   CHECK(snappy::Uncompress(compressed.data(), compressed.size(),
853                            &uncompressed));
854   CHECK_EQ(uncompressed, src);
855 }
856 
TEST(Snappy,IOVecEdgeCases)857 TEST(Snappy, IOVecEdgeCases) {
858   // Test some tricky edge cases in the iovec output that are not necessarily
859   // exercised by random tests.
860 
861   // Our output blocks look like this initially (the last iovec is bigger
862   // than depicted):
863   // [  ] [ ] [    ] [        ] [        ]
864   static const int kLengths[] = { 2, 1, 4, 8, 128 };
865 
866   struct iovec iov[ARRAYSIZE(kLengths)];
867   for (int i = 0; i < ARRAYSIZE(kLengths); ++i) {
868     iov[i].iov_base = new char[kLengths[i]];
869     iov[i].iov_len = kLengths[i];
870   }
871 
872   string compressed;
873   Varint::Append32(&compressed, 22);
874 
875   // A literal whose output crosses three blocks.
876   // [ab] [c] [123 ] [        ] [        ]
877   AppendLiteral(&compressed, "abc123");
878 
879   // A copy whose output crosses two blocks (source and destination
880   // segments marked).
881   // [ab] [c] [1231] [23      ] [        ]
882   //           ^--^   --
883   AppendCopy(&compressed, 3, 3);
884 
885   // A copy where the input is, at first, in the block before the output:
886   //
887   // [ab] [c] [1231] [231231  ] [        ]
888   //           ^---     ^---
889   // Then during the copy, the pointers move such that the input and
890   // output pointers are in the same block:
891   //
892   // [ab] [c] [1231] [23123123] [        ]
893   //                  ^-    ^-
894   // And then they move again, so that the output pointer is no longer
895   // in the same block as the input pointer:
896   // [ab] [c] [1231] [23123123] [123     ]
897   //                    ^--      ^--
898   AppendCopy(&compressed, 6, 9);
899 
900   // Finally, a copy where the input is from several blocks back,
901   // and it also crosses three blocks:
902   //
903   // [ab] [c] [1231] [23123123] [123b    ]
904   //   ^                            ^
905   // [ab] [c] [1231] [23123123] [123bc   ]
906   //       ^                         ^
907   // [ab] [c] [1231] [23123123] [123bc12 ]
908   //           ^-                     ^-
909   AppendCopy(&compressed, 17, 4);
910 
911   CHECK(snappy::RawUncompressToIOVec(
912       compressed.data(), compressed.size(), iov, ARRAYSIZE(iov)));
913   CHECK_EQ(0, memcmp(iov[0].iov_base, "ab", 2));
914   CHECK_EQ(0, memcmp(iov[1].iov_base, "c", 1));
915   CHECK_EQ(0, memcmp(iov[2].iov_base, "1231", 4));
916   CHECK_EQ(0, memcmp(iov[3].iov_base, "23123123", 8));
917   CHECK_EQ(0, memcmp(iov[4].iov_base, "123bc12", 7));
918 
919   for (int i = 0; i < ARRAYSIZE(kLengths); ++i) {
920     delete[] reinterpret_cast<char *>(iov[i].iov_base);
921   }
922 }
923 
TEST(Snappy,IOVecLiteralOverflow)924 TEST(Snappy, IOVecLiteralOverflow) {
925   static const int kLengths[] = { 3, 4 };
926 
927   struct iovec iov[ARRAYSIZE(kLengths)];
928   for (int i = 0; i < ARRAYSIZE(kLengths); ++i) {
929     iov[i].iov_base = new char[kLengths[i]];
930     iov[i].iov_len = kLengths[i];
931   }
932 
933   string compressed;
934   Varint::Append32(&compressed, 8);
935 
936   AppendLiteral(&compressed, "12345678");
937 
938   CHECK(!snappy::RawUncompressToIOVec(
939       compressed.data(), compressed.size(), iov, ARRAYSIZE(iov)));
940 
941   for (int i = 0; i < ARRAYSIZE(kLengths); ++i) {
942     delete[] reinterpret_cast<char *>(iov[i].iov_base);
943   }
944 }
945 
TEST(Snappy,IOVecCopyOverflow)946 TEST(Snappy, IOVecCopyOverflow) {
947   static const int kLengths[] = { 3, 4 };
948 
949   struct iovec iov[ARRAYSIZE(kLengths)];
950   for (int i = 0; i < ARRAYSIZE(kLengths); ++i) {
951     iov[i].iov_base = new char[kLengths[i]];
952     iov[i].iov_len = kLengths[i];
953   }
954 
955   string compressed;
956   Varint::Append32(&compressed, 8);
957 
958   AppendLiteral(&compressed, "123");
959   AppendCopy(&compressed, 3, 5);
960 
961   CHECK(!snappy::RawUncompressToIOVec(
962       compressed.data(), compressed.size(), iov, ARRAYSIZE(iov)));
963 
964   for (int i = 0; i < ARRAYSIZE(kLengths); ++i) {
965     delete[] reinterpret_cast<char *>(iov[i].iov_base);
966   }
967 }
968 
CheckUncompressedLength(const string & compressed,size_t * ulength)969 static bool CheckUncompressedLength(const string& compressed,
970                                     size_t* ulength) {
971   const bool result1 = snappy::GetUncompressedLength(compressed.data(),
972                                                      compressed.size(),
973                                                      ulength);
974 
975   snappy::ByteArraySource source(compressed.data(), compressed.size());
976   uint32 length;
977   const bool result2 = snappy::GetUncompressedLength(&source, &length);
978   CHECK_EQ(result1, result2);
979   return result1;
980 }
981 
TEST(SnappyCorruption,TruncatedVarint)982 TEST(SnappyCorruption, TruncatedVarint) {
983   string compressed, uncompressed;
984   size_t ulength;
985   compressed.push_back('\xf0');
986   CHECK(!CheckUncompressedLength(compressed, &ulength));
987   CHECK(!snappy::IsValidCompressedBuffer(compressed.data(), compressed.size()));
988   CHECK(!snappy::Uncompress(compressed.data(), compressed.size(),
989                             &uncompressed));
990 }
991 
TEST(SnappyCorruption,UnterminatedVarint)992 TEST(SnappyCorruption, UnterminatedVarint) {
993   string compressed, uncompressed;
994   size_t ulength;
995   compressed.push_back('\x80');
996   compressed.push_back('\x80');
997   compressed.push_back('\x80');
998   compressed.push_back('\x80');
999   compressed.push_back('\x80');
1000   compressed.push_back(10);
1001   CHECK(!CheckUncompressedLength(compressed, &ulength));
1002   CHECK(!snappy::IsValidCompressedBuffer(compressed.data(), compressed.size()));
1003   CHECK(!snappy::Uncompress(compressed.data(), compressed.size(),
1004                             &uncompressed));
1005 }
1006 
TEST(Snappy,ReadPastEndOfBuffer)1007 TEST(Snappy, ReadPastEndOfBuffer) {
1008   // Check that we do not read past end of input
1009 
1010   // Make a compressed string that ends with a single-byte literal
1011   string compressed;
1012   Varint::Append32(&compressed, 1);
1013   AppendLiteral(&compressed, "x");
1014 
1015   string uncompressed;
1016   DataEndingAtUnreadablePage c(compressed);
1017   CHECK(snappy::Uncompress(c.data(), c.size(), &uncompressed));
1018   CHECK_EQ(uncompressed, string("x"));
1019 }
1020 
1021 // Check for an infinite loop caused by a copy with offset==0
TEST(Snappy,ZeroOffsetCopy)1022 TEST(Snappy, ZeroOffsetCopy) {
1023   const char* compressed = "\x40\x12\x00\x00";
1024   //  \x40              Length (must be > kMaxIncrementCopyOverflow)
1025   //  \x12\x00\x00      Copy with offset==0, length==5
1026   char uncompressed[100];
1027   EXPECT_FALSE(snappy::RawUncompress(compressed, 4, uncompressed));
1028 }
1029 
TEST(Snappy,ZeroOffsetCopyValidation)1030 TEST(Snappy, ZeroOffsetCopyValidation) {
1031   const char* compressed = "\x05\x12\x00\x00";
1032   //  \x05              Length
1033   //  \x12\x00\x00      Copy with offset==0, length==5
1034   EXPECT_FALSE(snappy::IsValidCompressedBuffer(compressed, 4));
1035 }
1036 
1037 namespace {
1038 
TestFindMatchLength(const char * s1,const char * s2,unsigned length)1039 int TestFindMatchLength(const char* s1, const char *s2, unsigned length) {
1040   return snappy::internal::FindMatchLength(s1, s2, s2 + length);
1041 }
1042 
1043 }  // namespace
1044 
TEST(Snappy,FindMatchLength)1045 TEST(Snappy, FindMatchLength) {
1046   // Exercise all different code paths through the function.
1047   // 64-bit version:
1048 
1049   // Hit s1_limit in 64-bit loop, hit s1_limit in single-character loop.
1050   EXPECT_EQ(6, TestFindMatchLength("012345", "012345", 6));
1051   EXPECT_EQ(11, TestFindMatchLength("01234567abc", "01234567abc", 11));
1052 
1053   // Hit s1_limit in 64-bit loop, find a non-match in single-character loop.
1054   EXPECT_EQ(9, TestFindMatchLength("01234567abc", "01234567axc", 9));
1055 
1056   // Same, but edge cases.
1057   EXPECT_EQ(11, TestFindMatchLength("01234567abc!", "01234567abc!", 11));
1058   EXPECT_EQ(11, TestFindMatchLength("01234567abc!", "01234567abc?", 11));
1059 
1060   // Find non-match at once in first loop.
1061   EXPECT_EQ(0, TestFindMatchLength("01234567xxxxxxxx", "?1234567xxxxxxxx", 16));
1062   EXPECT_EQ(1, TestFindMatchLength("01234567xxxxxxxx", "0?234567xxxxxxxx", 16));
1063   EXPECT_EQ(4, TestFindMatchLength("01234567xxxxxxxx", "01237654xxxxxxxx", 16));
1064   EXPECT_EQ(7, TestFindMatchLength("01234567xxxxxxxx", "0123456?xxxxxxxx", 16));
1065 
1066   // Find non-match in first loop after one block.
1067   EXPECT_EQ(8, TestFindMatchLength("abcdefgh01234567xxxxxxxx",
1068                                    "abcdefgh?1234567xxxxxxxx", 24));
1069   EXPECT_EQ(9, TestFindMatchLength("abcdefgh01234567xxxxxxxx",
1070                                    "abcdefgh0?234567xxxxxxxx", 24));
1071   EXPECT_EQ(12, TestFindMatchLength("abcdefgh01234567xxxxxxxx",
1072                                     "abcdefgh01237654xxxxxxxx", 24));
1073   EXPECT_EQ(15, TestFindMatchLength("abcdefgh01234567xxxxxxxx",
1074                                     "abcdefgh0123456?xxxxxxxx", 24));
1075 
1076   // 32-bit version:
1077 
1078   // Short matches.
1079   EXPECT_EQ(0, TestFindMatchLength("01234567", "?1234567", 8));
1080   EXPECT_EQ(1, TestFindMatchLength("01234567", "0?234567", 8));
1081   EXPECT_EQ(2, TestFindMatchLength("01234567", "01?34567", 8));
1082   EXPECT_EQ(3, TestFindMatchLength("01234567", "012?4567", 8));
1083   EXPECT_EQ(4, TestFindMatchLength("01234567", "0123?567", 8));
1084   EXPECT_EQ(5, TestFindMatchLength("01234567", "01234?67", 8));
1085   EXPECT_EQ(6, TestFindMatchLength("01234567", "012345?7", 8));
1086   EXPECT_EQ(7, TestFindMatchLength("01234567", "0123456?", 8));
1087   EXPECT_EQ(7, TestFindMatchLength("01234567", "0123456?", 7));
1088   EXPECT_EQ(7, TestFindMatchLength("01234567!", "0123456??", 7));
1089 
1090   // Hit s1_limit in 32-bit loop, hit s1_limit in single-character loop.
1091   EXPECT_EQ(10, TestFindMatchLength("xxxxxxabcd", "xxxxxxabcd", 10));
1092   EXPECT_EQ(10, TestFindMatchLength("xxxxxxabcd?", "xxxxxxabcd?", 10));
1093   EXPECT_EQ(13, TestFindMatchLength("xxxxxxabcdef", "xxxxxxabcdef", 13));
1094 
1095   // Same, but edge cases.
1096   EXPECT_EQ(12, TestFindMatchLength("xxxxxx0123abc!", "xxxxxx0123abc!", 12));
1097   EXPECT_EQ(12, TestFindMatchLength("xxxxxx0123abc!", "xxxxxx0123abc?", 12));
1098 
1099   // Hit s1_limit in 32-bit loop, find a non-match in single-character loop.
1100   EXPECT_EQ(11, TestFindMatchLength("xxxxxx0123abc", "xxxxxx0123axc", 13));
1101 
1102   // Find non-match at once in first loop.
1103   EXPECT_EQ(6, TestFindMatchLength("xxxxxx0123xxxxxxxx",
1104                                    "xxxxxx?123xxxxxxxx", 18));
1105   EXPECT_EQ(7, TestFindMatchLength("xxxxxx0123xxxxxxxx",
1106                                    "xxxxxx0?23xxxxxxxx", 18));
1107   EXPECT_EQ(8, TestFindMatchLength("xxxxxx0123xxxxxxxx",
1108                                    "xxxxxx0132xxxxxxxx", 18));
1109   EXPECT_EQ(9, TestFindMatchLength("xxxxxx0123xxxxxxxx",
1110                                    "xxxxxx012?xxxxxxxx", 18));
1111 
1112   // Same, but edge cases.
1113   EXPECT_EQ(6, TestFindMatchLength("xxxxxx0123", "xxxxxx?123", 10));
1114   EXPECT_EQ(7, TestFindMatchLength("xxxxxx0123", "xxxxxx0?23", 10));
1115   EXPECT_EQ(8, TestFindMatchLength("xxxxxx0123", "xxxxxx0132", 10));
1116   EXPECT_EQ(9, TestFindMatchLength("xxxxxx0123", "xxxxxx012?", 10));
1117 
1118   // Find non-match in first loop after one block.
1119   EXPECT_EQ(10, TestFindMatchLength("xxxxxxabcd0123xx",
1120                                     "xxxxxxabcd?123xx", 16));
1121   EXPECT_EQ(11, TestFindMatchLength("xxxxxxabcd0123xx",
1122                                     "xxxxxxabcd0?23xx", 16));
1123   EXPECT_EQ(12, TestFindMatchLength("xxxxxxabcd0123xx",
1124                                     "xxxxxxabcd0132xx", 16));
1125   EXPECT_EQ(13, TestFindMatchLength("xxxxxxabcd0123xx",
1126                                     "xxxxxxabcd012?xx", 16));
1127 
1128   // Same, but edge cases.
1129   EXPECT_EQ(10, TestFindMatchLength("xxxxxxabcd0123", "xxxxxxabcd?123", 14));
1130   EXPECT_EQ(11, TestFindMatchLength("xxxxxxabcd0123", "xxxxxxabcd0?23", 14));
1131   EXPECT_EQ(12, TestFindMatchLength("xxxxxxabcd0123", "xxxxxxabcd0132", 14));
1132   EXPECT_EQ(13, TestFindMatchLength("xxxxxxabcd0123", "xxxxxxabcd012?", 14));
1133 }
1134 
TEST(Snappy,FindMatchLengthRandom)1135 TEST(Snappy, FindMatchLengthRandom) {
1136   const int kNumTrials = 10000;
1137   const int kTypicalLength = 10;
1138   ACMRandom rnd(FLAGS_test_random_seed);
1139 
1140   for (int i = 0; i < kNumTrials; i++) {
1141     string s, t;
1142     char a = rnd.Rand8();
1143     char b = rnd.Rand8();
1144     while (!rnd.OneIn(kTypicalLength)) {
1145       s.push_back(rnd.OneIn(2) ? a : b);
1146       t.push_back(rnd.OneIn(2) ? a : b);
1147     }
1148     DataEndingAtUnreadablePage u(s);
1149     DataEndingAtUnreadablePage v(t);
1150     int matched = snappy::internal::FindMatchLength(
1151         u.data(), v.data(), v.data() + t.size());
1152     if (matched == t.size()) {
1153       EXPECT_EQ(s, t);
1154     } else {
1155       EXPECT_NE(s[matched], t[matched]);
1156       for (int j = 0; j < matched; j++) {
1157         EXPECT_EQ(s[j], t[j]);
1158       }
1159     }
1160   }
1161 }
1162 
CompressFile(const char * fname)1163 static void CompressFile(const char* fname) {
1164   string fullinput;
1165   CHECK_OK(file::GetContents(fname, &fullinput, file::Defaults()));
1166 
1167   string compressed;
1168   Compress(fullinput.data(), fullinput.size(), SNAPPY, &compressed, false);
1169 
1170   CHECK_OK(file::SetContents(string(fname).append(".comp"), compressed,
1171                              file::Defaults()));
1172 }
1173 
UncompressFile(const char * fname)1174 static void UncompressFile(const char* fname) {
1175   string fullinput;
1176   CHECK_OK(file::GetContents(fname, &fullinput, file::Defaults()));
1177 
1178   size_t uncompLength;
1179   CHECK(CheckUncompressedLength(fullinput, &uncompLength));
1180 
1181   string uncompressed;
1182   uncompressed.resize(uncompLength);
1183   CHECK(snappy::Uncompress(fullinput.data(), fullinput.size(), &uncompressed));
1184 
1185   CHECK_OK(file::SetContents(string(fname).append(".uncomp"), uncompressed,
1186                              file::Defaults()));
1187 }
1188 
MeasureFile(const char * fname)1189 static void MeasureFile(const char* fname) {
1190   string fullinput;
1191   CHECK_OK(file::GetContents(fname, &fullinput, file::Defaults()));
1192   printf("%-40s :\n", fname);
1193 
1194   int start_len = (FLAGS_start_len < 0) ? fullinput.size() : FLAGS_start_len;
1195   int end_len = fullinput.size();
1196   if (FLAGS_end_len >= 0) {
1197     end_len = min<int>(fullinput.size(), FLAGS_end_len);
1198   }
1199   for (int len = start_len; len <= end_len; len++) {
1200     const char* const input = fullinput.data();
1201     int repeats = (FLAGS_bytes + len) / (len + 1);
1202     if (FLAGS_zlib)     Measure(input, len, ZLIB, repeats, 1024<<10);
1203     if (FLAGS_lzo)      Measure(input, len, LZO, repeats, 1024<<10);
1204     if (FLAGS_liblzf)   Measure(input, len, LIBLZF, repeats, 1024<<10);
1205     if (FLAGS_quicklz)  Measure(input, len, QUICKLZ, repeats, 1024<<10);
1206     if (FLAGS_fastlz)   Measure(input, len, FASTLZ, repeats, 1024<<10);
1207     if (FLAGS_snappy)    Measure(input, len, SNAPPY, repeats, 4096<<10);
1208 
1209     // For block-size based measurements
1210     if (0 && FLAGS_snappy) {
1211       Measure(input, len, SNAPPY, repeats, 8<<10);
1212       Measure(input, len, SNAPPY, repeats, 16<<10);
1213       Measure(input, len, SNAPPY, repeats, 32<<10);
1214       Measure(input, len, SNAPPY, repeats, 64<<10);
1215       Measure(input, len, SNAPPY, repeats, 256<<10);
1216       Measure(input, len, SNAPPY, repeats, 1024<<10);
1217     }
1218   }
1219 }
1220 
1221 static struct {
1222   const char* label;
1223   const char* filename;
1224   size_t size_limit;
1225 } files[] = {
1226   { "html", "html", 0 },
1227   { "urls", "urls.10K", 0 },
1228   { "jpg", "fireworks.jpeg", 0 },
1229   { "jpg_200", "fireworks.jpeg", 200 },
1230   { "pdf", "paper-100k.pdf", 0 },
1231   { "html4", "html_x_4", 0 },
1232   { "txt1", "alice29.txt", 0 },
1233   { "txt2", "asyoulik.txt", 0 },
1234   { "txt3", "lcet10.txt", 0 },
1235   { "txt4", "plrabn12.txt", 0 },
1236   { "pb", "geo.protodata", 0 },
1237   { "gaviota", "kppkn.gtb", 0 },
1238 };
1239 
BM_UFlat(int iters,int arg)1240 static void BM_UFlat(int iters, int arg) {
1241   StopBenchmarkTiming();
1242 
1243   // Pick file to process based on "arg"
1244   CHECK_GE(arg, 0);
1245   CHECK_LT(arg, ARRAYSIZE(files));
1246   string contents = ReadTestDataFile(files[arg].filename,
1247                                      files[arg].size_limit);
1248 
1249   string zcontents;
1250   snappy::Compress(contents.data(), contents.size(), &zcontents);
1251   char* dst = new char[contents.size()];
1252 
1253   SetBenchmarkBytesProcessed(static_cast<int64>(iters) *
1254                              static_cast<int64>(contents.size()));
1255   SetBenchmarkLabel(files[arg].label);
1256   StartBenchmarkTiming();
1257   while (iters-- > 0) {
1258     CHECK(snappy::RawUncompress(zcontents.data(), zcontents.size(), dst));
1259   }
1260   StopBenchmarkTiming();
1261 
1262   delete[] dst;
1263 }
1264 BENCHMARK(BM_UFlat)->DenseRange(0, ARRAYSIZE(files) - 1);
1265 
BM_UValidate(int iters,int arg)1266 static void BM_UValidate(int iters, int arg) {
1267   StopBenchmarkTiming();
1268 
1269   // Pick file to process based on "arg"
1270   CHECK_GE(arg, 0);
1271   CHECK_LT(arg, ARRAYSIZE(files));
1272   string contents = ReadTestDataFile(files[arg].filename,
1273                                      files[arg].size_limit);
1274 
1275   string zcontents;
1276   snappy::Compress(contents.data(), contents.size(), &zcontents);
1277 
1278   SetBenchmarkBytesProcessed(static_cast<int64>(iters) *
1279                              static_cast<int64>(contents.size()));
1280   SetBenchmarkLabel(files[arg].label);
1281   StartBenchmarkTiming();
1282   while (iters-- > 0) {
1283     CHECK(snappy::IsValidCompressedBuffer(zcontents.data(), zcontents.size()));
1284   }
1285   StopBenchmarkTiming();
1286 }
1287 BENCHMARK(BM_UValidate)->DenseRange(0, 4);
1288 
BM_UIOVec(int iters,int arg)1289 static void BM_UIOVec(int iters, int arg) {
1290   StopBenchmarkTiming();
1291 
1292   // Pick file to process based on "arg"
1293   CHECK_GE(arg, 0);
1294   CHECK_LT(arg, ARRAYSIZE(files));
1295   string contents = ReadTestDataFile(files[arg].filename,
1296                                      files[arg].size_limit);
1297 
1298   string zcontents;
1299   snappy::Compress(contents.data(), contents.size(), &zcontents);
1300 
1301   // Uncompress into an iovec containing ten entries.
1302   const int kNumEntries = 10;
1303   struct iovec iov[kNumEntries];
1304   char *dst = new char[contents.size()];
1305   int used_so_far = 0;
1306   for (int i = 0; i < kNumEntries; ++i) {
1307     iov[i].iov_base = dst + used_so_far;
1308     if (used_so_far == contents.size()) {
1309       iov[i].iov_len = 0;
1310       continue;
1311     }
1312 
1313     if (i == kNumEntries - 1) {
1314       iov[i].iov_len = contents.size() - used_so_far;
1315     } else {
1316       iov[i].iov_len = contents.size() / kNumEntries;
1317     }
1318     used_so_far += iov[i].iov_len;
1319   }
1320 
1321   SetBenchmarkBytesProcessed(static_cast<int64>(iters) *
1322                              static_cast<int64>(contents.size()));
1323   SetBenchmarkLabel(files[arg].label);
1324   StartBenchmarkTiming();
1325   while (iters-- > 0) {
1326     CHECK(snappy::RawUncompressToIOVec(zcontents.data(), zcontents.size(), iov,
1327                                        kNumEntries));
1328   }
1329   StopBenchmarkTiming();
1330 
1331   delete[] dst;
1332 }
1333 BENCHMARK(BM_UIOVec)->DenseRange(0, 4);
1334 
BM_UFlatSink(int iters,int arg)1335 static void BM_UFlatSink(int iters, int arg) {
1336   StopBenchmarkTiming();
1337 
1338   // Pick file to process based on "arg"
1339   CHECK_GE(arg, 0);
1340   CHECK_LT(arg, ARRAYSIZE(files));
1341   string contents = ReadTestDataFile(files[arg].filename,
1342                                      files[arg].size_limit);
1343 
1344   string zcontents;
1345   snappy::Compress(contents.data(), contents.size(), &zcontents);
1346   char* dst = new char[contents.size()];
1347 
1348   SetBenchmarkBytesProcessed(static_cast<int64>(iters) *
1349                              static_cast<int64>(contents.size()));
1350   SetBenchmarkLabel(files[arg].label);
1351   StartBenchmarkTiming();
1352   while (iters-- > 0) {
1353     snappy::ByteArraySource source(zcontents.data(), zcontents.size());
1354     snappy::UncheckedByteArraySink sink(dst);
1355     CHECK(snappy::Uncompress(&source, &sink));
1356   }
1357   StopBenchmarkTiming();
1358 
1359   string s(dst, contents.size());
1360   CHECK_EQ(contents, s);
1361 
1362   delete[] dst;
1363 }
1364 
1365 BENCHMARK(BM_UFlatSink)->DenseRange(0, ARRAYSIZE(files) - 1);
1366 
BM_ZFlat(int iters,int arg)1367 static void BM_ZFlat(int iters, int arg) {
1368   StopBenchmarkTiming();
1369 
1370   // Pick file to process based on "arg"
1371   CHECK_GE(arg, 0);
1372   CHECK_LT(arg, ARRAYSIZE(files));
1373   string contents = ReadTestDataFile(files[arg].filename,
1374                                      files[arg].size_limit);
1375 
1376   char* dst = new char[snappy::MaxCompressedLength(contents.size())];
1377 
1378   SetBenchmarkBytesProcessed(static_cast<int64>(iters) *
1379                              static_cast<int64>(contents.size()));
1380   StartBenchmarkTiming();
1381 
1382   size_t zsize = 0;
1383   while (iters-- > 0) {
1384     snappy::RawCompress(contents.data(), contents.size(), dst, &zsize);
1385   }
1386   StopBenchmarkTiming();
1387   const double compression_ratio =
1388       static_cast<double>(zsize) / std::max<size_t>(1, contents.size());
1389   SetBenchmarkLabel(StringPrintf("%s (%.2f %%)",
1390                                  files[arg].label, 100.0 * compression_ratio));
1391   VLOG(0) << StringPrintf("compression for %s: %zd -> %zd bytes",
1392                           files[arg].label, contents.size(), zsize);
1393   delete[] dst;
1394 }
1395 BENCHMARK(BM_ZFlat)->DenseRange(0, ARRAYSIZE(files) - 1);
1396 
1397 }  // namespace snappy
1398 
1399 
main(int argc,char ** argv)1400 int main(int argc, char** argv) {
1401   InitGoogle(argv[0], &argc, &argv, true);
1402   RunSpecifiedBenchmarks();
1403 
1404   if (argc >= 2) {
1405     for (int arg = 1; arg < argc; arg++) {
1406       if (FLAGS_write_compressed) {
1407         CompressFile(argv[arg]);
1408       } else if (FLAGS_write_uncompressed) {
1409         UncompressFile(argv[arg]);
1410       } else {
1411         MeasureFile(argv[arg]);
1412       }
1413     }
1414     return 0;
1415   }
1416 
1417   return RUN_ALL_TESTS();
1418 }
1419