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
45int 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
93int 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
148static 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
156static double
157get_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
175int  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