other-licenses/snappy/src/snappy-test.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 2011 Google Inc. All Rights Reserved.
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 // Various stubs for the unit tests for the open-source version of Snappy.
michael@0 30
michael@0 31 #include "snappy-test.h"
michael@0 32
michael@0 33 #ifdef HAVE_WINDOWS_H
michael@0 34 #define WIN32_LEAN_AND_MEAN
michael@0 35 #include <windows.h>
michael@0 36 #endif
michael@0 37
michael@0 38 #include <algorithm>
michael@0 39
michael@0 40 DEFINE_bool(run_microbenchmarks, true,
michael@0 41 "Run microbenchmarks before doing anything else.");
michael@0 42
michael@0 43 namespace snappy {
michael@0 44
michael@0 45 string ReadTestDataFile(const string& base) {
michael@0 46 string contents;
michael@0 47 const char* srcdir = getenv("srcdir"); // This is set by Automake.
michael@0 48 if (srcdir) {
michael@0 49 File::ReadFileToStringOrDie(
michael@0 50 string(srcdir) + "/testdata/" + base, &contents);
michael@0 51 } else {
michael@0 52 File::ReadFileToStringOrDie("testdata/" + base, &contents);
michael@0 53 }
michael@0 54 return contents;
michael@0 55 }
michael@0 56
michael@0 57 string StringPrintf(const char* format, ...) {
michael@0 58 char buf[4096];
michael@0 59 va_list ap;
michael@0 60 va_start(ap, format);
michael@0 61 vsnprintf(buf, sizeof(buf), format, ap);
michael@0 62 va_end(ap);
michael@0 63 return buf;
michael@0 64 }
michael@0 65
michael@0 66 bool benchmark_running = false;
michael@0 67 int64 benchmark_real_time_us = 0;
michael@0 68 int64 benchmark_cpu_time_us = 0;
michael@0 69 string *benchmark_label = NULL;
michael@0 70 int64 benchmark_bytes_processed = 0;
michael@0 71
michael@0 72 void ResetBenchmarkTiming() {
michael@0 73 benchmark_real_time_us = 0;
michael@0 74 benchmark_cpu_time_us = 0;
michael@0 75 }
michael@0 76
michael@0 77 #ifdef WIN32
michael@0 78 LARGE_INTEGER benchmark_start_real;
michael@0 79 FILETIME benchmark_start_cpu;
michael@0 80 #else // WIN32
michael@0 81 struct timeval benchmark_start_real;
michael@0 82 struct rusage benchmark_start_cpu;
michael@0 83 #endif // WIN32
michael@0 84
michael@0 85 void StartBenchmarkTiming() {
michael@0 86 #ifdef WIN32
michael@0 87 QueryPerformanceCounter(&benchmark_start_real);
michael@0 88 FILETIME dummy;
michael@0 89 CHECK(GetProcessTimes(
michael@0 90 GetCurrentProcess(), &dummy, &dummy, &dummy, &benchmark_start_cpu));
michael@0 91 #else
michael@0 92 gettimeofday(&benchmark_start_real, NULL);
michael@0 93 if (getrusage(RUSAGE_SELF, &benchmark_start_cpu) == -1) {
michael@0 94 perror("getrusage(RUSAGE_SELF)");
michael@0 95 exit(1);
michael@0 96 }
michael@0 97 #endif
michael@0 98 benchmark_running = true;
michael@0 99 }
michael@0 100
michael@0 101 void StopBenchmarkTiming() {
michael@0 102 if (!benchmark_running) {
michael@0 103 return;
michael@0 104 }
michael@0 105
michael@0 106 #ifdef WIN32
michael@0 107 LARGE_INTEGER benchmark_stop_real;
michael@0 108 LARGE_INTEGER benchmark_frequency;
michael@0 109 QueryPerformanceCounter(&benchmark_stop_real);
michael@0 110 QueryPerformanceFrequency(&benchmark_frequency);
michael@0 111
michael@0 112 double elapsed_real = static_cast<double>(
michael@0 113 benchmark_stop_real.QuadPart - benchmark_start_real.QuadPart) /
michael@0 114 benchmark_frequency.QuadPart;
michael@0 115 benchmark_real_time_us += elapsed_real * 1e6 + 0.5;
michael@0 116
michael@0 117 FILETIME benchmark_stop_cpu, dummy;
michael@0 118 CHECK(GetProcessTimes(
michael@0 119 GetCurrentProcess(), &dummy, &dummy, &dummy, &benchmark_stop_cpu));
michael@0 120
michael@0 121 ULARGE_INTEGER start_ulargeint;
michael@0 122 start_ulargeint.LowPart = benchmark_start_cpu.dwLowDateTime;
michael@0 123 start_ulargeint.HighPart = benchmark_start_cpu.dwHighDateTime;
michael@0 124
michael@0 125 ULARGE_INTEGER stop_ulargeint;
michael@0 126 stop_ulargeint.LowPart = benchmark_stop_cpu.dwLowDateTime;
michael@0 127 stop_ulargeint.HighPart = benchmark_stop_cpu.dwHighDateTime;
michael@0 128
michael@0 129 benchmark_cpu_time_us +=
michael@0 130 (stop_ulargeint.QuadPart - start_ulargeint.QuadPart + 5) / 10;
michael@0 131 #else // WIN32
michael@0 132 struct timeval benchmark_stop_real;
michael@0 133 gettimeofday(&benchmark_stop_real, NULL);
michael@0 134 benchmark_real_time_us +=
michael@0 135 1000000 * (benchmark_stop_real.tv_sec - benchmark_start_real.tv_sec);
michael@0 136 benchmark_real_time_us +=
michael@0 137 (benchmark_stop_real.tv_usec - benchmark_start_real.tv_usec);
michael@0 138
michael@0 139 struct rusage benchmark_stop_cpu;
michael@0 140 if (getrusage(RUSAGE_SELF, &benchmark_stop_cpu) == -1) {
michael@0 141 perror("getrusage(RUSAGE_SELF)");
michael@0 142 exit(1);
michael@0 143 }
michael@0 144 benchmark_cpu_time_us += 1000000 * (benchmark_stop_cpu.ru_utime.tv_sec -
michael@0 145 benchmark_start_cpu.ru_utime.tv_sec);
michael@0 146 benchmark_cpu_time_us += (benchmark_stop_cpu.ru_utime.tv_usec -
michael@0 147 benchmark_start_cpu.ru_utime.tv_usec);
michael@0 148 #endif // WIN32
michael@0 149
michael@0 150 benchmark_running = false;
michael@0 151 }
michael@0 152
michael@0 153 void SetBenchmarkLabel(const string& str) {
michael@0 154 if (benchmark_label) {
michael@0 155 delete benchmark_label;
michael@0 156 }
michael@0 157 benchmark_label = new string(str);
michael@0 158 }
michael@0 159
michael@0 160 void SetBenchmarkBytesProcessed(int64 bytes) {
michael@0 161 benchmark_bytes_processed = bytes;
michael@0 162 }
michael@0 163
michael@0 164 struct BenchmarkRun {
michael@0 165 int64 real_time_us;
michael@0 166 int64 cpu_time_us;
michael@0 167 };
michael@0 168
michael@0 169 struct BenchmarkCompareCPUTime {
michael@0 170 bool operator() (const BenchmarkRun& a, const BenchmarkRun& b) const {
michael@0 171 return a.cpu_time_us < b.cpu_time_us;
michael@0 172 }
michael@0 173 };
michael@0 174
michael@0 175 void Benchmark::Run() {
michael@0 176 for (int test_case_num = start_; test_case_num <= stop_; ++test_case_num) {
michael@0 177 // Run a few iterations first to find out approximately how fast
michael@0 178 // the benchmark is.
michael@0 179 const int kCalibrateIterations = 100;
michael@0 180 ResetBenchmarkTiming();
michael@0 181 StartBenchmarkTiming();
michael@0 182 (*function_)(kCalibrateIterations, test_case_num);
michael@0 183 StopBenchmarkTiming();
michael@0 184
michael@0 185 // Let each test case run for about 200ms, but at least as many
michael@0 186 // as we used to calibrate.
michael@0 187 // Run five times and pick the median.
michael@0 188 const int kNumRuns = 5;
michael@0 189 const int kMedianPos = kNumRuns / 2;
michael@0 190 int num_iterations = 0;
michael@0 191 if (benchmark_real_time_us > 0) {
michael@0 192 num_iterations = 200000 * kCalibrateIterations / benchmark_real_time_us;
michael@0 193 }
michael@0 194 num_iterations = max(num_iterations, kCalibrateIterations);
michael@0 195 BenchmarkRun benchmark_runs[kNumRuns];
michael@0 196
michael@0 197 for (int run = 0; run < kNumRuns; ++run) {
michael@0 198 ResetBenchmarkTiming();
michael@0 199 StartBenchmarkTiming();
michael@0 200 (*function_)(num_iterations, test_case_num);
michael@0 201 StopBenchmarkTiming();
michael@0 202
michael@0 203 benchmark_runs[run].real_time_us = benchmark_real_time_us;
michael@0 204 benchmark_runs[run].cpu_time_us = benchmark_cpu_time_us;
michael@0 205 }
michael@0 206
michael@0 207 nth_element(benchmark_runs,
michael@0 208 benchmark_runs + kMedianPos,
michael@0 209 benchmark_runs + kNumRuns,
michael@0 210 BenchmarkCompareCPUTime());
michael@0 211 int64 real_time_us = benchmark_runs[kMedianPos].real_time_us;
michael@0 212 int64 cpu_time_us = benchmark_runs[kMedianPos].cpu_time_us;
michael@0 213 int64 bytes_per_second = benchmark_bytes_processed * 1000000 / cpu_time_us;
michael@0 214
michael@0 215 string heading = StringPrintf("%s/%d", name_.c_str(), test_case_num);
michael@0 216 string human_readable_speed;
michael@0 217 if (bytes_per_second < 1024) {
michael@0 218 human_readable_speed = StringPrintf("%dB/s", bytes_per_second);
michael@0 219 } else if (bytes_per_second < 1024 * 1024) {
michael@0 220 human_readable_speed = StringPrintf(
michael@0 221 "%.1fkB/s", bytes_per_second / 1024.0f);
michael@0 222 } else if (bytes_per_second < 1024 * 1024 * 1024) {
michael@0 223 human_readable_speed = StringPrintf(
michael@0 224 "%.1fMB/s", bytes_per_second / (1024.0f * 1024.0f));
michael@0 225 } else {
michael@0 226 human_readable_speed = StringPrintf(
michael@0 227 "%.1fGB/s", bytes_per_second / (1024.0f * 1024.0f * 1024.0f));
michael@0 228 }
michael@0 229
michael@0 230 fprintf(stderr,
michael@0 231 #ifdef WIN32
michael@0 232 "%-18s %10I64d %10I64d %10d %s %s\n",
michael@0 233 #else
michael@0 234 "%-18s %10lld %10lld %10d %s %s\n",
michael@0 235 #endif
michael@0 236 heading.c_str(),
michael@0 237 static_cast<long long>(real_time_us * 1000 / num_iterations),
michael@0 238 static_cast<long long>(cpu_time_us * 1000 / num_iterations),
michael@0 239 num_iterations,
michael@0 240 human_readable_speed.c_str(),
michael@0 241 benchmark_label->c_str());
michael@0 242 }
michael@0 243 }
michael@0 244
michael@0 245 #ifdef HAVE_LIBZ
michael@0 246
michael@0 247 ZLib::ZLib()
michael@0 248 : comp_init_(false),
michael@0 249 uncomp_init_(false) {
michael@0 250 Reinit();
michael@0 251 }
michael@0 252
michael@0 253 ZLib::~ZLib() {
michael@0 254 if (comp_init_) { deflateEnd(&comp_stream_); }
michael@0 255 if (uncomp_init_) { inflateEnd(&uncomp_stream_); }
michael@0 256 }
michael@0 257
michael@0 258 void ZLib::Reinit() {
michael@0 259 compression_level_ = Z_DEFAULT_COMPRESSION;
michael@0 260 window_bits_ = MAX_WBITS;
michael@0 261 mem_level_ = 8; // DEF_MEM_LEVEL
michael@0 262 if (comp_init_) {
michael@0 263 deflateEnd(&comp_stream_);
michael@0 264 comp_init_ = false;
michael@0 265 }
michael@0 266 if (uncomp_init_) {
michael@0 267 inflateEnd(&uncomp_stream_);
michael@0 268 uncomp_init_ = false;
michael@0 269 }
michael@0 270 first_chunk_ = true;
michael@0 271 }
michael@0 272
michael@0 273 void ZLib::Reset() {
michael@0 274 first_chunk_ = true;
michael@0 275 }
michael@0 276
michael@0 277 // --------- COMPRESS MODE
michael@0 278
michael@0 279 // Initialization method to be called if we hit an error while
michael@0 280 // compressing. On hitting an error, call this method before returning
michael@0 281 // the error.
michael@0 282 void ZLib::CompressErrorInit() {
michael@0 283 deflateEnd(&comp_stream_);
michael@0 284 comp_init_ = false;
michael@0 285 Reset();
michael@0 286 }
michael@0 287
michael@0 288 int ZLib::DeflateInit() {
michael@0 289 return deflateInit2(&comp_stream_,
michael@0 290 compression_level_,
michael@0 291 Z_DEFLATED,
michael@0 292 window_bits_,
michael@0 293 mem_level_,
michael@0 294 Z_DEFAULT_STRATEGY);
michael@0 295 }
michael@0 296
michael@0 297 int ZLib::CompressInit(Bytef *dest, uLongf *destLen,
michael@0 298 const Bytef *source, uLong *sourceLen) {
michael@0 299 int err;
michael@0 300
michael@0 301 comp_stream_.next_in = (Bytef*)source;
michael@0 302 comp_stream_.avail_in = (uInt)*sourceLen;
michael@0 303 if ((uLong)comp_stream_.avail_in != *sourceLen) return Z_BUF_ERROR;
michael@0 304 comp_stream_.next_out = dest;
michael@0 305 comp_stream_.avail_out = (uInt)*destLen;
michael@0 306 if ((uLong)comp_stream_.avail_out != *destLen) return Z_BUF_ERROR;
michael@0 307
michael@0 308 if ( !first_chunk_ ) // only need to set up stream the first time through
michael@0 309 return Z_OK;
michael@0 310
michael@0 311 if (comp_init_) { // we've already initted it
michael@0 312 err = deflateReset(&comp_stream_);
michael@0 313 if (err != Z_OK) {
michael@0 314 LOG(WARNING) << "ERROR: Can't reset compress object; creating a new one";
michael@0 315 deflateEnd(&comp_stream_);
michael@0 316 comp_init_ = false;
michael@0 317 }
michael@0 318 }
michael@0 319 if (!comp_init_) { // first use
michael@0 320 comp_stream_.zalloc = (alloc_func)0;
michael@0 321 comp_stream_.zfree = (free_func)0;
michael@0 322 comp_stream_.opaque = (voidpf)0;
michael@0 323 err = DeflateInit();
michael@0 324 if (err != Z_OK) return err;
michael@0 325 comp_init_ = true;
michael@0 326 }
michael@0 327 return Z_OK;
michael@0 328 }
michael@0 329
michael@0 330 // In a perfect world we'd always have the full buffer to compress
michael@0 331 // when the time came, and we could just call Compress(). Alas, we
michael@0 332 // want to do chunked compression on our webserver. In this
michael@0 333 // application, we compress the header, send it off, then compress the
michael@0 334 // results, send them off, then compress the footer. Thus we need to
michael@0 335 // use the chunked compression features of zlib.
michael@0 336 int ZLib::CompressAtMostOrAll(Bytef *dest, uLongf *destLen,
michael@0 337 const Bytef *source, uLong *sourceLen,
michael@0 338 int flush_mode) { // Z_FULL_FLUSH or Z_FINISH
michael@0 339 int err;
michael@0 340
michael@0 341 if ( (err=CompressInit(dest, destLen, source, sourceLen)) != Z_OK )
michael@0 342 return err;
michael@0 343
michael@0 344 // This is used to figure out how many bytes we wrote *this chunk*
michael@0 345 int compressed_size = comp_stream_.total_out;
michael@0 346
michael@0 347 // Some setup happens only for the first chunk we compress in a run
michael@0 348 if ( first_chunk_ ) {
michael@0 349 first_chunk_ = false;
michael@0 350 }
michael@0 351
michael@0 352 // flush_mode is Z_FINISH for all mode, Z_SYNC_FLUSH for incremental
michael@0 353 // compression.
michael@0 354 err = deflate(&comp_stream_, flush_mode);
michael@0 355
michael@0 356 *sourceLen = comp_stream_.avail_in;
michael@0 357
michael@0 358 if ((err == Z_STREAM_END || err == Z_OK)
michael@0 359 && comp_stream_.avail_in == 0
michael@0 360 && comp_stream_.avail_out != 0 ) {
michael@0 361 // we processed everything ok and the output buffer was large enough.
michael@0 362 ;
michael@0 363 } else if (err == Z_STREAM_END && comp_stream_.avail_in > 0) {
michael@0 364 return Z_BUF_ERROR; // should never happen
michael@0 365 } else if (err != Z_OK && err != Z_STREAM_END && err != Z_BUF_ERROR) {
michael@0 366 // an error happened
michael@0 367 CompressErrorInit();
michael@0 368 return err;
michael@0 369 } else if (comp_stream_.avail_out == 0) { // not enough space
michael@0 370 err = Z_BUF_ERROR;
michael@0 371 }
michael@0 372
michael@0 373 assert(err == Z_OK || err == Z_STREAM_END || err == Z_BUF_ERROR);
michael@0 374 if (err == Z_STREAM_END)
michael@0 375 err = Z_OK;
michael@0 376
michael@0 377 // update the crc and other metadata
michael@0 378 compressed_size = comp_stream_.total_out - compressed_size; // delta
michael@0 379 *destLen = compressed_size;
michael@0 380
michael@0 381 return err;
michael@0 382 }
michael@0 383
michael@0 384 int ZLib::CompressChunkOrAll(Bytef *dest, uLongf *destLen,
michael@0 385 const Bytef *source, uLong sourceLen,
michael@0 386 int flush_mode) { // Z_FULL_FLUSH or Z_FINISH
michael@0 387 const int ret =
michael@0 388 CompressAtMostOrAll(dest, destLen, source, &sourceLen, flush_mode);
michael@0 389 if (ret == Z_BUF_ERROR)
michael@0 390 CompressErrorInit();
michael@0 391 return ret;
michael@0 392 }
michael@0 393
michael@0 394 // This routine only initializes the compression stream once. Thereafter, it
michael@0 395 // just does a deflateReset on the stream, which should be faster.
michael@0 396 int ZLib::Compress(Bytef *dest, uLongf *destLen,
michael@0 397 const Bytef *source, uLong sourceLen) {
michael@0 398 int err;
michael@0 399 if ( (err=CompressChunkOrAll(dest, destLen, source, sourceLen,
michael@0 400 Z_FINISH)) != Z_OK )
michael@0 401 return err;
michael@0 402 Reset(); // reset for next call to Compress
michael@0 403
michael@0 404 return Z_OK;
michael@0 405 }
michael@0 406
michael@0 407
michael@0 408 // --------- UNCOMPRESS MODE
michael@0 409
michael@0 410 int ZLib::InflateInit() {
michael@0 411 return inflateInit2(&uncomp_stream_, MAX_WBITS);
michael@0 412 }
michael@0 413
michael@0 414 // Initialization method to be called if we hit an error while
michael@0 415 // uncompressing. On hitting an error, call this method before
michael@0 416 // returning the error.
michael@0 417 void ZLib::UncompressErrorInit() {
michael@0 418 inflateEnd(&uncomp_stream_);
michael@0 419 uncomp_init_ = false;
michael@0 420 Reset();
michael@0 421 }
michael@0 422
michael@0 423 int ZLib::UncompressInit(Bytef *dest, uLongf *destLen,
michael@0 424 const Bytef *source, uLong *sourceLen) {
michael@0 425 int err;
michael@0 426
michael@0 427 uncomp_stream_.next_in = (Bytef*)source;
michael@0 428 uncomp_stream_.avail_in = (uInt)*sourceLen;
michael@0 429 // Check for source > 64K on 16-bit machine:
michael@0 430 if ((uLong)uncomp_stream_.avail_in != *sourceLen) return Z_BUF_ERROR;
michael@0 431
michael@0 432 uncomp_stream_.next_out = dest;
michael@0 433 uncomp_stream_.avail_out = (uInt)*destLen;
michael@0 434 if ((uLong)uncomp_stream_.avail_out != *destLen) return Z_BUF_ERROR;
michael@0 435
michael@0 436 if ( !first_chunk_ ) // only need to set up stream the first time through
michael@0 437 return Z_OK;
michael@0 438
michael@0 439 if (uncomp_init_) { // we've already initted it
michael@0 440 err = inflateReset(&uncomp_stream_);
michael@0 441 if (err != Z_OK) {
michael@0 442 LOG(WARNING)
michael@0 443 << "ERROR: Can't reset uncompress object; creating a new one";
michael@0 444 UncompressErrorInit();
michael@0 445 }
michael@0 446 }
michael@0 447 if (!uncomp_init_) {
michael@0 448 uncomp_stream_.zalloc = (alloc_func)0;
michael@0 449 uncomp_stream_.zfree = (free_func)0;
michael@0 450 uncomp_stream_.opaque = (voidpf)0;
michael@0 451 err = InflateInit();
michael@0 452 if (err != Z_OK) return err;
michael@0 453 uncomp_init_ = true;
michael@0 454 }
michael@0 455 return Z_OK;
michael@0 456 }
michael@0 457
michael@0 458 // If you compressed your data a chunk at a time, with CompressChunk,
michael@0 459 // you can uncompress it a chunk at a time with UncompressChunk.
michael@0 460 // Only difference bewteen chunked and unchunked uncompression
michael@0 461 // is the flush mode we use: Z_SYNC_FLUSH (chunked) or Z_FINISH (unchunked).
michael@0 462 int ZLib::UncompressAtMostOrAll(Bytef *dest, uLongf *destLen,
michael@0 463 const Bytef *source, uLong *sourceLen,
michael@0 464 int flush_mode) { // Z_SYNC_FLUSH or Z_FINISH
michael@0 465 int err = Z_OK;
michael@0 466
michael@0 467 if ( (err=UncompressInit(dest, destLen, source, sourceLen)) != Z_OK ) {
michael@0 468 LOG(WARNING) << "UncompressInit: Error: " << err << " SourceLen: "
michael@0 469 << *sourceLen;
michael@0 470 return err;
michael@0 471 }
michael@0 472
michael@0 473 // This is used to figure out how many output bytes we wrote *this chunk*:
michael@0 474 const uLong old_total_out = uncomp_stream_.total_out;
michael@0 475
michael@0 476 // This is used to figure out how many input bytes we read *this chunk*:
michael@0 477 const uLong old_total_in = uncomp_stream_.total_in;
michael@0 478
michael@0 479 // Some setup happens only for the first chunk we compress in a run
michael@0 480 if ( first_chunk_ ) {
michael@0 481 first_chunk_ = false; // so we don't do this again
michael@0 482
michael@0 483 // For the first chunk *only* (to avoid infinite troubles), we let
michael@0 484 // there be no actual data to uncompress. This sometimes triggers
michael@0 485 // when the input is only the gzip header, say.
michael@0 486 if ( *sourceLen == 0 ) {
michael@0 487 *destLen = 0;
michael@0 488 return Z_OK;
michael@0 489 }
michael@0 490 }
michael@0 491
michael@0 492 // We'll uncompress as much as we can. If we end OK great, otherwise
michael@0 493 // if we get an error that seems to be the gzip footer, we store the
michael@0 494 // gzip footer and return OK, otherwise we return the error.
michael@0 495
michael@0 496 // flush_mode is Z_SYNC_FLUSH for chunked mode, Z_FINISH for all mode.
michael@0 497 err = inflate(&uncomp_stream_, flush_mode);
michael@0 498
michael@0 499 // Figure out how many bytes of the input zlib slurped up:
michael@0 500 const uLong bytes_read = uncomp_stream_.total_in - old_total_in;
michael@0 501 CHECK_LE(source + bytes_read, source + *sourceLen);
michael@0 502 *sourceLen = uncomp_stream_.avail_in;
michael@0 503
michael@0 504 if ((err == Z_STREAM_END || err == Z_OK) // everything went ok
michael@0 505 && uncomp_stream_.avail_in == 0) { // and we read it all
michael@0 506 ;
michael@0 507 } else if (err == Z_STREAM_END && uncomp_stream_.avail_in > 0) {
michael@0 508 LOG(WARNING)
michael@0 509 << "UncompressChunkOrAll: Received some extra data, bytes total: "
michael@0 510 << uncomp_stream_.avail_in << " bytes: "
michael@0 511 << string(reinterpret_cast<const char *>(uncomp_stream_.next_in),
michael@0 512 min(int(uncomp_stream_.avail_in), 20));
michael@0 513 UncompressErrorInit();
michael@0 514 return Z_DATA_ERROR; // what's the extra data for?
michael@0 515 } else if (err != Z_OK && err != Z_STREAM_END && err != Z_BUF_ERROR) {
michael@0 516 // an error happened
michael@0 517 LOG(WARNING) << "UncompressChunkOrAll: Error: " << err
michael@0 518 << " avail_out: " << uncomp_stream_.avail_out;
michael@0 519 UncompressErrorInit();
michael@0 520 return err;
michael@0 521 } else if (uncomp_stream_.avail_out == 0) {
michael@0 522 err = Z_BUF_ERROR;
michael@0 523 }
michael@0 524
michael@0 525 assert(err == Z_OK || err == Z_BUF_ERROR || err == Z_STREAM_END);
michael@0 526 if (err == Z_STREAM_END)
michael@0 527 err = Z_OK;
michael@0 528
michael@0 529 *destLen = uncomp_stream_.total_out - old_total_out; // size for this call
michael@0 530
michael@0 531 return err;
michael@0 532 }
michael@0 533
michael@0 534 int ZLib::UncompressChunkOrAll(Bytef *dest, uLongf *destLen,
michael@0 535 const Bytef *source, uLong sourceLen,
michael@0 536 int flush_mode) { // Z_SYNC_FLUSH or Z_FINISH
michael@0 537 const int ret =
michael@0 538 UncompressAtMostOrAll(dest, destLen, source, &sourceLen, flush_mode);
michael@0 539 if (ret == Z_BUF_ERROR)
michael@0 540 UncompressErrorInit();
michael@0 541 return ret;
michael@0 542 }
michael@0 543
michael@0 544 int ZLib::UncompressAtMost(Bytef *dest, uLongf *destLen,
michael@0 545 const Bytef *source, uLong *sourceLen) {
michael@0 546 return UncompressAtMostOrAll(dest, destLen, source, sourceLen, Z_SYNC_FLUSH);
michael@0 547 }
michael@0 548
michael@0 549 // We make sure we've uncompressed everything, that is, the current
michael@0 550 // uncompress stream is at a compressed-buffer-EOF boundary. In gzip
michael@0 551 // mode, we also check the gzip footer to make sure we pass the gzip
michael@0 552 // consistency checks. We RETURN true iff both types of checks pass.
michael@0 553 bool ZLib::UncompressChunkDone() {
michael@0 554 assert(!first_chunk_ && uncomp_init_);
michael@0 555 // Make sure we're at the end-of-compressed-data point. This means
michael@0 556 // if we call inflate with Z_FINISH we won't consume any input or
michael@0 557 // write any output
michael@0 558 Bytef dummyin, dummyout;
michael@0 559 uLongf dummylen = 0;
michael@0 560 if ( UncompressChunkOrAll(&dummyout, &dummylen, &dummyin, 0, Z_FINISH)
michael@0 561 != Z_OK ) {
michael@0 562 return false;
michael@0 563 }
michael@0 564
michael@0 565 // Make sure that when we exit, we can start a new round of chunks later
michael@0 566 Reset();
michael@0 567
michael@0 568 return true;
michael@0 569 }
michael@0 570
michael@0 571 // Uncompresses the source buffer into the destination buffer.
michael@0 572 // The destination buffer must be long enough to hold the entire
michael@0 573 // decompressed contents.
michael@0 574 //
michael@0 575 // We only initialize the uncomp_stream once. Thereafter, we use
michael@0 576 // inflateReset, which should be faster.
michael@0 577 //
michael@0 578 // Returns Z_OK on success, otherwise, it returns a zlib error code.
michael@0 579 int ZLib::Uncompress(Bytef *dest, uLongf *destLen,
michael@0 580 const Bytef *source, uLong sourceLen) {
michael@0 581 int err;
michael@0 582 if ( (err=UncompressChunkOrAll(dest, destLen, source, sourceLen,
michael@0 583 Z_FINISH)) != Z_OK ) {
michael@0 584 Reset(); // let us try to compress again
michael@0 585 return err;
michael@0 586 }
michael@0 587 if ( !UncompressChunkDone() ) // calls Reset()
michael@0 588 return Z_DATA_ERROR;
michael@0 589 return Z_OK; // stream_end is ok
michael@0 590 }
michael@0 591
michael@0 592 #endif // HAVE_LIBZ
michael@0 593
michael@0 594 } // namespace snappy

mercurial