other-licenses/snappy/src/snappy-test.cc

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/other-licenses/snappy/src/snappy-test.cc	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,594 @@
     1.4 +// Copyright 2011 Google Inc. All Rights Reserved.
     1.5 +//
     1.6 +// Redistribution and use in source and binary forms, with or without
     1.7 +// modification, are permitted provided that the following conditions are
     1.8 +// met:
     1.9 +//
    1.10 +//     * Redistributions of source code must retain the above copyright
    1.11 +// notice, this list of conditions and the following disclaimer.
    1.12 +//     * Redistributions in binary form must reproduce the above
    1.13 +// copyright notice, this list of conditions and the following disclaimer
    1.14 +// in the documentation and/or other materials provided with the
    1.15 +// distribution.
    1.16 +//     * Neither the name of Google Inc. nor the names of its
    1.17 +// contributors may be used to endorse or promote products derived from
    1.18 +// this software without specific prior written permission.
    1.19 +//
    1.20 +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    1.21 +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    1.22 +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    1.23 +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    1.24 +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    1.25 +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    1.26 +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    1.27 +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    1.28 +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    1.29 +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    1.30 +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    1.31 +//
    1.32 +// Various stubs for the unit tests for the open-source version of Snappy.
    1.33 +
    1.34 +#include "snappy-test.h"
    1.35 +
    1.36 +#ifdef HAVE_WINDOWS_H
    1.37 +#define WIN32_LEAN_AND_MEAN
    1.38 +#include <windows.h>
    1.39 +#endif
    1.40 +
    1.41 +#include <algorithm>
    1.42 +
    1.43 +DEFINE_bool(run_microbenchmarks, true,
    1.44 +            "Run microbenchmarks before doing anything else.");
    1.45 +
    1.46 +namespace snappy {
    1.47 +
    1.48 +string ReadTestDataFile(const string& base) {
    1.49 +  string contents;
    1.50 +  const char* srcdir = getenv("srcdir");  // This is set by Automake.
    1.51 +  if (srcdir) {
    1.52 +    File::ReadFileToStringOrDie(
    1.53 +        string(srcdir) + "/testdata/" + base, &contents);
    1.54 +  } else {
    1.55 +    File::ReadFileToStringOrDie("testdata/" + base, &contents);
    1.56 +  }
    1.57 +  return contents;
    1.58 +}
    1.59 +
    1.60 +string StringPrintf(const char* format, ...) {
    1.61 +  char buf[4096];
    1.62 +  va_list ap;
    1.63 +  va_start(ap, format);
    1.64 +  vsnprintf(buf, sizeof(buf), format, ap);
    1.65 +  va_end(ap);
    1.66 +  return buf;
    1.67 +}
    1.68 +
    1.69 +bool benchmark_running = false;
    1.70 +int64 benchmark_real_time_us = 0;
    1.71 +int64 benchmark_cpu_time_us = 0;
    1.72 +string *benchmark_label = NULL;
    1.73 +int64 benchmark_bytes_processed = 0;
    1.74 +
    1.75 +void ResetBenchmarkTiming() {
    1.76 +  benchmark_real_time_us = 0;
    1.77 +  benchmark_cpu_time_us = 0;
    1.78 +}
    1.79 +
    1.80 +#ifdef WIN32
    1.81 +LARGE_INTEGER benchmark_start_real;
    1.82 +FILETIME benchmark_start_cpu;
    1.83 +#else  // WIN32
    1.84 +struct timeval benchmark_start_real;
    1.85 +struct rusage benchmark_start_cpu;
    1.86 +#endif  // WIN32
    1.87 +
    1.88 +void StartBenchmarkTiming() {
    1.89 +#ifdef WIN32
    1.90 +  QueryPerformanceCounter(&benchmark_start_real);
    1.91 +  FILETIME dummy;
    1.92 +  CHECK(GetProcessTimes(
    1.93 +      GetCurrentProcess(), &dummy, &dummy, &dummy, &benchmark_start_cpu));
    1.94 +#else
    1.95 +  gettimeofday(&benchmark_start_real, NULL);
    1.96 +  if (getrusage(RUSAGE_SELF, &benchmark_start_cpu) == -1) {
    1.97 +    perror("getrusage(RUSAGE_SELF)");
    1.98 +    exit(1);
    1.99 +  }
   1.100 +#endif
   1.101 +  benchmark_running = true;
   1.102 +}
   1.103 +
   1.104 +void StopBenchmarkTiming() {
   1.105 +  if (!benchmark_running) {
   1.106 +    return;
   1.107 +  }
   1.108 +
   1.109 +#ifdef WIN32
   1.110 +  LARGE_INTEGER benchmark_stop_real;
   1.111 +  LARGE_INTEGER benchmark_frequency;
   1.112 +  QueryPerformanceCounter(&benchmark_stop_real);
   1.113 +  QueryPerformanceFrequency(&benchmark_frequency);
   1.114 +
   1.115 +  double elapsed_real = static_cast<double>(
   1.116 +      benchmark_stop_real.QuadPart - benchmark_start_real.QuadPart) /
   1.117 +      benchmark_frequency.QuadPart;
   1.118 +  benchmark_real_time_us += elapsed_real * 1e6 + 0.5;
   1.119 +
   1.120 +  FILETIME benchmark_stop_cpu, dummy;
   1.121 +  CHECK(GetProcessTimes(
   1.122 +      GetCurrentProcess(), &dummy, &dummy, &dummy, &benchmark_stop_cpu));
   1.123 +
   1.124 +  ULARGE_INTEGER start_ulargeint;
   1.125 +  start_ulargeint.LowPart = benchmark_start_cpu.dwLowDateTime;
   1.126 +  start_ulargeint.HighPart = benchmark_start_cpu.dwHighDateTime;
   1.127 +
   1.128 +  ULARGE_INTEGER stop_ulargeint;
   1.129 +  stop_ulargeint.LowPart = benchmark_stop_cpu.dwLowDateTime;
   1.130 +  stop_ulargeint.HighPart = benchmark_stop_cpu.dwHighDateTime;
   1.131 +
   1.132 +  benchmark_cpu_time_us +=
   1.133 +      (stop_ulargeint.QuadPart - start_ulargeint.QuadPart + 5) / 10;
   1.134 +#else  // WIN32
   1.135 +  struct timeval benchmark_stop_real;
   1.136 +  gettimeofday(&benchmark_stop_real, NULL);
   1.137 +  benchmark_real_time_us +=
   1.138 +      1000000 * (benchmark_stop_real.tv_sec - benchmark_start_real.tv_sec);
   1.139 +  benchmark_real_time_us +=
   1.140 +      (benchmark_stop_real.tv_usec - benchmark_start_real.tv_usec);
   1.141 +
   1.142 +  struct rusage benchmark_stop_cpu;
   1.143 +  if (getrusage(RUSAGE_SELF, &benchmark_stop_cpu) == -1) {
   1.144 +    perror("getrusage(RUSAGE_SELF)");
   1.145 +    exit(1);
   1.146 +  }
   1.147 +  benchmark_cpu_time_us += 1000000 * (benchmark_stop_cpu.ru_utime.tv_sec -
   1.148 +                                      benchmark_start_cpu.ru_utime.tv_sec);
   1.149 +  benchmark_cpu_time_us += (benchmark_stop_cpu.ru_utime.tv_usec -
   1.150 +                            benchmark_start_cpu.ru_utime.tv_usec);
   1.151 +#endif  // WIN32
   1.152 +
   1.153 +  benchmark_running = false;
   1.154 +}
   1.155 +
   1.156 +void SetBenchmarkLabel(const string& str) {
   1.157 +  if (benchmark_label) {
   1.158 +    delete benchmark_label;
   1.159 +  }
   1.160 +  benchmark_label = new string(str);
   1.161 +}
   1.162 +
   1.163 +void SetBenchmarkBytesProcessed(int64 bytes) {
   1.164 +  benchmark_bytes_processed = bytes;
   1.165 +}
   1.166 +
   1.167 +struct BenchmarkRun {
   1.168 +  int64 real_time_us;
   1.169 +  int64 cpu_time_us;
   1.170 +};
   1.171 +
   1.172 +struct BenchmarkCompareCPUTime {
   1.173 +  bool operator() (const BenchmarkRun& a, const BenchmarkRun& b) const {
   1.174 +    return a.cpu_time_us < b.cpu_time_us;
   1.175 +  }
   1.176 +};
   1.177 +
   1.178 +void Benchmark::Run() {
   1.179 +  for (int test_case_num = start_; test_case_num <= stop_; ++test_case_num) {
   1.180 +    // Run a few iterations first to find out approximately how fast
   1.181 +    // the benchmark is.
   1.182 +    const int kCalibrateIterations = 100;
   1.183 +    ResetBenchmarkTiming();
   1.184 +    StartBenchmarkTiming();
   1.185 +    (*function_)(kCalibrateIterations, test_case_num);
   1.186 +    StopBenchmarkTiming();
   1.187 +
   1.188 +    // Let each test case run for about 200ms, but at least as many
   1.189 +    // as we used to calibrate.
   1.190 +    // Run five times and pick the median.
   1.191 +    const int kNumRuns = 5;
   1.192 +    const int kMedianPos = kNumRuns / 2;
   1.193 +    int num_iterations = 0;
   1.194 +    if (benchmark_real_time_us > 0) {
   1.195 +      num_iterations = 200000 * kCalibrateIterations / benchmark_real_time_us;
   1.196 +    }
   1.197 +    num_iterations = max(num_iterations, kCalibrateIterations);
   1.198 +    BenchmarkRun benchmark_runs[kNumRuns];
   1.199 +
   1.200 +    for (int run = 0; run < kNumRuns; ++run) {
   1.201 +      ResetBenchmarkTiming();
   1.202 +      StartBenchmarkTiming();
   1.203 +      (*function_)(num_iterations, test_case_num);
   1.204 +      StopBenchmarkTiming();
   1.205 +
   1.206 +      benchmark_runs[run].real_time_us = benchmark_real_time_us;
   1.207 +      benchmark_runs[run].cpu_time_us = benchmark_cpu_time_us;
   1.208 +    }
   1.209 +
   1.210 +    nth_element(benchmark_runs,
   1.211 +                benchmark_runs + kMedianPos,
   1.212 +                benchmark_runs + kNumRuns,
   1.213 +                BenchmarkCompareCPUTime());
   1.214 +    int64 real_time_us = benchmark_runs[kMedianPos].real_time_us;
   1.215 +    int64 cpu_time_us = benchmark_runs[kMedianPos].cpu_time_us;
   1.216 +    int64 bytes_per_second = benchmark_bytes_processed * 1000000 / cpu_time_us;
   1.217 +
   1.218 +    string heading = StringPrintf("%s/%d", name_.c_str(), test_case_num);
   1.219 +    string human_readable_speed;
   1.220 +    if (bytes_per_second < 1024) {
   1.221 +      human_readable_speed = StringPrintf("%dB/s", bytes_per_second);
   1.222 +    } else if (bytes_per_second < 1024 * 1024) {
   1.223 +      human_readable_speed = StringPrintf(
   1.224 +          "%.1fkB/s", bytes_per_second / 1024.0f);
   1.225 +    } else if (bytes_per_second < 1024 * 1024 * 1024) {
   1.226 +      human_readable_speed = StringPrintf(
   1.227 +          "%.1fMB/s", bytes_per_second / (1024.0f * 1024.0f));
   1.228 +    } else {
   1.229 +      human_readable_speed = StringPrintf(
   1.230 +          "%.1fGB/s", bytes_per_second / (1024.0f * 1024.0f * 1024.0f));
   1.231 +    }
   1.232 +
   1.233 +    fprintf(stderr,
   1.234 +#ifdef WIN32
   1.235 +            "%-18s %10I64d %10I64d %10d %s  %s\n",
   1.236 +#else
   1.237 +            "%-18s %10lld %10lld %10d %s  %s\n",
   1.238 +#endif
   1.239 +            heading.c_str(),
   1.240 +            static_cast<long long>(real_time_us * 1000 / num_iterations),
   1.241 +            static_cast<long long>(cpu_time_us * 1000 / num_iterations),
   1.242 +            num_iterations,
   1.243 +            human_readable_speed.c_str(),
   1.244 +            benchmark_label->c_str());
   1.245 +  }
   1.246 +}
   1.247 +
   1.248 +#ifdef HAVE_LIBZ
   1.249 +
   1.250 +ZLib::ZLib()
   1.251 +    : comp_init_(false),
   1.252 +      uncomp_init_(false) {
   1.253 +  Reinit();
   1.254 +}
   1.255 +
   1.256 +ZLib::~ZLib() {
   1.257 +  if (comp_init_)   { deflateEnd(&comp_stream_); }
   1.258 +  if (uncomp_init_) { inflateEnd(&uncomp_stream_); }
   1.259 +}
   1.260 +
   1.261 +void ZLib::Reinit() {
   1.262 +  compression_level_ = Z_DEFAULT_COMPRESSION;
   1.263 +  window_bits_ = MAX_WBITS;
   1.264 +  mem_level_ =  8;  // DEF_MEM_LEVEL
   1.265 +  if (comp_init_) {
   1.266 +    deflateEnd(&comp_stream_);
   1.267 +    comp_init_ = false;
   1.268 +  }
   1.269 +  if (uncomp_init_) {
   1.270 +    inflateEnd(&uncomp_stream_);
   1.271 +    uncomp_init_ = false;
   1.272 +  }
   1.273 +  first_chunk_ = true;
   1.274 +}
   1.275 +
   1.276 +void ZLib::Reset() {
   1.277 +  first_chunk_ = true;
   1.278 +}
   1.279 +
   1.280 +// --------- COMPRESS MODE
   1.281 +
   1.282 +// Initialization method to be called if we hit an error while
   1.283 +// compressing. On hitting an error, call this method before returning
   1.284 +// the error.
   1.285 +void ZLib::CompressErrorInit() {
   1.286 +  deflateEnd(&comp_stream_);
   1.287 +  comp_init_ = false;
   1.288 +  Reset();
   1.289 +}
   1.290 +
   1.291 +int ZLib::DeflateInit() {
   1.292 +  return deflateInit2(&comp_stream_,
   1.293 +                      compression_level_,
   1.294 +                      Z_DEFLATED,
   1.295 +                      window_bits_,
   1.296 +                      mem_level_,
   1.297 +                      Z_DEFAULT_STRATEGY);
   1.298 +}
   1.299 +
   1.300 +int ZLib::CompressInit(Bytef *dest, uLongf *destLen,
   1.301 +                       const Bytef *source, uLong *sourceLen) {
   1.302 +  int err;
   1.303 +
   1.304 +  comp_stream_.next_in = (Bytef*)source;
   1.305 +  comp_stream_.avail_in = (uInt)*sourceLen;
   1.306 +  if ((uLong)comp_stream_.avail_in != *sourceLen) return Z_BUF_ERROR;
   1.307 +  comp_stream_.next_out = dest;
   1.308 +  comp_stream_.avail_out = (uInt)*destLen;
   1.309 +  if ((uLong)comp_stream_.avail_out != *destLen) return Z_BUF_ERROR;
   1.310 +
   1.311 +  if ( !first_chunk_ )   // only need to set up stream the first time through
   1.312 +    return Z_OK;
   1.313 +
   1.314 +  if (comp_init_) {      // we've already initted it
   1.315 +    err = deflateReset(&comp_stream_);
   1.316 +    if (err != Z_OK) {
   1.317 +      LOG(WARNING) << "ERROR: Can't reset compress object; creating a new one";
   1.318 +      deflateEnd(&comp_stream_);
   1.319 +      comp_init_ = false;
   1.320 +    }
   1.321 +  }
   1.322 +  if (!comp_init_) {     // first use
   1.323 +    comp_stream_.zalloc = (alloc_func)0;
   1.324 +    comp_stream_.zfree = (free_func)0;
   1.325 +    comp_stream_.opaque = (voidpf)0;
   1.326 +    err = DeflateInit();
   1.327 +    if (err != Z_OK) return err;
   1.328 +    comp_init_ = true;
   1.329 +  }
   1.330 +  return Z_OK;
   1.331 +}
   1.332 +
   1.333 +// In a perfect world we'd always have the full buffer to compress
   1.334 +// when the time came, and we could just call Compress().  Alas, we
   1.335 +// want to do chunked compression on our webserver.  In this
   1.336 +// application, we compress the header, send it off, then compress the
   1.337 +// results, send them off, then compress the footer.  Thus we need to
   1.338 +// use the chunked compression features of zlib.
   1.339 +int ZLib::CompressAtMostOrAll(Bytef *dest, uLongf *destLen,
   1.340 +                              const Bytef *source, uLong *sourceLen,
   1.341 +                              int flush_mode) {   // Z_FULL_FLUSH or Z_FINISH
   1.342 +  int err;
   1.343 +
   1.344 +  if ( (err=CompressInit(dest, destLen, source, sourceLen)) != Z_OK )
   1.345 +    return err;
   1.346 +
   1.347 +  // This is used to figure out how many bytes we wrote *this chunk*
   1.348 +  int compressed_size = comp_stream_.total_out;
   1.349 +
   1.350 +  // Some setup happens only for the first chunk we compress in a run
   1.351 +  if ( first_chunk_ ) {
   1.352 +    first_chunk_ = false;
   1.353 +  }
   1.354 +
   1.355 +  // flush_mode is Z_FINISH for all mode, Z_SYNC_FLUSH for incremental
   1.356 +  // compression.
   1.357 +  err = deflate(&comp_stream_, flush_mode);
   1.358 +
   1.359 +  *sourceLen = comp_stream_.avail_in;
   1.360 +
   1.361 +  if ((err == Z_STREAM_END || err == Z_OK)
   1.362 +      && comp_stream_.avail_in == 0
   1.363 +      && comp_stream_.avail_out != 0 ) {
   1.364 +    // we processed everything ok and the output buffer was large enough.
   1.365 +    ;
   1.366 +  } else if (err == Z_STREAM_END && comp_stream_.avail_in > 0) {
   1.367 +    return Z_BUF_ERROR;                            // should never happen
   1.368 +  } else if (err != Z_OK && err != Z_STREAM_END && err != Z_BUF_ERROR) {
   1.369 +    // an error happened
   1.370 +    CompressErrorInit();
   1.371 +    return err;
   1.372 +  } else if (comp_stream_.avail_out == 0) {     // not enough space
   1.373 +    err = Z_BUF_ERROR;
   1.374 +  }
   1.375 +
   1.376 +  assert(err == Z_OK || err == Z_STREAM_END || err == Z_BUF_ERROR);
   1.377 +  if (err == Z_STREAM_END)
   1.378 +    err = Z_OK;
   1.379 +
   1.380 +  // update the crc and other metadata
   1.381 +  compressed_size = comp_stream_.total_out - compressed_size;  // delta
   1.382 +  *destLen = compressed_size;
   1.383 +
   1.384 +  return err;
   1.385 +}
   1.386 +
   1.387 +int ZLib::CompressChunkOrAll(Bytef *dest, uLongf *destLen,
   1.388 +                             const Bytef *source, uLong sourceLen,
   1.389 +                             int flush_mode) {   // Z_FULL_FLUSH or Z_FINISH
   1.390 +  const int ret =
   1.391 +    CompressAtMostOrAll(dest, destLen, source, &sourceLen, flush_mode);
   1.392 +  if (ret == Z_BUF_ERROR)
   1.393 +    CompressErrorInit();
   1.394 +  return ret;
   1.395 +}
   1.396 +
   1.397 +// This routine only initializes the compression stream once.  Thereafter, it
   1.398 +// just does a deflateReset on the stream, which should be faster.
   1.399 +int ZLib::Compress(Bytef *dest, uLongf *destLen,
   1.400 +                   const Bytef *source, uLong sourceLen) {
   1.401 +  int err;
   1.402 +  if ( (err=CompressChunkOrAll(dest, destLen, source, sourceLen,
   1.403 +                               Z_FINISH)) != Z_OK )
   1.404 +    return err;
   1.405 +  Reset();         // reset for next call to Compress
   1.406 +
   1.407 +  return Z_OK;
   1.408 +}
   1.409 +
   1.410 +
   1.411 +// --------- UNCOMPRESS MODE
   1.412 +
   1.413 +int ZLib::InflateInit() {
   1.414 +  return inflateInit2(&uncomp_stream_, MAX_WBITS);
   1.415 +}
   1.416 +
   1.417 +// Initialization method to be called if we hit an error while
   1.418 +// uncompressing. On hitting an error, call this method before
   1.419 +// returning the error.
   1.420 +void ZLib::UncompressErrorInit() {
   1.421 +  inflateEnd(&uncomp_stream_);
   1.422 +  uncomp_init_ = false;
   1.423 +  Reset();
   1.424 +}
   1.425 +
   1.426 +int ZLib::UncompressInit(Bytef *dest, uLongf *destLen,
   1.427 +                         const Bytef *source, uLong *sourceLen) {
   1.428 +  int err;
   1.429 +
   1.430 +  uncomp_stream_.next_in = (Bytef*)source;
   1.431 +  uncomp_stream_.avail_in = (uInt)*sourceLen;
   1.432 +  // Check for source > 64K on 16-bit machine:
   1.433 +  if ((uLong)uncomp_stream_.avail_in != *sourceLen) return Z_BUF_ERROR;
   1.434 +
   1.435 +  uncomp_stream_.next_out = dest;
   1.436 +  uncomp_stream_.avail_out = (uInt)*destLen;
   1.437 +  if ((uLong)uncomp_stream_.avail_out != *destLen) return Z_BUF_ERROR;
   1.438 +
   1.439 +  if ( !first_chunk_ )   // only need to set up stream the first time through
   1.440 +    return Z_OK;
   1.441 +
   1.442 +  if (uncomp_init_) {    // we've already initted it
   1.443 +    err = inflateReset(&uncomp_stream_);
   1.444 +    if (err != Z_OK) {
   1.445 +      LOG(WARNING)
   1.446 +        << "ERROR: Can't reset uncompress object; creating a new one";
   1.447 +      UncompressErrorInit();
   1.448 +    }
   1.449 +  }
   1.450 +  if (!uncomp_init_) {
   1.451 +    uncomp_stream_.zalloc = (alloc_func)0;
   1.452 +    uncomp_stream_.zfree = (free_func)0;
   1.453 +    uncomp_stream_.opaque = (voidpf)0;
   1.454 +    err = InflateInit();
   1.455 +    if (err != Z_OK) return err;
   1.456 +    uncomp_init_ = true;
   1.457 +  }
   1.458 +  return Z_OK;
   1.459 +}
   1.460 +
   1.461 +// If you compressed your data a chunk at a time, with CompressChunk,
   1.462 +// you can uncompress it a chunk at a time with UncompressChunk.
   1.463 +// Only difference bewteen chunked and unchunked uncompression
   1.464 +// is the flush mode we use: Z_SYNC_FLUSH (chunked) or Z_FINISH (unchunked).
   1.465 +int ZLib::UncompressAtMostOrAll(Bytef *dest, uLongf *destLen,
   1.466 +                                const Bytef *source, uLong *sourceLen,
   1.467 +                                int flush_mode) {  // Z_SYNC_FLUSH or Z_FINISH
   1.468 +  int err = Z_OK;
   1.469 +
   1.470 +  if ( (err=UncompressInit(dest, destLen, source, sourceLen)) != Z_OK ) {
   1.471 +    LOG(WARNING) << "UncompressInit: Error: " << err << " SourceLen: "
   1.472 +                 << *sourceLen;
   1.473 +    return err;
   1.474 +  }
   1.475 +
   1.476 +  // This is used to figure out how many output bytes we wrote *this chunk*:
   1.477 +  const uLong old_total_out = uncomp_stream_.total_out;
   1.478 +
   1.479 +  // This is used to figure out how many input bytes we read *this chunk*:
   1.480 +  const uLong old_total_in = uncomp_stream_.total_in;
   1.481 +
   1.482 +  // Some setup happens only for the first chunk we compress in a run
   1.483 +  if ( first_chunk_ ) {
   1.484 +    first_chunk_ = false;                          // so we don't do this again
   1.485 +
   1.486 +    // For the first chunk *only* (to avoid infinite troubles), we let
   1.487 +    // there be no actual data to uncompress.  This sometimes triggers
   1.488 +    // when the input is only the gzip header, say.
   1.489 +    if ( *sourceLen == 0 ) {
   1.490 +      *destLen = 0;
   1.491 +      return Z_OK;
   1.492 +    }
   1.493 +  }
   1.494 +
   1.495 +  // We'll uncompress as much as we can.  If we end OK great, otherwise
   1.496 +  // if we get an error that seems to be the gzip footer, we store the
   1.497 +  // gzip footer and return OK, otherwise we return the error.
   1.498 +
   1.499 +  // flush_mode is Z_SYNC_FLUSH for chunked mode, Z_FINISH for all mode.
   1.500 +  err = inflate(&uncomp_stream_, flush_mode);
   1.501 +
   1.502 +  // Figure out how many bytes of the input zlib slurped up:
   1.503 +  const uLong bytes_read = uncomp_stream_.total_in - old_total_in;
   1.504 +  CHECK_LE(source + bytes_read, source + *sourceLen);
   1.505 +  *sourceLen = uncomp_stream_.avail_in;
   1.506 +
   1.507 +  if ((err == Z_STREAM_END || err == Z_OK)  // everything went ok
   1.508 +             && uncomp_stream_.avail_in == 0) {    // and we read it all
   1.509 +    ;
   1.510 +  } else if (err == Z_STREAM_END && uncomp_stream_.avail_in > 0) {
   1.511 +    LOG(WARNING)
   1.512 +      << "UncompressChunkOrAll: Received some extra data, bytes total: "
   1.513 +      << uncomp_stream_.avail_in << " bytes: "
   1.514 +      << string(reinterpret_cast<const char *>(uncomp_stream_.next_in),
   1.515 +                min(int(uncomp_stream_.avail_in), 20));
   1.516 +    UncompressErrorInit();
   1.517 +    return Z_DATA_ERROR;       // what's the extra data for?
   1.518 +  } else if (err != Z_OK && err != Z_STREAM_END && err != Z_BUF_ERROR) {
   1.519 +    // an error happened
   1.520 +    LOG(WARNING) << "UncompressChunkOrAll: Error: " << err
   1.521 +                 << " avail_out: " << uncomp_stream_.avail_out;
   1.522 +    UncompressErrorInit();
   1.523 +    return err;
   1.524 +  } else if (uncomp_stream_.avail_out == 0) {
   1.525 +    err = Z_BUF_ERROR;
   1.526 +  }
   1.527 +
   1.528 +  assert(err == Z_OK || err == Z_BUF_ERROR || err == Z_STREAM_END);
   1.529 +  if (err == Z_STREAM_END)
   1.530 +    err = Z_OK;
   1.531 +
   1.532 +  *destLen = uncomp_stream_.total_out - old_total_out;  // size for this call
   1.533 +
   1.534 +  return err;
   1.535 +}
   1.536 +
   1.537 +int ZLib::UncompressChunkOrAll(Bytef *dest, uLongf *destLen,
   1.538 +                               const Bytef *source, uLong sourceLen,
   1.539 +                               int flush_mode) {  // Z_SYNC_FLUSH or Z_FINISH
   1.540 +  const int ret =
   1.541 +    UncompressAtMostOrAll(dest, destLen, source, &sourceLen, flush_mode);
   1.542 +  if (ret == Z_BUF_ERROR)
   1.543 +    UncompressErrorInit();
   1.544 +  return ret;
   1.545 +}
   1.546 +
   1.547 +int ZLib::UncompressAtMost(Bytef *dest, uLongf *destLen,
   1.548 +                          const Bytef *source, uLong *sourceLen) {
   1.549 +  return UncompressAtMostOrAll(dest, destLen, source, sourceLen, Z_SYNC_FLUSH);
   1.550 +}
   1.551 +
   1.552 +// We make sure we've uncompressed everything, that is, the current
   1.553 +// uncompress stream is at a compressed-buffer-EOF boundary.  In gzip
   1.554 +// mode, we also check the gzip footer to make sure we pass the gzip
   1.555 +// consistency checks.  We RETURN true iff both types of checks pass.
   1.556 +bool ZLib::UncompressChunkDone() {
   1.557 +  assert(!first_chunk_ && uncomp_init_);
   1.558 +  // Make sure we're at the end-of-compressed-data point.  This means
   1.559 +  // if we call inflate with Z_FINISH we won't consume any input or
   1.560 +  // write any output
   1.561 +  Bytef dummyin, dummyout;
   1.562 +  uLongf dummylen = 0;
   1.563 +  if ( UncompressChunkOrAll(&dummyout, &dummylen, &dummyin, 0, Z_FINISH)
   1.564 +       != Z_OK ) {
   1.565 +    return false;
   1.566 +  }
   1.567 +
   1.568 +  // Make sure that when we exit, we can start a new round of chunks later
   1.569 +  Reset();
   1.570 +
   1.571 +  return true;
   1.572 +}
   1.573 +
   1.574 +// Uncompresses the source buffer into the destination buffer.
   1.575 +// The destination buffer must be long enough to hold the entire
   1.576 +// decompressed contents.
   1.577 +//
   1.578 +// We only initialize the uncomp_stream once.  Thereafter, we use
   1.579 +// inflateReset, which should be faster.
   1.580 +//
   1.581 +// Returns Z_OK on success, otherwise, it returns a zlib error code.
   1.582 +int ZLib::Uncompress(Bytef *dest, uLongf *destLen,
   1.583 +                     const Bytef *source, uLong sourceLen) {
   1.584 +  int err;
   1.585 +  if ( (err=UncompressChunkOrAll(dest, destLen, source, sourceLen,
   1.586 +                                 Z_FINISH)) != Z_OK ) {
   1.587 +    Reset();                           // let us try to compress again
   1.588 +    return err;
   1.589 +  }
   1.590 +  if ( !UncompressChunkDone() )        // calls Reset()
   1.591 +    return Z_DATA_ERROR;
   1.592 +  return Z_OK;  // stream_end is ok
   1.593 +}
   1.594 +
   1.595 +#endif  // HAVE_LIBZ
   1.596 +
   1.597 +}  // namespace snappy

mercurial