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