xref: /aosp_15_r20/external/zlib/contrib/bench/zlib_bench.cc (revision 86ee64e75fa5f8bce2c8c356138035642429cd05)
1*86ee64e7SAndroid Build Coastguard Worker /*
2*86ee64e7SAndroid Build Coastguard Worker  * Copyright 2018 The Chromium Authors
3*86ee64e7SAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
4*86ee64e7SAndroid Build Coastguard Worker  * found in the Chromium source repository LICENSE file.
5*86ee64e7SAndroid Build Coastguard Worker  *
6*86ee64e7SAndroid Build Coastguard Worker  * A benchmark test harness for measuring decoding performance of gzip or zlib
7*86ee64e7SAndroid Build Coastguard Worker  * (deflate) encoded compressed data. Given a file containing any data, encode
8*86ee64e7SAndroid Build Coastguard Worker  * (compress) it into gzip or zlib format and then decode (uncompress). Output
9*86ee64e7SAndroid Build Coastguard Worker  * the median and maximum encoding and decoding rates in MB/s.
10*86ee64e7SAndroid Build Coastguard Worker  *
11*86ee64e7SAndroid Build Coastguard Worker  * Raw deflate (no gzip or zlib stream wrapper) mode is also supported. Select
12*86ee64e7SAndroid Build Coastguard Worker  * it with the [raw] argument. Use the [gzip] [zlib] arguments to select those
13*86ee64e7SAndroid Build Coastguard Worker  * stream wrappers.
14*86ee64e7SAndroid Build Coastguard Worker  *
15*86ee64e7SAndroid Build Coastguard Worker  * Note this code can be compiled outside of the Chromium build system against
16*86ee64e7SAndroid Build Coastguard Worker  * the system zlib (-lz) with g++ or clang++ as follows:
17*86ee64e7SAndroid Build Coastguard Worker  *
18*86ee64e7SAndroid Build Coastguard Worker  *   g++|clang++ -O3 -Wall -std=c++11 zlib_bench.cc -lstdc++ -lz
19*86ee64e7SAndroid Build Coastguard Worker  */
20*86ee64e7SAndroid Build Coastguard Worker 
21*86ee64e7SAndroid Build Coastguard Worker #include <algorithm>
22*86ee64e7SAndroid Build Coastguard Worker #include <chrono>
23*86ee64e7SAndroid Build Coastguard Worker #include <fstream>
24*86ee64e7SAndroid Build Coastguard Worker #include <memory>
25*86ee64e7SAndroid Build Coastguard Worker #include <string>
26*86ee64e7SAndroid Build Coastguard Worker #include <vector>
27*86ee64e7SAndroid Build Coastguard Worker 
28*86ee64e7SAndroid Build Coastguard Worker #include <memory.h>
29*86ee64e7SAndroid Build Coastguard Worker #include <stdint.h>
30*86ee64e7SAndroid Build Coastguard Worker #include <stdio.h>
31*86ee64e7SAndroid Build Coastguard Worker #include <stdlib.h>
32*86ee64e7SAndroid Build Coastguard Worker 
33*86ee64e7SAndroid Build Coastguard Worker #include "zlib.h"
34*86ee64e7SAndroid Build Coastguard Worker 
error_exit(const char * error,int code)35*86ee64e7SAndroid Build Coastguard Worker void error_exit(const char* error, int code) {
36*86ee64e7SAndroid Build Coastguard Worker   fprintf(stderr, "%s (%d)\n", error, code);
37*86ee64e7SAndroid Build Coastguard Worker   exit(code);
38*86ee64e7SAndroid Build Coastguard Worker }
39*86ee64e7SAndroid Build Coastguard Worker 
string_data(std::string * s)40*86ee64e7SAndroid Build Coastguard Worker inline char* string_data(std::string* s) {
41*86ee64e7SAndroid Build Coastguard Worker   return s->empty() ? nullptr : &*s->begin();
42*86ee64e7SAndroid Build Coastguard Worker }
43*86ee64e7SAndroid Build Coastguard Worker 
44*86ee64e7SAndroid Build Coastguard Worker struct Data {
DataData45*86ee64e7SAndroid Build Coastguard Worker   Data(size_t s) { data.reset(new (std::nothrow) char[size = s]); }
46*86ee64e7SAndroid Build Coastguard Worker   std::unique_ptr<char[]> data;
47*86ee64e7SAndroid Build Coastguard Worker   size_t size;
48*86ee64e7SAndroid Build Coastguard Worker   std::string name;
49*86ee64e7SAndroid Build Coastguard Worker };
50*86ee64e7SAndroid Build Coastguard Worker 
read_file_data_or_exit(const char * name)51*86ee64e7SAndroid Build Coastguard Worker Data read_file_data_or_exit(const char* name) {
52*86ee64e7SAndroid Build Coastguard Worker   std::ifstream file(name, std::ios::in | std::ios::binary);
53*86ee64e7SAndroid Build Coastguard Worker   if (!file) {
54*86ee64e7SAndroid Build Coastguard Worker     perror(name);
55*86ee64e7SAndroid Build Coastguard Worker     exit(1);
56*86ee64e7SAndroid Build Coastguard Worker   }
57*86ee64e7SAndroid Build Coastguard Worker 
58*86ee64e7SAndroid Build Coastguard Worker   file.seekg(0, std::ios::end);
59*86ee64e7SAndroid Build Coastguard Worker   Data data(file.tellg());
60*86ee64e7SAndroid Build Coastguard Worker   file.seekg(0, std::ios::beg);
61*86ee64e7SAndroid Build Coastguard Worker 
62*86ee64e7SAndroid Build Coastguard Worker   if (file && data.data)
63*86ee64e7SAndroid Build Coastguard Worker     file.read(data.data.get(), data.size);
64*86ee64e7SAndroid Build Coastguard Worker 
65*86ee64e7SAndroid Build Coastguard Worker   if (!file || !data.data || !data.size) {
66*86ee64e7SAndroid Build Coastguard Worker     perror((std::string("failed: reading ") + name).c_str());
67*86ee64e7SAndroid Build Coastguard Worker     exit(1);
68*86ee64e7SAndroid Build Coastguard Worker   }
69*86ee64e7SAndroid Build Coastguard Worker 
70*86ee64e7SAndroid Build Coastguard Worker   data.name = std::string(name);
71*86ee64e7SAndroid Build Coastguard Worker   return data;
72*86ee64e7SAndroid Build Coastguard Worker }
73*86ee64e7SAndroid Build Coastguard Worker 
zlib_estimate_compressed_size(size_t input_size)74*86ee64e7SAndroid Build Coastguard Worker size_t zlib_estimate_compressed_size(size_t input_size) {
75*86ee64e7SAndroid Build Coastguard Worker   return compressBound(input_size);
76*86ee64e7SAndroid Build Coastguard Worker }
77*86ee64e7SAndroid Build Coastguard Worker 
78*86ee64e7SAndroid Build Coastguard Worker enum zlib_wrapper {
79*86ee64e7SAndroid Build Coastguard Worker   kWrapperNONE,
80*86ee64e7SAndroid Build Coastguard Worker   kWrapperZLIB,
81*86ee64e7SAndroid Build Coastguard Worker   kWrapperGZIP,
82*86ee64e7SAndroid Build Coastguard Worker   kWrapperZRAW,
83*86ee64e7SAndroid Build Coastguard Worker };
84*86ee64e7SAndroid Build Coastguard Worker 
zlib_stream_wrapper_type(zlib_wrapper type)85*86ee64e7SAndroid Build Coastguard Worker inline int zlib_stream_wrapper_type(zlib_wrapper type) {
86*86ee64e7SAndroid Build Coastguard Worker   if (type == kWrapperZLIB) // zlib DEFLATE stream wrapper
87*86ee64e7SAndroid Build Coastguard Worker     return MAX_WBITS;
88*86ee64e7SAndroid Build Coastguard Worker   if (type == kWrapperGZIP) // gzip DEFLATE stream wrapper
89*86ee64e7SAndroid Build Coastguard Worker     return MAX_WBITS + 16;
90*86ee64e7SAndroid Build Coastguard Worker   if (type == kWrapperZRAW) // no wrapper, use raw DEFLATE
91*86ee64e7SAndroid Build Coastguard Worker     return -MAX_WBITS;
92*86ee64e7SAndroid Build Coastguard Worker   error_exit("bad wrapper type", int(type));
93*86ee64e7SAndroid Build Coastguard Worker   return 0;
94*86ee64e7SAndroid Build Coastguard Worker }
95*86ee64e7SAndroid Build Coastguard Worker 
zlib_wrapper_name(zlib_wrapper type)96*86ee64e7SAndroid Build Coastguard Worker const char* zlib_wrapper_name(zlib_wrapper type) {
97*86ee64e7SAndroid Build Coastguard Worker   if (type == kWrapperZLIB)
98*86ee64e7SAndroid Build Coastguard Worker     return "ZLIB";
99*86ee64e7SAndroid Build Coastguard Worker   if (type == kWrapperGZIP)
100*86ee64e7SAndroid Build Coastguard Worker     return "GZIP";
101*86ee64e7SAndroid Build Coastguard Worker   if (type == kWrapperZRAW)
102*86ee64e7SAndroid Build Coastguard Worker     return "RAW";
103*86ee64e7SAndroid Build Coastguard Worker   error_exit("bad wrapper type", int(type));
104*86ee64e7SAndroid Build Coastguard Worker   return nullptr;
105*86ee64e7SAndroid Build Coastguard Worker }
106*86ee64e7SAndroid Build Coastguard Worker 
107*86ee64e7SAndroid Build Coastguard Worker static int zlib_strategy = Z_DEFAULT_STRATEGY;
108*86ee64e7SAndroid Build Coastguard Worker 
zlib_level_strategy_name(int compression_level)109*86ee64e7SAndroid Build Coastguard Worker const char* zlib_level_strategy_name(int compression_level) {
110*86ee64e7SAndroid Build Coastguard Worker   if (compression_level == 0)
111*86ee64e7SAndroid Build Coastguard Worker     return "";  // strategy is meaningless at level 0
112*86ee64e7SAndroid Build Coastguard Worker   if (zlib_strategy == Z_HUFFMAN_ONLY)
113*86ee64e7SAndroid Build Coastguard Worker     return "huffman ";
114*86ee64e7SAndroid Build Coastguard Worker   if (zlib_strategy == Z_RLE)
115*86ee64e7SAndroid Build Coastguard Worker     return "rle ";
116*86ee64e7SAndroid Build Coastguard Worker   if (zlib_strategy == Z_DEFAULT_STRATEGY)
117*86ee64e7SAndroid Build Coastguard Worker     return "";
118*86ee64e7SAndroid Build Coastguard Worker   error_exit("bad strategy", zlib_strategy);
119*86ee64e7SAndroid Build Coastguard Worker   return nullptr;
120*86ee64e7SAndroid Build Coastguard Worker }
121*86ee64e7SAndroid Build Coastguard Worker 
122*86ee64e7SAndroid Build Coastguard Worker static int zlib_compression_level = Z_DEFAULT_COMPRESSION;
123*86ee64e7SAndroid Build Coastguard Worker 
zlib_compress(const zlib_wrapper type,const char * input,const size_t input_size,std::string * output,bool resize_output=false)124*86ee64e7SAndroid Build Coastguard Worker void zlib_compress(
125*86ee64e7SAndroid Build Coastguard Worker     const zlib_wrapper type,
126*86ee64e7SAndroid Build Coastguard Worker     const char* input,
127*86ee64e7SAndroid Build Coastguard Worker     const size_t input_size,
128*86ee64e7SAndroid Build Coastguard Worker     std::string* output,
129*86ee64e7SAndroid Build Coastguard Worker     bool resize_output = false)
130*86ee64e7SAndroid Build Coastguard Worker {
131*86ee64e7SAndroid Build Coastguard Worker   if (resize_output)
132*86ee64e7SAndroid Build Coastguard Worker     output->resize(zlib_estimate_compressed_size(input_size));
133*86ee64e7SAndroid Build Coastguard Worker   size_t output_size = output->size();
134*86ee64e7SAndroid Build Coastguard Worker 
135*86ee64e7SAndroid Build Coastguard Worker   z_stream stream;
136*86ee64e7SAndroid Build Coastguard Worker   memset(&stream, 0, sizeof(stream));
137*86ee64e7SAndroid Build Coastguard Worker 
138*86ee64e7SAndroid Build Coastguard Worker   int result = deflateInit2(&stream, zlib_compression_level, Z_DEFLATED,
139*86ee64e7SAndroid Build Coastguard Worker       zlib_stream_wrapper_type(type), MAX_MEM_LEVEL, zlib_strategy);
140*86ee64e7SAndroid Build Coastguard Worker   if (result != Z_OK)
141*86ee64e7SAndroid Build Coastguard Worker     error_exit("deflateInit2 failed", result);
142*86ee64e7SAndroid Build Coastguard Worker 
143*86ee64e7SAndroid Build Coastguard Worker   stream.next_out = (Bytef*)string_data(output);
144*86ee64e7SAndroid Build Coastguard Worker   stream.avail_out = (uInt)output_size;
145*86ee64e7SAndroid Build Coastguard Worker   stream.next_in = (z_const Bytef*)input;
146*86ee64e7SAndroid Build Coastguard Worker   stream.avail_in = (uInt)input_size;
147*86ee64e7SAndroid Build Coastguard Worker 
148*86ee64e7SAndroid Build Coastguard Worker   result = deflate(&stream, Z_FINISH);
149*86ee64e7SAndroid Build Coastguard Worker   if (stream.avail_in > 0)
150*86ee64e7SAndroid Build Coastguard Worker     error_exit("compress: input was not consumed", Z_DATA_ERROR);
151*86ee64e7SAndroid Build Coastguard Worker   if (result == Z_STREAM_END)
152*86ee64e7SAndroid Build Coastguard Worker     output_size = stream.total_out;
153*86ee64e7SAndroid Build Coastguard Worker   result |= deflateEnd(&stream);
154*86ee64e7SAndroid Build Coastguard Worker   if (result != Z_STREAM_END)
155*86ee64e7SAndroid Build Coastguard Worker     error_exit("compress failed", result);
156*86ee64e7SAndroid Build Coastguard Worker 
157*86ee64e7SAndroid Build Coastguard Worker   if (resize_output)
158*86ee64e7SAndroid Build Coastguard Worker     output->resize(output_size);
159*86ee64e7SAndroid Build Coastguard Worker }
160*86ee64e7SAndroid Build Coastguard Worker 
zlib_uncompress(const zlib_wrapper type,const std::string & input,const size_t output_size,std::string * output)161*86ee64e7SAndroid Build Coastguard Worker void zlib_uncompress(
162*86ee64e7SAndroid Build Coastguard Worker     const zlib_wrapper type,
163*86ee64e7SAndroid Build Coastguard Worker     const std::string& input,
164*86ee64e7SAndroid Build Coastguard Worker     const size_t output_size,
165*86ee64e7SAndroid Build Coastguard Worker     std::string* output)
166*86ee64e7SAndroid Build Coastguard Worker {
167*86ee64e7SAndroid Build Coastguard Worker   z_stream stream;
168*86ee64e7SAndroid Build Coastguard Worker   memset(&stream, 0, sizeof(stream));
169*86ee64e7SAndroid Build Coastguard Worker 
170*86ee64e7SAndroid Build Coastguard Worker   int result = inflateInit2(&stream, zlib_stream_wrapper_type(type));
171*86ee64e7SAndroid Build Coastguard Worker   if (result != Z_OK)
172*86ee64e7SAndroid Build Coastguard Worker     error_exit("inflateInit2 failed", result);
173*86ee64e7SAndroid Build Coastguard Worker 
174*86ee64e7SAndroid Build Coastguard Worker   stream.next_out = (Bytef*)string_data(output);
175*86ee64e7SAndroid Build Coastguard Worker   stream.avail_out = (uInt)output->size();
176*86ee64e7SAndroid Build Coastguard Worker   stream.next_in = (z_const Bytef*)input.data();
177*86ee64e7SAndroid Build Coastguard Worker   stream.avail_in = (uInt)input.size();
178*86ee64e7SAndroid Build Coastguard Worker 
179*86ee64e7SAndroid Build Coastguard Worker   result = inflate(&stream, Z_FINISH);
180*86ee64e7SAndroid Build Coastguard Worker   if (stream.total_out != output_size)
181*86ee64e7SAndroid Build Coastguard Worker     result = Z_DATA_ERROR;
182*86ee64e7SAndroid Build Coastguard Worker   result |= inflateEnd(&stream);
183*86ee64e7SAndroid Build Coastguard Worker   if (result == Z_STREAM_END)
184*86ee64e7SAndroid Build Coastguard Worker     return;
185*86ee64e7SAndroid Build Coastguard Worker 
186*86ee64e7SAndroid Build Coastguard Worker   std::string error("uncompress failed: ");
187*86ee64e7SAndroid Build Coastguard Worker   if (stream.msg)
188*86ee64e7SAndroid Build Coastguard Worker     error.append(stream.msg);
189*86ee64e7SAndroid Build Coastguard Worker   error_exit(error.c_str(), result);
190*86ee64e7SAndroid Build Coastguard Worker }
191*86ee64e7SAndroid Build Coastguard Worker 
verify_equal(const char * input,size_t size,std::string * output)192*86ee64e7SAndroid Build Coastguard Worker void verify_equal(const char* input, size_t size, std::string* output) {
193*86ee64e7SAndroid Build Coastguard Worker   const char* data = string_data(output);
194*86ee64e7SAndroid Build Coastguard Worker   if (output->size() == size && !memcmp(data, input, size))
195*86ee64e7SAndroid Build Coastguard Worker     return;
196*86ee64e7SAndroid Build Coastguard Worker   fprintf(stderr, "uncompressed data does not match the input data\n");
197*86ee64e7SAndroid Build Coastguard Worker   exit(3);
198*86ee64e7SAndroid Build Coastguard Worker }
199*86ee64e7SAndroid Build Coastguard Worker 
check_file(const Data & file,zlib_wrapper type,int mode)200*86ee64e7SAndroid Build Coastguard Worker void check_file(const Data& file, zlib_wrapper type, int mode) {
201*86ee64e7SAndroid Build Coastguard Worker   printf("%s %d %s%s\n", zlib_wrapper_name(type), zlib_compression_level,
202*86ee64e7SAndroid Build Coastguard Worker     zlib_level_strategy_name(zlib_compression_level), file.name.c_str());
203*86ee64e7SAndroid Build Coastguard Worker 
204*86ee64e7SAndroid Build Coastguard Worker   // Compress the file data.
205*86ee64e7SAndroid Build Coastguard Worker   std::string compressed;
206*86ee64e7SAndroid Build Coastguard Worker   zlib_compress(type, file.data.get(), file.size, &compressed, true);
207*86ee64e7SAndroid Build Coastguard Worker 
208*86ee64e7SAndroid Build Coastguard Worker   // Output compressed data integrity check: the data crc32.
209*86ee64e7SAndroid Build Coastguard Worker   unsigned long check = crc32_z(0, Z_NULL, 0);
210*86ee64e7SAndroid Build Coastguard Worker   const Bytef* data = (const Bytef*)compressed.data();
211*86ee64e7SAndroid Build Coastguard Worker   static_assert(sizeof(z_size_t) == sizeof(size_t), "z_size_t size");
212*86ee64e7SAndroid Build Coastguard Worker   check = crc32_z(check, data, (z_size_t)compressed.size());
213*86ee64e7SAndroid Build Coastguard Worker 
214*86ee64e7SAndroid Build Coastguard Worker   const size_t compressed_length = compressed.size();
215*86ee64e7SAndroid Build Coastguard Worker   printf("data crc32 %.8lx length %zu\n", check, compressed_length);
216*86ee64e7SAndroid Build Coastguard Worker 
217*86ee64e7SAndroid Build Coastguard Worker   // Output gzip or zlib DEFLATE stream internal check data.
218*86ee64e7SAndroid Build Coastguard Worker   if (type == kWrapperGZIP) {
219*86ee64e7SAndroid Build Coastguard Worker     uint32_t prev_word, last_word;
220*86ee64e7SAndroid Build Coastguard Worker     data += compressed_length - 8;
221*86ee64e7SAndroid Build Coastguard Worker     prev_word = data[3] << 24 | data[2] << 16 | data[1] << 8 | data[0];
222*86ee64e7SAndroid Build Coastguard Worker     data += 4;  // last compressed data word
223*86ee64e7SAndroid Build Coastguard Worker     last_word = data[3] << 24 | data[2] << 16 | data[1] << 8 | data[0];
224*86ee64e7SAndroid Build Coastguard Worker     printf("gzip crc32 %.8x length %u\n", prev_word, last_word);
225*86ee64e7SAndroid Build Coastguard Worker   } else if (type == kWrapperZLIB) {
226*86ee64e7SAndroid Build Coastguard Worker     uint32_t last_word;
227*86ee64e7SAndroid Build Coastguard Worker     data += compressed_length - 4;
228*86ee64e7SAndroid Build Coastguard Worker     last_word = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3];
229*86ee64e7SAndroid Build Coastguard Worker     printf("zlib adler %.8x\n", last_word);
230*86ee64e7SAndroid Build Coastguard Worker   }
231*86ee64e7SAndroid Build Coastguard Worker 
232*86ee64e7SAndroid Build Coastguard Worker   if (mode == 2)  // --check-binary: output compressed data.
233*86ee64e7SAndroid Build Coastguard Worker     fwrite(compressed.data(), compressed_length, 1, stdout);
234*86ee64e7SAndroid Build Coastguard Worker 
235*86ee64e7SAndroid Build Coastguard Worker   if (fflush(stdout), ferror(stdout))
236*86ee64e7SAndroid Build Coastguard Worker     error_exit("check file: error writing output", 3);
237*86ee64e7SAndroid Build Coastguard Worker }
238*86ee64e7SAndroid Build Coastguard Worker 
zlib_file(const char * name,zlib_wrapper type,int width,int check,bool output_csv_format)239*86ee64e7SAndroid Build Coastguard Worker void zlib_file(const char* name,
240*86ee64e7SAndroid Build Coastguard Worker                zlib_wrapper type,
241*86ee64e7SAndroid Build Coastguard Worker                int width,
242*86ee64e7SAndroid Build Coastguard Worker                int check,
243*86ee64e7SAndroid Build Coastguard Worker                bool output_csv_format) {
244*86ee64e7SAndroid Build Coastguard Worker   /*
245*86ee64e7SAndroid Build Coastguard Worker    * Read the file data.
246*86ee64e7SAndroid Build Coastguard Worker    */
247*86ee64e7SAndroid Build Coastguard Worker   struct Data file = read_file_data_or_exit(name);
248*86ee64e7SAndroid Build Coastguard Worker   const int length = static_cast<int>(file.size);
249*86ee64e7SAndroid Build Coastguard Worker   const char* data = file.data.get();
250*86ee64e7SAndroid Build Coastguard Worker 
251*86ee64e7SAndroid Build Coastguard Worker   /*
252*86ee64e7SAndroid Build Coastguard Worker    * Compress file: report output data checks and return.
253*86ee64e7SAndroid Build Coastguard Worker    */
254*86ee64e7SAndroid Build Coastguard Worker   if (check) {
255*86ee64e7SAndroid Build Coastguard Worker     file.name = file.name.substr(file.name.find_last_of("/\\") + 1);
256*86ee64e7SAndroid Build Coastguard Worker     check_file(file, type, check);
257*86ee64e7SAndroid Build Coastguard Worker     return;
258*86ee64e7SAndroid Build Coastguard Worker   }
259*86ee64e7SAndroid Build Coastguard Worker 
260*86ee64e7SAndroid Build Coastguard Worker   /*
261*86ee64e7SAndroid Build Coastguard Worker    * Report compression strategy and file name.
262*86ee64e7SAndroid Build Coastguard Worker    */
263*86ee64e7SAndroid Build Coastguard Worker   const char* strategy = zlib_level_strategy_name(zlib_compression_level);
264*86ee64e7SAndroid Build Coastguard Worker   if (!output_csv_format) {
265*86ee64e7SAndroid Build Coastguard Worker     printf("%s%-40s :\n", strategy, name);
266*86ee64e7SAndroid Build Coastguard Worker   }
267*86ee64e7SAndroid Build Coastguard Worker 
268*86ee64e7SAndroid Build Coastguard Worker   /*
269*86ee64e7SAndroid Build Coastguard Worker    * Chop the data into blocks.
270*86ee64e7SAndroid Build Coastguard Worker    */
271*86ee64e7SAndroid Build Coastguard Worker   const int block_size = 1 << 20;
272*86ee64e7SAndroid Build Coastguard Worker   const int blocks = (length + block_size - 1) / block_size;
273*86ee64e7SAndroid Build Coastguard Worker 
274*86ee64e7SAndroid Build Coastguard Worker   std::vector<const char*> input(blocks);
275*86ee64e7SAndroid Build Coastguard Worker   std::vector<size_t> input_length(blocks);
276*86ee64e7SAndroid Build Coastguard Worker   std::vector<std::string> compressed(blocks);
277*86ee64e7SAndroid Build Coastguard Worker   std::vector<std::string> output(blocks);
278*86ee64e7SAndroid Build Coastguard Worker 
279*86ee64e7SAndroid Build Coastguard Worker   for (int b = 0; b < blocks; ++b) {
280*86ee64e7SAndroid Build Coastguard Worker     int input_start = b * block_size;
281*86ee64e7SAndroid Build Coastguard Worker     int input_limit = std::min<int>((b + 1) * block_size, length);
282*86ee64e7SAndroid Build Coastguard Worker     input[b] = data + input_start;
283*86ee64e7SAndroid Build Coastguard Worker     input_length[b] = input_limit - input_start;
284*86ee64e7SAndroid Build Coastguard Worker   }
285*86ee64e7SAndroid Build Coastguard Worker 
286*86ee64e7SAndroid Build Coastguard Worker   /*
287*86ee64e7SAndroid Build Coastguard Worker    * Run the zlib compress/uncompress loop a few times with |repeats| to
288*86ee64e7SAndroid Build Coastguard Worker    * process about 10MB of data if the length is small relative to 10MB.
289*86ee64e7SAndroid Build Coastguard Worker    * If length is large relative to 10MB, process the data once.
290*86ee64e7SAndroid Build Coastguard Worker    */
291*86ee64e7SAndroid Build Coastguard Worker   const int mega_byte = 1024 * 1024;
292*86ee64e7SAndroid Build Coastguard Worker   const int repeats = (10 * mega_byte + length) / (length + 1);
293*86ee64e7SAndroid Build Coastguard Worker   const int runs = 5;
294*86ee64e7SAndroid Build Coastguard Worker   double ctime[runs];
295*86ee64e7SAndroid Build Coastguard Worker   double utime[runs];
296*86ee64e7SAndroid Build Coastguard Worker 
297*86ee64e7SAndroid Build Coastguard Worker   for (int run = 0; run < runs; ++run) {
298*86ee64e7SAndroid Build Coastguard Worker     const auto now = [] { return std::chrono::steady_clock::now(); };
299*86ee64e7SAndroid Build Coastguard Worker 
300*86ee64e7SAndroid Build Coastguard Worker     // Pre-grow the output buffer so we don't measure string resize time.
301*86ee64e7SAndroid Build Coastguard Worker     for (int b = 0; b < blocks; ++b)
302*86ee64e7SAndroid Build Coastguard Worker       compressed[b].resize(zlib_estimate_compressed_size(block_size));
303*86ee64e7SAndroid Build Coastguard Worker 
304*86ee64e7SAndroid Build Coastguard Worker     auto start = now();
305*86ee64e7SAndroid Build Coastguard Worker     for (int b = 0; b < blocks; ++b)
306*86ee64e7SAndroid Build Coastguard Worker       for (int r = 0; r < repeats; ++r)
307*86ee64e7SAndroid Build Coastguard Worker         zlib_compress(type, input[b], input_length[b], &compressed[b]);
308*86ee64e7SAndroid Build Coastguard Worker     ctime[run] = std::chrono::duration<double>(now() - start).count();
309*86ee64e7SAndroid Build Coastguard Worker 
310*86ee64e7SAndroid Build Coastguard Worker     // Compress again, resizing compressed, so we don't leave junk at the
311*86ee64e7SAndroid Build Coastguard Worker     // end of the compressed string that could confuse zlib_uncompress().
312*86ee64e7SAndroid Build Coastguard Worker     for (int b = 0; b < blocks; ++b)
313*86ee64e7SAndroid Build Coastguard Worker       zlib_compress(type, input[b], input_length[b], &compressed[b], true);
314*86ee64e7SAndroid Build Coastguard Worker 
315*86ee64e7SAndroid Build Coastguard Worker     for (int b = 0; b < blocks; ++b)
316*86ee64e7SAndroid Build Coastguard Worker       output[b].resize(input_length[b]);
317*86ee64e7SAndroid Build Coastguard Worker 
318*86ee64e7SAndroid Build Coastguard Worker     start = now();
319*86ee64e7SAndroid Build Coastguard Worker     for (int r = 0; r < repeats; ++r)
320*86ee64e7SAndroid Build Coastguard Worker       for (int b = 0; b < blocks; ++b)
321*86ee64e7SAndroid Build Coastguard Worker         zlib_uncompress(type, compressed[b], input_length[b], &output[b]);
322*86ee64e7SAndroid Build Coastguard Worker     utime[run] = std::chrono::duration<double>(now() - start).count();
323*86ee64e7SAndroid Build Coastguard Worker 
324*86ee64e7SAndroid Build Coastguard Worker     for (int b = 0; b < blocks; ++b)
325*86ee64e7SAndroid Build Coastguard Worker       verify_equal(input[b], input_length[b], &output[b]);
326*86ee64e7SAndroid Build Coastguard Worker   }
327*86ee64e7SAndroid Build Coastguard Worker 
328*86ee64e7SAndroid Build Coastguard Worker   /*
329*86ee64e7SAndroid Build Coastguard Worker    * Output the median/maximum compress/uncompress rates in MB/s.
330*86ee64e7SAndroid Build Coastguard Worker    */
331*86ee64e7SAndroid Build Coastguard Worker   size_t output_length = 0;
332*86ee64e7SAndroid Build Coastguard Worker   for (size_t i = 0; i < compressed.size(); ++i)
333*86ee64e7SAndroid Build Coastguard Worker     output_length += compressed[i].size();
334*86ee64e7SAndroid Build Coastguard Worker 
335*86ee64e7SAndroid Build Coastguard Worker   std::sort(ctime, ctime + runs);
336*86ee64e7SAndroid Build Coastguard Worker   std::sort(utime, utime + runs);
337*86ee64e7SAndroid Build Coastguard Worker 
338*86ee64e7SAndroid Build Coastguard Worker   double deflate_rate_med, inflate_rate_med, deflate_rate_max, inflate_rate_max;
339*86ee64e7SAndroid Build Coastguard Worker   deflate_rate_med = length * repeats / mega_byte / ctime[runs / 2];
340*86ee64e7SAndroid Build Coastguard Worker   inflate_rate_med = length * repeats / mega_byte / utime[runs / 2];
341*86ee64e7SAndroid Build Coastguard Worker   deflate_rate_max = length * repeats / mega_byte / ctime[0];
342*86ee64e7SAndroid Build Coastguard Worker   inflate_rate_max = length * repeats / mega_byte / utime[0];
343*86ee64e7SAndroid Build Coastguard Worker   double compress_ratio = output_length * 100.0 / length;
344*86ee64e7SAndroid Build Coastguard Worker 
345*86ee64e7SAndroid Build Coastguard Worker   if (!output_csv_format) {
346*86ee64e7SAndroid Build Coastguard Worker     // type, block size, compression ratio, etc
347*86ee64e7SAndroid Build Coastguard Worker     printf("%s: [b %dM] bytes %*d -> %*u %4.2f%%", zlib_wrapper_name(type),
348*86ee64e7SAndroid Build Coastguard Worker            block_size / (1 << 20), width, length, width,
349*86ee64e7SAndroid Build Coastguard Worker            unsigned(output_length), compress_ratio);
350*86ee64e7SAndroid Build Coastguard Worker 
351*86ee64e7SAndroid Build Coastguard Worker     // compress / uncompress median (max) rates
352*86ee64e7SAndroid Build Coastguard Worker     printf(" comp %5.1f (%5.1f) MB/s uncomp %5.1f (%5.1f) MB/s\n",
353*86ee64e7SAndroid Build Coastguard Worker            deflate_rate_med, deflate_rate_max, inflate_rate_med,
354*86ee64e7SAndroid Build Coastguard Worker            inflate_rate_max);
355*86ee64e7SAndroid Build Coastguard Worker   } else {
356*86ee64e7SAndroid Build Coastguard Worker     printf("%s\t%.5lf\t%.5lf\t%.5lf\t%.5lf\t%.5lf\n", name, deflate_rate_med,
357*86ee64e7SAndroid Build Coastguard Worker            inflate_rate_med, deflate_rate_max, inflate_rate_max,
358*86ee64e7SAndroid Build Coastguard Worker            compress_ratio);
359*86ee64e7SAndroid Build Coastguard Worker   }
360*86ee64e7SAndroid Build Coastguard Worker }
361*86ee64e7SAndroid Build Coastguard Worker 
362*86ee64e7SAndroid Build Coastguard Worker static int argn = 1;
363*86ee64e7SAndroid Build Coastguard Worker 
get_option(int argc,char * argv[],const char * option)364*86ee64e7SAndroid Build Coastguard Worker char* get_option(int argc, char* argv[], const char* option) {
365*86ee64e7SAndroid Build Coastguard Worker   if (argn < argc)
366*86ee64e7SAndroid Build Coastguard Worker     return !strcmp(argv[argn], option) ? argv[argn++] : nullptr;
367*86ee64e7SAndroid Build Coastguard Worker   return nullptr;
368*86ee64e7SAndroid Build Coastguard Worker }
369*86ee64e7SAndroid Build Coastguard Worker 
get_compression(int argc,char * argv[],int & value)370*86ee64e7SAndroid Build Coastguard Worker bool get_compression(int argc, char* argv[], int& value) {
371*86ee64e7SAndroid Build Coastguard Worker   if (argn < argc)
372*86ee64e7SAndroid Build Coastguard Worker     value = isdigit(argv[argn][0]) ? atoi(argv[argn++]) : -1;
373*86ee64e7SAndroid Build Coastguard Worker   return value >= 0 && value <= 9;
374*86ee64e7SAndroid Build Coastguard Worker }
375*86ee64e7SAndroid Build Coastguard Worker 
get_field_width(int argc,char * argv[],int & value)376*86ee64e7SAndroid Build Coastguard Worker void get_field_width(int argc, char* argv[], int& value) {
377*86ee64e7SAndroid Build Coastguard Worker   value = atoi(argv[argn++]);
378*86ee64e7SAndroid Build Coastguard Worker }
379*86ee64e7SAndroid Build Coastguard Worker 
usage_exit(const char * program)380*86ee64e7SAndroid Build Coastguard Worker void usage_exit(const char* program) {
381*86ee64e7SAndroid Build Coastguard Worker   static auto* options =
382*86ee64e7SAndroid Build Coastguard Worker       "gzip|zlib|raw"
383*86ee64e7SAndroid Build Coastguard Worker       " [--compression 0:9] [--huffman|--rle] [--field width] [--check]"
384*86ee64e7SAndroid Build Coastguard Worker       " [--csv]";
385*86ee64e7SAndroid Build Coastguard Worker   printf("usage: %s %s files ...\n", program, options);
386*86ee64e7SAndroid Build Coastguard Worker   printf("zlib version: %s\n", ZLIB_VERSION);
387*86ee64e7SAndroid Build Coastguard Worker   exit(1);
388*86ee64e7SAndroid Build Coastguard Worker }
389*86ee64e7SAndroid Build Coastguard Worker 
main(int argc,char * argv[])390*86ee64e7SAndroid Build Coastguard Worker int main(int argc, char* argv[]) {
391*86ee64e7SAndroid Build Coastguard Worker   zlib_wrapper type;
392*86ee64e7SAndroid Build Coastguard Worker   if (get_option(argc, argv, "zlib"))
393*86ee64e7SAndroid Build Coastguard Worker     type = kWrapperZLIB;
394*86ee64e7SAndroid Build Coastguard Worker   else if (get_option(argc, argv, "gzip"))
395*86ee64e7SAndroid Build Coastguard Worker     type = kWrapperGZIP;
396*86ee64e7SAndroid Build Coastguard Worker   else if (get_option(argc, argv, "raw"))
397*86ee64e7SAndroid Build Coastguard Worker     type = kWrapperZRAW;
398*86ee64e7SAndroid Build Coastguard Worker   else
399*86ee64e7SAndroid Build Coastguard Worker     usage_exit(argv[0]);
400*86ee64e7SAndroid Build Coastguard Worker 
401*86ee64e7SAndroid Build Coastguard Worker   int size_field_width = 0;
402*86ee64e7SAndroid Build Coastguard Worker   int file_check = 0;
403*86ee64e7SAndroid Build Coastguard Worker   bool output_csv = false;
404*86ee64e7SAndroid Build Coastguard Worker   while (argn < argc && argv[argn][0] == '-') {
405*86ee64e7SAndroid Build Coastguard Worker     if (get_option(argc, argv, "--compression")) {
406*86ee64e7SAndroid Build Coastguard Worker       if (!get_compression(argc, argv, zlib_compression_level))
407*86ee64e7SAndroid Build Coastguard Worker         usage_exit(argv[0]);
408*86ee64e7SAndroid Build Coastguard Worker     } else if (get_option(argc, argv, "--huffman")) {
409*86ee64e7SAndroid Build Coastguard Worker       zlib_strategy = Z_HUFFMAN_ONLY;
410*86ee64e7SAndroid Build Coastguard Worker     } else if (get_option(argc, argv, "--rle")) {
411*86ee64e7SAndroid Build Coastguard Worker       zlib_strategy = Z_RLE;
412*86ee64e7SAndroid Build Coastguard Worker     } else if (get_option(argc, argv, "--check")) {
413*86ee64e7SAndroid Build Coastguard Worker       file_check = 1;
414*86ee64e7SAndroid Build Coastguard Worker     } else if (get_option(argc, argv, "--check-binary")) {
415*86ee64e7SAndroid Build Coastguard Worker       file_check = 2;
416*86ee64e7SAndroid Build Coastguard Worker     } else if (get_option(argc, argv, "--field")) {
417*86ee64e7SAndroid Build Coastguard Worker       get_field_width(argc, argv, size_field_width);
418*86ee64e7SAndroid Build Coastguard Worker     } else if (get_option(argc, argv, "--csv")) {
419*86ee64e7SAndroid Build Coastguard Worker       output_csv = true;
420*86ee64e7SAndroid Build Coastguard Worker       printf(
421*86ee64e7SAndroid Build Coastguard Worker           "filename\tcompression\tdecompression\tcomp_max\t"
422*86ee64e7SAndroid Build Coastguard Worker           "decomp_max\tcompress_ratio\n");
423*86ee64e7SAndroid Build Coastguard Worker     } else {
424*86ee64e7SAndroid Build Coastguard Worker       usage_exit(argv[0]);
425*86ee64e7SAndroid Build Coastguard Worker     }
426*86ee64e7SAndroid Build Coastguard Worker   }
427*86ee64e7SAndroid Build Coastguard Worker 
428*86ee64e7SAndroid Build Coastguard Worker   if (argn >= argc)
429*86ee64e7SAndroid Build Coastguard Worker     usage_exit(argv[0]);
430*86ee64e7SAndroid Build Coastguard Worker 
431*86ee64e7SAndroid Build Coastguard Worker   if (size_field_width < 6)
432*86ee64e7SAndroid Build Coastguard Worker     size_field_width = 6;
433*86ee64e7SAndroid Build Coastguard Worker   while (argn < argc) {
434*86ee64e7SAndroid Build Coastguard Worker     zlib_file(argv[argn++], type, size_field_width, file_check, output_csv);
435*86ee64e7SAndroid Build Coastguard Worker   }
436*86ee64e7SAndroid Build Coastguard Worker 
437*86ee64e7SAndroid Build Coastguard Worker   return 0;
438*86ee64e7SAndroid Build Coastguard Worker }
439