other-licenses/snappy/src/snappy_unittest.cc

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 // Copyright 2005 and onwards Google Inc.
michael@0 2 //
michael@0 3 // Redistribution and use in source and binary forms, with or without
michael@0 4 // modification, are permitted provided that the following conditions are
michael@0 5 // met:
michael@0 6 //
michael@0 7 // * Redistributions of source code must retain the above copyright
michael@0 8 // notice, this list of conditions and the following disclaimer.
michael@0 9 // * Redistributions in binary form must reproduce the above
michael@0 10 // copyright notice, this list of conditions and the following disclaimer
michael@0 11 // in the documentation and/or other materials provided with the
michael@0 12 // distribution.
michael@0 13 // * Neither the name of Google Inc. nor the names of its
michael@0 14 // contributors may be used to endorse or promote products derived from
michael@0 15 // this software without specific prior written permission.
michael@0 16 //
michael@0 17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
michael@0 18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
michael@0 19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
michael@0 20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
michael@0 21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
michael@0 22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
michael@0 23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
michael@0 24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
michael@0 25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
michael@0 26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
michael@0 27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
michael@0 28
michael@0 29 #include <math.h>
michael@0 30 #include <stdlib.h>
michael@0 31
michael@0 32
michael@0 33 #include <algorithm>
michael@0 34 #include <string>
michael@0 35 #include <vector>
michael@0 36
michael@0 37 #include "snappy.h"
michael@0 38 #include "snappy-internal.h"
michael@0 39 #include "snappy-test.h"
michael@0 40 #include "snappy-sinksource.h"
michael@0 41
michael@0 42 DEFINE_int32(start_len, -1,
michael@0 43 "Starting prefix size for testing (-1: just full file contents)");
michael@0 44 DEFINE_int32(end_len, -1,
michael@0 45 "Starting prefix size for testing (-1: just full file contents)");
michael@0 46 DEFINE_int32(bytes, 10485760,
michael@0 47 "How many bytes to compress/uncompress per file for timing");
michael@0 48
michael@0 49 DEFINE_bool(zlib, false,
michael@0 50 "Run zlib compression (http://www.zlib.net)");
michael@0 51 DEFINE_bool(lzo, false,
michael@0 52 "Run LZO compression (http://www.oberhumer.com/opensource/lzo/)");
michael@0 53 DEFINE_bool(quicklz, false,
michael@0 54 "Run quickLZ compression (http://www.quicklz.com/)");
michael@0 55 DEFINE_bool(liblzf, false,
michael@0 56 "Run libLZF compression "
michael@0 57 "(http://www.goof.com/pcg/marc/liblzf.html)");
michael@0 58 DEFINE_bool(fastlz, false,
michael@0 59 "Run FastLZ compression (http://www.fastlz.org/");
michael@0 60 DEFINE_bool(snappy, true, "Run snappy compression");
michael@0 61
michael@0 62
michael@0 63 DEFINE_bool(write_compressed, false,
michael@0 64 "Write compressed versions of each file to <file>.comp");
michael@0 65 DEFINE_bool(write_uncompressed, false,
michael@0 66 "Write uncompressed versions of each file to <file>.uncomp");
michael@0 67
michael@0 68 namespace snappy {
michael@0 69
michael@0 70
michael@0 71 #ifdef HAVE_FUNC_MMAP
michael@0 72
michael@0 73 // To test against code that reads beyond its input, this class copies a
michael@0 74 // string to a newly allocated group of pages, the last of which
michael@0 75 // is made unreadable via mprotect. Note that we need to allocate the
michael@0 76 // memory with mmap(), as POSIX allows mprotect() only on memory allocated
michael@0 77 // with mmap(), and some malloc/posix_memalign implementations expect to
michael@0 78 // be able to read previously allocated memory while doing heap allocations.
michael@0 79 class DataEndingAtUnreadablePage {
michael@0 80 public:
michael@0 81 explicit DataEndingAtUnreadablePage(const string& s) {
michael@0 82 const size_t page_size = getpagesize();
michael@0 83 const size_t size = s.size();
michael@0 84 // Round up space for string to a multiple of page_size.
michael@0 85 size_t space_for_string = (size + page_size - 1) & ~(page_size - 1);
michael@0 86 alloc_size_ = space_for_string + page_size;
michael@0 87 mem_ = mmap(NULL, alloc_size_,
michael@0 88 PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
michael@0 89 CHECK_NE(MAP_FAILED, mem_);
michael@0 90 protected_page_ = reinterpret_cast<char*>(mem_) + space_for_string;
michael@0 91 char* dst = protected_page_ - size;
michael@0 92 memcpy(dst, s.data(), size);
michael@0 93 data_ = dst;
michael@0 94 size_ = size;
michael@0 95 // Make guard page unreadable.
michael@0 96 CHECK_EQ(0, mprotect(protected_page_, page_size, PROT_NONE));
michael@0 97 }
michael@0 98
michael@0 99 ~DataEndingAtUnreadablePage() {
michael@0 100 // Undo the mprotect.
michael@0 101 CHECK_EQ(0, mprotect(protected_page_, getpagesize(), PROT_READ|PROT_WRITE));
michael@0 102 CHECK_EQ(0, munmap(mem_, alloc_size_));
michael@0 103 }
michael@0 104
michael@0 105 const char* data() const { return data_; }
michael@0 106 size_t size() const { return size_; }
michael@0 107
michael@0 108 private:
michael@0 109 size_t alloc_size_;
michael@0 110 void* mem_;
michael@0 111 char* protected_page_;
michael@0 112 const char* data_;
michael@0 113 size_t size_;
michael@0 114 };
michael@0 115
michael@0 116 #else // HAVE_FUNC_MMAP
michael@0 117
michael@0 118 // Fallback for systems without mmap.
michael@0 119 typedef string DataEndingAtUnreadablePage;
michael@0 120
michael@0 121 #endif
michael@0 122
michael@0 123 enum CompressorType {
michael@0 124 ZLIB, LZO, LIBLZF, QUICKLZ, FASTLZ, SNAPPY
michael@0 125 };
michael@0 126
michael@0 127 const char* names[] = {
michael@0 128 "ZLIB", "LZO", "LIBLZF", "QUICKLZ", "FASTLZ", "SNAPPY"
michael@0 129 };
michael@0 130
michael@0 131 static size_t MinimumRequiredOutputSpace(size_t input_size,
michael@0 132 CompressorType comp) {
michael@0 133 switch (comp) {
michael@0 134 #ifdef ZLIB_VERSION
michael@0 135 case ZLIB:
michael@0 136 return ZLib::MinCompressbufSize(input_size);
michael@0 137 #endif // ZLIB_VERSION
michael@0 138
michael@0 139 #ifdef LZO_VERSION
michael@0 140 case LZO:
michael@0 141 return input_size + input_size/64 + 16 + 3;
michael@0 142 #endif // LZO_VERSION
michael@0 143
michael@0 144 #ifdef LZF_VERSION
michael@0 145 case LIBLZF:
michael@0 146 return input_size;
michael@0 147 #endif // LZF_VERSION
michael@0 148
michael@0 149 #ifdef QLZ_VERSION_MAJOR
michael@0 150 case QUICKLZ:
michael@0 151 return input_size + 36000; // 36000 is used for scratch.
michael@0 152 #endif // QLZ_VERSION_MAJOR
michael@0 153
michael@0 154 #ifdef FASTLZ_VERSION
michael@0 155 case FASTLZ:
michael@0 156 return max(static_cast<int>(ceil(input_size * 1.05)), 66);
michael@0 157 #endif // FASTLZ_VERSION
michael@0 158
michael@0 159 case SNAPPY:
michael@0 160 return snappy::MaxCompressedLength(input_size);
michael@0 161
michael@0 162 default:
michael@0 163 LOG(FATAL) << "Unknown compression type number " << comp;
michael@0 164 }
michael@0 165 }
michael@0 166
michael@0 167 // Returns true if we successfully compressed, false otherwise.
michael@0 168 //
michael@0 169 // If compressed_is_preallocated is set, do not resize the compressed buffer.
michael@0 170 // This is typically what you want for a benchmark, in order to not spend
michael@0 171 // time in the memory allocator. If you do set this flag, however,
michael@0 172 // "compressed" must be preinitialized to at least MinCompressbufSize(comp)
michael@0 173 // number of bytes, and may contain junk bytes at the end after return.
michael@0 174 static bool Compress(const char* input, size_t input_size, CompressorType comp,
michael@0 175 string* compressed, bool compressed_is_preallocated) {
michael@0 176 if (!compressed_is_preallocated) {
michael@0 177 compressed->resize(MinimumRequiredOutputSpace(input_size, comp));
michael@0 178 }
michael@0 179
michael@0 180 switch (comp) {
michael@0 181 #ifdef ZLIB_VERSION
michael@0 182 case ZLIB: {
michael@0 183 ZLib zlib;
michael@0 184 uLongf destlen = compressed->size();
michael@0 185 int ret = zlib.Compress(
michael@0 186 reinterpret_cast<Bytef*>(string_as_array(compressed)),
michael@0 187 &destlen,
michael@0 188 reinterpret_cast<const Bytef*>(input),
michael@0 189 input_size);
michael@0 190 CHECK_EQ(Z_OK, ret);
michael@0 191 if (!compressed_is_preallocated) {
michael@0 192 compressed->resize(destlen);
michael@0 193 }
michael@0 194 return true;
michael@0 195 }
michael@0 196 #endif // ZLIB_VERSION
michael@0 197
michael@0 198 #ifdef LZO_VERSION
michael@0 199 case LZO: {
michael@0 200 unsigned char* mem = new unsigned char[LZO1X_1_15_MEM_COMPRESS];
michael@0 201 lzo_uint destlen;
michael@0 202 int ret = lzo1x_1_15_compress(
michael@0 203 reinterpret_cast<const uint8*>(input),
michael@0 204 input_size,
michael@0 205 reinterpret_cast<uint8*>(string_as_array(compressed)),
michael@0 206 &destlen,
michael@0 207 mem);
michael@0 208 CHECK_EQ(LZO_E_OK, ret);
michael@0 209 delete[] mem;
michael@0 210 if (!compressed_is_preallocated) {
michael@0 211 compressed->resize(destlen);
michael@0 212 }
michael@0 213 break;
michael@0 214 }
michael@0 215 #endif // LZO_VERSION
michael@0 216
michael@0 217 #ifdef LZF_VERSION
michael@0 218 case LIBLZF: {
michael@0 219 int destlen = lzf_compress(input,
michael@0 220 input_size,
michael@0 221 string_as_array(compressed),
michael@0 222 input_size);
michael@0 223 if (destlen == 0) {
michael@0 224 // lzf *can* cause lots of blowup when compressing, so they
michael@0 225 // recommend to limit outsize to insize, and just not compress
michael@0 226 // if it's bigger. Ideally, we'd just swap input and output.
michael@0 227 compressed->assign(input, input_size);
michael@0 228 destlen = input_size;
michael@0 229 }
michael@0 230 if (!compressed_is_preallocated) {
michael@0 231 compressed->resize(destlen);
michael@0 232 }
michael@0 233 break;
michael@0 234 }
michael@0 235 #endif // LZF_VERSION
michael@0 236
michael@0 237 #ifdef QLZ_VERSION_MAJOR
michael@0 238 case QUICKLZ: {
michael@0 239 qlz_state_compress *state_compress = new qlz_state_compress;
michael@0 240 int destlen = qlz_compress(input,
michael@0 241 string_as_array(compressed),
michael@0 242 input_size,
michael@0 243 state_compress);
michael@0 244 delete state_compress;
michael@0 245 CHECK_NE(0, destlen);
michael@0 246 if (!compressed_is_preallocated) {
michael@0 247 compressed->resize(destlen);
michael@0 248 }
michael@0 249 break;
michael@0 250 }
michael@0 251 #endif // QLZ_VERSION_MAJOR
michael@0 252
michael@0 253 #ifdef FASTLZ_VERSION
michael@0 254 case FASTLZ: {
michael@0 255 // Use level 1 compression since we mostly care about speed.
michael@0 256 int destlen = fastlz_compress_level(
michael@0 257 1,
michael@0 258 input,
michael@0 259 input_size,
michael@0 260 string_as_array(compressed));
michael@0 261 if (!compressed_is_preallocated) {
michael@0 262 compressed->resize(destlen);
michael@0 263 }
michael@0 264 CHECK_NE(destlen, 0);
michael@0 265 break;
michael@0 266 }
michael@0 267 #endif // FASTLZ_VERSION
michael@0 268
michael@0 269 case SNAPPY: {
michael@0 270 size_t destlen;
michael@0 271 snappy::RawCompress(input, input_size,
michael@0 272 string_as_array(compressed),
michael@0 273 &destlen);
michael@0 274 CHECK_LE(destlen, snappy::MaxCompressedLength(input_size));
michael@0 275 if (!compressed_is_preallocated) {
michael@0 276 compressed->resize(destlen);
michael@0 277 }
michael@0 278 break;
michael@0 279 }
michael@0 280
michael@0 281
michael@0 282 default: {
michael@0 283 return false; // the asked-for library wasn't compiled in
michael@0 284 }
michael@0 285 }
michael@0 286 return true;
michael@0 287 }
michael@0 288
michael@0 289 static bool Uncompress(const string& compressed, CompressorType comp,
michael@0 290 int size, string* output) {
michael@0 291 switch (comp) {
michael@0 292 #ifdef ZLIB_VERSION
michael@0 293 case ZLIB: {
michael@0 294 output->resize(size);
michael@0 295 ZLib zlib;
michael@0 296 uLongf destlen = output->size();
michael@0 297 int ret = zlib.Uncompress(
michael@0 298 reinterpret_cast<Bytef*>(string_as_array(output)),
michael@0 299 &destlen,
michael@0 300 reinterpret_cast<const Bytef*>(compressed.data()),
michael@0 301 compressed.size());
michael@0 302 CHECK_EQ(Z_OK, ret);
michael@0 303 CHECK_EQ(static_cast<uLongf>(size), destlen);
michael@0 304 break;
michael@0 305 }
michael@0 306 #endif // ZLIB_VERSION
michael@0 307
michael@0 308 #ifdef LZO_VERSION
michael@0 309 case LZO: {
michael@0 310 output->resize(size);
michael@0 311 lzo_uint destlen;
michael@0 312 int ret = lzo1x_decompress(
michael@0 313 reinterpret_cast<const uint8*>(compressed.data()),
michael@0 314 compressed.size(),
michael@0 315 reinterpret_cast<uint8*>(string_as_array(output)),
michael@0 316 &destlen,
michael@0 317 NULL);
michael@0 318 CHECK_EQ(LZO_E_OK, ret);
michael@0 319 CHECK_EQ(static_cast<lzo_uint>(size), destlen);
michael@0 320 break;
michael@0 321 }
michael@0 322 #endif // LZO_VERSION
michael@0 323
michael@0 324 #ifdef LZF_VERSION
michael@0 325 case LIBLZF: {
michael@0 326 output->resize(size);
michael@0 327 int destlen = lzf_decompress(compressed.data(),
michael@0 328 compressed.size(),
michael@0 329 string_as_array(output),
michael@0 330 output->size());
michael@0 331 if (destlen == 0) {
michael@0 332 // This error probably means we had decided not to compress,
michael@0 333 // and thus have stored input in output directly.
michael@0 334 output->assign(compressed.data(), compressed.size());
michael@0 335 destlen = compressed.size();
michael@0 336 }
michael@0 337 CHECK_EQ(destlen, size);
michael@0 338 break;
michael@0 339 }
michael@0 340 #endif // LZF_VERSION
michael@0 341
michael@0 342 #ifdef QLZ_VERSION_MAJOR
michael@0 343 case QUICKLZ: {
michael@0 344 output->resize(size);
michael@0 345 qlz_state_decompress *state_decompress = new qlz_state_decompress;
michael@0 346 int destlen = qlz_decompress(compressed.data(),
michael@0 347 string_as_array(output),
michael@0 348 state_decompress);
michael@0 349 delete state_decompress;
michael@0 350 CHECK_EQ(destlen, size);
michael@0 351 break;
michael@0 352 }
michael@0 353 #endif // QLZ_VERSION_MAJOR
michael@0 354
michael@0 355 #ifdef FASTLZ_VERSION
michael@0 356 case FASTLZ: {
michael@0 357 output->resize(size);
michael@0 358 int destlen = fastlz_decompress(compressed.data(),
michael@0 359 compressed.length(),
michael@0 360 string_as_array(output),
michael@0 361 size);
michael@0 362 CHECK_EQ(destlen, size);
michael@0 363 break;
michael@0 364 }
michael@0 365 #endif // FASTLZ_VERSION
michael@0 366
michael@0 367 case SNAPPY: {
michael@0 368 snappy::RawUncompress(compressed.data(), compressed.size(),
michael@0 369 string_as_array(output));
michael@0 370 break;
michael@0 371 }
michael@0 372
michael@0 373
michael@0 374 default: {
michael@0 375 return false; // the asked-for library wasn't compiled in
michael@0 376 }
michael@0 377 }
michael@0 378 return true;
michael@0 379 }
michael@0 380
michael@0 381 static void Measure(const char* data,
michael@0 382 size_t length,
michael@0 383 CompressorType comp,
michael@0 384 int repeats,
michael@0 385 int block_size) {
michael@0 386 // Run tests a few time and pick median running times
michael@0 387 static const int kRuns = 5;
michael@0 388 double ctime[kRuns];
michael@0 389 double utime[kRuns];
michael@0 390 int compressed_size = 0;
michael@0 391
michael@0 392 {
michael@0 393 // Chop the input into blocks
michael@0 394 int num_blocks = (length + block_size - 1) / block_size;
michael@0 395 vector<const char*> input(num_blocks);
michael@0 396 vector<size_t> input_length(num_blocks);
michael@0 397 vector<string> compressed(num_blocks);
michael@0 398 vector<string> output(num_blocks);
michael@0 399 for (int b = 0; b < num_blocks; b++) {
michael@0 400 int input_start = b * block_size;
michael@0 401 int input_limit = min<int>((b+1)*block_size, length);
michael@0 402 input[b] = data+input_start;
michael@0 403 input_length[b] = input_limit-input_start;
michael@0 404
michael@0 405 // Pre-grow the output buffer so we don't measure string append time.
michael@0 406 compressed[b].resize(MinimumRequiredOutputSpace(block_size, comp));
michael@0 407 }
michael@0 408
michael@0 409 // First, try one trial compression to make sure the code is compiled in
michael@0 410 if (!Compress(input[0], input_length[0], comp, &compressed[0], true)) {
michael@0 411 LOG(WARNING) << "Skipping " << names[comp] << ": "
michael@0 412 << "library not compiled in";
michael@0 413 return;
michael@0 414 }
michael@0 415
michael@0 416 for (int run = 0; run < kRuns; run++) {
michael@0 417 CycleTimer ctimer, utimer;
michael@0 418
michael@0 419 for (int b = 0; b < num_blocks; b++) {
michael@0 420 // Pre-grow the output buffer so we don't measure string append time.
michael@0 421 compressed[b].resize(MinimumRequiredOutputSpace(block_size, comp));
michael@0 422 }
michael@0 423
michael@0 424 ctimer.Start();
michael@0 425 for (int b = 0; b < num_blocks; b++)
michael@0 426 for (int i = 0; i < repeats; i++)
michael@0 427 Compress(input[b], input_length[b], comp, &compressed[b], true);
michael@0 428 ctimer.Stop();
michael@0 429
michael@0 430 // Compress once more, with resizing, so we don't leave junk
michael@0 431 // at the end that will confuse the decompressor.
michael@0 432 for (int b = 0; b < num_blocks; b++) {
michael@0 433 Compress(input[b], input_length[b], comp, &compressed[b], false);
michael@0 434 }
michael@0 435
michael@0 436 for (int b = 0; b < num_blocks; b++) {
michael@0 437 output[b].resize(input_length[b]);
michael@0 438 }
michael@0 439
michael@0 440 utimer.Start();
michael@0 441 for (int i = 0; i < repeats; i++)
michael@0 442 for (int b = 0; b < num_blocks; b++)
michael@0 443 Uncompress(compressed[b], comp, input_length[b], &output[b]);
michael@0 444 utimer.Stop();
michael@0 445
michael@0 446 ctime[run] = ctimer.Get();
michael@0 447 utime[run] = utimer.Get();
michael@0 448 }
michael@0 449
michael@0 450 compressed_size = 0;
michael@0 451 for (int i = 0; i < compressed.size(); i++) {
michael@0 452 compressed_size += compressed[i].size();
michael@0 453 }
michael@0 454 }
michael@0 455
michael@0 456 sort(ctime, ctime + kRuns);
michael@0 457 sort(utime, utime + kRuns);
michael@0 458 const int med = kRuns/2;
michael@0 459
michael@0 460 float comp_rate = (length / ctime[med]) * repeats / 1048576.0;
michael@0 461 float uncomp_rate = (length / utime[med]) * repeats / 1048576.0;
michael@0 462 string x = names[comp];
michael@0 463 x += ":";
michael@0 464 string urate = (uncomp_rate >= 0)
michael@0 465 ? StringPrintf("%.1f", uncomp_rate)
michael@0 466 : string("?");
michael@0 467 printf("%-7s [b %dM] bytes %6d -> %6d %4.1f%% "
michael@0 468 "comp %5.1f MB/s uncomp %5s MB/s\n",
michael@0 469 x.c_str(),
michael@0 470 block_size/(1<<20),
michael@0 471 static_cast<int>(length), static_cast<uint32>(compressed_size),
michael@0 472 (compressed_size * 100.0) / max<int>(1, length),
michael@0 473 comp_rate,
michael@0 474 urate.c_str());
michael@0 475 }
michael@0 476
michael@0 477
michael@0 478 static int VerifyString(const string& input) {
michael@0 479 string compressed;
michael@0 480 DataEndingAtUnreadablePage i(input);
michael@0 481 const size_t written = snappy::Compress(i.data(), i.size(), &compressed);
michael@0 482 CHECK_EQ(written, compressed.size());
michael@0 483 CHECK_LE(compressed.size(),
michael@0 484 snappy::MaxCompressedLength(input.size()));
michael@0 485 CHECK(snappy::IsValidCompressedBuffer(compressed.data(), compressed.size()));
michael@0 486
michael@0 487 string uncompressed;
michael@0 488 DataEndingAtUnreadablePage c(compressed);
michael@0 489 CHECK(snappy::Uncompress(c.data(), c.size(), &uncompressed));
michael@0 490 CHECK_EQ(uncompressed, input);
michael@0 491 return uncompressed.size();
michael@0 492 }
michael@0 493
michael@0 494
michael@0 495 // Test that data compressed by a compressor that does not
michael@0 496 // obey block sizes is uncompressed properly.
michael@0 497 static void VerifyNonBlockedCompression(const string& input) {
michael@0 498 if (input.length() > snappy::kBlockSize) {
michael@0 499 // We cannot test larger blocks than the maximum block size, obviously.
michael@0 500 return;
michael@0 501 }
michael@0 502
michael@0 503 string prefix;
michael@0 504 Varint::Append32(&prefix, input.size());
michael@0 505
michael@0 506 // Setup compression table
michael@0 507 snappy::internal::WorkingMemory wmem;
michael@0 508 int table_size;
michael@0 509 uint16* table = wmem.GetHashTable(input.size(), &table_size);
michael@0 510
michael@0 511 // Compress entire input in one shot
michael@0 512 string compressed;
michael@0 513 compressed += prefix;
michael@0 514 compressed.resize(prefix.size()+snappy::MaxCompressedLength(input.size()));
michael@0 515 char* dest = string_as_array(&compressed) + prefix.size();
michael@0 516 char* end = snappy::internal::CompressFragment(input.data(), input.size(),
michael@0 517 dest, table, table_size);
michael@0 518 compressed.resize(end - compressed.data());
michael@0 519
michael@0 520 // Uncompress into string
michael@0 521 string uncomp_str;
michael@0 522 CHECK(snappy::Uncompress(compressed.data(), compressed.size(), &uncomp_str));
michael@0 523 CHECK_EQ(uncomp_str, input);
michael@0 524
michael@0 525 }
michael@0 526
michael@0 527 // Expand the input so that it is at least K times as big as block size
michael@0 528 static string Expand(const string& input) {
michael@0 529 static const int K = 3;
michael@0 530 string data = input;
michael@0 531 while (data.size() < K * snappy::kBlockSize) {
michael@0 532 data += input;
michael@0 533 }
michael@0 534 return data;
michael@0 535 }
michael@0 536
michael@0 537 static int Verify(const string& input) {
michael@0 538 VLOG(1) << "Verifying input of size " << input.size();
michael@0 539
michael@0 540 // Compress using string based routines
michael@0 541 const int result = VerifyString(input);
michael@0 542
michael@0 543
michael@0 544 VerifyNonBlockedCompression(input);
michael@0 545 if (!input.empty()) {
michael@0 546 VerifyNonBlockedCompression(Expand(input));
michael@0 547 }
michael@0 548
michael@0 549
michael@0 550 return result;
michael@0 551 }
michael@0 552
michael@0 553 // This test checks to ensure that snappy doesn't coredump if it gets
michael@0 554 // corrupted data.
michael@0 555
michael@0 556 static bool IsValidCompressedBuffer(const string& c) {
michael@0 557 return snappy::IsValidCompressedBuffer(c.data(), c.size());
michael@0 558 }
michael@0 559 static bool Uncompress(const string& c, string* u) {
michael@0 560 return snappy::Uncompress(c.data(), c.size(), u);
michael@0 561 }
michael@0 562
michael@0 563 TYPED_TEST(CorruptedTest, VerifyCorrupted) {
michael@0 564 string source = "making sure we don't crash with corrupted input";
michael@0 565 VLOG(1) << source;
michael@0 566 string dest;
michael@0 567 TypeParam uncmp;
michael@0 568 snappy::Compress(source.data(), source.size(), &dest);
michael@0 569
michael@0 570 // Mess around with the data. It's hard to simulate all possible
michael@0 571 // corruptions; this is just one example ...
michael@0 572 CHECK_GT(dest.size(), 3);
michael@0 573 dest[1]--;
michael@0 574 dest[3]++;
michael@0 575 // this really ought to fail.
michael@0 576 CHECK(!IsValidCompressedBuffer(TypeParam(dest)));
michael@0 577 CHECK(!Uncompress(TypeParam(dest), &uncmp));
michael@0 578
michael@0 579 // This is testing for a security bug - a buffer that decompresses to 100k
michael@0 580 // but we lie in the snappy header and only reserve 0 bytes of memory :)
michael@0 581 source.resize(100000);
michael@0 582 for (int i = 0; i < source.length(); ++i) {
michael@0 583 source[i] = 'A';
michael@0 584 }
michael@0 585 snappy::Compress(source.data(), source.size(), &dest);
michael@0 586 dest[0] = dest[1] = dest[2] = dest[3] = 0;
michael@0 587 CHECK(!IsValidCompressedBuffer(TypeParam(dest)));
michael@0 588 CHECK(!Uncompress(TypeParam(dest), &uncmp));
michael@0 589
michael@0 590 if (sizeof(void *) == 4) {
michael@0 591 // Another security check; check a crazy big length can't DoS us with an
michael@0 592 // over-allocation.
michael@0 593 // Currently this is done only for 32-bit builds. On 64-bit builds,
michael@0 594 // where 3GBytes might be an acceptable allocation size, Uncompress()
michael@0 595 // attempts to decompress, and sometimes causes the test to run out of
michael@0 596 // memory.
michael@0 597 dest[0] = dest[1] = dest[2] = dest[3] = 0xff;
michael@0 598 // This decodes to a really large size, i.e., 3221225471 bytes
michael@0 599 dest[4] = 'k';
michael@0 600 CHECK(!IsValidCompressedBuffer(TypeParam(dest)));
michael@0 601 CHECK(!Uncompress(TypeParam(dest), &uncmp));
michael@0 602 dest[0] = dest[1] = dest[2] = 0xff;
michael@0 603 dest[3] = 0x7f;
michael@0 604 CHECK(!IsValidCompressedBuffer(TypeParam(dest)));
michael@0 605 CHECK(!Uncompress(TypeParam(dest), &uncmp));
michael@0 606 } else {
michael@0 607 LOG(WARNING) << "Crazy decompression lengths not checked on 64-bit build";
michael@0 608 }
michael@0 609
michael@0 610 // try reading stuff in from a bad file.
michael@0 611 for (int i = 1; i <= 3; ++i) {
michael@0 612 string data = ReadTestDataFile(StringPrintf("baddata%d.snappy", i).c_str());
michael@0 613 string uncmp;
michael@0 614 // check that we don't return a crazy length
michael@0 615 size_t ulen;
michael@0 616 CHECK(!snappy::GetUncompressedLength(data.data(), data.size(), &ulen)
michael@0 617 || (ulen < (1<<20)));
michael@0 618 uint32 ulen2;
michael@0 619 snappy::ByteArraySource source(data.data(), data.size());
michael@0 620 CHECK(!snappy::GetUncompressedLength(&source, &ulen2) ||
michael@0 621 (ulen2 < (1<<20)));
michael@0 622 CHECK(!IsValidCompressedBuffer(TypeParam(data)));
michael@0 623 CHECK(!Uncompress(TypeParam(data), &uncmp));
michael@0 624 }
michael@0 625 }
michael@0 626
michael@0 627 // Helper routines to construct arbitrary compressed strings.
michael@0 628 // These mirror the compression code in snappy.cc, but are copied
michael@0 629 // here so that we can bypass some limitations in the how snappy.cc
michael@0 630 // invokes these routines.
michael@0 631 static void AppendLiteral(string* dst, const string& literal) {
michael@0 632 if (literal.empty()) return;
michael@0 633 int n = literal.size() - 1;
michael@0 634 if (n < 60) {
michael@0 635 // Fit length in tag byte
michael@0 636 dst->push_back(0 | (n << 2));
michael@0 637 } else {
michael@0 638 // Encode in upcoming bytes
michael@0 639 char number[4];
michael@0 640 int count = 0;
michael@0 641 while (n > 0) {
michael@0 642 number[count++] = n & 0xff;
michael@0 643 n >>= 8;
michael@0 644 }
michael@0 645 dst->push_back(0 | ((59+count) << 2));
michael@0 646 *dst += string(number, count);
michael@0 647 }
michael@0 648 *dst += literal;
michael@0 649 }
michael@0 650
michael@0 651 static void AppendCopy(string* dst, int offset, int length) {
michael@0 652 while (length > 0) {
michael@0 653 // Figure out how much to copy in one shot
michael@0 654 int to_copy;
michael@0 655 if (length >= 68) {
michael@0 656 to_copy = 64;
michael@0 657 } else if (length > 64) {
michael@0 658 to_copy = 60;
michael@0 659 } else {
michael@0 660 to_copy = length;
michael@0 661 }
michael@0 662 length -= to_copy;
michael@0 663
michael@0 664 if ((to_copy < 12) && (offset < 2048)) {
michael@0 665 assert(to_copy-4 < 8); // Must fit in 3 bits
michael@0 666 dst->push_back(1 | ((to_copy-4) << 2) | ((offset >> 8) << 5));
michael@0 667 dst->push_back(offset & 0xff);
michael@0 668 } else if (offset < 65536) {
michael@0 669 dst->push_back(2 | ((to_copy-1) << 2));
michael@0 670 dst->push_back(offset & 0xff);
michael@0 671 dst->push_back(offset >> 8);
michael@0 672 } else {
michael@0 673 dst->push_back(3 | ((to_copy-1) << 2));
michael@0 674 dst->push_back(offset & 0xff);
michael@0 675 dst->push_back((offset >> 8) & 0xff);
michael@0 676 dst->push_back((offset >> 16) & 0xff);
michael@0 677 dst->push_back((offset >> 24) & 0xff);
michael@0 678 }
michael@0 679 }
michael@0 680 }
michael@0 681
michael@0 682 TEST(Snappy, SimpleTests) {
michael@0 683 Verify("");
michael@0 684 Verify("a");
michael@0 685 Verify("ab");
michael@0 686 Verify("abc");
michael@0 687
michael@0 688 Verify("aaaaaaa" + string(16, 'b') + string("aaaaa") + "abc");
michael@0 689 Verify("aaaaaaa" + string(256, 'b') + string("aaaaa") + "abc");
michael@0 690 Verify("aaaaaaa" + string(2047, 'b') + string("aaaaa") + "abc");
michael@0 691 Verify("aaaaaaa" + string(65536, 'b') + string("aaaaa") + "abc");
michael@0 692 Verify("abcaaaaaaa" + string(65536, 'b') + string("aaaaa") + "abc");
michael@0 693 }
michael@0 694
michael@0 695 // Verify max blowup (lots of four-byte copies)
michael@0 696 TEST(Snappy, MaxBlowup) {
michael@0 697 string input;
michael@0 698 for (int i = 0; i < 20000; i++) {
michael@0 699 ACMRandom rnd(i);
michael@0 700 uint32 bytes = static_cast<uint32>(rnd.Next());
michael@0 701 input.append(reinterpret_cast<char*>(&bytes), sizeof(bytes));
michael@0 702 }
michael@0 703 for (int i = 19999; i >= 0; i--) {
michael@0 704 ACMRandom rnd(i);
michael@0 705 uint32 bytes = static_cast<uint32>(rnd.Next());
michael@0 706 input.append(reinterpret_cast<char*>(&bytes), sizeof(bytes));
michael@0 707 }
michael@0 708 Verify(input);
michael@0 709 }
michael@0 710
michael@0 711 TEST(Snappy, RandomData) {
michael@0 712 ACMRandom rnd(FLAGS_test_random_seed);
michael@0 713
michael@0 714 const int num_ops = 20000;
michael@0 715 for (int i = 0; i < num_ops; i++) {
michael@0 716 if ((i % 1000) == 0) {
michael@0 717 VLOG(0) << "Random op " << i << " of " << num_ops;
michael@0 718 }
michael@0 719
michael@0 720 string x;
michael@0 721 int len = rnd.Uniform(4096);
michael@0 722 if (i < 100) {
michael@0 723 len = 65536 + rnd.Uniform(65536);
michael@0 724 }
michael@0 725 while (x.size() < len) {
michael@0 726 int run_len = 1;
michael@0 727 if (rnd.OneIn(10)) {
michael@0 728 run_len = rnd.Skewed(8);
michael@0 729 }
michael@0 730 char c = (i < 100) ? rnd.Uniform(256) : rnd.Skewed(3);
michael@0 731 while (run_len-- > 0 && x.size() < len) {
michael@0 732 x += c;
michael@0 733 }
michael@0 734 }
michael@0 735
michael@0 736 Verify(x);
michael@0 737 }
michael@0 738 }
michael@0 739
michael@0 740 TEST(Snappy, FourByteOffset) {
michael@0 741 // The new compressor cannot generate four-byte offsets since
michael@0 742 // it chops up the input into 32KB pieces. So we hand-emit the
michael@0 743 // copy manually.
michael@0 744
michael@0 745 // The two fragments that make up the input string.
michael@0 746 string fragment1 = "012345689abcdefghijklmnopqrstuvwxyz";
michael@0 747 string fragment2 = "some other string";
michael@0 748
michael@0 749 // How many times each fragment is emitted.
michael@0 750 const int n1 = 2;
michael@0 751 const int n2 = 100000 / fragment2.size();
michael@0 752 const int length = n1 * fragment1.size() + n2 * fragment2.size();
michael@0 753
michael@0 754 string compressed;
michael@0 755 Varint::Append32(&compressed, length);
michael@0 756
michael@0 757 AppendLiteral(&compressed, fragment1);
michael@0 758 string src = fragment1;
michael@0 759 for (int i = 0; i < n2; i++) {
michael@0 760 AppendLiteral(&compressed, fragment2);
michael@0 761 src += fragment2;
michael@0 762 }
michael@0 763 AppendCopy(&compressed, src.size(), fragment1.size());
michael@0 764 src += fragment1;
michael@0 765 CHECK_EQ(length, src.size());
michael@0 766
michael@0 767 string uncompressed;
michael@0 768 CHECK(snappy::IsValidCompressedBuffer(compressed.data(), compressed.size()));
michael@0 769 CHECK(snappy::Uncompress(compressed.data(), compressed.size(), &uncompressed));
michael@0 770 CHECK_EQ(uncompressed, src);
michael@0 771 }
michael@0 772
michael@0 773
michael@0 774 static bool CheckUncompressedLength(const string& compressed,
michael@0 775 size_t* ulength) {
michael@0 776 const bool result1 = snappy::GetUncompressedLength(compressed.data(),
michael@0 777 compressed.size(),
michael@0 778 ulength);
michael@0 779
michael@0 780 snappy::ByteArraySource source(compressed.data(), compressed.size());
michael@0 781 uint32 length;
michael@0 782 const bool result2 = snappy::GetUncompressedLength(&source, &length);
michael@0 783 CHECK_EQ(result1, result2);
michael@0 784 return result1;
michael@0 785 }
michael@0 786
michael@0 787 TEST(SnappyCorruption, TruncatedVarint) {
michael@0 788 string compressed, uncompressed;
michael@0 789 size_t ulength;
michael@0 790 compressed.push_back('\xf0');
michael@0 791 CHECK(!CheckUncompressedLength(compressed, &ulength));
michael@0 792 CHECK(!snappy::IsValidCompressedBuffer(compressed.data(), compressed.size()));
michael@0 793 CHECK(!snappy::Uncompress(compressed.data(), compressed.size(),
michael@0 794 &uncompressed));
michael@0 795 }
michael@0 796
michael@0 797 TEST(SnappyCorruption, UnterminatedVarint) {
michael@0 798 string compressed, uncompressed;
michael@0 799 size_t ulength;
michael@0 800 compressed.push_back(128);
michael@0 801 compressed.push_back(128);
michael@0 802 compressed.push_back(128);
michael@0 803 compressed.push_back(128);
michael@0 804 compressed.push_back(128);
michael@0 805 compressed.push_back(10);
michael@0 806 CHECK(!CheckUncompressedLength(compressed, &ulength));
michael@0 807 CHECK(!snappy::IsValidCompressedBuffer(compressed.data(), compressed.size()));
michael@0 808 CHECK(!snappy::Uncompress(compressed.data(), compressed.size(),
michael@0 809 &uncompressed));
michael@0 810 }
michael@0 811
michael@0 812 TEST(Snappy, ReadPastEndOfBuffer) {
michael@0 813 // Check that we do not read past end of input
michael@0 814
michael@0 815 // Make a compressed string that ends with a single-byte literal
michael@0 816 string compressed;
michael@0 817 Varint::Append32(&compressed, 1);
michael@0 818 AppendLiteral(&compressed, "x");
michael@0 819
michael@0 820 string uncompressed;
michael@0 821 DataEndingAtUnreadablePage c(compressed);
michael@0 822 CHECK(snappy::Uncompress(c.data(), c.size(), &uncompressed));
michael@0 823 CHECK_EQ(uncompressed, string("x"));
michael@0 824 }
michael@0 825
michael@0 826 // Check for an infinite loop caused by a copy with offset==0
michael@0 827 TEST(Snappy, ZeroOffsetCopy) {
michael@0 828 const char* compressed = "\x40\x12\x00\x00";
michael@0 829 // \x40 Length (must be > kMaxIncrementCopyOverflow)
michael@0 830 // \x12\x00\x00 Copy with offset==0, length==5
michael@0 831 char uncompressed[100];
michael@0 832 EXPECT_FALSE(snappy::RawUncompress(compressed, 4, uncompressed));
michael@0 833 }
michael@0 834
michael@0 835 TEST(Snappy, ZeroOffsetCopyValidation) {
michael@0 836 const char* compressed = "\x05\x12\x00\x00";
michael@0 837 // \x05 Length
michael@0 838 // \x12\x00\x00 Copy with offset==0, length==5
michael@0 839 EXPECT_FALSE(snappy::IsValidCompressedBuffer(compressed, 4));
michael@0 840 }
michael@0 841
michael@0 842
michael@0 843 namespace {
michael@0 844
michael@0 845 int TestFindMatchLength(const char* s1, const char *s2, unsigned length) {
michael@0 846 return snappy::internal::FindMatchLength(s1, s2, s2 + length);
michael@0 847 }
michael@0 848
michael@0 849 } // namespace
michael@0 850
michael@0 851 TEST(Snappy, FindMatchLength) {
michael@0 852 // Exercise all different code paths through the function.
michael@0 853 // 64-bit version:
michael@0 854
michael@0 855 // Hit s1_limit in 64-bit loop, hit s1_limit in single-character loop.
michael@0 856 EXPECT_EQ(6, TestFindMatchLength("012345", "012345", 6));
michael@0 857 EXPECT_EQ(11, TestFindMatchLength("01234567abc", "01234567abc", 11));
michael@0 858
michael@0 859 // Hit s1_limit in 64-bit loop, find a non-match in single-character loop.
michael@0 860 EXPECT_EQ(9, TestFindMatchLength("01234567abc", "01234567axc", 9));
michael@0 861
michael@0 862 // Same, but edge cases.
michael@0 863 EXPECT_EQ(11, TestFindMatchLength("01234567abc!", "01234567abc!", 11));
michael@0 864 EXPECT_EQ(11, TestFindMatchLength("01234567abc!", "01234567abc?", 11));
michael@0 865
michael@0 866 // Find non-match at once in first loop.
michael@0 867 EXPECT_EQ(0, TestFindMatchLength("01234567xxxxxxxx", "?1234567xxxxxxxx", 16));
michael@0 868 EXPECT_EQ(1, TestFindMatchLength("01234567xxxxxxxx", "0?234567xxxxxxxx", 16));
michael@0 869 EXPECT_EQ(4, TestFindMatchLength("01234567xxxxxxxx", "01237654xxxxxxxx", 16));
michael@0 870 EXPECT_EQ(7, TestFindMatchLength("01234567xxxxxxxx", "0123456?xxxxxxxx", 16));
michael@0 871
michael@0 872 // Find non-match in first loop after one block.
michael@0 873 EXPECT_EQ(8, TestFindMatchLength("abcdefgh01234567xxxxxxxx",
michael@0 874 "abcdefgh?1234567xxxxxxxx", 24));
michael@0 875 EXPECT_EQ(9, TestFindMatchLength("abcdefgh01234567xxxxxxxx",
michael@0 876 "abcdefgh0?234567xxxxxxxx", 24));
michael@0 877 EXPECT_EQ(12, TestFindMatchLength("abcdefgh01234567xxxxxxxx",
michael@0 878 "abcdefgh01237654xxxxxxxx", 24));
michael@0 879 EXPECT_EQ(15, TestFindMatchLength("abcdefgh01234567xxxxxxxx",
michael@0 880 "abcdefgh0123456?xxxxxxxx", 24));
michael@0 881
michael@0 882 // 32-bit version:
michael@0 883
michael@0 884 // Short matches.
michael@0 885 EXPECT_EQ(0, TestFindMatchLength("01234567", "?1234567", 8));
michael@0 886 EXPECT_EQ(1, TestFindMatchLength("01234567", "0?234567", 8));
michael@0 887 EXPECT_EQ(2, TestFindMatchLength("01234567", "01?34567", 8));
michael@0 888 EXPECT_EQ(3, TestFindMatchLength("01234567", "012?4567", 8));
michael@0 889 EXPECT_EQ(4, TestFindMatchLength("01234567", "0123?567", 8));
michael@0 890 EXPECT_EQ(5, TestFindMatchLength("01234567", "01234?67", 8));
michael@0 891 EXPECT_EQ(6, TestFindMatchLength("01234567", "012345?7", 8));
michael@0 892 EXPECT_EQ(7, TestFindMatchLength("01234567", "0123456?", 8));
michael@0 893 EXPECT_EQ(7, TestFindMatchLength("01234567", "0123456?", 7));
michael@0 894 EXPECT_EQ(7, TestFindMatchLength("01234567!", "0123456??", 7));
michael@0 895
michael@0 896 // Hit s1_limit in 32-bit loop, hit s1_limit in single-character loop.
michael@0 897 EXPECT_EQ(10, TestFindMatchLength("xxxxxxabcd", "xxxxxxabcd", 10));
michael@0 898 EXPECT_EQ(10, TestFindMatchLength("xxxxxxabcd?", "xxxxxxabcd?", 10));
michael@0 899 EXPECT_EQ(13, TestFindMatchLength("xxxxxxabcdef", "xxxxxxabcdef", 13));
michael@0 900
michael@0 901 // Same, but edge cases.
michael@0 902 EXPECT_EQ(12, TestFindMatchLength("xxxxxx0123abc!", "xxxxxx0123abc!", 12));
michael@0 903 EXPECT_EQ(12, TestFindMatchLength("xxxxxx0123abc!", "xxxxxx0123abc?", 12));
michael@0 904
michael@0 905 // Hit s1_limit in 32-bit loop, find a non-match in single-character loop.
michael@0 906 EXPECT_EQ(11, TestFindMatchLength("xxxxxx0123abc", "xxxxxx0123axc", 13));
michael@0 907
michael@0 908 // Find non-match at once in first loop.
michael@0 909 EXPECT_EQ(6, TestFindMatchLength("xxxxxx0123xxxxxxxx",
michael@0 910 "xxxxxx?123xxxxxxxx", 18));
michael@0 911 EXPECT_EQ(7, TestFindMatchLength("xxxxxx0123xxxxxxxx",
michael@0 912 "xxxxxx0?23xxxxxxxx", 18));
michael@0 913 EXPECT_EQ(8, TestFindMatchLength("xxxxxx0123xxxxxxxx",
michael@0 914 "xxxxxx0132xxxxxxxx", 18));
michael@0 915 EXPECT_EQ(9, TestFindMatchLength("xxxxxx0123xxxxxxxx",
michael@0 916 "xxxxxx012?xxxxxxxx", 18));
michael@0 917
michael@0 918 // Same, but edge cases.
michael@0 919 EXPECT_EQ(6, TestFindMatchLength("xxxxxx0123", "xxxxxx?123", 10));
michael@0 920 EXPECT_EQ(7, TestFindMatchLength("xxxxxx0123", "xxxxxx0?23", 10));
michael@0 921 EXPECT_EQ(8, TestFindMatchLength("xxxxxx0123", "xxxxxx0132", 10));
michael@0 922 EXPECT_EQ(9, TestFindMatchLength("xxxxxx0123", "xxxxxx012?", 10));
michael@0 923
michael@0 924 // Find non-match in first loop after one block.
michael@0 925 EXPECT_EQ(10, TestFindMatchLength("xxxxxxabcd0123xx",
michael@0 926 "xxxxxxabcd?123xx", 16));
michael@0 927 EXPECT_EQ(11, TestFindMatchLength("xxxxxxabcd0123xx",
michael@0 928 "xxxxxxabcd0?23xx", 16));
michael@0 929 EXPECT_EQ(12, TestFindMatchLength("xxxxxxabcd0123xx",
michael@0 930 "xxxxxxabcd0132xx", 16));
michael@0 931 EXPECT_EQ(13, TestFindMatchLength("xxxxxxabcd0123xx",
michael@0 932 "xxxxxxabcd012?xx", 16));
michael@0 933
michael@0 934 // Same, but edge cases.
michael@0 935 EXPECT_EQ(10, TestFindMatchLength("xxxxxxabcd0123", "xxxxxxabcd?123", 14));
michael@0 936 EXPECT_EQ(11, TestFindMatchLength("xxxxxxabcd0123", "xxxxxxabcd0?23", 14));
michael@0 937 EXPECT_EQ(12, TestFindMatchLength("xxxxxxabcd0123", "xxxxxxabcd0132", 14));
michael@0 938 EXPECT_EQ(13, TestFindMatchLength("xxxxxxabcd0123", "xxxxxxabcd012?", 14));
michael@0 939 }
michael@0 940
michael@0 941 TEST(Snappy, FindMatchLengthRandom) {
michael@0 942 const int kNumTrials = 10000;
michael@0 943 const int kTypicalLength = 10;
michael@0 944 ACMRandom rnd(FLAGS_test_random_seed);
michael@0 945
michael@0 946 for (int i = 0; i < kNumTrials; i++) {
michael@0 947 string s, t;
michael@0 948 char a = rnd.Rand8();
michael@0 949 char b = rnd.Rand8();
michael@0 950 while (!rnd.OneIn(kTypicalLength)) {
michael@0 951 s.push_back(rnd.OneIn(2) ? a : b);
michael@0 952 t.push_back(rnd.OneIn(2) ? a : b);
michael@0 953 }
michael@0 954 DataEndingAtUnreadablePage u(s);
michael@0 955 DataEndingAtUnreadablePage v(t);
michael@0 956 int matched = snappy::internal::FindMatchLength(
michael@0 957 u.data(), v.data(), v.data() + t.size());
michael@0 958 if (matched == t.size()) {
michael@0 959 EXPECT_EQ(s, t);
michael@0 960 } else {
michael@0 961 EXPECT_NE(s[matched], t[matched]);
michael@0 962 for (int j = 0; j < matched; j++) {
michael@0 963 EXPECT_EQ(s[j], t[j]);
michael@0 964 }
michael@0 965 }
michael@0 966 }
michael@0 967 }
michael@0 968
michael@0 969
michael@0 970 static void CompressFile(const char* fname) {
michael@0 971 string fullinput;
michael@0 972 File::ReadFileToStringOrDie(fname, &fullinput);
michael@0 973
michael@0 974 string compressed;
michael@0 975 Compress(fullinput.data(), fullinput.size(), SNAPPY, &compressed, false);
michael@0 976
michael@0 977 File::WriteStringToFileOrDie(compressed,
michael@0 978 string(fname).append(".comp").c_str());
michael@0 979 }
michael@0 980
michael@0 981 static void UncompressFile(const char* fname) {
michael@0 982 string fullinput;
michael@0 983 File::ReadFileToStringOrDie(fname, &fullinput);
michael@0 984
michael@0 985 size_t uncompLength;
michael@0 986 CHECK(CheckUncompressedLength(fullinput, &uncompLength));
michael@0 987
michael@0 988 string uncompressed;
michael@0 989 uncompressed.resize(uncompLength);
michael@0 990 CHECK(snappy::Uncompress(fullinput.data(), fullinput.size(), &uncompressed));
michael@0 991
michael@0 992 File::WriteStringToFileOrDie(uncompressed,
michael@0 993 string(fname).append(".uncomp").c_str());
michael@0 994 }
michael@0 995
michael@0 996 static void MeasureFile(const char* fname) {
michael@0 997 string fullinput;
michael@0 998 File::ReadFileToStringOrDie(fname, &fullinput);
michael@0 999 printf("%-40s :\n", fname);
michael@0 1000
michael@0 1001 int start_len = (FLAGS_start_len < 0) ? fullinput.size() : FLAGS_start_len;
michael@0 1002 int end_len = fullinput.size();
michael@0 1003 if (FLAGS_end_len >= 0) {
michael@0 1004 end_len = min<int>(fullinput.size(), FLAGS_end_len);
michael@0 1005 }
michael@0 1006 for (int len = start_len; len <= end_len; len++) {
michael@0 1007 const char* const input = fullinput.data();
michael@0 1008 int repeats = (FLAGS_bytes + len) / (len + 1);
michael@0 1009 if (FLAGS_zlib) Measure(input, len, ZLIB, repeats, 1024<<10);
michael@0 1010 if (FLAGS_lzo) Measure(input, len, LZO, repeats, 1024<<10);
michael@0 1011 if (FLAGS_liblzf) Measure(input, len, LIBLZF, repeats, 1024<<10);
michael@0 1012 if (FLAGS_quicklz) Measure(input, len, QUICKLZ, repeats, 1024<<10);
michael@0 1013 if (FLAGS_fastlz) Measure(input, len, FASTLZ, repeats, 1024<<10);
michael@0 1014 if (FLAGS_snappy) Measure(input, len, SNAPPY, repeats, 4096<<10);
michael@0 1015
michael@0 1016 // For block-size based measurements
michael@0 1017 if (0 && FLAGS_snappy) {
michael@0 1018 Measure(input, len, SNAPPY, repeats, 8<<10);
michael@0 1019 Measure(input, len, SNAPPY, repeats, 16<<10);
michael@0 1020 Measure(input, len, SNAPPY, repeats, 32<<10);
michael@0 1021 Measure(input, len, SNAPPY, repeats, 64<<10);
michael@0 1022 Measure(input, len, SNAPPY, repeats, 256<<10);
michael@0 1023 Measure(input, len, SNAPPY, repeats, 1024<<10);
michael@0 1024 }
michael@0 1025 }
michael@0 1026 }
michael@0 1027
michael@0 1028 static struct {
michael@0 1029 const char* label;
michael@0 1030 const char* filename;
michael@0 1031 } files[] = {
michael@0 1032 { "html", "html" },
michael@0 1033 { "urls", "urls.10K" },
michael@0 1034 { "jpg", "house.jpg" },
michael@0 1035 { "pdf", "mapreduce-osdi-1.pdf" },
michael@0 1036 { "html4", "html_x_4" },
michael@0 1037 { "cp", "cp.html" },
michael@0 1038 { "c", "fields.c" },
michael@0 1039 { "lsp", "grammar.lsp" },
michael@0 1040 { "xls", "kennedy.xls" },
michael@0 1041 { "txt1", "alice29.txt" },
michael@0 1042 { "txt2", "asyoulik.txt" },
michael@0 1043 { "txt3", "lcet10.txt" },
michael@0 1044 { "txt4", "plrabn12.txt" },
michael@0 1045 { "bin", "ptt5" },
michael@0 1046 { "sum", "sum" },
michael@0 1047 { "man", "xargs.1" },
michael@0 1048 { "pb", "geo.protodata" },
michael@0 1049 { "gaviota", "kppkn.gtb" },
michael@0 1050 };
michael@0 1051
michael@0 1052 static void BM_UFlat(int iters, int arg) {
michael@0 1053 StopBenchmarkTiming();
michael@0 1054
michael@0 1055 // Pick file to process based on "arg"
michael@0 1056 CHECK_GE(arg, 0);
michael@0 1057 CHECK_LT(arg, ARRAYSIZE(files));
michael@0 1058 string contents = ReadTestDataFile(files[arg].filename);
michael@0 1059
michael@0 1060 string zcontents;
michael@0 1061 snappy::Compress(contents.data(), contents.size(), &zcontents);
michael@0 1062 char* dst = new char[contents.size()];
michael@0 1063
michael@0 1064 SetBenchmarkBytesProcessed(static_cast<int64>(iters) *
michael@0 1065 static_cast<int64>(contents.size()));
michael@0 1066 SetBenchmarkLabel(files[arg].label);
michael@0 1067 StartBenchmarkTiming();
michael@0 1068 while (iters-- > 0) {
michael@0 1069 CHECK(snappy::RawUncompress(zcontents.data(), zcontents.size(), dst));
michael@0 1070 }
michael@0 1071 StopBenchmarkTiming();
michael@0 1072
michael@0 1073 delete[] dst;
michael@0 1074 }
michael@0 1075 BENCHMARK(BM_UFlat)->DenseRange(0, 17);
michael@0 1076
michael@0 1077 static void BM_UValidate(int iters, int arg) {
michael@0 1078 StopBenchmarkTiming();
michael@0 1079
michael@0 1080 // Pick file to process based on "arg"
michael@0 1081 CHECK_GE(arg, 0);
michael@0 1082 CHECK_LT(arg, ARRAYSIZE(files));
michael@0 1083 string contents = ReadTestDataFile(files[arg].filename);
michael@0 1084
michael@0 1085 string zcontents;
michael@0 1086 snappy::Compress(contents.data(), contents.size(), &zcontents);
michael@0 1087
michael@0 1088 SetBenchmarkBytesProcessed(static_cast<int64>(iters) *
michael@0 1089 static_cast<int64>(contents.size()));
michael@0 1090 SetBenchmarkLabel(files[arg].label);
michael@0 1091 StartBenchmarkTiming();
michael@0 1092 while (iters-- > 0) {
michael@0 1093 CHECK(snappy::IsValidCompressedBuffer(zcontents.data(), zcontents.size()));
michael@0 1094 }
michael@0 1095 StopBenchmarkTiming();
michael@0 1096 }
michael@0 1097 BENCHMARK(BM_UValidate)->DenseRange(0, 4);
michael@0 1098
michael@0 1099
michael@0 1100 static void BM_ZFlat(int iters, int arg) {
michael@0 1101 StopBenchmarkTiming();
michael@0 1102
michael@0 1103 // Pick file to process based on "arg"
michael@0 1104 CHECK_GE(arg, 0);
michael@0 1105 CHECK_LT(arg, ARRAYSIZE(files));
michael@0 1106 string contents = ReadTestDataFile(files[arg].filename);
michael@0 1107
michael@0 1108 char* dst = new char[snappy::MaxCompressedLength(contents.size())];
michael@0 1109
michael@0 1110 SetBenchmarkBytesProcessed(static_cast<int64>(iters) *
michael@0 1111 static_cast<int64>(contents.size()));
michael@0 1112 StartBenchmarkTiming();
michael@0 1113
michael@0 1114 size_t zsize = 0;
michael@0 1115 while (iters-- > 0) {
michael@0 1116 snappy::RawCompress(contents.data(), contents.size(), dst, &zsize);
michael@0 1117 }
michael@0 1118 StopBenchmarkTiming();
michael@0 1119 const double compression_ratio =
michael@0 1120 static_cast<double>(zsize) / std::max<size_t>(1, contents.size());
michael@0 1121 SetBenchmarkLabel(StringPrintf("%s (%.2f %%)",
michael@0 1122 files[arg].label, 100.0 * compression_ratio));
michael@0 1123 VLOG(0) << StringPrintf("compression for %s: %zd -> %zd bytes",
michael@0 1124 files[arg].label, contents.size(), zsize);
michael@0 1125 delete[] dst;
michael@0 1126 }
michael@0 1127 BENCHMARK(BM_ZFlat)->DenseRange(0, 17);
michael@0 1128
michael@0 1129
michael@0 1130 } // namespace snappy
michael@0 1131
michael@0 1132
michael@0 1133 int main(int argc, char** argv) {
michael@0 1134 InitGoogle(argv[0], &argc, &argv, true);
michael@0 1135 File::Init();
michael@0 1136 RunSpecifiedBenchmarks();
michael@0 1137
michael@0 1138
michael@0 1139 if (argc >= 2) {
michael@0 1140 for (int arg = 1; arg < argc; arg++) {
michael@0 1141 if (FLAGS_write_compressed) {
michael@0 1142 CompressFile(argv[arg]);
michael@0 1143 } else if (FLAGS_write_uncompressed) {
michael@0 1144 UncompressFile(argv[arg]);
michael@0 1145 } else {
michael@0 1146 MeasureFile(argv[arg]);
michael@0 1147 }
michael@0 1148 }
michael@0 1149 return 0;
michael@0 1150 }
michael@0 1151
michael@0 1152 return RUN_ALL_TESTS();
michael@0 1153 }

mercurial