git.y1.nz

gbdk-2020

GameBoy Development Kit
download: https://git.y1.nz/archives/gbdk.tar.gz
README | Files | Log | Refs | LICENSE

gbdk-support/gbcompress/zx0/zx0_salvador_c_UNUSED

      1 /*
      2  * salvador.c - command line compression utility for the salvador library
      3  *
      4  * Copyright (C) 2021 Emmanuel Marty
      5  *
      6  * This software is provided 'as-is', without any express or implied
      7  * warranty.  In no event will the authors be held liable for any damages
      8  * arising from the use of this software.
      9  *
     10  * Permission is granted to anyone to use this software for any purpose,
     11  * including commercial applications, and to alter it and redistribute it
     12  * freely, subject to the following restrictions:
     13  *
     14  * 1. The origin of this software must not be misrepresented; you must not
     15  *    claim that you wrote the original software. If you use this software
     16  *    in a product, an acknowledgment in the product documentation would be
     17  *    appreciated but is not required.
     18  * 2. Altered source versions must be plainly marked as such, and must not be
     19  *    misrepresented as being the original software.
     20  * 3. This notice may not be removed or altered from any source distribution.
     21  */
     22 
     23 /*
     24  * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori
     25  *
     26  * Implements the ZX0 encoding designed by Einar Saukas. https://github.com/einar-saukas/ZX0
     27  * Also inspired by Charles Bloom's compression blog. http://cbloomrants.blogspot.com/
     28  *
     29  */
     30 
     31 #include <stdio.h>
     32 #include <stdlib.h>
     33 #include <string.h>
     34 #ifdef _WIN32
     35 #include <windows.h>
     36 #include <sys/timeb.h>
     37 #else
     38 #include <sys/time.h>
     39 #endif
     40 #include "libsalvador.h"
     41 
     42 #define OPT_VERBOSE        1
     43 #define OPT_STATS          2
     44 #define OPT_BACKWARD       4
     45 #define OPT_CLASSIC        8
     46 
     47 #define TOOL_VERSION "1.4.2"
     48 
     49 /*---------------------------------------------------------------------------*/
     50 
     51 #ifdef _WIN32
     52 LARGE_INTEGER hpc_frequency;
     53 BOOL hpc_available = FALSE;
     54 #endif
     55 
     56 static void do_init_time() {
     57 #ifdef _WIN32
     58    hpc_frequency.QuadPart = 0;
     59    hpc_available = QueryPerformanceFrequency(&hpc_frequency);
     60 #endif
     61 }
     62 
     63 static long long do_get_time() {
     64    long long nTime;
     65 
     66 #ifdef _WIN32
     67    if (hpc_available) {
     68       LARGE_INTEGER nCurTime;
     69 
     70       /* Use HPC hardware for best precision */
     71       QueryPerformanceCounter(&nCurTime);
     72       nTime = (long long)(nCurTime.QuadPart * 1000000LL / hpc_frequency.QuadPart);
     73    }
     74    else {
     75       struct _timeb tb;
     76       _ftime(&tb);
     77 
     78       nTime = ((long long)tb.time * 1000LL + (long long)tb.millitm) * 1000LL;
     79    }
     80 #else
     81    struct timeval tm;
     82    gettimeofday(&tm, NULL);
     83 
     84    nTime = (long long)tm.tv_sec * 1000000LL + (long long)tm.tv_usec;
     85 #endif
     86    return nTime;
     87 }
     88 
     89 static void do_reverse_buffer(unsigned char *pBuffer, size_t nBufferSize) {
     90    size_t nMidPoint = nBufferSize / 2;
     91    size_t i, j;
     92 
     93    for (i = 0, j = nBufferSize - 1; i < nMidPoint; i++, j--) {
     94       unsigned char c = pBuffer[i];
     95       pBuffer[i] = pBuffer[j];
     96       pBuffer[j] = c;
     97    }
     98 }
     99 
    100 /*---------------------------------------------------------------------------*/
    101 
    102 static void compression_progress(long long nOriginalSize, long long nCompressedSize) {
    103    if (nOriginalSize >= 512 * 1024) {
    104       fprintf(stdout, "\r%lld => %lld (%g %%)     \b\b\b\b\b", nOriginalSize, nCompressedSize, (double)(nCompressedSize * 100.0 / nOriginalSize));
    105       fflush(stdout);
    106    }
    107 }
    108 
    109 static int do_compress(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, const unsigned int nOptions, const unsigned int nMaxWindowSize) {
    110    long long nStartTime = 0LL, nEndTime = 0LL;
    111    size_t nOriginalSize = 0L, nCompressedSize = 0L, nMaxCompressedSize;
    112    int nFlags = (nOptions & OPT_CLASSIC) ? 0 : FLG_IS_INVERTED;
    113    salvador_stats stats;
    114    unsigned char *pDecompressedData;
    115    unsigned char *pCompressedData;
    116 
    117    if (nOptions & OPT_BACKWARD)
    118       nFlags |= FLG_IS_BACKWARD;
    119 
    120    if (nOptions & OPT_VERBOSE) {
    121       nStartTime = do_get_time();
    122    }
    123 
    124    FILE* f_dict = NULL;
    125    size_t nDictionarySize = 0;
    126    if (pszDictionaryFilename) {
    127       /* Open the dictionary */
    128       f_dict = fopen(pszDictionaryFilename, "rb");
    129       if (!f_dict) {
    130          fprintf(stderr, "error opening dictionary '%s' for reading\n", pszDictionaryFilename);
    131          return 100;
    132       }
    133 
    134       /* Get dictionary size */
    135       fseek(f_dict, 0, SEEK_END);
    136       nDictionarySize = (size_t)ftell(f_dict);
    137       fseek(f_dict, 0, SEEK_SET);
    138 
    139       if (nDictionarySize > BLOCK_SIZE) nDictionarySize = BLOCK_SIZE;
    140    }
    141 
    142    /* Read the whole original file in memory */
    143 
    144    FILE *f_in = fopen(pszInFilename, "rb");
    145    if (!f_in) {
    146       if (f_dict) fclose(f_dict);
    147       fprintf(stderr, "error opening '%s' for reading\n", pszInFilename);
    148       return 100;
    149    }
    150 
    151    fseek(f_in, 0, SEEK_END);
    152    nOriginalSize = (size_t)ftell(f_in);
    153    fseek(f_in, 0, SEEK_SET);
    154 
    155    pDecompressedData = (unsigned char*)malloc(nDictionarySize + nOriginalSize);
    156    if (!pDecompressedData) {
    157       fclose(f_in);
    158       if (f_dict) fclose(f_dict);
    159       fprintf(stderr, "out of memory for reading '%s', %zu bytes needed\n", pszInFilename, nOriginalSize);
    160       return 100;
    161    }
    162 
    163    if (f_dict) {
    164       /* Read dictionary data */
    165       if (fread(pDecompressedData + ((nOptions & OPT_BACKWARD) ? nOriginalSize : 0), 1, nDictionarySize, f_dict) != nDictionarySize) {
    166          free(pDecompressedData);
    167          fclose(f_in);
    168          fclose(f_dict);
    169          fprintf(stderr, "I/O error while reading dictionary '%s'\n", pszDictionaryFilename);
    170          return 100;
    171       }
    172 
    173       fclose(f_dict);
    174       f_dict = NULL;
    175    }
    176 
    177    /* Read input file data */
    178    if (fread(pDecompressedData + ((nOptions & OPT_BACKWARD) ? 0 : nDictionarySize), 1, nOriginalSize, f_in) != nOriginalSize) {
    179       free(pDecompressedData);
    180       fclose(f_in);
    181       fprintf(stderr, "I/O error while reading '%s'\n", pszInFilename);
    182       return 100;
    183    }
    184 
    185    fclose(f_in);
    186    f_in = NULL;
    187 
    188    if (nOptions & OPT_BACKWARD)
    189       do_reverse_buffer(pDecompressedData, nDictionarySize + nOriginalSize);
    190 
    191    /* Allocate max compressed size */
    192 
    193    nMaxCompressedSize = salvador_get_max_compressed_size(nDictionarySize + nOriginalSize);
    194 
    195    pCompressedData = (unsigned char*)malloc(nMaxCompressedSize);
    196    if (!pCompressedData) {
    197       free(pDecompressedData);
    198       fprintf(stderr, "out of memory for compressing '%s', %zu bytes needed\n", pszInFilename, nMaxCompressedSize);
    199       return 100;
    200    }
    201 
    202    memset(pCompressedData, 0, nMaxCompressedSize);
    203 
    204    nCompressedSize = salvador_compress(pDecompressedData, pCompressedData, nDictionarySize + nOriginalSize, nMaxCompressedSize, nFlags, nMaxWindowSize, nDictionarySize, compression_progress, &stats);
    205 
    206    if (nOptions & OPT_VERBOSE) {
    207       nEndTime = do_get_time();
    208    }
    209 
    210    if (nCompressedSize == (size_t)-1) {
    211       free(pCompressedData);
    212       free(pDecompressedData);
    213       fprintf(stderr, "compression error for '%s'\n", pszInFilename);
    214       return 100;
    215    }
    216 
    217    if (nOptions & OPT_BACKWARD)
    218       do_reverse_buffer(pCompressedData, nCompressedSize);
    219 
    220    /* Write whole compressed file out */
    221 
    222    FILE *f_out = fopen(pszOutFilename, "wb");
    223    if (!f_out) {
    224       free(pCompressedData);
    225       free(pDecompressedData);
    226       fprintf(stderr, "error opening '%s' for writing\n", pszOutFilename);
    227       return 100;
    228    }
    229    
    230    fwrite(pCompressedData, 1, nCompressedSize, f_out);
    231    fclose(f_out);
    232 
    233    free(pCompressedData);
    234    free(pDecompressedData);
    235 
    236    if (nOptions & OPT_VERBOSE) {
    237       double fDelta = ((double)(nEndTime - nStartTime)) / 1000000.0;
    238       double fSpeed = ((double)nOriginalSize / 1048576.0) / fDelta;
    239       fprintf(stdout, "\rCompressed '%s' in %g seconds, %.02g Mb/s, %d tokens (%g bytes/token), %zu into %zu bytes ==> %g %%\n",
    240          pszInFilename, fDelta, fSpeed, stats.commands_divisor, (double)nOriginalSize / (double)stats.commands_divisor,
    241          nOriginalSize, nCompressedSize, (double)(nCompressedSize * 100.0 / nOriginalSize));
    242    }
    243 
    244    if (nOptions & OPT_STATS) {
    245       if (stats.literals_divisor > 0)
    246          fprintf(stdout, "Literals: min: %d avg: %d max: %d count: %d\n", stats.min_literals, stats.total_literals / stats.literals_divisor, stats.max_literals, stats.literals_divisor);
    247       else
    248          fprintf(stdout, "Literals: none\n");
    249 
    250       fprintf(stdout, "Normal matches: %d rep matches: %d EOD: %d\n",
    251          stats.num_normal_matches, stats.num_rep_matches, stats.num_eod);
    252 
    253       if (stats.match_divisor > 0) {
    254          fprintf(stdout, "Offsets: min: %d avg: %d max: %d count: %d\n", stats.min_offset, (int)(stats.total_offsets / (long long)stats.match_divisor), stats.max_offset, stats.match_divisor);
    255          fprintf(stdout, "Match lens: min: %d avg: %d max: %d count: %d\n", stats.min_match_len, stats.total_match_lens / stats.match_divisor, stats.max_match_len, stats.match_divisor);
    256       }
    257       else {
    258          fprintf(stdout, "Offsets: none\n");
    259          fprintf(stdout, "Match lens: none\n");
    260       }
    261       if (stats.rle1_divisor > 0) {
    262          fprintf(stdout, "RLE1 lens: min: %d avg: %d max: %d count: %d\n", stats.min_rle1_len, stats.total_rle1_lens / stats.rle1_divisor, stats.max_rle1_len, stats.rle1_divisor);
    263       }
    264       else {
    265          fprintf(stdout, "RLE1 lens: none\n");
    266       }
    267       if (stats.rle2_divisor > 0) {
    268          fprintf(stdout, "RLE2 lens: min: %d avg: %d max: %d count: %d\n", stats.min_rle2_len, stats.total_rle2_lens / stats.rle2_divisor, stats.max_rle2_len, stats.rle2_divisor);
    269       }
    270       else {
    271          fprintf(stdout, "RLE2 lens: none\n");
    272       }
    273       fprintf(stdout, "Safe distance: %d (0x%X)\n", stats.safe_dist, stats.safe_dist);
    274    }
    275    return 0;
    276 }
    277 
    278 /*---------------------------------------------------------------------------*/
    279 
    280 static int do_decompress(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, const unsigned int nOptions) {
    281    long long nStartTime = 0LL, nEndTime = 0LL;
    282    size_t nCompressedSize, nMaxDecompressedSize, nOriginalSize;
    283    unsigned char *pCompressedData;
    284    unsigned char *pDecompressedData;
    285    int nFlags = (nOptions & OPT_CLASSIC) ? 0 : FLG_IS_INVERTED;
    286 
    287    if (nOptions & OPT_BACKWARD)
    288       nFlags |= FLG_IS_BACKWARD;
    289 
    290    /* Read the whole compressed file in memory */
    291 
    292    FILE *f_in = fopen(pszInFilename, "rb");
    293    if (!f_in) {
    294       fprintf(stderr, "error opening '%s' for reading\n", pszInFilename);
    295       return 100;
    296    }
    297 
    298    fseek(f_in, 0, SEEK_END);
    299    nCompressedSize = (size_t)ftell(f_in);
    300    fseek(f_in, 0, SEEK_SET);
    301 
    302    pCompressedData = (unsigned char*)malloc(nCompressedSize);
    303    if (!pCompressedData) {
    304       fclose(f_in);
    305       fprintf(stderr, "out of memory for reading '%s', %zu bytes needed\n", pszInFilename, nCompressedSize);
    306       return 100;
    307    }
    308 
    309    if (fread(pCompressedData, 1, nCompressedSize, f_in) != nCompressedSize) {
    310       free(pCompressedData);
    311       fclose(f_in);
    312       fprintf(stderr, "I/O error while reading '%s'\n", pszInFilename);
    313       return 100;
    314    }
    315 
    316    fclose(f_in);
    317    f_in = NULL;
    318 
    319    if (nOptions & OPT_BACKWARD)
    320       do_reverse_buffer(pCompressedData, nCompressedSize);
    321 
    322    /* Get max decompressed size */
    323 
    324    nMaxDecompressedSize = salvador_get_max_decompressed_size(pCompressedData, nCompressedSize, nFlags);
    325    if (nMaxDecompressedSize == (size_t)-1) {
    326       free(pCompressedData);
    327       fprintf(stderr, "invalid compressed format for file '%s'\n", pszInFilename);
    328       return 100;
    329    }
    330 
    331    FILE* f_dict = NULL;
    332    size_t nDictionarySize = 0;
    333    if (pszDictionaryFilename) {
    334       /* Open the dictionary */
    335       f_dict = fopen(pszDictionaryFilename, "rb");
    336       if (!f_dict) {
    337          fprintf(stderr, "error opening dictionary '%s' for reading\n", pszDictionaryFilename);
    338          return 100;
    339       }
    340 
    341       /* Get dictionary size */
    342       fseek(f_dict, 0, SEEK_END);
    343       nDictionarySize = (size_t)ftell(f_dict);
    344       fseek(f_dict, 0, SEEK_SET);
    345 
    346       if (nDictionarySize > BLOCK_SIZE) nDictionarySize = BLOCK_SIZE;
    347    }
    348 
    349    /* Allocate max decompressed size */
    350 
    351    pDecompressedData = (unsigned char*)malloc(nDictionarySize + nMaxDecompressedSize);
    352    if (!pDecompressedData) {
    353       free(pCompressedData);
    354       if (f_dict) fclose(f_dict);
    355       fprintf(stderr, "out of memory for decompressing '%s', %zu bytes needed\n", pszInFilename, nMaxDecompressedSize);
    356       return 100;
    357    }
    358 
    359    memset(pDecompressedData, 0, nDictionarySize + nMaxDecompressedSize);
    360 
    361    if (f_dict) {
    362       /* Read dictionary data */
    363       if (fread(pDecompressedData, 1, nDictionarySize, f_dict) != nDictionarySize) {
    364          free(pDecompressedData);
    365          fclose(f_dict);
    366          fprintf(stderr, "I/O error while reading dictionary '%s'\n", pszDictionaryFilename);
    367          return 100;
    368       }
    369 
    370       fclose(f_dict);
    371       f_dict = NULL;
    372 
    373       if (nOptions & OPT_BACKWARD)
    374          do_reverse_buffer(pDecompressedData, nDictionarySize);
    375    }
    376 
    377    if (nOptions & OPT_VERBOSE) {
    378       nStartTime = do_get_time();
    379    }
    380 
    381    nOriginalSize = salvador_decompress(pCompressedData, pDecompressedData, nCompressedSize, nMaxDecompressedSize, nDictionarySize, nFlags);
    382    if (nOriginalSize == (size_t)-1) {
    383       free(pDecompressedData);
    384       free(pCompressedData);
    385 
    386       fprintf(stderr, "decompression error for '%s'\n", pszInFilename);
    387       return 100;
    388    }
    389 
    390    if (nOptions & OPT_VERBOSE) {
    391       nEndTime = do_get_time();
    392    }
    393 
    394    if (nOptions & OPT_BACKWARD)
    395       do_reverse_buffer(pDecompressedData + nDictionarySize, nOriginalSize);
    396 
    397    /* Write whole decompressed file out */
    398 
    399    FILE *f_out = fopen(pszOutFilename, "wb");
    400    if (!f_out) {
    401       free(pDecompressedData);
    402       free(pCompressedData);
    403 
    404       fprintf(stderr, "error opening '%s' for writing\n", pszOutFilename);
    405       return 100;
    406    }
    407    
    408    fwrite(pDecompressedData + nDictionarySize, 1, nOriginalSize, f_out);
    409    fclose(f_out);
    410 
    411    free(pDecompressedData);
    412    free(pCompressedData);
    413 
    414    if (nOptions & OPT_VERBOSE) {
    415       double fDelta = ((double)(nEndTime - nStartTime)) / 1000000.0;
    416       double fSpeed = ((double)nOriginalSize / 1048576.0) / fDelta;
    417       fprintf(stdout, "Decompressed '%s' in %g seconds, %g Mb/s\n",
    418          pszInFilename, fDelta, fSpeed);
    419    }
    420 
    421    return 0;
    422 }
    423 
    424 /*---------------------------------------------------------------------------*/
    425 
    426 static int do_compare(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, const unsigned int nOptions) {
    427    long long nStartTime = 0LL, nEndTime = 0LL;
    428    size_t nCompressedSize, nMaxDecompressedSize, nOriginalSize, nDecompressedSize;
    429    unsigned char *pCompressedData = NULL;
    430    unsigned char *pOriginalData = NULL;
    431    unsigned char *pDecompressedData = NULL;
    432    int nFlags = (nOptions & OPT_CLASSIC) ? 0 : FLG_IS_INVERTED;
    433 
    434    if (nOptions & OPT_BACKWARD)
    435       nFlags |= FLG_IS_BACKWARD;
    436 
    437    /* Read the whole compressed file in memory */
    438 
    439    FILE *f_in = fopen(pszInFilename, "rb");
    440    if (!f_in) {
    441       fprintf(stderr, "error opening '%s' for reading\n", pszInFilename);
    442       return 100;
    443    }
    444 
    445    fseek(f_in, 0, SEEK_END);
    446    nCompressedSize = (size_t)ftell(f_in);
    447    fseek(f_in, 0, SEEK_SET);
    448 
    449    pCompressedData = (unsigned char*)malloc(nCompressedSize);
    450    if (!pCompressedData) {
    451       fclose(f_in);
    452       fprintf(stderr, "out of memory for reading '%s', %zu bytes needed\n", pszInFilename, nCompressedSize);
    453       return 100;
    454    }
    455 
    456    if (fread(pCompressedData, 1, nCompressedSize, f_in) != nCompressedSize) {
    457       free(pCompressedData);
    458       fclose(f_in);
    459       fprintf(stderr, "I/O error while reading '%s'\n", pszInFilename);
    460       return 100;
    461    }
    462 
    463    fclose(f_in);
    464    f_in = NULL;
    465 
    466    if (nOptions & OPT_BACKWARD)
    467       do_reverse_buffer(pCompressedData, nCompressedSize);
    468 
    469    /* Read the whole original file in memory */
    470 
    471    f_in = fopen(pszOutFilename, "rb");
    472    if (!f_in) {
    473       free(pCompressedData);
    474       fprintf(stderr, "error opening '%s' for reading\n", pszInFilename);
    475       return 100;
    476    }
    477 
    478    fseek(f_in, 0, SEEK_END);
    479    nOriginalSize = (size_t)ftell(f_in);
    480    fseek(f_in, 0, SEEK_SET);
    481 
    482    pOriginalData = (unsigned char*)malloc(nOriginalSize);
    483    if (!pOriginalData) {
    484       fclose(f_in);
    485       free(pCompressedData);
    486       fprintf(stderr, "out of memory for reading '%s', %zu bytes needed\n", pszInFilename, nOriginalSize);
    487       return 100;
    488    }
    489 
    490    if (fread(pOriginalData, 1, nOriginalSize, f_in) != nOriginalSize) {
    491       free(pOriginalData);
    492       fclose(f_in);
    493       free(pCompressedData);
    494       fprintf(stderr, "I/O error while reading '%s'\n", pszInFilename);
    495       return 100;
    496    }
    497 
    498    fclose(f_in);
    499    f_in = NULL;
    500 
    501    /* Get max decompressed size */
    502 
    503    nMaxDecompressedSize = salvador_get_max_decompressed_size(pCompressedData, nCompressedSize, nFlags);
    504    if (nMaxDecompressedSize == (size_t)-1) {
    505       free(pOriginalData);
    506       free(pCompressedData);
    507       fprintf(stderr, "invalid compressed format for file '%s'\n", pszInFilename);
    508       return 100;
    509    }
    510 
    511    FILE* f_dict = NULL;
    512    size_t nDictionarySize = 0;
    513    if (pszDictionaryFilename) {
    514       /* Open the dictionary */
    515       f_dict = fopen(pszDictionaryFilename, "rb");
    516       if (!f_dict) {
    517          free(pOriginalData);
    518          free(pCompressedData);
    519          fprintf(stderr, "error opening dictionary '%s' for reading\n", pszDictionaryFilename);
    520          return 100;
    521       }
    522 
    523       /* Get dictionary size */
    524       fseek(f_dict, 0, SEEK_END);
    525       nDictionarySize = (size_t)ftell(f_dict);
    526       fseek(f_dict, 0, SEEK_SET);
    527 
    528       if (nDictionarySize > BLOCK_SIZE) nDictionarySize = BLOCK_SIZE;
    529    }
    530 
    531    /* Allocate max decompressed size */
    532 
    533    pDecompressedData = (unsigned char*)malloc(nDictionarySize + nMaxDecompressedSize);
    534    if (!pDecompressedData) {
    535       free(pOriginalData);
    536       free(pCompressedData);
    537       if (f_dict) fclose(f_dict);
    538       fprintf(stderr, "out of memory for decompressing '%s', %zu bytes needed\n", pszInFilename, nMaxDecompressedSize);
    539       return 100;
    540    }
    541 
    542    memset(pDecompressedData, 0, nDictionarySize + nMaxDecompressedSize);
    543 
    544    if (f_dict) {
    545       /* Read dictionary data */
    546       if (fread(pDecompressedData, 1, nDictionarySize, f_dict) != nDictionarySize) {
    547          free(pDecompressedData);
    548          free(pOriginalData);
    549          free(pCompressedData);
    550          fclose(f_dict);
    551          fprintf(stderr, "I/O error while reading dictionary '%s'\n", pszDictionaryFilename);
    552          return 100;
    553       }
    554 
    555       fclose(f_dict);
    556       f_dict = NULL;
    557 
    558       if (nOptions & OPT_BACKWARD)
    559          do_reverse_buffer(pDecompressedData, nDictionarySize);
    560    }
    561 
    562    if (nOptions & OPT_VERBOSE) {
    563       nStartTime = do_get_time();
    564    }
    565 
    566    nDecompressedSize = salvador_decompress(pCompressedData, pDecompressedData, nCompressedSize, nMaxDecompressedSize, nDictionarySize, nFlags);
    567    if (nDecompressedSize == (size_t)-1) {
    568       free(pDecompressedData);
    569       free(pOriginalData);
    570       free(pCompressedData);
    571 
    572       fprintf(stderr, "decompression error for '%s'\n", pszInFilename);
    573       return 100;
    574    }
    575 
    576    if (nOptions & OPT_VERBOSE) {
    577       nEndTime = do_get_time();
    578    }
    579 
    580    if (nOptions & OPT_BACKWARD)
    581       do_reverse_buffer(pDecompressedData + nDictionarySize, nDecompressedSize);
    582 
    583    if (nDecompressedSize != nOriginalSize || memcmp(pDecompressedData + nDictionarySize, pOriginalData, nOriginalSize)) {
    584       free(pDecompressedData);
    585       free(pOriginalData);
    586       free(pCompressedData);
    587 
    588       fprintf(stderr, "error comparing compressed file '%s' with original '%s'\n", pszInFilename, pszOutFilename);
    589       return 100;
    590    }
    591 
    592    free(pDecompressedData);
    593    free(pOriginalData);
    594    free(pCompressedData);
    595 
    596    if (nOptions & OPT_VERBOSE) {
    597       double fDelta = ((double)(nEndTime - nStartTime)) / 1000000.0;
    598       double fSpeed = ((double)nOriginalSize / 1048576.0) / fDelta;
    599       fprintf(stdout, "Compared '%s' in %g seconds, %g Mb/s\n",
    600          pszInFilename, fDelta, fSpeed);
    601    }
    602 
    603    return 0;
    604 }
    605 
    606 /*---------------------------------------------------------------------------*/
    607 
    608 static void generate_compressible_data(unsigned char *pBuffer, size_t nBufferSize, unsigned int nSeed, int nNumLiteralValues, float fMatchProbability) {
    609    size_t nIndex = 0;
    610    int nMatchProbability = (int)(fMatchProbability * 1023.0f);
    611 
    612    srand(nSeed);
    613    
    614    if (nBufferSize == 0) return;
    615    pBuffer[nIndex++] = rand() % nNumLiteralValues;
    616 
    617    while (nIndex < nBufferSize) {
    618       if ((rand() & 1023) >= nMatchProbability) {
    619          size_t nLiteralCount = rand() & 127;
    620          if (nLiteralCount > (nBufferSize - nIndex))
    621             nLiteralCount = nBufferSize - nIndex;
    622 
    623          while (nLiteralCount--)
    624             pBuffer[nIndex++] = rand() % nNumLiteralValues;
    625       }
    626       else {
    627          size_t nMatchLength = MIN_MATCH_SIZE + (rand() & 1023);
    628          size_t nMatchOffset;
    629 
    630          if (nMatchLength > (nBufferSize - nIndex))
    631             nMatchLength = nBufferSize - nIndex;
    632          if (nMatchLength > nIndex)
    633             nMatchLength = nIndex;
    634 
    635          if (nMatchLength < nIndex)
    636             nMatchOffset = rand() % (nIndex - nMatchLength);
    637          else
    638             nMatchOffset = 0;
    639 
    640          while (nMatchLength--) {
    641             pBuffer[nIndex] = pBuffer[nIndex - nMatchOffset];
    642             nIndex++;
    643          }
    644       }
    645    }
    646 }
    647 
    648 static void xor_data(unsigned char *pBuffer, size_t nBufferSize, unsigned int nSeed, float fXorProbability) {
    649    size_t nIndex = 0;
    650    int nXorProbability = (int)(fXorProbability * 1023.0f);
    651 
    652    srand(nSeed);
    653 
    654    while (nIndex < nBufferSize) {
    655       if ((rand() & 1023) < nXorProbability) {
    656          pBuffer[nIndex] ^= 0xff;
    657       }
    658       nIndex++;
    659    }
    660 }
    661 
    662 static int do_self_test(const unsigned int nOptions, const unsigned int nMaxWindowSize, const int nIsQuickTest) {
    663    unsigned char *pGeneratedData;
    664    unsigned char *pCompressedData;
    665    unsigned char *pTmpCompressedData;
    666    unsigned char *pTmpDecompressedData;
    667    size_t nGeneratedDataSize;
    668    size_t nMaxCompressedDataSize;
    669    unsigned int nSeed = 123;
    670    int nFlags = FLG_IS_INVERTED;
    671    int i;
    672 
    673    if (nOptions & OPT_BACKWARD)
    674       nFlags |= FLG_IS_BACKWARD;
    675 
    676    pGeneratedData = (unsigned char*)malloc(4 * BLOCK_SIZE);
    677    if (!pGeneratedData) {
    678       fprintf(stderr, "out of memory, %d bytes needed\n", 4 * BLOCK_SIZE);
    679       return 100;
    680    }
    681 
    682    nMaxCompressedDataSize = salvador_get_max_compressed_size(4 * BLOCK_SIZE);
    683    pCompressedData = (unsigned char*)malloc(nMaxCompressedDataSize);
    684    if (!pCompressedData) {
    685       free(pGeneratedData);
    686       pGeneratedData = NULL;
    687 
    688       fprintf(stderr, "out of memory, %zu bytes needed\n", nMaxCompressedDataSize);
    689       return 100;
    690    }
    691 
    692    pTmpCompressedData = (unsigned char*)malloc(nMaxCompressedDataSize);
    693    if (!pTmpCompressedData) {
    694       free(pCompressedData);
    695       pCompressedData = NULL;
    696       free(pGeneratedData);
    697       pGeneratedData = NULL;
    698 
    699       fprintf(stderr, "out of memory, %zu bytes needed\n", nMaxCompressedDataSize);
    700       return 100;
    701    }
    702 
    703    pTmpDecompressedData = (unsigned char*)malloc(4 * BLOCK_SIZE);
    704    if (!pTmpDecompressedData) {
    705       free(pTmpCompressedData);
    706       pTmpCompressedData = NULL;
    707       free(pCompressedData);
    708       pCompressedData = NULL;
    709       free(pGeneratedData);
    710       pGeneratedData = NULL;
    711 
    712       fprintf(stderr, "out of memory, %d bytes needed\n", 4 * BLOCK_SIZE);
    713       return 100;
    714    }
    715 
    716    memset(pGeneratedData, 0, 4 * BLOCK_SIZE);
    717    memset(pCompressedData, 0, nMaxCompressedDataSize);
    718    memset(pTmpCompressedData, 0, nMaxCompressedDataSize);
    719 
    720    /* Test compressing with a too small buffer to do anything, expect to fail cleanly */
    721    for (i = 0; i < 12; i++) {
    722       generate_compressible_data(pGeneratedData, i, nSeed, 256, 0.5f);
    723       salvador_compress(pGeneratedData, pCompressedData, i, i, nFlags, nMaxWindowSize, 0 /* dictionary size */, NULL, NULL);
    724    }
    725 
    726    size_t nDataSizeStep = 128;
    727    float fProbabilitySizeStep = nIsQuickTest ? 0.005f : 0.0005f;
    728 
    729    for (nGeneratedDataSize = 1024; nGeneratedDataSize <= (nIsQuickTest ? 1024U : (4U * BLOCK_SIZE)); nGeneratedDataSize += nDataSizeStep) {
    730       float fMatchProbability;
    731 
    732       fprintf(stdout, "size %zu", nGeneratedDataSize);
    733       for (fMatchProbability = 0; fMatchProbability <= 0.995f; fMatchProbability += fProbabilitySizeStep) {
    734          int nNumLiteralValues[12] = { 1, 2, 3, 15, 30, 56, 96, 137, 178, 191, 255, 256 };
    735          float fXorProbability;
    736 
    737          fputc('.', stdout);
    738          fflush(stdout);
    739 
    740          for (i = 0; i < 12; i++) {
    741             /* Generate data to compress */
    742             generate_compressible_data(pGeneratedData, nGeneratedDataSize, nSeed, nNumLiteralValues[i], fMatchProbability);
    743 
    744             /* Try to compress it, expected to succeed */
    745             size_t nActualCompressedSize = salvador_compress(pGeneratedData, pCompressedData, nGeneratedDataSize, salvador_get_max_compressed_size(nGeneratedDataSize),
    746                nFlags, nMaxWindowSize, 0 /* dictionary size */, NULL, NULL);
    747             if (nActualCompressedSize == (size_t)-1 || nActualCompressedSize < (1 + 1 + 1 /* footer */)) {
    748                free(pTmpDecompressedData);
    749                pTmpDecompressedData = NULL;
    750                free(pTmpCompressedData);
    751                pTmpCompressedData = NULL;
    752                free(pCompressedData);
    753                pCompressedData = NULL;
    754                free(pGeneratedData);
    755                pGeneratedData = NULL;
    756 
    757                fprintf(stderr, "\nself-test: error compressing size %zu, seed %u, match probability %f, literals range %d\n", nGeneratedDataSize, nSeed, fMatchProbability, nNumLiteralValues[i]);
    758                return 100;
    759             }
    760 
    761             /* Try to decompress it, expected to succeed */
    762             size_t nActualDecompressedSize;
    763             nActualDecompressedSize = salvador_decompress(pCompressedData, pTmpDecompressedData, nActualCompressedSize, nGeneratedDataSize, 0 /* dictionary size */, nFlags);
    764             if (nActualDecompressedSize == (size_t)-1) {
    765                free(pTmpDecompressedData);
    766                pTmpDecompressedData = NULL;
    767                free(pTmpCompressedData);
    768                pTmpCompressedData = NULL;
    769                free(pCompressedData);
    770                pCompressedData = NULL;
    771                free(pGeneratedData);
    772                pGeneratedData = NULL;
    773 
    774                fprintf(stderr, "\nself-test: error decompressing size %zu, seed %u, match probability %f, literals range %d\n", nGeneratedDataSize, nSeed, fMatchProbability, nNumLiteralValues[i]);
    775                return 100;
    776             }
    777 
    778             if (memcmp(pGeneratedData, pTmpDecompressedData, nGeneratedDataSize)) {
    779                free(pTmpDecompressedData);
    780                pTmpDecompressedData = NULL;
    781                free(pTmpCompressedData);
    782                pTmpCompressedData = NULL;
    783                free(pCompressedData);
    784                pCompressedData = NULL;
    785                free(pGeneratedData);
    786                pGeneratedData = NULL;
    787 
    788                fprintf(stderr, "\nself-test: error comparing decompressed and original data, size %zu, seed %u, match probability %f, literals range %d\n", nGeneratedDataSize, nSeed, fMatchProbability, nNumLiteralValues[i]);
    789                return 100;
    790             }
    791 
    792             /* Try to decompress corrupted data, expected to fail cleanly, without crashing or corrupting memory outside the output buffer */
    793             for (fXorProbability = 0.05f; fXorProbability <= 0.5f; fXorProbability += 0.05f) {
    794                memcpy(pTmpCompressedData, pCompressedData, nActualCompressedSize);
    795                xor_data(pTmpCompressedData, nActualCompressedSize, nSeed, fXorProbability);
    796                salvador_decompress(pTmpCompressedData, pGeneratedData, nActualCompressedSize, nGeneratedDataSize, 0 /* dictionary size */, nFlags);
    797             }
    798          }
    799 
    800          nSeed++;
    801       }
    802 
    803       fputc(10, stdout);
    804       fflush(stdout);
    805 
    806       nDataSizeStep <<= 1;
    807       if (nDataSizeStep > (128 * 4096))
    808          nDataSizeStep = 128 * 4096;
    809       fProbabilitySizeStep *= 1.25;
    810       if (fProbabilitySizeStep > (0.0005f * 4096))
    811          fProbabilitySizeStep = 0.0005f * 4096;
    812    }
    813 
    814    free(pTmpDecompressedData);
    815    pTmpDecompressedData = NULL;
    816 
    817    free(pTmpCompressedData);
    818    pTmpCompressedData = NULL;
    819 
    820    free(pCompressedData);
    821    pCompressedData = NULL;
    822 
    823    free(pGeneratedData);
    824    pGeneratedData = NULL;
    825 
    826    fprintf(stdout, "All tests passed.\n");
    827    return 0;
    828 }
    829 
    830 /*---------------------------------------------------------------------------*/
    831 
    832 static int do_compr_benchmark(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, const unsigned int nOptions, const unsigned int nMaxWindowSize) {
    833    size_t nFileSize, nMaxCompressedSize;
    834    unsigned char *pFileData;
    835    unsigned char *pCompressedData;
    836    int nFlags = FLG_IS_INVERTED;
    837    int i;
    838 
    839    if (pszDictionaryFilename) {
    840       fprintf(stderr, "in-memory benchmarking does not support dictionaries\n");
    841       return 100;
    842    }
    843 
    844    /* Read the whole original file in memory */
    845 
    846    FILE *f_in = fopen(pszInFilename, "rb");
    847    if (!f_in) {
    848       fprintf(stderr, "error opening '%s' for reading\n", pszInFilename);
    849       return 100;
    850    }
    851 
    852    fseek(f_in, 0, SEEK_END);
    853    nFileSize = (size_t)ftell(f_in);
    854    fseek(f_in, 0, SEEK_SET);
    855 
    856    pFileData = (unsigned char*)malloc(nFileSize);
    857    if (!pFileData) {
    858       fclose(f_in);
    859       fprintf(stderr, "out of memory for reading '%s', %zu bytes needed\n", pszInFilename, nFileSize);
    860       return 100;
    861    }
    862 
    863    if (fread(pFileData, 1, nFileSize, f_in) != nFileSize) {
    864       free(pFileData);
    865       fclose(f_in);
    866       fprintf(stderr, "I/O error while reading '%s'\n", pszInFilename);
    867       return 100;
    868    }
    869 
    870    fclose(f_in);
    871    f_in = NULL;
    872 
    873    if (nOptions & OPT_BACKWARD)
    874       do_reverse_buffer(pFileData, nFileSize);
    875 
    876    /* Allocate max compressed size */
    877 
    878    nMaxCompressedSize = salvador_get_max_compressed_size(nFileSize);
    879 
    880    pCompressedData = (unsigned char*)malloc(nMaxCompressedSize + 2048);
    881    if (!pCompressedData) {
    882       free(pFileData);
    883       fprintf(stderr, "out of memory for compressing '%s', %zu bytes needed\n", pszInFilename, nMaxCompressedSize);
    884       return 100;
    885    }
    886 
    887    memset(pCompressedData + 1024, 0, nMaxCompressedSize);
    888 
    889    long long nBestCompTime = -1;
    890 
    891    size_t nActualCompressedSize = 0;
    892    size_t nRightGuardPos = nMaxCompressedSize;
    893 
    894    for (i = 0; i < 5; i++) {
    895       unsigned char nGuard = 0x33 + i;
    896       int j;
    897 
    898       /* Write guard bytes around the output buffer, to help check for writes outside of it by the compressor */
    899       memset(pCompressedData, nGuard, 1024);
    900       memset(pCompressedData + 1024 + nRightGuardPos, nGuard, 1024);
    901 
    902       long long t0 = do_get_time();
    903       nActualCompressedSize = salvador_compress(pFileData, pCompressedData + 1024, nFileSize, nRightGuardPos, nFlags, nMaxWindowSize, 0 /* dictionary size */, NULL, NULL);
    904       long long t1 = do_get_time();
    905       if (nActualCompressedSize == (size_t)-1) {
    906          free(pCompressedData);
    907          free(pFileData);
    908          fprintf(stderr, "compression error\n");
    909          return 100;
    910       }
    911 
    912       long long nCurDecTime = t1 - t0;
    913       if (nBestCompTime == -1 || nBestCompTime > nCurDecTime)
    914          nBestCompTime = nCurDecTime;
    915 
    916       /* Check guard bytes before the output buffer */
    917       for (j = 0; j < 1024; j++) {
    918          if (pCompressedData[j] != nGuard) {
    919             free(pCompressedData);
    920             free(pFileData);
    921             fprintf(stderr, "error, wrote outside of output buffer at %d!\n", j - 1024);
    922             return 100;
    923          }
    924       }
    925 
    926       /* Check guard bytes after the output buffer */
    927       for (j = 0; j < 1024; j++) {
    928          if (pCompressedData[1024 + nRightGuardPos + j] != nGuard) {
    929             free(pCompressedData);
    930             free(pFileData);
    931             fprintf(stderr, "error, wrote outside of output buffer at %d!\n", j);
    932             return 100;
    933          }
    934       }
    935 
    936       nRightGuardPos = nActualCompressedSize;
    937    }
    938 
    939    if (nOptions & OPT_BACKWARD)
    940       do_reverse_buffer(pCompressedData + 1024, nActualCompressedSize);
    941 
    942    if (pszOutFilename) {
    943       FILE *f_out;
    944 
    945       /* Write whole compressed file out */
    946 
    947       f_out = fopen(pszOutFilename, "wb");
    948       if (f_out) {
    949          fwrite(pCompressedData + 1024, 1, nActualCompressedSize, f_out);
    950          fclose(f_out);
    951       }
    952    }
    953 
    954    free(pCompressedData);
    955    free(pFileData);
    956 
    957    fprintf(stdout, "compressed size: %zu bytes\n", nActualCompressedSize);
    958    fprintf(stdout, "compression time: %lld microseconds (%g Mb/s)\n", nBestCompTime, ((double)nActualCompressedSize / 1024.0) / ((double)nBestCompTime / 1000.0));
    959 
    960    return 0;
    961 }
    962 
    963 /*---------------------------------------------------------------------------*/
    964 
    965 static int do_dec_benchmark(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, const unsigned int nOptions) {
    966    size_t nFileSize, nMaxDecompressedSize;
    967    unsigned char *pFileData;
    968    unsigned char *pDecompressedData;
    969    int nFlags = FLG_IS_INVERTED;
    970    int i;
    971 
    972    if (pszDictionaryFilename) {
    973       fprintf(stderr, "in-memory benchmarking does not support dictionaries\n");
    974       return 100;
    975    }
    976 
    977    /* Read the whole compressed file in memory */
    978 
    979    FILE *f_in = fopen(pszInFilename, "rb");
    980    if (!f_in) {
    981       fprintf(stderr, "error opening '%s' for reading\n", pszInFilename);
    982       return 100;
    983    }
    984 
    985    fseek(f_in, 0, SEEK_END);
    986    nFileSize = (size_t)ftell(f_in);
    987    fseek(f_in, 0, SEEK_SET);
    988 
    989    pFileData = (unsigned char*)malloc(nFileSize);
    990    if (!pFileData) {
    991       fclose(f_in);
    992       fprintf(stderr, "out of memory for reading '%s', %zu bytes needed\n", pszInFilename, nFileSize);
    993       return 100;
    994    }
    995 
    996    if (fread(pFileData, 1, nFileSize, f_in) != nFileSize) {
    997       free(pFileData);
    998       fclose(f_in);
    999       fprintf(stderr, "I/O error while reading '%s'\n", pszInFilename);
   1000       return 100;
   1001    }
   1002 
   1003    fclose(f_in);
   1004    f_in = NULL;
   1005 
   1006    if (nOptions & OPT_BACKWARD)
   1007       do_reverse_buffer(pFileData, nFileSize);
   1008 
   1009    /* Allocate max decompressed size */
   1010 
   1011    nMaxDecompressedSize = salvador_get_max_decompressed_size(pFileData, nFileSize, nFlags);
   1012    if (nMaxDecompressedSize == (size_t)-1) {
   1013       free(pFileData);
   1014       fprintf(stderr, "invalid compressed format for file '%s'\n", pszInFilename);
   1015       return 100;
   1016    }
   1017 
   1018    pDecompressedData = (unsigned char*)malloc(nMaxDecompressedSize);
   1019    if (!pDecompressedData) {
   1020       free(pFileData);
   1021       fprintf(stderr, "out of memory for decompressing '%s', %zu bytes needed\n", pszInFilename, nMaxDecompressedSize);
   1022       return 100;
   1023    }
   1024 
   1025    memset(pDecompressedData, 0, nMaxDecompressedSize);
   1026 
   1027    long long nBestDecTime = -1;
   1028 
   1029    size_t nActualDecompressedSize = 0;
   1030    for (i = 0; i < 50; i++) {
   1031       long long t0 = do_get_time();
   1032       nActualDecompressedSize = salvador_decompress(pFileData, pDecompressedData, nFileSize, nMaxDecompressedSize, 0 /* dictionary size */, nFlags);
   1033       long long t1 = do_get_time();
   1034       if (nActualDecompressedSize == (size_t)-1) {
   1035          free(pDecompressedData);
   1036          free(pFileData);
   1037          fprintf(stderr, "decompression error\n");
   1038          return 100;
   1039       }
   1040 
   1041       long long nCurDecTime = t1 - t0;
   1042       if (nBestDecTime == -1 || nBestDecTime > nCurDecTime)
   1043          nBestDecTime = nCurDecTime;
   1044    }
   1045 
   1046    if (nOptions & OPT_BACKWARD)
   1047       do_reverse_buffer(pDecompressedData, nActualDecompressedSize);
   1048 
   1049    if (pszOutFilename) {
   1050       FILE *f_out;
   1051 
   1052       /* Write whole decompressed file out */
   1053 
   1054       f_out = fopen(pszOutFilename, "wb");
   1055       if (f_out) {
   1056          fwrite(pDecompressedData, 1, nActualDecompressedSize, f_out);
   1057          fclose(f_out);
   1058       }
   1059    }
   1060 
   1061    free(pDecompressedData);
   1062    free(pFileData);
   1063 
   1064    fprintf(stdout, "decompressed size: %zu bytes\n", nActualDecompressedSize);
   1065    fprintf(stdout, "decompression time: %lld microseconds (%g Mb/s)\n", nBestDecTime, ((double)nActualDecompressedSize / 1024.0) / ((double)nBestDecTime / 1000.0));
   1066 
   1067    return 0;
   1068 }
   1069 
   1070 /*---------------------------------------------------------------------------*/
   1071 
   1072 int main(int argc, char **argv) {
   1073    int i;
   1074    const char *pszInFilename = NULL;
   1075    const char *pszOutFilename = NULL;
   1076    const char *pszDictionaryFilename = NULL;
   1077    int nArgsError = 0;
   1078    int nCommandDefined = 0;
   1079    int nVerifyCompression = 0;
   1080    char cCommand = 'z';
   1081    unsigned int nOptions = 0;
   1082    unsigned int nMaxWindowSize = 0;
   1083 
   1084    for (i = 1; i < argc; i++) {
   1085       if (!strcmp(argv[i], "-d")) {
   1086          if (!nCommandDefined) {
   1087             nCommandDefined = 1;
   1088             cCommand = 'd';
   1089          }
   1090          else
   1091             nArgsError = 1;
   1092       }
   1093       else if (!strcmp(argv[i], "-z")) {
   1094          if (!nCommandDefined) {
   1095             nCommandDefined = 1;
   1096             cCommand = 'z';
   1097          }
   1098          else
   1099             nArgsError = 1;
   1100       }
   1101       else if (!strcmp(argv[i], "-c")) {
   1102          if (!nVerifyCompression) {
   1103             nVerifyCompression = 1;
   1104          }
   1105          else
   1106             nArgsError = 1;
   1107       }
   1108       else if (!strcmp(argv[i], "-cbench")) {
   1109          if (!nCommandDefined) {
   1110             nCommandDefined = 1;
   1111             cCommand = 'B';
   1112          }
   1113          else
   1114             nArgsError = 1;
   1115       }
   1116       else if (!strcmp(argv[i], "-dbench")) {
   1117          if (!nCommandDefined) {
   1118             nCommandDefined = 1;
   1119             cCommand = 'b';
   1120          }
   1121          else
   1122             nArgsError = 1;
   1123       }
   1124       else if (!strcmp(argv[i], "-test")) {
   1125          if (!nCommandDefined) {
   1126             nCommandDefined = 1;
   1127             cCommand = 't';
   1128          }
   1129          else
   1130             nArgsError = 1;
   1131       }
   1132       else if (!strcmp(argv[i], "-quicktest")) {
   1133          if (!nCommandDefined) {
   1134             nCommandDefined = 1;
   1135             cCommand = 'T';
   1136          }
   1137          else
   1138             nArgsError = 1;
   1139       }
   1140       else if (!strcmp(argv[i], "-D")) {
   1141          if (!pszDictionaryFilename && (i + 1) < argc) {
   1142             pszDictionaryFilename = argv[i + 1];
   1143             i++;
   1144          }
   1145          else
   1146             nArgsError = 1;
   1147       }
   1148       else if (!strncmp(argv[i], "-D", 2)) {
   1149          if (!pszDictionaryFilename) {
   1150             pszDictionaryFilename = argv[i] + 2;
   1151          }
   1152          else
   1153             nArgsError = 1;
   1154       }
   1155       else if (!strcmp(argv[i], "-v")) {
   1156          if ((nOptions & OPT_VERBOSE) == 0) {
   1157             nOptions |= OPT_VERBOSE;
   1158          }
   1159          else
   1160             nArgsError = 1;
   1161       }
   1162       else if (!strcmp(argv[i], "-w")) {
   1163          if (!nMaxWindowSize && (i + 1) < argc) {
   1164             char *pEnd = NULL;
   1165             nMaxWindowSize = (int)strtol(argv[i + 1], &pEnd, 10);
   1166             if (pEnd && pEnd != argv[i + 1] && (nMaxWindowSize >= 16 && nMaxWindowSize <= MAX_OFFSET)) {
   1167                i++;
   1168             }
   1169             else {
   1170                nArgsError = 1;
   1171             }
   1172          }
   1173          else
   1174             nArgsError = 1;
   1175       }
   1176       else if (!strncmp(argv[i], "-w", 2)) {
   1177          if (!nMaxWindowSize) {
   1178             char *pEnd = NULL;
   1179             nMaxWindowSize = (int)strtol(argv[i] + 2, &pEnd, 10);
   1180             if (!(pEnd && pEnd != (argv[i] + 2) && (nMaxWindowSize >= 16 && nMaxWindowSize <= MAX_OFFSET))) {
   1181                nArgsError = 1;
   1182             }
   1183          }
   1184          else
   1185             nArgsError = 1;
   1186       }
   1187       else if (!strcmp(argv[i], "-stats")) {
   1188          if ((nOptions & OPT_STATS) == 0) {
   1189             nOptions |= OPT_STATS;
   1190          }
   1191          else
   1192             nArgsError = 1;
   1193       }
   1194       else if (!strcmp(argv[i], "-b")) {
   1195          if ((nOptions & OPT_BACKWARD) == 0) {
   1196             nOptions |= OPT_BACKWARD;
   1197          }
   1198          else
   1199             nArgsError = 1;
   1200       }
   1201       else if (!strcmp(argv[i], "-classic")) {
   1202          if ((nOptions & OPT_CLASSIC) == 0) {
   1203             nOptions |= OPT_CLASSIC;
   1204          }
   1205          else
   1206             nArgsError = 1;
   1207       }
   1208       else {
   1209          if (!pszInFilename)
   1210             pszInFilename = argv[i];
   1211          else {
   1212             if (!pszOutFilename)
   1213                pszOutFilename = argv[i];
   1214             else
   1215                nArgsError = 1;
   1216          }
   1217       }
   1218    }
   1219 
   1220    if (!nArgsError && cCommand == 't') {
   1221       return do_self_test(nOptions, nMaxWindowSize, 0);
   1222    }
   1223    else if (!nArgsError && cCommand == 'T') {
   1224       return do_self_test(nOptions, nMaxWindowSize, 1);
   1225    }
   1226 
   1227    if (nArgsError || !pszInFilename || !pszOutFilename) {
   1228       fprintf(stderr, "salvador command-line tool v" TOOL_VERSION " by Emmanuel Marty\n");
   1229       fprintf(stderr, "usage: %s [-c] [-d] [-v] [-b] <infile> <outfile>\n", argv[0]);
   1230       fprintf(stderr, "        -c: check resulting stream after compressing\n");
   1231       fprintf(stderr, "        -d: decompress (default: compress)\n");
   1232       fprintf(stderr, "        -b: backwards compression or decompression\n");
   1233       fprintf(stderr, " -w <size>: maximum window size, in bytes (16..32639), defaults to maximum\n");
   1234       fprintf(stderr, " -D <file>: use dictionary file\n");
   1235       fprintf(stderr, "   -cbench: benchmark in-memory compression\n");
   1236       fprintf(stderr, "   -dbench: benchmark in-memory decompression\n");
   1237       fprintf(stderr, "     -test: run full automated self-tests\n");
   1238       fprintf(stderr, "-quicktest: run quick automated self-tests\n");
   1239       fprintf(stderr, "    -stats: show compressed data stats\n");
   1240       fprintf(stderr, "  -classic: encode and decode using classical (V1) format, defaults to modern (V2)\n");
   1241       fprintf(stderr, "        -v: be verbose\n");
   1242       return 100;
   1243    }
   1244 
   1245    do_init_time();
   1246 
   1247    if (cCommand == 'z') {
   1248       int nResult = do_compress(pszInFilename, pszOutFilename, pszDictionaryFilename, nOptions, nMaxWindowSize);
   1249       if (nResult == 0 && nVerifyCompression) {
   1250          return do_compare(pszOutFilename, pszInFilename, pszDictionaryFilename, nOptions);
   1251       } else {
   1252          return nResult;
   1253       }
   1254    }
   1255    else if (cCommand == 'd') {
   1256       return do_decompress(pszInFilename, pszOutFilename, pszDictionaryFilename, nOptions);
   1257    }
   1258    else if (cCommand == 'B') {
   1259       return do_compr_benchmark(pszInFilename, pszOutFilename, pszDictionaryFilename, nOptions, nMaxWindowSize);
   1260    }
   1261    else if (cCommand == 'b') {
   1262       return do_dec_benchmark(pszInFilename, pszOutFilename, pszDictionaryFilename, nOptions);
   1263    }
   1264    else {
   1265       return 100;
   1266    }
   1267 }

This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.