1/* lzotest.c -- very comprehensive test driver for the LZO library
2
3   This file is part of the LZO real-time data compression library.
4
5   Copyright (C) 2008 Markus Franz Xaver Johannes Oberhumer
6   Copyright (C) 2007 Markus Franz Xaver Johannes Oberhumer
7   Copyright (C) 2006 Markus Franz Xaver Johannes Oberhumer
8   Copyright (C) 2005 Markus Franz Xaver Johannes Oberhumer
9   Copyright (C) 2004 Markus Franz Xaver Johannes Oberhumer
10   Copyright (C) 2003 Markus Franz Xaver Johannes Oberhumer
11   Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer
12   Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer
13   Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer
14   Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer
15   Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer
16   Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer
17   Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer
18   All Rights Reserved.
19
20   The LZO library is free software; you can redistribute it and/or
21   modify it under the terms of the GNU General Public License as
22   published by the Free Software Foundation; either version 2 of
23   the License, or (at your option) any later version.
24
25   The LZO library is distributed in the hope that it will be useful,
26   but WITHOUT ANY WARRANTY; without even the implied warranty of
27   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
28   GNU General Public License for more details.
29
30   You should have received a copy of the GNU General Public License
31   along with the LZO library; see the file COPYING.
32   If not, write to the Free Software Foundation, Inc.,
33   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
34
35   Markus F.X.J. Oberhumer
36   <markus@oberhumer.com>
37   http://www.oberhumer.com/opensource/lzo/
38 */
39
40
41#include "lzo/lzoconf.h"
42
43
44/*************************************************************************
45// util
46**************************************************************************/
47
48/* portability layer */
49#define WANT_LZO_MALLOC 1
50#define WANT_LZO_FREAD 1
51#define WANT_LZO_WILDARGV 1
52#define WANT_LZO_UCLOCK 1
53#include "examples/portab.h"
54
55#if defined(HAVE_STRNICMP) && !defined(HAVE_STRNCASECMP)
56#  define strncasecmp(a,b,c) strnicmp(a,b,c)
57#  define HAVE_STRNCASECMP 1
58#endif
59
60#if 0
61#  define is_digit(x)   (isdigit((unsigned char)(x)))
62#  define is_space(x)   (isspace((unsigned char)(x)))
63#else
64#  define is_digit(x)   ((unsigned)(x) - '0' <= 9)
65#  define is_space(x)   ((x)==' ' || (x)=='\t' || (x)=='\r' || (x)=='\n')
66#endif
67
68#include "mygetopt.h"
69#include "mygetopt.ch"
70
71
72/*************************************************************************
73// compression include section
74**************************************************************************/
75
76#define HAVE_LZO1_H 1
77#define HAVE_LZO1A_H 1
78#define HAVE_LZO1B_H 1
79#define HAVE_LZO1C_H 1
80#define HAVE_LZO1F_H 1
81#define HAVE_LZO1X_H 1
82#define HAVE_LZO1Y_H 1
83#define HAVE_LZO1Z_H 1
84#define HAVE_LZO2A_H 1
85
86#if defined(NO_ZLIB_H) || (SIZEOF_INT < 4)
87#undef HAVE_ZLIB_H
88#endif
89#if defined(NO_BZLIB_H) || (SIZEOF_INT != 4)
90#undef HAVE_BZLIB_H
91#endif
92
93#if 0 && defined(LZO_OS_DOS16)
94/* don't make this test program too big */
95#undef HAVE_LZO1_H
96#undef HAVE_LZO1A_H
97#undef HAVE_LZO1C_H
98#undef HAVE_LZO1Z_H
99#undef HAVE_LZO2A_H
100#undef HAVE_LZO2B_H
101#undef HAVE_ZLIB_H
102#endif
103
104
105/* LZO algorithms */
106#if defined(HAVE_LZO1_H)
107#  include "lzo/lzo1.h"
108#endif
109#if defined(HAVE_LZO1A_H)
110#  include "lzo/lzo1a.h"
111#endif
112#if defined(HAVE_LZO1B_H)
113#  include "lzo/lzo1b.h"
114#endif
115#if defined(HAVE_LZO1C_H)
116#  include "lzo/lzo1c.h"
117#endif
118#if defined(HAVE_LZO1F_H)
119#  include "lzo/lzo1f.h"
120#endif
121#if defined(HAVE_LZO1X_H)
122#  include "lzo/lzo1x.h"
123#endif
124#if defined(HAVE_LZO1Y_H)
125#  include "lzo/lzo1y.h"
126#endif
127#if defined(HAVE_LZO1Z_H)
128#  include "lzo/lzo1z.h"
129#endif
130#if defined(HAVE_LZO2A_H)
131#  include "lzo/lzo2a.h"
132#endif
133#if defined(HAVE_LZO2B_H)
134#  include "lzo/lzo2b.h"
135#endif
136
137/* other compressors */
138#if defined(__LZO_PROFESSIONAL__)
139#  include "lzopro/t_config.ch"
140#endif
141#if defined(HAVE_ZLIB_H)
142#  include <zlib.h>
143#  define ALG_ZLIB
144#endif
145#if defined(HAVE_BZLIB_H)
146#  include <bzlib.h>
147#  define ALG_BZIP2
148#endif
149
150
151/*************************************************************************
152// enumerate all methods
153**************************************************************************/
154
155enum {
156/* compression algorithms */
157    M_LZO1B_1     =     1,
158    M_LZO1B_2, M_LZO1B_3, M_LZO1B_4, M_LZO1B_5,
159    M_LZO1B_6, M_LZO1B_7, M_LZO1B_8, M_LZO1B_9,
160
161    M_LZO1C_1     =    11,
162    M_LZO1C_2, M_LZO1C_3, M_LZO1C_4, M_LZO1C_5,
163    M_LZO1C_6, M_LZO1C_7, M_LZO1C_8, M_LZO1C_9,
164
165    M_LZO1        =    21,
166    M_LZO1A       =    31,
167
168    M_LZO1B_99    =   901,
169    M_LZO1B_999   =   902,
170    M_LZO1C_99    =   911,
171    M_LZO1C_999   =   912,
172    M_LZO1_99     =   921,
173    M_LZO1A_99    =   931,
174
175    M_LZO1F_1     =    61,
176    M_LZO1F_999   =   962,
177    M_LZO1X_1     =    71,
178    M_LZO1X_1_11  =   111,
179    M_LZO1X_1_12  =   112,
180    M_LZO1X_1_15  =   115,
181    M_LZO1X_999   =   972,
182    M_LZO1Y_1     =    81,
183    M_LZO1Y_999   =   982,
184    M_LZO1Z_999   =   992,
185
186    M_LZO2A_999   =   942,
187    M_LZO2B_999   =   952,
188
189    M_LAST_LZO_COMPRESSOR = 998,
190
191/* other compressors */
192#if defined(ALG_ZLIB)
193    M_ZLIB_8_1 =  1101,
194    M_ZLIB_8_2, M_ZLIB_8_3, M_ZLIB_8_4, M_ZLIB_8_5,
195    M_ZLIB_8_6, M_ZLIB_8_7, M_ZLIB_8_8, M_ZLIB_8_9,
196#endif
197#if defined(ALG_BZIP2)
198    M_BZIP2_1  =  1201,
199    M_BZIP2_2, M_BZIP2_3, M_BZIP2_4, M_BZIP2_5,
200    M_BZIP2_6, M_BZIP2_7, M_BZIP2_8, M_BZIP2_9,
201#endif
202
203/* dummy compressor - for benchmarking */
204    M_MEMCPY      =   999,
205
206    M_LAST_COMPRESSOR = 4999,
207
208/* dummy algorithms - for benchmarking */
209    M_MEMSET      =  5001,
210
211/* checksum algorithms - for benchmarking */
212    M_ADLER32     =  6001,
213    M_CRC32       =  6002,
214#if defined(ALG_ZLIB)
215    M_Z_ADLER32   =  6011,
216    M_Z_CRC32     =  6012,
217#endif
218
219    M_UNUSED
220};
221
222
223/*************************************************************************
224// command line options
225**************************************************************************/
226
227struct corpus_entry_t;
228
229int opt_verbose = 2;
230
231long opt_c_loops = 0;
232long opt_d_loops = 0;
233const struct corpus_entry_t *opt_corpus = NULL;
234const char *opt_corpus_path = NULL;
235const char *opt_dump_compressed_data = NULL;
236
237lzo_bool opt_use_safe_decompressor = 0;
238lzo_bool opt_use_asm_decompressor = 0;
239lzo_bool opt_use_asm_fast_decompressor = 0;
240lzo_bool opt_optimize_compressed_data = 0;
241
242int opt_dict = 0;
243lzo_uint opt_max_dict_len = LZO_UINT_MAX;
244const char *opt_dictionary_file = NULL;
245
246lzo_bool opt_read_from_stdin = 0;
247
248/* set these to 1 to measure the speed impact of a checksum */
249lzo_bool opt_compute_adler32 = 0;
250lzo_bool opt_compute_crc32 = 0;
251static lzo_uint32 adler_in, adler_out;
252static lzo_uint32 crc_in, crc_out;
253
254lzo_bool opt_execution_time = 0;
255int opt_uclock = -1;
256lzo_bool opt_clear_wrkmem = 0;
257
258static const lzo_bool opt_try_to_compress_0_bytes = 1;
259
260
261/*************************************************************************
262// misc globals
263**************************************************************************/
264
265static const char *progname = "";
266static lzo_uclock_handle_t uch;
267
268/* for statistics and benchmark */
269int opt_totals = 0;
270static unsigned long total_n = 0;
271static unsigned long total_c_len = 0;
272static unsigned long total_d_len = 0;
273static unsigned long total_blocks = 0;
274static double total_perc = 0.0;
275static const char *total_method_name = NULL;
276static unsigned total_method_names = 0;
277/* Note: the average value of a rate (e.g. compression speed) is defined
278 * by the Harmonic Mean (and _not_ by the Arithmethic Mean ) */
279static unsigned long total_c_mbs_n = 0;
280static unsigned long total_d_mbs_n = 0;
281static double total_c_mbs_harmonic = 0.0;
282static double total_d_mbs_harmonic = 0.0;
283static double total_c_mbs_sum = 0.0;
284static double total_d_mbs_sum = 0.0;
285
286
287#if defined(HAVE_LZO1X_H)
288int default_method = M_LZO1X_1;
289#elif defined(HAVE_LZO1B_H)
290int default_method = M_LZO1B_1;
291#elif defined(HAVE_LZO1C_H)
292int default_method = M_LZO1C_1;
293#elif defined(HAVE_LZO1F_H)
294int default_method = M_LZO1F_1;
295#elif defined(HAVE_LZO1Y_H)
296int default_method = M_LZO1Y_1;
297#else
298int default_method = M_MEMCPY;
299#endif
300
301
302static const int benchmark_methods[] = {
303    M_LZO1B_1, M_LZO1B_9,
304    M_LZO1C_1, M_LZO1C_9,
305    M_LZO1F_1,
306    M_LZO1X_1,
307    0
308};
309
310static const int x1_methods[] = {
311    M_LZO1, M_LZO1A, M_LZO1B_1, M_LZO1C_1, M_LZO1F_1, M_LZO1X_1, M_LZO1Y_1,
312    0
313};
314
315static const int x99_methods[] = {
316    M_LZO1_99, M_LZO1A_99, M_LZO1B_99, M_LZO1C_99,
317    0
318};
319
320static const int x999_methods[] = {
321    M_LZO1B_999, M_LZO1C_999, M_LZO1F_999, M_LZO1X_999, M_LZO1Y_999,
322    M_LZO1Z_999,
323    M_LZO2A_999,
324    0
325};
326
327
328/* exit codes of this test program */
329#define EXIT_OK         0
330#define EXIT_USAGE      1
331#define EXIT_FILE       2
332#define EXIT_MEM        3
333#define EXIT_ADLER      4
334#define EXIT_LZO_ERROR  5
335#define EXIT_LZO_INIT   6
336#define EXIT_INTERNAL   7
337
338
339/*************************************************************************
340// memory setup
341**************************************************************************/
342
343static lzo_uint opt_block_size;
344static lzo_uint opt_max_data_len;
345
346typedef struct {
347    lzo_bytep   ptr;
348    lzo_uint    len;
349    lzo_uint32  adler;
350    lzo_uint32  crc;
351    lzo_bytep   alloc_ptr;
352    lzo_uint    alloc_len;
353    lzo_uint    saved_len;
354} mblock_t;
355
356static mblock_t file_data;
357static mblock_t block1;
358static mblock_t block2;
359static mblock_t wrkmem;
360static mblock_t dict;
361
362
363static void mb_alloc_extra(mblock_t *mb, lzo_uint len, lzo_uint extra_bottom, lzo_uint extra_top)
364{
365    mb->alloc_ptr = mb->ptr = NULL;
366    mb->alloc_len = mb->len = 0;
367
368    mb->alloc_len = extra_bottom + len + extra_top;
369    if (mb->alloc_len == 0) mb->alloc_len = 1;
370    mb->alloc_ptr = (lzo_bytep) lzo_malloc(mb->alloc_len);
371
372    if (mb->alloc_ptr == NULL) {
373        fprintf(stderr, "%s: out of memory (wanted %lu bytes)\n", progname, (unsigned long)mb->alloc_len);
374        exit(EXIT_MEM);
375    }
376
377    mb->ptr = mb->alloc_ptr + extra_bottom;
378    mb->len = mb->saved_len = len;
379    mb->adler = 1;
380    mb->crc = 0;
381}
382
383
384static void mb_alloc(mblock_t *mb, lzo_uint len)
385{
386    mb_alloc_extra(mb, len, 0, 0);
387}
388
389
390static void mb_free(mblock_t *mb)
391{
392    if (!mb) return;
393    if (mb->alloc_ptr) lzo_free(mb->alloc_ptr);
394    mb->alloc_ptr = mb->ptr = NULL;
395    mb->alloc_len = mb->len = 0;
396}
397
398
399static lzo_uint get_max_compression_expansion(int m, lzo_uint bl)
400{
401    if (m == M_MEMCPY || m >= M_LAST_COMPRESSOR)
402        return 0;
403    if (m == M_LZO2A_999 || m == M_LZO2B_999)
404        return bl / 8 + 256;
405    if (m > 0  && m < M_LAST_LZO_COMPRESSOR)
406        return bl / 16 +  64 + 3;
407    return bl / 8 + 256;
408}
409
410static lzo_uint get_max_decompression_overrun(int m, lzo_uint bl)
411{
412    LZO_UNUSED(m);
413    LZO_UNUSED(bl);
414    /* may overwrite 3 bytes past the end of the decompressed block */
415    if (opt_use_asm_fast_decompressor)
416        return  (lzo_uint) sizeof(lzo_voidp) - 1;
417    return 0;
418}
419
420
421/*************************************************************************
422// dictionary support
423**************************************************************************/
424
425static void dict_alloc(lzo_uint max_dict_len)
426{
427    lzo_uint l = 0xbfff;    /* MAX_DICT_LEN */
428    if (max_dict_len > 0 && l > max_dict_len)
429        l = max_dict_len;
430    mb_alloc(&dict, l);
431}
432
433
434/* this default dictionary does not provide good contexts... */
435static void dict_set_default(void)
436{
437    lzo_uint d = 0;
438    unsigned i, j;
439
440    dict.len = 16 * 256;
441    if (dict.len > dict.alloc_len)
442        dict.len = dict.alloc_len;
443
444    lzo_memset(dict.ptr, 0, dict.len);
445
446    for (i = 0; i < 256; i++)
447        for (j = 0; j < 16; j++) {
448            if (d >= dict.len)
449                goto done;
450            dict.ptr[d++] = (unsigned char) i;
451        }
452
453done:
454    dict.adler = lzo_adler32(1, dict.ptr, dict.len);
455}
456
457
458static void dict_load(const char *file_name)
459{
460    FILE *fp;
461
462    dict.len = 0;
463    fp = fopen(file_name,"rb");
464    if (fp)
465    {
466        dict.len = (lzo_uint) lzo_fread(fp, dict.ptr, dict.alloc_len);
467        fclose(fp);
468        dict.adler = lzo_adler32(1, dict.ptr, dict.len);
469    }
470}
471
472
473/*************************************************************************
474// compression database
475**************************************************************************/
476
477typedef struct
478{
479    const char *            name;
480    int                     id;
481    lzo_uint32              mem_compress;
482    lzo_uint32              mem_decompress;
483    lzo_compress_t          compress;
484    lzo_optimize_t          optimize;
485    lzo_decompress_t        decompress;
486    lzo_decompress_t        decompress_safe;
487    lzo_decompress_t        decompress_asm;
488    lzo_decompress_t        decompress_asm_safe;
489    lzo_decompress_t        decompress_asm_fast;
490    lzo_decompress_t        decompress_asm_fast_safe;
491    lzo_compress_dict_t     compress_dict;
492    lzo_decompress_dict_t   decompress_dict_safe;
493}
494compress_t;
495
496#include "asm.h"
497
498#include "wrap.h"
499#define M_PRIVATE       LZO_PRIVATE
500#define m_uint          lzo_uint
501#define m_uint32        lzo_uint32
502#define m_voidp         lzo_voidp
503#define m_bytep         lzo_bytep
504#define m_uintp         lzo_uintp
505#include "wrapmisc.h"
506
507static const compress_t compress_database[] = {
508#include "db.h"
509{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
510};
511
512
513#if defined(LZOTEST_USE_DYNLOAD)
514#  include "dynload/db.ch"
515#endif
516
517
518/*************************************************************************
519// method info
520**************************************************************************/
521
522static
523lzo_decompress_t get_decomp_info ( const compress_t *c, const char **nn )
524{
525    lzo_decompress_t d = 0;
526    const char *n = NULL;
527
528    /* safe has priority over asm/fast */
529    if (!d && opt_use_safe_decompressor && opt_use_asm_fast_decompressor)
530    {
531        d = c->decompress_asm_fast_safe;
532        n = " [fs]";
533    }
534    if (!d && opt_use_safe_decompressor && opt_use_asm_decompressor)
535    {
536        d = c->decompress_asm_safe;
537        n = " [as]";
538    }
539    if (!d && opt_use_safe_decompressor)
540    {
541        d = c->decompress_safe;
542        n = " [s]";
543    }
544    if (!d && opt_use_asm_fast_decompressor)
545    {
546        d = c->decompress_asm_fast;
547        n = " [f]";
548    }
549    if (!d && opt_use_asm_decompressor)
550    {
551        d = c->decompress_asm;
552        n = " [a]";
553    }
554    if (!d)
555    {
556        d = c->decompress;
557        n = "";
558    }
559    if (!d)
560        n = "(null)";
561
562    if (opt_dict && c->decompress_dict_safe)
563        n = "";
564
565    if (nn)
566        *nn = n;
567    return d;
568}
569
570
571static
572const compress_t *find_method_by_id ( int method )
573{
574    const compress_t *db;
575    size_t size = sizeof(compress_database) / sizeof(*(compress_database));
576    size_t i;
577
578#if defined(LZOTEST_USE_DYNLOAD)
579#  include "dynload/find_id.ch"
580#endif
581
582    db = compress_database;
583    for (i = 0; i < size && db->name != NULL; i++, db++)
584    {
585        if (method == db->id)
586            return db;
587    }
588    return NULL;
589}
590
591
592static
593const compress_t *find_method_by_name ( const char *name )
594{
595    const compress_t *db;
596    size_t size = sizeof(compress_database) / sizeof(*(compress_database));
597    size_t i;
598
599#if defined(LZOTEST_USE_DYNLOAD)
600#  include "dynload/find_name.ch"
601#endif
602
603    db = compress_database;
604    for (i = 0; i < size && db->name != NULL; i++, db++)
605    {
606        size_t n = strlen(db->name);
607
608#if defined(HAVE_STRNCASECMP)
609        if (strncasecmp(name,db->name,n) == 0 && (!name[n] || name[n] == ','))
610            return db;
611#else
612        if (strncmp(name,db->name,n) == 0 && (!name[n] || name[n] == ','))
613            return db;
614#endif
615    }
616    return NULL;
617}
618
619
620static
621lzo_bool is_compressor ( const compress_t *c )
622{
623    return (c->id <= M_LAST_COMPRESSOR || c->id >= 9721);
624}
625
626
627/*************************************************************************
628// check that memory gets accessed within bounds
629**************************************************************************/
630
631void memchecker_init ( mblock_t *mb, lzo_xint l, unsigned char random_byte )
632{
633    lzo_uint i;
634    lzo_uint len = (lzo_uint) l;
635    lzo_bytep p;
636
637    assert(len <= mb->len);
638
639    /* bottom */
640    p = mb->ptr;
641    for (i = 0; i < 16 && p > mb->alloc_ptr; i++)
642        *--p = random_byte++;
643    /* top */
644    p = mb->ptr + len;
645    for (i = 0; i < 16 && p < mb->alloc_ptr + mb->alloc_len; i++)
646        *p++ = random_byte++;
647#if 0 || defined(LZO_DEBUG)
648    /* fill in garbage */
649    p = mb->ptr;
650    random_byte |= 1;
651    for (i = 0; i < len; i++, random_byte += 2)
652        *p++ = random_byte;
653#endif
654}
655
656
657int memchecker_check ( mblock_t *mb, lzo_xint l, unsigned char random_byte )
658{
659    lzo_uint i;
660    lzo_uint len = (lzo_uint) l;
661    lzo_bytep p;
662
663    assert(len <= mb->len);
664
665    /* bottom */
666    p = mb->ptr;
667    for (i = 0; i < 16 && p > mb->alloc_ptr; i++)
668        if (*--p != random_byte++)
669            return -1;
670    /* top */
671    p = mb->ptr + len;
672    for (i = 0; i < 16 && p < mb->alloc_ptr + mb->alloc_len; i++)
673        if (*p++ != random_byte++)
674            return -1;
675    return 0;
676}
677
678
679/*************************************************************************
680// compress a block
681**************************************************************************/
682
683static
684int call_compressor   ( const compress_t *c,
685                        const lzo_bytep src, lzo_uint  src_len,
686                              lzo_bytep dst, lzo_uintp dst_len )
687{
688    int r = -100;
689
690    if (c && c->compress && wrkmem.len >= c->mem_compress)
691    {
692        unsigned char random_byte = (unsigned char) src_len;
693        memchecker_init(&wrkmem, c->mem_compress, random_byte);
694        if (opt_clear_wrkmem)
695            lzo_memset(wrkmem.ptr, 0, c->mem_compress);
696
697        if (opt_dict && c->compress_dict)
698            r = c->compress_dict(src,src_len,dst,dst_len,wrkmem.ptr,dict.ptr,dict.len);
699        else
700            r = c->compress(src,src_len,dst,dst_len,wrkmem.ptr);
701
702        if (memchecker_check(&wrkmem, c->mem_compress, random_byte) != 0)
703            printf("WARNING: wrkmem overwrite error (compress) !!!\n");
704    }
705
706    if (r == 0 && opt_compute_adler32)
707    {
708        lzo_uint32 adler;
709        adler = lzo_adler32(0, NULL, 0);
710        adler = lzo_adler32(adler, src, src_len);
711        adler_in = adler;
712    }
713    if (r == 0 && opt_compute_crc32)
714    {
715        lzo_uint32 crc;
716        crc = lzo_crc32(0, NULL, 0);
717        crc = lzo_crc32(crc, src, src_len);
718        crc_in = crc;
719    }
720
721    return r;
722}
723
724
725/*************************************************************************
726// decompress a block
727**************************************************************************/
728
729static
730int call_decompressor ( const compress_t *c, lzo_decompress_t d,
731                        const lzo_bytep src, lzo_uint  src_len,
732                              lzo_bytep dst, lzo_uintp dst_len )
733{
734    int r = -100;
735
736    if (c && d && wrkmem.len >= c->mem_decompress)
737    {
738        unsigned char random_byte = (unsigned char) src_len;
739        memchecker_init(&wrkmem, c->mem_decompress, random_byte);
740        if (opt_clear_wrkmem)
741            lzo_memset(wrkmem.ptr, 0, c->mem_decompress);
742
743        if (opt_dict && c->decompress_dict_safe)
744            r = c->decompress_dict_safe(src,src_len,dst,dst_len,wrkmem.ptr,dict.ptr,dict.len);
745        else
746            r = d(src,src_len,dst,dst_len,wrkmem.ptr);
747
748        if (memchecker_check(&wrkmem, c->mem_decompress, random_byte) != 0)
749            printf("WARNING: wrkmem overwrite error (decompress) !!!\n");
750    }
751
752    if (r == 0 && opt_compute_adler32)
753        adler_out = lzo_adler32(1, dst, *dst_len);
754    if (r == 0 && opt_compute_crc32)
755        crc_out = lzo_crc32(0, dst, *dst_len);
756
757    return r;
758}
759
760
761/*************************************************************************
762// optimize a block
763**************************************************************************/
764
765static
766int call_optimizer   ( const compress_t *c,
767                             lzo_bytep src, lzo_uint  src_len,
768                             lzo_bytep dst, lzo_uintp dst_len )
769{
770    if (c && c->optimize && wrkmem.len >= c->mem_decompress)
771        return c->optimize(src,src_len,dst,dst_len,wrkmem.ptr);
772    return 0;
773}
774
775
776/***********************************************************************
777// read a file
778************************************************************************/
779
780static int load_file(const char *file_name, lzo_uint max_len)
781{
782    FILE *fp;
783    long ll = -1;
784    lzo_uint l;
785    int r;
786    mblock_t *mb = &file_data;
787
788    mb_free(mb);
789
790    fp = fopen(file_name,"rb");
791    if (fp == NULL)
792    {
793        fprintf(stderr,"%s: ",file_name);
794        perror("fopen");
795        fflush(stderr);
796        return EXIT_FILE;
797    }
798    r = fseek(fp,(long)max_len,SEEK_SET);
799    if (r != 0)
800        r = fseek(fp,0,SEEK_END);
801    if (r == 0)
802    {
803        ll = ftell(fp);
804        r = fseek(fp,0,SEEK_SET);
805    }
806    if (r != 0 || ll < 0)
807    {
808        fprintf(stderr,"%s: ",file_name);
809        perror("fseek");
810        fflush(stderr);
811        return EXIT_FILE;
812    }
813
814    l = (lzo_uint) ll;
815    if (max_len > 0 && l > max_len)
816        l = max_len;
817    mb_alloc(mb, l);
818    mb->len = (lzo_uint) lzo_fread(fp, mb->ptr, mb->len);
819    if (fclose(fp) != 0)
820    {
821        mb_free(mb);
822        fprintf(stderr,"%s: ",file_name);
823        perror("fclose");
824        fflush(stderr);
825        return EXIT_FILE;
826    }
827
828    return EXIT_OK;
829}
830
831
832/***********************************************************************
833// print some compression statistics
834************************************************************************/
835
836static double t_div(double a, double b)
837{
838    return b > 0.00001 ? a / b : 0;
839}
840
841static double set_perc_d(double perc, char *s)
842{
843    if (perc <= 0) {
844        strcpy(s, "0.0");
845        return 0;
846    }
847    if (perc <= 100 - 1.0 / 16) {
848        sprintf(s, "%4.1f", perc);
849    }
850    else {
851        long p = (long) (perc + 0.5);
852        if (p < 100)
853            strcpy(s, "???");
854        else if (p >= 9999)
855            strcpy(s, "9999");
856        else
857            sprintf(s, "%ld", p);
858    }
859    return perc;
860}
861
862static double set_perc(unsigned long c_len, unsigned long d_len, char *s)
863{
864    double perc = 0.0;
865    if (d_len > 0)
866        perc = c_len * 100.0 / d_len;
867    return set_perc_d(perc, s);
868}
869
870
871static
872void print_stats ( const char *method_name, const char *file_name,
873                   long t_loops, long c_loops, long d_loops,
874                   double t_secs, double c_secs, double d_secs,
875                   unsigned long c_len, unsigned long d_len,
876                   unsigned long blocks )
877{
878    unsigned long x_len = d_len;
879    unsigned long t_bytes, c_bytes, d_bytes;
880    double c_mbs, d_mbs, t_mbs;
881    double perc;
882    char perc_str[4+1];
883
884    perc = set_perc(c_len, d_len, perc_str);
885
886    c_bytes = x_len * c_loops * t_loops;
887    d_bytes = x_len * d_loops * t_loops;
888    t_bytes = c_bytes + d_bytes;
889
890    if (opt_uclock == 0)
891        c_secs = d_secs = t_secs = 0.0;
892
893    /* speed in uncompressed megabytes per second (1 megabyte = 1.000.000 bytes) */
894    c_mbs = (c_secs > 0.001) ? (c_bytes / c_secs) / 1000000.0 : 0;
895    d_mbs = (d_secs > 0.001) ? (d_bytes / d_secs) / 1000000.0 : 0;
896    t_mbs = (t_secs > 0.001) ? (t_bytes / t_secs) / 1000000.0 : 0;
897
898    total_n++;
899    total_c_len += c_len;
900    total_d_len += d_len;
901    total_blocks += blocks;
902    total_perc += perc;
903    if (c_mbs > 0) {
904        total_c_mbs_n += 1;
905        total_c_mbs_harmonic += 1.0 / c_mbs;
906        total_c_mbs_sum += c_mbs;
907    }
908    if (d_mbs > 0) {
909        total_d_mbs_n += 1;
910        total_d_mbs_harmonic += 1.0 / d_mbs;
911        total_d_mbs_sum += d_mbs;
912    }
913
914    if (opt_verbose >= 2)
915    {
916        printf("  compressed into %lu bytes,  %s%%  (%s%.3f bits/byte)\n",
917               c_len, perc_str, "", perc * 0.08);
918
919#if 0
920        printf("%-15s %5ld: ","overall", t_loops);
921        printf("%10lu bytes, %8.2f secs, %8.3f MB/sec\n",
922               t_bytes, t_secs, t_mbs);
923#else
924        LZO_UNUSED(t_mbs);
925#endif
926        printf("%-15s %5ld: ","compress", c_loops);
927        printf("%10lu bytes, %8.2f secs, %8.3f MB/sec\n",
928               c_bytes, c_secs, c_mbs);
929        printf("%-15s %5ld: ","decompress", d_loops);
930        printf("%10lu bytes, %8.2f secs, %8.3f MB/sec\n",
931               d_bytes, d_secs, d_mbs);
932        printf("\n");
933    }
934
935    /* create a line for util/table.pl */
936    if (opt_verbose >= 1)
937    {
938        /* get basename */
939        const char *n, *nn, *b;
940        for (nn = n = b = file_name; *nn; nn++)
941            if (*nn == '/' || *nn == '\\' || *nn == ':')
942                b = nn + 1;
943            else
944                n = b;
945
946        printf("%-13s| %-14s %8lu %4lu %9lu %4s %s%8.3f %8.3f |\n",
947               method_name, n, d_len, blocks, c_len, perc_str, "", c_mbs, d_mbs);
948    }
949
950    if (opt_verbose >= 2)
951        printf("\n");
952}
953
954
955static
956void print_totals ( void )
957{
958    char perc_str[4+1];
959
960    if ((opt_verbose >= 1 && total_n > 1) || (opt_totals >= 2))
961    {
962        unsigned long n = total_n > 0 ? total_n : 1;
963        const char *t1 = "-------";
964        const char *t2 = total_method_names == 1 ? total_method_name : "";
965#if 1 && defined(__ACCLIB_PCLOCK_CH_INCLUDED)
966        char uclock_mode[32+1];
967        sprintf(uclock_mode, "[clock=%d]", uch.mode);
968        t1 = uclock_mode;
969        if (opt_uclock == 0) t1 = t2;
970#endif
971
972#if defined(LZOTEST_USE_DYNLOAD)
973#  include "dynload/print_totals.ch"
974#endif
975
976#if 1
977        set_perc_d(total_perc / n, perc_str);
978        printf("%-13s  %-12s %10lu %4.1f %9lu %4s %8.3f %8.3f\n",
979            t1, "***AVG***",
980            total_d_len / n, total_blocks * 1.0 / n, total_c_len / n, perc_str,
981            t_div(total_c_mbs_n, total_c_mbs_harmonic),
982            t_div(total_d_mbs_n, total_d_mbs_harmonic));
983#endif
984        set_perc(total_c_len, total_d_len, perc_str);
985        printf("%-13s  %-12s %10lu %4lu %9lu %4s %s%8.3f %8.3f\n",
986            t2, "***TOTALS***",
987            total_d_len, total_blocks, total_c_len, perc_str, "",
988            t_div(total_c_mbs_n, total_c_mbs_harmonic),
989            t_div(total_d_mbs_n, total_d_mbs_harmonic));
990    }
991}
992
993
994/*************************************************************************
995// compress and decompress a file
996**************************************************************************/
997
998static
999int process_file ( const compress_t *c, lzo_decompress_t decompress,
1000                   const char *method_name,
1001                   const char *file_name,
1002                   long t_loops, long c_loops, long d_loops )
1003{
1004    long t_i;
1005    unsigned long blocks = 0;
1006    unsigned long compressed_len = 0;
1007    double t_time = 0, c_time = 0, d_time = 0;
1008    lzo_uclock_t t_start, t_stop, x_start, x_stop;
1009    FILE *fp_dump = NULL;
1010
1011    if (opt_dump_compressed_data)
1012        fp_dump = fopen(opt_dump_compressed_data,"wb");
1013
1014/* process the file */
1015
1016    lzo_uclock_flush_cpu_cache(&uch, 0);
1017    lzo_uclock_read(&uch, &t_start);
1018    for (t_i = 0; t_i < t_loops; t_i++)
1019    {
1020        lzo_uint len, c_len, c_len_max, d_len = 0;
1021        const lzo_bytep d = file_data.ptr;
1022
1023        len = file_data.len;
1024        c_len = 0;
1025        blocks = 0;
1026
1027        /* process blocks */
1028        if (len > 0 || opt_try_to_compress_0_bytes) do
1029        {
1030            lzo_uint bl;
1031            long c_i;
1032            int r;
1033            unsigned char random_byte = (unsigned char) file_data.len;
1034#if 1 && defined(CLOCKS_PER_SEC)
1035            random_byte ^= (unsigned char) clock();
1036#endif
1037            blocks++;
1038
1039            bl = len > opt_block_size ? opt_block_size : len;
1040            /* update lengths for memchecker_xxx() */
1041            block1.len = bl + get_max_compression_expansion(c->id, bl);
1042            block2.len = bl + get_max_decompression_overrun(c->id, bl);
1043#if defined(__LZO_CHECKER)
1044            /* malloc a block of the exact size to detect any overrun */
1045            assert(block1.alloc_ptr == NULL);
1046            assert(block2.alloc_ptr == NULL);
1047            mb_alloc(&block1, block1.len);
1048            mb_alloc(&block2, block2.len);
1049#endif
1050            assert(block1.len <= block1.saved_len);
1051            assert(block2.len <= block2.saved_len);
1052
1053            memchecker_init(&block1, block1.len, random_byte);
1054            memchecker_init(&block2, block2.len, random_byte);
1055
1056        /* compress the block */
1057            c_len = c_len_max = 0;
1058            lzo_uclock_flush_cpu_cache(&uch, 0);
1059            lzo_uclock_read(&uch, &x_start);
1060            for (r = 0, c_i = 0; r == 0 && c_i < c_loops; c_i++)
1061            {
1062                c_len = block1.len;
1063                r = call_compressor(c, d, bl, block1.ptr, &c_len);
1064                if (r == 0 && c_len > c_len_max)
1065                    c_len_max = c_len;
1066                if (r == 0 && c_len > block1.len)
1067                    goto compress_overrun;
1068            }
1069            lzo_uclock_read(&uch, &x_stop);
1070            c_time += lzo_uclock_get_elapsed(&uch, &x_start, &x_stop);
1071            if (r != 0)
1072            {
1073                printf("  compression failed in block %ld (%d) (%lu %lu)\n",
1074                    blocks, r, (long)c_len, (long)bl);
1075                return EXIT_LZO_ERROR;
1076            }
1077            if (memchecker_check(&block1, block1.len, random_byte) != 0)
1078            {
1079compress_overrun:
1080                printf("  compression overwrite error in block %lu "
1081                    "(%lu %lu %lu %lu)\n",
1082                    blocks, (long)c_len, (long)d_len, (long)bl, (long)block1.len);
1083                return EXIT_LZO_ERROR;
1084            }
1085
1086        /* optimize the compressed block */
1087            if (c_len < bl && opt_optimize_compressed_data)
1088            {
1089                d_len = bl;
1090                r = call_optimizer(c, block1.ptr, c_len, block2.ptr, &d_len);
1091                if (r != 0 || d_len != bl)
1092                {
1093                    printf("  optimization failed in block %lu (%d) "
1094                        "(%lu %lu %lu)\n", blocks, r,
1095                        (long)c_len, (long)d_len, (long)bl);
1096                    return EXIT_LZO_ERROR;
1097                }
1098                if (memchecker_check(&block1, block1.len, random_byte) != 0 ||
1099                    memchecker_check(&block2, block2.len, random_byte) != 0)
1100                {
1101                    printf("  optimize overwrite error in block %lu "
1102                        "(%lu %lu %lu %lu)\n",
1103                        blocks, (long)c_len, (long)d_len, (long)bl, (long)block1.len);
1104                    return EXIT_LZO_ERROR;
1105                }
1106            }
1107
1108            /* dump compressed data to disk */
1109            if (fp_dump)
1110            {
1111                (void) lzo_fwrite(fp_dump, block1.ptr, c_len);
1112                fflush(fp_dump);
1113            }
1114
1115        /* decompress the block and verify */
1116            lzo_uclock_flush_cpu_cache(&uch, 0);
1117            lzo_uclock_read(&uch, &x_start);
1118            for (r = 0, c_i = 0; r == 0 && c_i < d_loops; c_i++)
1119            {
1120                d_len = bl;
1121                r = call_decompressor(c, decompress, block1.ptr, c_len, block2.ptr, &d_len);
1122                if (d_len != bl)
1123                    break;
1124            }
1125            lzo_uclock_read(&uch, &x_stop);
1126            d_time += lzo_uclock_get_elapsed(&uch, &x_start, &x_stop);
1127            if (r != 0)
1128            {
1129                printf("  decompression failed in block %lu (%d) "
1130                    "(%lu %lu %lu)\n", blocks, r,
1131                    (long)c_len, (long)d_len, (long)bl);
1132                return EXIT_LZO_ERROR;
1133            }
1134            if (d_len != bl)
1135            {
1136                printf("  decompression size error in block %lu (%lu %lu %lu)\n",
1137                    blocks, (long)c_len, (long)d_len, (long)bl);
1138                return EXIT_LZO_ERROR;
1139            }
1140            if (is_compressor(c))
1141            {
1142                if (lzo_memcmp(d, block2.ptr, bl) != 0)
1143                {
1144                    lzo_uint x = 0;
1145                    while (x < bl && block2.ptr[x] == d[x])
1146                        x++;
1147                    printf("  decompression data error in block %lu at offset "
1148                        "%lu (%lu %lu)\n", blocks, (long)x,
1149                        (long)c_len, (long)d_len);
1150                    if (opt_compute_adler32)
1151                        printf("      checksum: 0x%08lx 0x%08lx\n",
1152                            (long)adler_in, (long)adler_out);
1153#if 0
1154                    printf("Orig:  ");
1155                    r = (x >= 10) ? -10 : 0 - (int) x;
1156                    for (j = r; j <= 10 && x + j < bl; j++)
1157                        printf(" %02x", (int)d[x+j]);
1158                    printf("\nDecomp:");
1159                    for (j = r; j <= 10 && x + j < bl; j++)
1160                        printf(" %02x", (int)block2.ptr[x+j]);
1161                    printf("\n");
1162#endif
1163                    return EXIT_LZO_ERROR;
1164                }
1165                if ((opt_compute_adler32 && adler_in != adler_out) ||
1166                    (opt_compute_crc32 && crc_in != crc_out))
1167                {
1168                    printf("  checksum error in block %lu (%lu %lu)\n",
1169                        blocks, (long)c_len, (long)d_len);
1170                    printf("      adler32: 0x%08lx 0x%08lx\n",
1171                        (long)adler_in, (long)adler_out);
1172                    printf("      crc32: 0x%08lx 0x%08lx\n",
1173                        (long)crc_in, (long)crc_out);
1174                    return EXIT_LZO_ERROR;
1175                }
1176            }
1177
1178            if (memchecker_check(&block2, block2.len, random_byte) != 0)
1179            {
1180                printf("  decompression overwrite error in block %lu "
1181                    "(%lu %lu %lu %lu)\n",
1182                    blocks, (long)c_len, (long)d_len, (long)bl, (long)block2.len);
1183                return EXIT_LZO_ERROR;
1184            }
1185
1186#if defined(__LZO_CHECKER)
1187            /* free in reverse order of allocations */
1188            mb_free(&block2);
1189            mb_free(&block1);
1190#endif
1191
1192            d += bl;
1193            len -= bl;
1194            compressed_len += (unsigned long) c_len_max;
1195        }
1196        while (len > 0);
1197    }
1198    lzo_uclock_read(&uch, &t_stop);
1199    t_time += lzo_uclock_get_elapsed(&uch, &t_start, &t_stop);
1200
1201    if (fp_dump)
1202        fclose(fp_dump);
1203    opt_dump_compressed_data = NULL;    /* only dump the first file */
1204
1205    print_stats(method_name, file_name,
1206                t_loops, c_loops, d_loops,
1207                t_time, c_time, d_time,
1208                compressed_len, (unsigned long) file_data.len, blocks);
1209    if (total_method_name != c->name) {
1210        total_method_name = c->name;
1211        total_method_names += 1;
1212    }
1213
1214    return EXIT_OK;
1215}
1216
1217
1218
1219static
1220int do_file ( int method, const char *file_name,
1221              long c_loops, long d_loops,
1222              lzo_uint32p p_adler, lzo_uint32p p_crc )
1223{
1224    int r;
1225    const compress_t *c;
1226    lzo_decompress_t decompress;
1227    lzo_uint32 adler, crc;
1228    char method_name[256+1];
1229    const char *n;
1230    const long t_loops = 1;
1231
1232    adler_in = adler_out = 0;
1233    crc_in = crc_out = 0;
1234    if (p_adler)
1235        *p_adler = 0;
1236    if (p_crc)
1237        *p_crc = 0;
1238
1239    c = find_method_by_id(method);
1240    if (c == NULL || c->name == NULL || c->compress == NULL)
1241        return EXIT_INTERNAL;
1242    decompress = get_decomp_info(c,&n);
1243    if (!decompress || n == NULL || wrkmem.len < c->mem_decompress)
1244        return EXIT_INTERNAL;
1245    strcpy(method_name,c->name);
1246    strcat(method_name,n);
1247
1248    if (c_loops < 1)  c_loops = 1;
1249    if (d_loops < 1)  d_loops = 1;
1250
1251    fflush(stdout); fflush(stderr);
1252
1253    /* read the whole file */
1254    r = load_file(file_name, opt_max_data_len);
1255    if (r != 0)
1256        return r;
1257
1258    /* compute some checksums */
1259    adler = lzo_adler32(0, NULL, 0);
1260    adler = lzo_adler32(adler, file_data.ptr, file_data.len);
1261    if (p_adler)
1262        *p_adler = adler;
1263    crc = lzo_crc32(0, NULL, 0);
1264    crc = lzo_crc32(crc, file_data.ptr, file_data.len);
1265    if (p_crc)
1266        *p_crc = crc;
1267
1268    if (opt_verbose >= 2)
1269    {
1270        printf("File %s: %lu bytes   (0x%08lx, 0x%08lx)\n",
1271                file_name, (long) file_data.len, (long) adler, (long) crc);
1272        printf("  compressing %lu bytes (%ld/%ld/%ld loops, %lu block-size)\n",
1273                (long) file_data.len, t_loops, c_loops, d_loops, (long) opt_block_size);
1274        printf("  %s\n", method_name);
1275    }
1276
1277    r = process_file(c, decompress, method_name, file_name,
1278                     t_loops, c_loops, d_loops);
1279
1280    return r;
1281}
1282
1283
1284/*************************************************************************
1285// Calgary Corpus and Silesia Corpus test suite driver
1286**************************************************************************/
1287
1288struct corpus_entry_t
1289{
1290    const char *name;
1291    long loops;
1292    lzo_uint32 adler;
1293    lzo_uint32 crc;
1294};
1295
1296static const struct corpus_entry_t calgary_corpus[] =
1297{
1298    { "bib",       8,  0x4bd09e98L, 0xb856ebe8L },
1299    { "book1",     1,  0xd4d3613eL, 0x24e19972L },
1300    { "book2",     1,  0x6fe14cc3L, 0xba0f3f26L },
1301    { "geo",       6,  0xf3cc5be0L, 0x4d3a6ed0L },
1302    { "news",      2,  0x2ed405b8L, 0xcafac853L },
1303    { "obj1",     35,  0x3887dd2cL, 0xc7b0cd26L },
1304    { "obj2",      4,  0xf89407c4L, 0x3ae33007L },
1305    { "paper1",   17,  0xfe65ce62L, 0x2b6baca0L },
1306    { "paper2",   11,  0x1238b7c2L, 0xf76cba72L },
1307    { "pic",       4,  0xf61a5702L, 0x4b17e59cL },
1308    { "progc",    25,  0x4c00ba45L, 0x6fb16094L },
1309    { "progl",    20,  0x4cba738eL, 0xddbf6baaL },
1310    { "progp",    28,  0x7495b92bL, 0x493a1809L },
1311    { "trans",    15,  0x52a2cec8L, 0xcdec06a6L },
1312    { NULL,        0,  0x00000000L, 0x00000000L }
1313};
1314
1315static const struct corpus_entry_t silesia_corpus[] =
1316{
1317    { "dickens",   1,  0x170f606fL, 0xaf3a6b76L },
1318    { "mozilla",   1,  0x1188dd4eL, 0x7fb0ab7dL },
1319    { "mr",        1,  0xaea14b97L, 0xa341883fL },
1320    { "nci",       1,  0x0af16f1fL, 0x60ff63d3L },
1321    { "ooffice",   1,  0x83c8f689L, 0xa023e1faL },
1322    { "osdb",      1,  0xb825b790L, 0xa0ca388cL },
1323    { "reymont",   1,  0xce5c82caL, 0x50d35f03L },
1324    { "samba",     1,  0x19dbb9f5L, 0x2beac5f3L },
1325    { "sao",       1,  0x7edfc4a9L, 0xfda125bfL },
1326    { "webster",   1,  0xf2962fc6L, 0x01f5a2e9L },
1327    { "xml",       1,  0xeccd03d6L, 0xff8f3051L },
1328    { "x-ray",     1,  0xc95435a0L, 0xc86a35c6L },
1329    { NULL,        0,  0x00000000L, 0x00000000L }
1330};
1331
1332
1333static
1334int do_corpus ( const struct corpus_entry_t *corpus, int method, const char *path,
1335                long c_loops, long d_loops )
1336{
1337    size_t i, n;
1338    char name[256];
1339
1340    if (path == NULL || strlen(path) >= sizeof(name) - 12)
1341        return EXIT_USAGE;
1342
1343    strcpy(name,path);
1344    n = strlen(name);
1345    if (n > 0 && name[n-1] != '/' && name[n-1] != '\\' && name[n-1] != ':')
1346    {
1347        strcat(name,"/");
1348        n++;
1349    }
1350
1351    for (i = 0; corpus[i].name != NULL; i++)
1352    {
1353        lzo_uint32 adler, crc;
1354        long c = c_loops * corpus[i].loops;
1355        long d = d_loops * corpus[i].loops;
1356        int r;
1357
1358        strcpy(name+n,corpus[i].name);
1359        r = do_file(method, name, c, d, &adler, &crc);
1360        if (r != 0)
1361            return r;
1362        if (adler != corpus[i].adler)
1363        {
1364            printf("  invalid test suite\n");
1365            return EXIT_ADLER;
1366        }
1367        if (corpus[i].crc && crc != corpus[i].crc)
1368        {
1369            printf("  internal checksum error !!  (0x%08lx 0x%08lx)\n",
1370                    (long) crc, (long) corpus[i].crc);
1371            return EXIT_INTERNAL;
1372        }
1373    }
1374    return EXIT_OK;
1375}
1376
1377
1378/*************************************************************************
1379// usage
1380**************************************************************************/
1381
1382static
1383void usage ( const char *name, int exit_code, lzo_bool show_methods )
1384{
1385    FILE *f;
1386    int i;
1387
1388    f = stdout;
1389
1390    fflush(stdout); fflush(stderr);
1391
1392    fprintf(f,"Usage: %s [option..] file...\n", name);
1393    fprintf(f,"\n");
1394    fprintf(f,"Options:\n");
1395    fprintf(f,"  -m#     compression method\n");
1396    fprintf(f,"  -b#     set input block size (default %ld, max %ld)\n",
1397        (long) opt_block_size, (long) opt_max_data_len);
1398    fprintf(f,"  -n#     number of compression/decompression runs\n");
1399    fprintf(f,"  -c#     number of compression runs\n");
1400    fprintf(f,"  -d#     number of decompression runs\n");
1401    fprintf(f,"  -S      use safe decompressor (if available)\n");
1402    fprintf(f,"  -A      use assembler decompressor (if available)\n");
1403    fprintf(f,"  -F      use fast assembler decompressor (if available)\n");
1404    fprintf(f,"  -O      optimize compressed data (if available)\n");
1405    fprintf(f,"  -s DIR  process Calgary Corpus test suite in directory `DIR'\n");
1406    fprintf(f,"  -@      read list of files to compress from stdin\n");
1407    fprintf(f,"  -q      be quiet\n");
1408    fprintf(f,"  -Q      be very quiet\n");
1409    fprintf(f,"  -v      be verbose\n");
1410    fprintf(f,"  -L      display software license\n");
1411
1412    if (show_methods)
1413    {
1414#if defined(__ACCLIB_PCLOCK_CH_INCLUDED)
1415        lzo_uclock_t t_dummy;
1416        lzo_uclock_read(&uch, &t_dummy);
1417        (void) lzo_uclock_get_elapsed(&uch, &t_dummy, &t_dummy);
1418        fprintf(f,"\nAll timings are recorded using uclock mode %d %s.\n", uch.mode, uch.name);
1419#endif
1420        fprintf(f,"\n\n");
1421        fprintf(f,"The following compression methods are available:\n");
1422        fprintf(f,"\n");
1423        fprintf(f,"  usage   name           memory          available extras\n");
1424        fprintf(f,"  -----   ----           ------          ----------------\n");
1425
1426        for (i = 0; i <= M_LAST_COMPRESSOR; i++)
1427        {
1428            const compress_t *c;
1429            c = find_method_by_id(i);
1430            if (c)
1431            {
1432                char n[16];
1433                static const char * const s[3] = {"          ", ", ", ""};
1434                int j = 0;
1435                unsigned long m = c->mem_compress;
1436
1437                sprintf(n,"-m%d",i);
1438                fprintf(f,"  %-6s  %-13s",n,c->name);
1439#if 1
1440                fprintf(f,"%9ld", m);
1441#else
1442                m = (m + 1023) / 1024;
1443                fprintf(f,"%6ld kB", m);
1444#endif
1445
1446                if (c->decompress_safe)
1447                    fprintf(f, "%s%s", j++ == 0 ? s[0] : s[1], "safe");
1448                if (c->decompress_asm)
1449                    fprintf(f, "%s%s", j++ == 0 ? s[0] : s[1], "asm");
1450                if (c->decompress_asm_safe)
1451                    fprintf(f, "%s%s", j++ == 0 ? s[0] : s[1], "asm+safe");
1452                if (c->decompress_asm_fast)
1453                    fprintf(f, "%s%s", j++ == 0 ? s[0] : s[1], "fastasm");
1454                if (c->decompress_asm_fast_safe)
1455                    fprintf(f, "%s%s", j++ == 0 ? s[0] : s[1], "fastasm+safe");
1456                if (c->optimize)
1457                    fprintf(f, "%s%s", j++ == 0 ? s[0] : s[1], "optimize");
1458                if (j > 0)
1459                    fprintf(f, s[2]);
1460                fprintf(f,"\n");
1461            }
1462        }
1463    }
1464    else
1465    {
1466        fprintf(f,"\n");
1467        fprintf(f,"Type '%s -m' to list all available methods.\n", name);
1468    }
1469
1470    fflush(f);
1471    if (exit_code < 0)
1472        exit_code = EXIT_USAGE;
1473    exit(exit_code);
1474}
1475
1476
1477static
1478void license(void)
1479{
1480    FILE *f;
1481
1482    f = stdout;
1483
1484    fflush(stdout); fflush(stderr);
1485
1486#if defined(__LZO_PROFESSIONAL__)
1487#  include "lzopro/license.ch"
1488#else
1489fprintf(f,
1490"   The LZO library is free software; you can redistribute it and/or\n"
1491"   modify it under the terms of the GNU General Public License as\n"
1492"   published by the Free Software Foundation; either version 2 of\n"
1493"   the License, or (at your option) any later version.\n"
1494"\n"
1495"   The LZO library is distributed in the hope that it will be useful,\n"
1496"   but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
1497"   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
1498"   GNU General Public License for more details.\n"
1499    );
1500fprintf(f,
1501"\n"
1502"   You should have received a copy of the GNU General Public License\n"
1503"   along with the LZO library; see the file COPYING.\n"
1504"   If not, write to the Free Software Foundation, Inc.,\n"
1505"   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n"
1506"\n"
1507"   Markus F.X.J. Oberhumer\n"
1508"   <markus@oberhumer.com>\n"
1509"   http://www.oberhumer.com/opensource/lzo/\n"
1510"\n"
1511    );
1512#endif
1513
1514    fflush(f);
1515    exit(EXIT_OK);
1516}
1517
1518
1519/*************************************************************************
1520// parse method option '-m'
1521**************************************************************************/
1522
1523static int methods[256+1];
1524static int methods_n = 0;
1525
1526static void add_method(int m)
1527{
1528    int i;
1529
1530    if (m > 0)
1531    {
1532        if (!find_method_by_id(m)) {
1533            fprintf(stdout,"%s: invalid method %d\n",progname,m);
1534            exit(EXIT_USAGE);
1535        }
1536
1537        for (i = 0; i < methods_n; i++)
1538            if (methods[i] == m)
1539                return;
1540
1541        if (methods_n >= 256)
1542        {
1543            fprintf(stderr,"%s: too many methods\n",progname);
1544            exit(EXIT_USAGE);
1545        }
1546
1547        methods[methods_n++] = m;
1548        methods[methods_n] = 0;
1549    }
1550}
1551
1552
1553static void add_methods(const int *ml)
1554{
1555    while (*ml != 0)
1556        add_method(*ml++);
1557}
1558
1559
1560static void add_all_methods(int first, int last)
1561{
1562    int m;
1563
1564    for (m = first; m <= last; m++)
1565        if (find_method_by_id(m) != NULL)
1566            add_method(m);
1567}
1568
1569
1570static int m_strcmp(const char *a, const char *b)
1571{
1572    size_t n;
1573
1574    if (a[0] == 0 || b[0] == 0)
1575        return 1;
1576    n = strlen(b);
1577    if (strncmp(a,b,n) == 0 && (a[n] == 0 || a[n] == ','))
1578        return 0;
1579    return 1;
1580}
1581
1582
1583static lzo_bool m_strisdigit(const char *s)
1584{
1585    for (;;)
1586    {
1587        if (!is_digit(*s))
1588            return 0;
1589        s++;
1590        if (*s == 0 || *s == ',')
1591            return 1;
1592    }
1593}
1594
1595
1596static void parse_methods(const char *p)
1597{
1598    const compress_t *c;
1599
1600    for (;;)
1601    {
1602        if (p == NULL || p[0] == 0)
1603            usage(progname,-1,1);
1604        else if ((c = find_method_by_name(p)) != NULL)
1605            add_method(c->id);
1606        else if (m_strcmp(p,"all") == 0 || m_strcmp(p,"avail") == 0)
1607            add_all_methods(1,M_LAST_COMPRESSOR);
1608        else if (m_strcmp(p,"ALL") == 0)
1609        {
1610            add_all_methods(1,M_LAST_COMPRESSOR);
1611            add_all_methods(9721,9729);
1612            add_all_methods(9781,9789);
1613        }
1614        else if (m_strcmp(p,"lzo") == 0)
1615            add_all_methods(1,M_MEMCPY);
1616        else if (m_strcmp(p,"bench") == 0)
1617            add_methods(benchmark_methods);
1618        else if (m_strcmp(p,"m1") == 0)
1619            add_methods(x1_methods);
1620        else if (m_strcmp(p,"m99") == 0)
1621            add_methods(x99_methods);
1622        else if (m_strcmp(p,"m999") == 0)
1623            add_methods(x999_methods);
1624        else if (m_strcmp(p,"1x999") == 0)
1625            add_all_methods(9721,9729);
1626        else if (m_strcmp(p,"1y999") == 0)
1627            add_all_methods(9821,9829);
1628#if defined(ALG_ZLIB)
1629        else if (m_strcmp(p,"zlib") == 0)
1630            add_all_methods(M_ZLIB_8_1,M_ZLIB_8_9);
1631#endif
1632#if defined(ALG_BZIP2)
1633        else if (m_strcmp(p,"bzip2") == 0)
1634            add_all_methods(M_BZIP2_1,M_BZIP2_9);
1635#endif
1636#if defined(__LZO_PROFESSIONAL__)
1637#  include "lzopro/t_opt_m.ch"
1638#endif
1639        else if (m_strisdigit(p))
1640            add_method(atoi(p));
1641        else
1642        {
1643            printf("%s: invalid method '%s'\n\n",progname,p);
1644            exit(EXIT_USAGE);
1645        }
1646
1647        while (*p && *p != ',')
1648            p++;
1649        while (*p == ',')
1650            p++;
1651        if (*p == 0)
1652            return;
1653    }
1654}
1655
1656
1657/*************************************************************************
1658// options
1659**************************************************************************/
1660
1661enum {
1662    OPT_LONGOPT_ONLY = 512,
1663    OPT_ADLER32,
1664    OPT_CALGARY_CORPUS,
1665    OPT_CLEAR_WRKMEM,
1666    OPT_CRC32,
1667    OPT_DICT,
1668    OPT_DUMP,
1669    OPT_EXECUTION_TIME,
1670    OPT_MAX_DATA_LEN,
1671    OPT_MAX_DICT_LEN,
1672    OPT_SILESIA_CORPUS,
1673    OPT_UCLOCK,
1674#if defined(LZOTEST_USE_DYNLOAD)
1675#  include "dynload/o_enum.ch"
1676#endif
1677    OPT_UNUSED
1678};
1679
1680static const struct mfx_option longopts[] =
1681{
1682 /* { name  has_arg  *flag  val } */
1683    {"help",             0, 0, 'h'+256}, /* give help */
1684    {"license",          0, 0, 'L'},     /* display software license */
1685    {"quiet",            0, 0, 'q'},     /* quiet mode */
1686    {"verbose",          0, 0, 'v'},     /* verbose mode */
1687    {"version",          0, 0, 'V'+256}, /* display version number */
1688
1689    {"adler32",          0, 0, OPT_ADLER32},
1690    {"calgary-corpus",   1, 0, OPT_CALGARY_CORPUS},
1691    {"clear-wrkmem",     0, 0, OPT_CLEAR_WRKMEM},
1692    {"clock",            1, 0, OPT_UCLOCK},
1693    {"corpus",           1, 0, OPT_CALGARY_CORPUS},
1694    {"crc32",            0, 0, OPT_CRC32},
1695    {"dict",             1, 0, OPT_DICT},
1696    {"dump-compressed",  1, 0, OPT_DUMP},
1697    {"execution-time",   0, 0, OPT_EXECUTION_TIME},
1698    {"max-data-length",  1, 0, OPT_MAX_DATA_LEN},
1699    {"max-dict-length",  1, 0, OPT_MAX_DICT_LEN},
1700    {"silesia-corpus",   1, 0, OPT_SILESIA_CORPUS},
1701    {"uclock",           1, 0, OPT_UCLOCK},
1702    {"methods",          1, 0, 'm'},
1703    {"totals",           0, 0, 'T'},
1704#if defined(LZOTEST_USE_DYNLOAD)
1705#  include "dynload/o_longopts.ch"
1706#endif
1707
1708    { 0, 0, 0, 0 }
1709};
1710
1711
1712static int do_option(int optc)
1713{
1714    switch (optc)
1715    {
1716    case 'A':
1717        opt_use_asm_decompressor = 1;
1718        break;
1719    case 'b':
1720        opt_block_size = 0; /* set to opt_max_data_len later */
1721        if (mfx_optarg)
1722        {
1723            if (!mfx_optarg || !is_digit(mfx_optarg[0]))
1724                return optc;
1725            opt_block_size = atol(mfx_optarg);
1726        }
1727        break;
1728    case 'c':
1729    case 'C':
1730        if (!mfx_optarg || !is_digit(mfx_optarg[0]))
1731            return optc;
1732        opt_c_loops = atol(mfx_optarg);
1733        break;
1734    case 'd':
1735    case 'D':
1736        if (!mfx_optarg || !is_digit(mfx_optarg[0]))
1737            return optc;
1738        opt_d_loops = atol(mfx_optarg);
1739        break;
1740    case 'F':
1741        opt_use_asm_fast_decompressor = 1;
1742        break;
1743    case 'h':
1744    case 'H':
1745    case '?':
1746    case 'h'+256:
1747        usage(progname,EXIT_OK,0);
1748        break;
1749    case 'L':
1750        license();
1751        break;
1752    case 'm':
1753        parse_methods(mfx_optarg);
1754        break;
1755    case 'n':
1756        if (!mfx_optarg || !is_digit(mfx_optarg[0]))
1757            return optc;
1758        opt_c_loops = opt_d_loops = atol(mfx_optarg);
1759        break;
1760    case 'O':
1761        opt_optimize_compressed_data = 1;
1762        break;
1763    case 'q':
1764        opt_verbose -= 1;
1765        break;
1766    case 'Q':
1767        opt_verbose = 0;
1768        break;
1769    case 's':
1770    case OPT_CALGARY_CORPUS:
1771        if (!mfx_optarg || !mfx_optarg[0])
1772            return optc;
1773        opt_corpus_path = mfx_optarg;
1774        opt_corpus = calgary_corpus;
1775        break;
1776    case OPT_SILESIA_CORPUS:
1777        if (!mfx_optarg || !mfx_optarg[0])
1778            return optc;
1779        opt_corpus_path = mfx_optarg;
1780        opt_corpus = silesia_corpus;
1781        break;
1782    case 'S':
1783        opt_use_safe_decompressor = 1;
1784        break;
1785    case 'T':
1786        opt_totals += 1;
1787        break;
1788    case 'v':
1789        opt_verbose += 1;
1790        break;
1791    case 'V':
1792    case 'V'+256:
1793        exit(EXIT_OK);
1794        break;
1795    case '@':
1796        opt_read_from_stdin = 1;
1797        break;
1798
1799    case '1': case '2': case '3': case '4': case '5':
1800    case '6': case '7': case '8': case '9':
1801        /* this is a dirty hack... */
1802        parse_methods(nextchar-1);
1803        if (nextchar[0])
1804        {
1805            nextchar = NULL;
1806            mfx_optind++;
1807        }
1808        break;
1809
1810    case OPT_ADLER32:
1811        opt_compute_adler32 = 1;
1812        break;
1813    case OPT_CLEAR_WRKMEM:
1814        opt_clear_wrkmem = 1;
1815        break;
1816    case OPT_CRC32:
1817        opt_compute_crc32 = 1;
1818        break;
1819    case OPT_DICT:
1820        opt_dict = 1;
1821        opt_dictionary_file = mfx_optarg;
1822        break;
1823    case OPT_EXECUTION_TIME:
1824        opt_execution_time = 1;
1825        break;
1826    case OPT_DUMP:
1827        opt_dump_compressed_data = mfx_optarg;
1828        break;
1829    case OPT_MAX_DATA_LEN:
1830        if (!mfx_optarg || !is_digit(mfx_optarg[0]))
1831            return optc;
1832        opt_max_data_len = atol(mfx_optarg);
1833        break;
1834    case OPT_MAX_DICT_LEN:
1835        if (!mfx_optarg || !is_digit(mfx_optarg[0]))
1836            return optc;
1837        opt_max_dict_len = atol(mfx_optarg);
1838        break;
1839    case OPT_UCLOCK:
1840        if (!mfx_optarg || !is_digit(mfx_optarg[0]))
1841            return optc;
1842        opt_uclock = atoi(mfx_optarg);
1843#if defined(__ACCLIB_PCLOCK_CH_INCLUDED)
1844        if (opt_uclock > 0)
1845            uch.mode = opt_uclock;
1846#endif
1847        break;
1848
1849#if defined(LZOTEST_USE_DYNLOAD)
1850#  include "dynload/o_do_option.ch"
1851#endif
1852
1853    case '\0':
1854        return -1;
1855    case ':':
1856        return -2;
1857    default:
1858        fprintf(stderr,"%s: internal error in getopt (%d)\n",progname,optc);
1859        return -3;
1860    }
1861    return 0;
1862}
1863
1864
1865static int get_options(int argc, char **argv)
1866{
1867    int optc;
1868
1869    mfx_optind = 0;
1870    mfx_opterr = 1;
1871    while ((optc = mfx_getopt_long (argc, argv,
1872                      "Ab::c:C:d:D:FhHLm::n:OqQs:STvV@123456789"
1873#if defined(LZOTEST_USE_DYNLOAD)
1874#  include "dynload/o_shortopts.ch"
1875#endif
1876                      , longopts, (int *)0)) >= 0)
1877    {
1878        if (do_option(optc) != 0)
1879            exit(EXIT_USAGE);
1880    }
1881
1882    return mfx_optind;
1883}
1884
1885
1886/*************************************************************************
1887// main
1888**************************************************************************/
1889
1890int __lzo_cdecl_main main(int argc, char *argv[])
1891{
1892    int r = EXIT_OK;
1893    int i, ii;
1894    int m;
1895    time_t t_total;
1896    const char *s;
1897
1898    lzo_wildargv(&argc, &argv);
1899    lzo_uclock_open(&uch);
1900
1901    progname = argv[0];
1902    for (s = progname; *s; s++)
1903        if ((*s == '/' || *s == '\\') && s[1])
1904            progname = s + 1;
1905
1906#if defined(__LZO_PROFESSIONAL__)
1907    printf("\nLZO Professional real-time data compression library (v%s, %s).\n",
1908           lzo_version_string(), lzo_version_date());
1909    printf("Copyright (C) 1996-2008 Markus Franz Xaver Johannes Oberhumer\nAll Rights Reserved.\n\n");
1910#elif defined(LZOTEST_USE_DYNLOAD)
1911#  include "dynload/init.ch"
1912#else
1913    printf("\nLZO real-time data compression library (v%s, %s).\n",
1914           lzo_version_string(), lzo_version_date());
1915    printf("Copyright (C) 1996-2008 Markus Franz Xaver Johannes Oberhumer\nAll Rights Reserved.\n\n");
1916#endif
1917
1918
1919/*
1920 * Step 1: initialize the LZO library
1921 */
1922
1923    if (lzo_init() != LZO_E_OK)
1924    {
1925        printf("internal error - lzo_init() failed !!!\n");
1926        printf("(this usually indicates a compiler bug - try recompiling\nwithout optimizations, and enable `-DLZO_DEBUG' for diagnostics)\n");
1927        exit(1);
1928    }
1929
1930
1931/*
1932 * Step 2: setup default options
1933 */
1934
1935    opt_max_data_len = 64 * 1024L * 1024L;
1936    opt_block_size = 256 * 1024L;
1937
1938#if defined(LZO_ARCH_I086) && defined(ACC_MM_AHSHIFT)
1939#  if 1 && defined(LZO_ARCH_I086PM) && defined(BLX286)
1940    opt_max_data_len = 32 * 1024L * 1024L;
1941#  else
1942    opt_max_data_len = 14 * 1024L * 1024L;
1943#  endif
1944    /* reduce memory requirements for ancient 16-bit DOS 640kB real-mode */
1945    if (ACC_MM_AHSHIFT != 3) {
1946        opt_max_data_len = 16 * 1024L;
1947    }
1948#elif defined(LZO_OS_TOS)
1949    /* reduce memory requirements for 14 MB machines */
1950    opt_max_data_len = 8 * 1024L * 1024L;
1951#endif
1952
1953
1954
1955/*
1956 * Step 3: parse options
1957 */
1958
1959    if (argc < 2)
1960        usage(progname,-1,0);
1961    i = get_options(argc,argv);
1962
1963    if (methods_n == 0)
1964        add_method(default_method);
1965    if (methods_n > 1 && opt_read_from_stdin)
1966    {
1967        printf("%s: cannot use multiple methods and '-@'\n", progname);
1968        exit(EXIT_USAGE);
1969    }
1970
1971    if (opt_block_size == 0)
1972        opt_block_size = opt_max_data_len;
1973    if (opt_block_size > opt_max_data_len)
1974        opt_block_size = opt_max_data_len;
1975
1976    if (opt_c_loops < 1)
1977        opt_c_loops = 1;
1978    if (opt_d_loops < 1)
1979        opt_d_loops = 1;
1980
1981
1982/*
1983 * Step 4: start work
1984 */
1985
1986    wrkmem.len = 0;
1987    for (ii = 0; ii < methods_n; ii++) {
1988        const compress_t *c = find_method_by_id(methods[ii]);
1989        assert(c != NULL);
1990        if (c->mem_compress > wrkmem.len)
1991            wrkmem.len = c->mem_compress;
1992        if (c->mem_decompress > wrkmem.len)
1993            wrkmem.len = c->mem_decompress;
1994    }
1995
1996    mb_alloc(&wrkmem, wrkmem.len);
1997
1998#if !defined(__LZO_CHECKER)
1999    mb_alloc_extra(&block1, opt_block_size + get_max_compression_expansion(-1, opt_block_size), 16, 16);
2000    mb_alloc_extra(&block2, opt_block_size + get_max_decompression_overrun(-1, opt_block_size), 16, 16);
2001#endif
2002
2003    if (opt_dict)
2004    {
2005        opt_optimize_compressed_data = 0;
2006        dict_alloc(opt_max_dict_len);
2007        if (opt_dictionary_file)
2008        {
2009            dict_load(opt_dictionary_file);
2010            if (dict.len > 0)
2011                printf("Using dictionary '%s', %ld bytes, ID 0x%08lx.\n",
2012                        opt_dictionary_file,
2013                        (long) dict.len, (long) dict.adler);
2014        }
2015        if (dict.len == 0)
2016        {
2017            dict_set_default();
2018            printf("Using default dictionary, %ld bytes, ID 0x%08lx.\n",
2019                    (long) dict.len, (long) dict.adler);
2020        }
2021    }
2022
2023    t_total = time(NULL);
2024    ii = i;
2025    for (m = 0; m < methods_n && r == EXIT_OK; m++)
2026    {
2027        int method = methods[m];
2028
2029        i = ii;
2030        if (i >= argc && opt_corpus_path == NULL && !opt_read_from_stdin)
2031            usage(progname,-1,0);
2032        if (m == 0 && opt_verbose >= 1)
2033            printf("%lu block-size\n\n", (long) opt_block_size);
2034
2035        assert(find_method_by_id(method) != NULL);
2036
2037        if (opt_corpus_path != NULL)
2038            r = do_corpus(opt_corpus, method, opt_corpus_path,
2039                          opt_c_loops, opt_d_loops);
2040        else
2041        {
2042            for ( ; i < argc && r == EXIT_OK; i++)
2043            {
2044                r = do_file(method,argv[i],opt_c_loops,opt_d_loops,NULL,NULL);
2045                if (r == EXIT_FILE)     /* ignore file errors */
2046                    r = EXIT_OK;
2047            }
2048            if (opt_read_from_stdin)
2049            {
2050                char buf[512], *p;
2051
2052                while (r == EXIT_OK && fgets(buf,sizeof(buf)-1,stdin) != NULL)
2053                {
2054                    buf[sizeof(buf)-1] = 0;
2055                    p = buf + strlen(buf);
2056                    while (p > buf && is_space(p[-1]))
2057                            *--p = 0;
2058                    p = buf;
2059                    while (*p && is_space(*p))
2060                        p++;
2061                    if (*p)
2062                        r = do_file(method,p,opt_c_loops,opt_d_loops,NULL,NULL);
2063                    if (r == EXIT_FILE)     /* ignore file errors */
2064                        r = EXIT_OK;
2065                }
2066                opt_read_from_stdin = 0;
2067            }
2068        }
2069    }
2070
2071#if defined(LZOTEST_USE_DYNLOAD)
2072#  include "dynload/exit.ch"
2073#endif
2074    t_total = time(NULL) - t_total;
2075
2076    if (opt_totals)
2077        print_totals();
2078    if (opt_execution_time || (methods_n > 1 && opt_verbose >= 1))
2079        printf("\n%s: execution time: %lu seconds\n", progname, (long) t_total);
2080    if (r != EXIT_OK)
2081        printf("\n%s: exit code: %d\n", progname, r);
2082
2083    lzo_uclock_close(&uch);
2084    return r;
2085}
2086
2087
2088/*
2089vi:ts=4:et
2090*/
2091
2092