]> git.cworth.org Git - apitrace/blob - thirdparty/snappy/snappy_unittest.cc
Merge branch 'opengl-4.3'
[apitrace] / thirdparty / snappy / snappy_unittest.cc
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
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");
67
68 namespace snappy {
69
70
71 #ifdef HAVE_FUNC_MMAP
72
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   }
98
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   }
104
105   const char* data() const { return data_; }
106   size_t size() const { return size_; }
107
108  private:
109   size_t alloc_size_;
110   void* mem_;
111   char* protected_page_;
112   const char* data_;
113   size_t size_;
114 };
115
116 #else  // HAVE_FUNC_MMAP
117
118 // Fallback for systems without mmap.
119 typedef string DataEndingAtUnreadablePage;
120
121 #endif
122
123 enum CompressorType {
124   ZLIB, LZO, LIBLZF, QUICKLZ, FASTLZ, SNAPPY
125 };
126
127 const char* names[] = {
128   "ZLIB", "LZO", "LIBLZF", "QUICKLZ", "FASTLZ", "SNAPPY"
129 };
130
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
138
139 #ifdef LZO_VERSION
140     case LZO:
141       return input_size + input_size/64 + 16 + 3;
142 #endif  // LZO_VERSION
143
144 #ifdef LZF_VERSION
145     case LIBLZF:
146       return input_size;
147 #endif  // LZF_VERSION
148
149 #ifdef QLZ_VERSION_MAJOR
150     case QUICKLZ:
151       return input_size + 36000;  // 36000 is used for scratch.
152 #endif  // QLZ_VERSION_MAJOR
153
154 #ifdef FASTLZ_VERSION
155     case FASTLZ:
156       return max(static_cast<int>(ceil(input_size * 1.05)), 66);
157 #endif  // FASTLZ_VERSION
158
159     case SNAPPY:
160       return snappy::MaxCompressedLength(input_size);
161
162     default:
163       LOG(FATAL) << "Unknown compression type number " << comp;
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.
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
282     default: {
283       return false;     // the asked-for library wasn't compiled in
284     }
285   }
286   return true;
287 }
288
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
307
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
323
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
341
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
354
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
366
367     case SNAPPY: {
368       snappy::RawUncompress(compressed.data(), compressed.size(),
369                             string_as_array(output));
370       break;
371     }
372
373
374     default: {
375       return false;     // the asked-for library wasn't compiled in
376     }
377   }
378   return true;
379 }
380
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;
391
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;
404
405       // Pre-grow the output buffer so we don't measure string append time.
406       compressed[b].resize(MinimumRequiredOutputSpace(block_size, comp));
407     }
408
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     }
415
416     for (int run = 0; run < kRuns; run++) {
417       CycleTimer ctimer, utimer;
418
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       }
423
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();
429
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       }
435
436       for (int b = 0; b < num_blocks; b++) {
437         output[b].resize(input_length[b]);
438       }
439
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();
445
446       ctime[run] = ctimer.Get();
447       utime[run] = utimer.Get();
448     }
449
450     compressed_size = 0;
451     for (int i = 0; i < compressed.size(); i++) {
452       compressed_size += compressed[i].size();
453     }
454   }
455
456   sort(ctime, ctime + kRuns);
457   sort(utime, utime + kRuns);
458   const int med = kRuns/2;
459
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 }
476
477
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()));
486
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 }
493
494
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   }
502
503   string prefix;
504   Varint::Append32(&prefix, input.size());
505
506   // Setup compression table
507   snappy::internal::WorkingMemory wmem;
508   int table_size;
509   uint16* table = wmem.GetHashTable(input.size(), &table_size);
510
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());
519
520   // Uncompress into string
521   string uncomp_str;
522   CHECK(snappy::Uncompress(compressed.data(), compressed.size(), &uncomp_str));
523   CHECK_EQ(uncomp_str, input);
524
525 }
526
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 }
536
537 static int Verify(const string& input) {
538   VLOG(1) << "Verifying input of size " << input.size();
539
540   // Compress using string based routines
541   const int result = VerifyString(input);
542
543
544   VerifyNonBlockedCompression(input);
545   if (!input.empty()) {
546     VerifyNonBlockedCompression(Expand(input));
547   }
548
549
550   return result;
551 }
552
553 // This test checks to ensure that snappy doesn't coredump if it gets
554 // corrupted data.
555
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 }
562
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);
569
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));
578
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));
589
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 3 GB 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., about 3 GB.
599     dest[4] = 'k';
600     CHECK(!IsValidCompressedBuffer(TypeParam(dest)));
601     CHECK(!Uncompress(TypeParam(dest), &uncmp));
602   } else {
603     LOG(WARNING) << "Crazy decompression lengths not checked on 64-bit build";
604   }
605
606   // This decodes to about 2 MB; much smaller, but should still fail.
607   dest[0] = dest[1] = dest[2] = 0xff;
608   dest[3] = 0x00;
609   CHECK(!IsValidCompressedBuffer(TypeParam(dest)));
610   CHECK(!Uncompress(TypeParam(dest), &uncmp));
611
612   // try reading stuff in from a bad file.
613   for (int i = 1; i <= 3; ++i) {
614     string data = ReadTestDataFile(StringPrintf("baddata%d.snappy", i).c_str());
615     string uncmp;
616     // check that we don't return a crazy length
617     size_t ulen;
618     CHECK(!snappy::GetUncompressedLength(data.data(), data.size(), &ulen)
619           || (ulen < (1<<20)));
620     uint32 ulen2;
621     snappy::ByteArraySource source(data.data(), data.size());
622     CHECK(!snappy::GetUncompressedLength(&source, &ulen2) ||
623           (ulen2 < (1<<20)));
624     CHECK(!IsValidCompressedBuffer(TypeParam(data)));
625     CHECK(!Uncompress(TypeParam(data), &uncmp));
626   }
627 }
628
629 // Helper routines to construct arbitrary compressed strings.
630 // These mirror the compression code in snappy.cc, but are copied
631 // here so that we can bypass some limitations in the how snappy.cc
632 // invokes these routines.
633 static void AppendLiteral(string* dst, const string& literal) {
634   if (literal.empty()) return;
635   int n = literal.size() - 1;
636   if (n < 60) {
637     // Fit length in tag byte
638     dst->push_back(0 | (n << 2));
639   } else {
640     // Encode in upcoming bytes
641     char number[4];
642     int count = 0;
643     while (n > 0) {
644       number[count++] = n & 0xff;
645       n >>= 8;
646     }
647     dst->push_back(0 | ((59+count) << 2));
648     *dst += string(number, count);
649   }
650   *dst += literal;
651 }
652
653 static void AppendCopy(string* dst, int offset, int length) {
654   while (length > 0) {
655     // Figure out how much to copy in one shot
656     int to_copy;
657     if (length >= 68) {
658       to_copy = 64;
659     } else if (length > 64) {
660       to_copy = 60;
661     } else {
662       to_copy = length;
663     }
664     length -= to_copy;
665
666     if ((to_copy < 12) && (offset < 2048)) {
667       assert(to_copy-4 < 8);            // Must fit in 3 bits
668       dst->push_back(1 | ((to_copy-4) << 2) | ((offset >> 8) << 5));
669       dst->push_back(offset & 0xff);
670     } else if (offset < 65536) {
671       dst->push_back(2 | ((to_copy-1) << 2));
672       dst->push_back(offset & 0xff);
673       dst->push_back(offset >> 8);
674     } else {
675       dst->push_back(3 | ((to_copy-1) << 2));
676       dst->push_back(offset & 0xff);
677       dst->push_back((offset >> 8) & 0xff);
678       dst->push_back((offset >> 16) & 0xff);
679       dst->push_back((offset >> 24) & 0xff);
680     }
681   }
682 }
683
684 TEST(Snappy, SimpleTests) {
685   Verify("");
686   Verify("a");
687   Verify("ab");
688   Verify("abc");
689
690   Verify("aaaaaaa" + string(16, 'b') + string("aaaaa") + "abc");
691   Verify("aaaaaaa" + string(256, 'b') + string("aaaaa") + "abc");
692   Verify("aaaaaaa" + string(2047, 'b') + string("aaaaa") + "abc");
693   Verify("aaaaaaa" + string(65536, 'b') + string("aaaaa") + "abc");
694   Verify("abcaaaaaaa" + string(65536, 'b') + string("aaaaa") + "abc");
695 }
696
697 // Verify max blowup (lots of four-byte copies)
698 TEST(Snappy, MaxBlowup) {
699   string input;
700   for (int i = 0; i < 20000; i++) {
701     ACMRandom rnd(i);
702     uint32 bytes = static_cast<uint32>(rnd.Next());
703     input.append(reinterpret_cast<char*>(&bytes), sizeof(bytes));
704   }
705   for (int i = 19999; i >= 0; i--) {
706     ACMRandom rnd(i);
707     uint32 bytes = static_cast<uint32>(rnd.Next());
708     input.append(reinterpret_cast<char*>(&bytes), sizeof(bytes));
709   }
710   Verify(input);
711 }
712
713 TEST(Snappy, RandomData) {
714   ACMRandom rnd(FLAGS_test_random_seed);
715
716   const int num_ops = 20000;
717   for (int i = 0; i < num_ops; i++) {
718     if ((i % 1000) == 0) {
719       VLOG(0) << "Random op " << i << " of " << num_ops;
720     }
721
722     string x;
723     int len = rnd.Uniform(4096);
724     if (i < 100) {
725       len = 65536 + rnd.Uniform(65536);
726     }
727     while (x.size() < len) {
728       int run_len = 1;
729       if (rnd.OneIn(10)) {
730         run_len = rnd.Skewed(8);
731       }
732       char c = (i < 100) ? rnd.Uniform(256) : rnd.Skewed(3);
733       while (run_len-- > 0 && x.size() < len) {
734         x += c;
735       }
736     }
737
738     Verify(x);
739   }
740 }
741
742 TEST(Snappy, FourByteOffset) {
743   // The new compressor cannot generate four-byte offsets since
744   // it chops up the input into 32KB pieces.  So we hand-emit the
745   // copy manually.
746
747   // The two fragments that make up the input string.
748   string fragment1 = "012345689abcdefghijklmnopqrstuvwxyz";
749   string fragment2 = "some other string";
750
751   // How many times each fragment is emitted.
752   const int n1 = 2;
753   const int n2 = 100000 / fragment2.size();
754   const int length = n1 * fragment1.size() + n2 * fragment2.size();
755
756   string compressed;
757   Varint::Append32(&compressed, length);
758
759   AppendLiteral(&compressed, fragment1);
760   string src = fragment1;
761   for (int i = 0; i < n2; i++) {
762     AppendLiteral(&compressed, fragment2);
763     src += fragment2;
764   }
765   AppendCopy(&compressed, src.size(), fragment1.size());
766   src += fragment1;
767   CHECK_EQ(length, src.size());
768
769   string uncompressed;
770   CHECK(snappy::IsValidCompressedBuffer(compressed.data(), compressed.size()));
771   CHECK(snappy::Uncompress(compressed.data(), compressed.size(), &uncompressed));
772   CHECK_EQ(uncompressed, src);
773 }
774
775
776 static bool CheckUncompressedLength(const string& compressed,
777                                     size_t* ulength) {
778   const bool result1 = snappy::GetUncompressedLength(compressed.data(),
779                                                      compressed.size(),
780                                                      ulength);
781
782   snappy::ByteArraySource source(compressed.data(), compressed.size());
783   uint32 length;
784   const bool result2 = snappy::GetUncompressedLength(&source, &length);
785   CHECK_EQ(result1, result2);
786   return result1;
787 }
788
789 TEST(SnappyCorruption, TruncatedVarint) {
790   string compressed, uncompressed;
791   size_t ulength;
792   compressed.push_back('\xf0');
793   CHECK(!CheckUncompressedLength(compressed, &ulength));
794   CHECK(!snappy::IsValidCompressedBuffer(compressed.data(), compressed.size()));
795   CHECK(!snappy::Uncompress(compressed.data(), compressed.size(),
796                             &uncompressed));
797 }
798
799 TEST(SnappyCorruption, UnterminatedVarint) {
800   string compressed, uncompressed;
801   size_t ulength;
802   compressed.push_back(128);
803   compressed.push_back(128);
804   compressed.push_back(128);
805   compressed.push_back(128);
806   compressed.push_back(128);
807   compressed.push_back(10);
808   CHECK(!CheckUncompressedLength(compressed, &ulength));
809   CHECK(!snappy::IsValidCompressedBuffer(compressed.data(), compressed.size()));
810   CHECK(!snappy::Uncompress(compressed.data(), compressed.size(),
811                             &uncompressed));
812 }
813
814 TEST(Snappy, ReadPastEndOfBuffer) {
815   // Check that we do not read past end of input
816
817   // Make a compressed string that ends with a single-byte literal
818   string compressed;
819   Varint::Append32(&compressed, 1);
820   AppendLiteral(&compressed, "x");
821
822   string uncompressed;
823   DataEndingAtUnreadablePage c(compressed);
824   CHECK(snappy::Uncompress(c.data(), c.size(), &uncompressed));
825   CHECK_EQ(uncompressed, string("x"));
826 }
827
828 // Check for an infinite loop caused by a copy with offset==0
829 TEST(Snappy, ZeroOffsetCopy) {
830   const char* compressed = "\x40\x12\x00\x00";
831   //  \x40              Length (must be > kMaxIncrementCopyOverflow)
832   //  \x12\x00\x00      Copy with offset==0, length==5
833   char uncompressed[100];
834   EXPECT_FALSE(snappy::RawUncompress(compressed, 4, uncompressed));
835 }
836
837 TEST(Snappy, ZeroOffsetCopyValidation) {
838   const char* compressed = "\x05\x12\x00\x00";
839   //  \x05              Length
840   //  \x12\x00\x00      Copy with offset==0, length==5
841   EXPECT_FALSE(snappy::IsValidCompressedBuffer(compressed, 4));
842 }
843
844
845 namespace {
846
847 int TestFindMatchLength(const char* s1, const char *s2, unsigned length) {
848   return snappy::internal::FindMatchLength(s1, s2, s2 + length);
849 }
850
851 }  // namespace
852
853 TEST(Snappy, FindMatchLength) {
854   // Exercise all different code paths through the function.
855   // 64-bit version:
856
857   // Hit s1_limit in 64-bit loop, hit s1_limit in single-character loop.
858   EXPECT_EQ(6, TestFindMatchLength("012345", "012345", 6));
859   EXPECT_EQ(11, TestFindMatchLength("01234567abc", "01234567abc", 11));
860
861   // Hit s1_limit in 64-bit loop, find a non-match in single-character loop.
862   EXPECT_EQ(9, TestFindMatchLength("01234567abc", "01234567axc", 9));
863
864   // Same, but edge cases.
865   EXPECT_EQ(11, TestFindMatchLength("01234567abc!", "01234567abc!", 11));
866   EXPECT_EQ(11, TestFindMatchLength("01234567abc!", "01234567abc?", 11));
867
868   // Find non-match at once in first loop.
869   EXPECT_EQ(0, TestFindMatchLength("01234567xxxxxxxx", "?1234567xxxxxxxx", 16));
870   EXPECT_EQ(1, TestFindMatchLength("01234567xxxxxxxx", "0?234567xxxxxxxx", 16));
871   EXPECT_EQ(4, TestFindMatchLength("01234567xxxxxxxx", "01237654xxxxxxxx", 16));
872   EXPECT_EQ(7, TestFindMatchLength("01234567xxxxxxxx", "0123456?xxxxxxxx", 16));
873
874   // Find non-match in first loop after one block.
875   EXPECT_EQ(8, TestFindMatchLength("abcdefgh01234567xxxxxxxx",
876                                    "abcdefgh?1234567xxxxxxxx", 24));
877   EXPECT_EQ(9, TestFindMatchLength("abcdefgh01234567xxxxxxxx",
878                                    "abcdefgh0?234567xxxxxxxx", 24));
879   EXPECT_EQ(12, TestFindMatchLength("abcdefgh01234567xxxxxxxx",
880                                     "abcdefgh01237654xxxxxxxx", 24));
881   EXPECT_EQ(15, TestFindMatchLength("abcdefgh01234567xxxxxxxx",
882                                     "abcdefgh0123456?xxxxxxxx", 24));
883
884   // 32-bit version:
885
886   // Short matches.
887   EXPECT_EQ(0, TestFindMatchLength("01234567", "?1234567", 8));
888   EXPECT_EQ(1, TestFindMatchLength("01234567", "0?234567", 8));
889   EXPECT_EQ(2, TestFindMatchLength("01234567", "01?34567", 8));
890   EXPECT_EQ(3, TestFindMatchLength("01234567", "012?4567", 8));
891   EXPECT_EQ(4, TestFindMatchLength("01234567", "0123?567", 8));
892   EXPECT_EQ(5, TestFindMatchLength("01234567", "01234?67", 8));
893   EXPECT_EQ(6, TestFindMatchLength("01234567", "012345?7", 8));
894   EXPECT_EQ(7, TestFindMatchLength("01234567", "0123456?", 8));
895   EXPECT_EQ(7, TestFindMatchLength("01234567", "0123456?", 7));
896   EXPECT_EQ(7, TestFindMatchLength("01234567!", "0123456??", 7));
897
898   // Hit s1_limit in 32-bit loop, hit s1_limit in single-character loop.
899   EXPECT_EQ(10, TestFindMatchLength("xxxxxxabcd", "xxxxxxabcd", 10));
900   EXPECT_EQ(10, TestFindMatchLength("xxxxxxabcd?", "xxxxxxabcd?", 10));
901   EXPECT_EQ(13, TestFindMatchLength("xxxxxxabcdef", "xxxxxxabcdef", 13));
902
903   // Same, but edge cases.
904   EXPECT_EQ(12, TestFindMatchLength("xxxxxx0123abc!", "xxxxxx0123abc!", 12));
905   EXPECT_EQ(12, TestFindMatchLength("xxxxxx0123abc!", "xxxxxx0123abc?", 12));
906
907   // Hit s1_limit in 32-bit loop, find a non-match in single-character loop.
908   EXPECT_EQ(11, TestFindMatchLength("xxxxxx0123abc", "xxxxxx0123axc", 13));
909
910   // Find non-match at once in first loop.
911   EXPECT_EQ(6, TestFindMatchLength("xxxxxx0123xxxxxxxx",
912                                    "xxxxxx?123xxxxxxxx", 18));
913   EXPECT_EQ(7, TestFindMatchLength("xxxxxx0123xxxxxxxx",
914                                    "xxxxxx0?23xxxxxxxx", 18));
915   EXPECT_EQ(8, TestFindMatchLength("xxxxxx0123xxxxxxxx",
916                                    "xxxxxx0132xxxxxxxx", 18));
917   EXPECT_EQ(9, TestFindMatchLength("xxxxxx0123xxxxxxxx",
918                                    "xxxxxx012?xxxxxxxx", 18));
919
920   // Same, but edge cases.
921   EXPECT_EQ(6, TestFindMatchLength("xxxxxx0123", "xxxxxx?123", 10));
922   EXPECT_EQ(7, TestFindMatchLength("xxxxxx0123", "xxxxxx0?23", 10));
923   EXPECT_EQ(8, TestFindMatchLength("xxxxxx0123", "xxxxxx0132", 10));
924   EXPECT_EQ(9, TestFindMatchLength("xxxxxx0123", "xxxxxx012?", 10));
925
926   // Find non-match in first loop after one block.
927   EXPECT_EQ(10, TestFindMatchLength("xxxxxxabcd0123xx",
928                                     "xxxxxxabcd?123xx", 16));
929   EXPECT_EQ(11, TestFindMatchLength("xxxxxxabcd0123xx",
930                                     "xxxxxxabcd0?23xx", 16));
931   EXPECT_EQ(12, TestFindMatchLength("xxxxxxabcd0123xx",
932                                     "xxxxxxabcd0132xx", 16));
933   EXPECT_EQ(13, TestFindMatchLength("xxxxxxabcd0123xx",
934                                     "xxxxxxabcd012?xx", 16));
935
936   // Same, but edge cases.
937   EXPECT_EQ(10, TestFindMatchLength("xxxxxxabcd0123", "xxxxxxabcd?123", 14));
938   EXPECT_EQ(11, TestFindMatchLength("xxxxxxabcd0123", "xxxxxxabcd0?23", 14));
939   EXPECT_EQ(12, TestFindMatchLength("xxxxxxabcd0123", "xxxxxxabcd0132", 14));
940   EXPECT_EQ(13, TestFindMatchLength("xxxxxxabcd0123", "xxxxxxabcd012?", 14));
941 }
942
943 TEST(Snappy, FindMatchLengthRandom) {
944   const int kNumTrials = 10000;
945   const int kTypicalLength = 10;
946   ACMRandom rnd(FLAGS_test_random_seed);
947
948   for (int i = 0; i < kNumTrials; i++) {
949     string s, t;
950     char a = rnd.Rand8();
951     char b = rnd.Rand8();
952     while (!rnd.OneIn(kTypicalLength)) {
953       s.push_back(rnd.OneIn(2) ? a : b);
954       t.push_back(rnd.OneIn(2) ? a : b);
955     }
956     DataEndingAtUnreadablePage u(s);
957     DataEndingAtUnreadablePage v(t);
958     int matched = snappy::internal::FindMatchLength(
959         u.data(), v.data(), v.data() + t.size());
960     if (matched == t.size()) {
961       EXPECT_EQ(s, t);
962     } else {
963       EXPECT_NE(s[matched], t[matched]);
964       for (int j = 0; j < matched; j++) {
965         EXPECT_EQ(s[j], t[j]);
966       }
967     }
968   }
969 }
970
971
972 static void CompressFile(const char* fname) {
973   string fullinput;
974   File::ReadFileToStringOrDie(fname, &fullinput);
975
976   string compressed;
977   Compress(fullinput.data(), fullinput.size(), SNAPPY, &compressed, false);
978
979   File::WriteStringToFileOrDie(compressed,
980                                string(fname).append(".comp").c_str());
981 }
982
983 static void UncompressFile(const char* fname) {
984   string fullinput;
985   File::ReadFileToStringOrDie(fname, &fullinput);
986
987   size_t uncompLength;
988   CHECK(CheckUncompressedLength(fullinput, &uncompLength));
989
990   string uncompressed;
991   uncompressed.resize(uncompLength);
992   CHECK(snappy::Uncompress(fullinput.data(), fullinput.size(), &uncompressed));
993
994   File::WriteStringToFileOrDie(uncompressed,
995                                string(fname).append(".uncomp").c_str());
996 }
997
998 static void MeasureFile(const char* fname) {
999   string fullinput;
1000   File::ReadFileToStringOrDie(fname, &fullinput);
1001   printf("%-40s :\n", fname);
1002
1003   int start_len = (FLAGS_start_len < 0) ? fullinput.size() : FLAGS_start_len;
1004   int end_len = fullinput.size();
1005   if (FLAGS_end_len >= 0) {
1006     end_len = min<int>(fullinput.size(), FLAGS_end_len);
1007   }
1008   for (int len = start_len; len <= end_len; len++) {
1009     const char* const input = fullinput.data();
1010     int repeats = (FLAGS_bytes + len) / (len + 1);
1011     if (FLAGS_zlib)     Measure(input, len, ZLIB, repeats, 1024<<10);
1012     if (FLAGS_lzo)      Measure(input, len, LZO, repeats, 1024<<10);
1013     if (FLAGS_liblzf)   Measure(input, len, LIBLZF, repeats, 1024<<10);
1014     if (FLAGS_quicklz)  Measure(input, len, QUICKLZ, repeats, 1024<<10);
1015     if (FLAGS_fastlz)   Measure(input, len, FASTLZ, repeats, 1024<<10);
1016     if (FLAGS_snappy)    Measure(input, len, SNAPPY, repeats, 4096<<10);
1017
1018     // For block-size based measurements
1019     if (0 && FLAGS_snappy) {
1020       Measure(input, len, SNAPPY, repeats, 8<<10);
1021       Measure(input, len, SNAPPY, repeats, 16<<10);
1022       Measure(input, len, SNAPPY, repeats, 32<<10);
1023       Measure(input, len, SNAPPY, repeats, 64<<10);
1024       Measure(input, len, SNAPPY, repeats, 256<<10);
1025       Measure(input, len, SNAPPY, repeats, 1024<<10);
1026     }
1027   }
1028 }
1029
1030 static struct {
1031   const char* label;
1032   const char* filename;
1033 } files[] = {
1034   { "html", "html" },
1035   { "urls", "urls.10K" },
1036   { "jpg", "house.jpg" },
1037   { "pdf", "mapreduce-osdi-1.pdf" },
1038   { "html4", "html_x_4" },
1039   { "cp", "cp.html" },
1040   { "c", "fields.c" },
1041   { "lsp", "grammar.lsp" },
1042   { "xls", "kennedy.xls" },
1043   { "txt1", "alice29.txt" },
1044   { "txt2", "asyoulik.txt" },
1045   { "txt3", "lcet10.txt" },
1046   { "txt4", "plrabn12.txt" },
1047   { "bin", "ptt5" },
1048   { "sum", "sum" },
1049   { "man", "xargs.1" },
1050   { "pb", "geo.protodata" },
1051   { "gaviota", "kppkn.gtb" },
1052 };
1053
1054 static void BM_UFlat(int iters, int arg) {
1055   StopBenchmarkTiming();
1056
1057   // Pick file to process based on "arg"
1058   CHECK_GE(arg, 0);
1059   CHECK_LT(arg, ARRAYSIZE(files));
1060   string contents = ReadTestDataFile(files[arg].filename);
1061
1062   string zcontents;
1063   snappy::Compress(contents.data(), contents.size(), &zcontents);
1064   char* dst = new char[contents.size()];
1065
1066   SetBenchmarkBytesProcessed(static_cast<int64>(iters) *
1067                              static_cast<int64>(contents.size()));
1068   SetBenchmarkLabel(files[arg].label);
1069   StartBenchmarkTiming();
1070   while (iters-- > 0) {
1071     CHECK(snappy::RawUncompress(zcontents.data(), zcontents.size(), dst));
1072   }
1073   StopBenchmarkTiming();
1074
1075   delete[] dst;
1076 }
1077 BENCHMARK(BM_UFlat)->DenseRange(0, 17);
1078
1079 static void BM_UValidate(int iters, int arg) {
1080   StopBenchmarkTiming();
1081
1082   // Pick file to process based on "arg"
1083   CHECK_GE(arg, 0);
1084   CHECK_LT(arg, ARRAYSIZE(files));
1085   string contents = ReadTestDataFile(files[arg].filename);
1086
1087   string zcontents;
1088   snappy::Compress(contents.data(), contents.size(), &zcontents);
1089
1090   SetBenchmarkBytesProcessed(static_cast<int64>(iters) *
1091                              static_cast<int64>(contents.size()));
1092   SetBenchmarkLabel(files[arg].label);
1093   StartBenchmarkTiming();
1094   while (iters-- > 0) {
1095     CHECK(snappy::IsValidCompressedBuffer(zcontents.data(), zcontents.size()));
1096   }
1097   StopBenchmarkTiming();
1098 }
1099 BENCHMARK(BM_UValidate)->DenseRange(0, 4);
1100
1101
1102 static void BM_ZFlat(int iters, int arg) {
1103   StopBenchmarkTiming();
1104
1105   // Pick file to process based on "arg"
1106   CHECK_GE(arg, 0);
1107   CHECK_LT(arg, ARRAYSIZE(files));
1108   string contents = ReadTestDataFile(files[arg].filename);
1109
1110   char* dst = new char[snappy::MaxCompressedLength(contents.size())];
1111
1112   SetBenchmarkBytesProcessed(static_cast<int64>(iters) *
1113                              static_cast<int64>(contents.size()));
1114   StartBenchmarkTiming();
1115
1116   size_t zsize = 0;
1117   while (iters-- > 0) {
1118     snappy::RawCompress(contents.data(), contents.size(), dst, &zsize);
1119   }
1120   StopBenchmarkTiming();
1121   const double compression_ratio =
1122       static_cast<double>(zsize) / std::max<size_t>(1, contents.size());
1123   SetBenchmarkLabel(StringPrintf("%s (%.2f %%)",
1124                                  files[arg].label, 100.0 * compression_ratio));
1125   VLOG(0) << StringPrintf("compression for %s: %zd -> %zd bytes",
1126                           files[arg].label, contents.size(), zsize);
1127   delete[] dst;
1128 }
1129 BENCHMARK(BM_ZFlat)->DenseRange(0, 17);
1130
1131
1132 }  // namespace snappy
1133
1134
1135 int main(int argc, char** argv) {
1136   InitGoogle(argv[0], &argc, &argv, true);
1137   File::Init();
1138   RunSpecifiedBenchmarks();
1139
1140
1141   if (argc >= 2) {
1142     for (int arg = 1; arg < argc; arg++) {
1143       if (FLAGS_write_compressed) {
1144         CompressFile(argv[arg]);
1145       } else if (FLAGS_write_uncompressed) {
1146         UncompressFile(argv[arg]);
1147       } else {
1148         MeasureFile(argv[arg]);
1149       }
1150     }
1151     return 0;
1152   }
1153
1154   return RUN_ALL_TESTS();
1155 }