1 /*
2 * Copyright (C) 2008 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28 /* this small program is used to measure the performance of zlib's inflate
29 * algorithm...
30 */
31
32 /* most code lifted from the public-domain http://www.zlib.net/zpipe.c */
33
34 #include <zlib.h>
35 #include <time.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <errno.h>
40 #include <unistd.h>
41 #include <sys/time.h>
42
43 #define CHUNK 32768
44
def(FILE * source,FILE * dest,int level)45 int def(FILE *source, FILE *dest, int level)
46 {
47 int ret, flush;
48 unsigned have;
49 z_stream strm;
50 unsigned char in[CHUNK];
51 unsigned char out[CHUNK];
52
53 /* allocate deflate state */
54 strm.zalloc = Z_NULL;
55 strm.zfree = Z_NULL;
56 strm.opaque = Z_NULL;
57 ret = deflateInit(&strm, level);
58 if (ret != Z_OK)
59 return ret;
60
61 /* compress until end of file */
62 do {
63 strm.avail_in = fread(in, 1, CHUNK, source);
64 if (ferror(source)) {
65 (void)deflateEnd(&strm);
66 return Z_ERRNO;
67 }
68 flush = feof(source) ? Z_FINISH : Z_NO_FLUSH;
69 strm.next_in = in;
70
71 /* run deflate() on input until output buffer not full, finish
72 compression if all of source has been read in */
73 do {
74 strm.avail_out = CHUNK;
75 strm.next_out = out;
76 ret = deflate(&strm, flush); /* no bad return value */
77 have = CHUNK - strm.avail_out;
78 if (fwrite(out, 1, have, dest) != have || ferror(dest)) {
79 (void)deflateEnd(&strm);
80 return Z_ERRNO;
81 }
82 } while (strm.avail_out == 0);
83
84 /* done when last data in file processed */
85 } while (flush != Z_FINISH);
86
87 /* clean up and return */
88 (void)deflateEnd(&strm);
89 return Z_OK;
90 }
91
92
inf(FILE * source)93 int inf(FILE *source)
94 {
95 int ret;
96 unsigned have;
97 z_stream strm;
98 static unsigned char in[CHUNK];
99 static unsigned char out[CHUNK];
100
101 /* allocate inflate state */
102 strm.zalloc = Z_NULL;
103 strm.zfree = Z_NULL;
104 strm.opaque = Z_NULL;
105 strm.avail_in = 0;
106 strm.next_in = Z_NULL;
107 ret = inflateInit(&strm);
108 if (ret != Z_OK)
109 return ret;
110
111 /* decompress until deflate stream ends or end of file */
112 do {
113 strm.avail_in = fread(in, 1, CHUNK, source);
114 if (ferror(source)) {
115 (void)inflateEnd(&strm);
116 return Z_ERRNO;
117 }
118 if (strm.avail_in == 0)
119 break;
120 strm.next_in = in;
121
122 /* run inflate() on input until output buffer not full */
123 do {
124 strm.avail_out = CHUNK;
125 strm.next_out = out;
126 ret = inflate(&strm, Z_NO_FLUSH);
127 switch (ret) {
128 case Z_NEED_DICT:
129 ret = Z_DATA_ERROR; /* and fall through */
130 case Z_DATA_ERROR:
131 case Z_MEM_ERROR:
132 (void)inflateEnd(&strm);
133 return ret;
134 }
135 } while (strm.avail_out == 0);
136
137 /* done when inflate() says it's done */
138 } while (ret != Z_STREAM_END);
139
140 /* clean up and return */
141 (void)inflateEnd(&strm);
142 return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
143 }
144
145 #define DEFAULT_REPEAT 10
146 #define DEFAULT_LEVEL 9
147
usage(void)148 static void usage(void)
149 {
150 fprintf(stderr, "usage: test_zlib [options] filename [filename2 ...]\n" );
151 fprintf(stderr, "options: -r NN repeat count (default %d)\n", DEFAULT_REPEAT );
152 fprintf(stderr, " -N set compression level (default %d)\n", DEFAULT_LEVEL );
153 exit(1);
154 }
155
156 static double
get_time_usec(void)157 get_time_usec( void )
158 {
159 #ifdef HAVE_ANDROID_OS
160 struct timespec ts;
161
162 if ( clock_gettime( CLOCK_MONOTONIC, &ts ) < 0 )
163 fprintf(stderr, "clock_gettime: %s\n", strerror(errno) );
164
165 return ts.tv_sec*1e6 + ts.tv_nsec*1e-3;
166 #else
167 struct timeval tv;
168 if (gettimeofday( &tv, NULL ) < 0)
169 fprintf(stderr, "gettimeofday: %s\n", strerror(errno) );
170
171 return tv.tv_sec*1000000. + tv.tv_usec*1.0;
172 #endif
173 }
174
main(int argc,char ** argv)175 int main( int argc, char** argv )
176 {
177 FILE* f;
178 char tempfile[256];
179 int repeat_count = DEFAULT_REPEAT;
180 int compression_level = DEFAULT_LEVEL;
181 double usec0, usec1;
182
183 if (argc < 2)
184 usage();
185
186 for ( ; argc > 1 && argv[1][0] == '-'; argc--, argv++) {
187 const char* arg = &argv[1][1];
188 switch (arg[0]) {
189 case 'r':
190 if (arg[1] == 0) {
191 if (argc < 3)
192 usage();
193 arg = argv[2];
194 argc--;
195 argv++;
196 } else
197 arg += 1;
198
199 repeat_count = strtol(arg, NULL, 10);
200
201 if (repeat_count <= 0)
202 repeat_count = 1;
203 break;
204
205 case '0': case '1': case '2': case '3': case '4':
206 case '5': case '6': case '7': case '8': case '9':
207 compression_level = arg[0] - '0';
208 break;
209
210 default:
211 usage();
212 }
213 }
214
215 sprintf(tempfile, "/tmp/ztest.%d", getpid() );
216
217 for ( ; argc > 1; argc--, argv++ )
218 {
219 /* first, compress the file into a temporary storage */
220 FILE* f = fopen(argv[1], "rb");
221 FILE* out = NULL;
222 long fsize;
223 int ret, rr;
224
225 if (f == NULL) {
226 fprintf(stderr, "could not open '%s': %s\n", argv[1], strerror(errno) );
227 continue;
228 }
229
230 printf( "testing %s\n", argv[1] );
231 fseek( f, 0, SEEK_END );
232 fsize = ftell(f);
233 fseek( f, 0, SEEK_SET );
234
235 out = fopen( tempfile, "wb" );
236 if (out == NULL) {
237 fprintf(stderr, "could not create '%s': %s\n", tempfile, strerror(errno));
238 fclose(f);
239 continue;
240 }
241
242 usec0 = get_time_usec();
243
244 ret = def( f, out, compression_level );
245
246 usec1 = get_time_usec() - usec0;
247 printf( "compression took: %10.3f ms (%.2f KB/s)\n", usec1/1e3, fsize*(1e6/1024)/usec1 );
248
249 fclose( out );
250 fclose(f);
251
252 usec0 = get_time_usec();
253 f = fopen( tempfile, "rb" );
254
255 for ( rr = repeat_count; rr > 0; rr -- )
256 {
257 fseek( f, 0, SEEK_SET );
258 inf(f);
259 }
260 fclose( f );
261 usec1 = get_time_usec() - usec0;
262 printf( "decompression took: %10.3f ms (%.2f KB/s, %d passes)\n", usec1/1e3, fsize*(1e6/1024)*repeat_count/usec1, repeat_count );
263 }
264
265 unlink(tempfile);
266 return 0;
267 }
268