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