The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/contrib/zstd/programs/fileio.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*
    2  * Copyright (c) Yann Collet, Facebook, Inc.
    3  * All rights reserved.
    4  *
    5  * This source code is licensed under both the BSD-style license (found in the
    6  * LICENSE file in the root directory of this source tree) and the GPLv2 (found
    7  * in the COPYING file in the root directory of this source tree).
    8  * You may select, at your option, one of the above-listed licenses.
    9  */
   10 
   11 
   12 /* *************************************
   13 *  Compiler Options
   14 ***************************************/
   15 #ifdef _MSC_VER   /* Visual */
   16 #  pragma warning(disable : 4127)  /* disable: C4127: conditional expression is constant */
   17 #  pragma warning(disable : 4204)  /* non-constant aggregate initializer */
   18 #endif
   19 #if defined(__MINGW32__) && !defined(_POSIX_SOURCE)
   20 #  define _POSIX_SOURCE 1          /* disable %llu warnings with MinGW on Windows */
   21 #endif
   22 
   23 /*-*************************************
   24 *  Includes
   25 ***************************************/
   26 #include "platform.h"   /* Large Files support, SET_BINARY_MODE */
   27 #include "util.h"       /* UTIL_getFileSize, UTIL_isRegularFile, UTIL_isSameFile */
   28 #include <stdio.h>      /* fprintf, open, fdopen, fread, _fileno, stdin, stdout */
   29 #include <stdlib.h>     /* malloc, free */
   30 #include <string.h>     /* strcmp, strlen */
   31 #include <fcntl.h>      /* O_WRONLY */
   32 #include <assert.h>
   33 #include <errno.h>      /* errno */
   34 #include <limits.h>     /* INT_MAX */
   35 #include <signal.h>
   36 #include "timefn.h"     /* UTIL_getTime, UTIL_clockSpanMicro */
   37 
   38 #if defined (_MSC_VER)
   39 #  include <sys/stat.h>
   40 #  include <io.h>
   41 #endif
   42 
   43 #include "../lib/common/mem.h"     /* U32, U64 */
   44 #include "fileio.h"
   45 
   46 #define ZSTD_STATIC_LINKING_ONLY   /* ZSTD_magicNumber, ZSTD_frameHeaderSize_max */
   47 #include "../lib/zstd.h"
   48 #include "../lib/zstd_errors.h"  /* ZSTD_error_frameParameter_windowTooLarge */
   49 
   50 #if defined(ZSTD_GZCOMPRESS) || defined(ZSTD_GZDECOMPRESS)
   51 #  include <zlib.h>
   52 #  if !defined(z_const)
   53 #    define z_const
   54 #  endif
   55 #endif
   56 
   57 #if defined(ZSTD_LZMACOMPRESS) || defined(ZSTD_LZMADECOMPRESS)
   58 #  include <lzma.h>
   59 #endif
   60 
   61 #define LZ4_MAGICNUMBER 0x184D2204
   62 #if defined(ZSTD_LZ4COMPRESS) || defined(ZSTD_LZ4DECOMPRESS)
   63 #  define LZ4F_ENABLE_OBSOLETE_ENUMS
   64 #  include <lz4frame.h>
   65 #  include <lz4.h>
   66 #endif
   67 
   68 
   69 /*-*************************************
   70 *  Constants
   71 ***************************************/
   72 #define ADAPT_WINDOWLOG_DEFAULT 23   /* 8 MB */
   73 #define DICTSIZE_MAX (32 MB)   /* protection against large input (attack scenario) */
   74 
   75 #define FNSPACE 30
   76 
   77 /* Default file permissions 0666 (modulated by umask) */
   78 #if !defined(_WIN32)
   79 /* These macros aren't defined on windows. */
   80 #define DEFAULT_FILE_PERMISSIONS (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
   81 #else
   82 #define DEFAULT_FILE_PERMISSIONS (0666)
   83 #endif
   84 
   85 /*-*************************************
   86 *  Macros
   87 ***************************************/
   88 #define KB *(1 <<10)
   89 #define MB *(1 <<20)
   90 #define GB *(1U<<30)
   91 #undef MAX
   92 #define MAX(a,b) ((a)>(b) ? (a) : (b))
   93 
   94 struct FIO_display_prefs_s {
   95     int displayLevel;   /* 0 : no display;  1: errors;  2: + result + interaction + warnings;  3: + progression;  4: + information */
   96     FIO_progressSetting_e progressSetting;
   97 };
   98 
   99 static FIO_display_prefs_t g_display_prefs = {2, FIO_ps_auto};
  100 
  101 #define DISPLAY(...)         fprintf(stderr, __VA_ARGS__)
  102 #define DISPLAYOUT(...)      fprintf(stdout, __VA_ARGS__)
  103 #define DISPLAYLEVEL(l, ...) { if (g_display_prefs.displayLevel>=l) { DISPLAY(__VA_ARGS__); } }
  104 
  105 static const U64 g_refreshRate = SEC_TO_MICRO / 6;
  106 static UTIL_time_t g_displayClock = UTIL_TIME_INITIALIZER;
  107 
  108 #define READY_FOR_UPDATE() ((g_display_prefs.progressSetting != FIO_ps_never) && UTIL_clockSpanMicro(g_displayClock) > g_refreshRate)
  109 #define DELAY_NEXT_UPDATE() { g_displayClock = UTIL_getTime(); }
  110 #define DISPLAYUPDATE(l, ...) {                              \
  111         if (g_display_prefs.displayLevel>=l && (g_display_prefs.progressSetting != FIO_ps_never)) { \
  112             if (READY_FOR_UPDATE() || (g_display_prefs.displayLevel>=4)) { \
  113                 DELAY_NEXT_UPDATE();                         \
  114                 DISPLAY(__VA_ARGS__);                        \
  115                 if (g_display_prefs.displayLevel>=4) fflush(stderr);       \
  116     }   }   }
  117 
  118 #undef MIN  /* in case it would be already defined */
  119 #define MIN(a,b)    ((a) < (b) ? (a) : (b))
  120 
  121 
  122 #define EXM_THROW(error, ...)                                             \
  123 {                                                                         \
  124     DISPLAYLEVEL(1, "zstd: ");                                            \
  125     DISPLAYLEVEL(5, "Error defined at %s, line %i : \n", __FILE__, __LINE__); \
  126     DISPLAYLEVEL(1, "error %i : ", error);                                \
  127     DISPLAYLEVEL(1, __VA_ARGS__);                                         \
  128     DISPLAYLEVEL(1, " \n");                                               \
  129     exit(error);                                                          \
  130 }
  131 
  132 #define CHECK_V(v, f)                                \
  133     v = f;                                           \
  134     if (ZSTD_isError(v)) {                           \
  135         DISPLAYLEVEL(5, "%s \n", #f);                \
  136         EXM_THROW(11, "%s", ZSTD_getErrorName(v));   \
  137     }
  138 #define CHECK(f) { size_t err; CHECK_V(err, f); }
  139 
  140 
  141 /*-************************************
  142 *  Signal (Ctrl-C trapping)
  143 **************************************/
  144 static const char* g_artefact = NULL;
  145 static void INThandler(int sig)
  146 {
  147     assert(sig==SIGINT); (void)sig;
  148 #if !defined(_MSC_VER)
  149     signal(sig, SIG_IGN);  /* this invocation generates a buggy warning in Visual Studio */
  150 #endif
  151     if (g_artefact) {
  152         assert(UTIL_isRegularFile(g_artefact));
  153         remove(g_artefact);
  154     }
  155     DISPLAY("\n");
  156     exit(2);
  157 }
  158 static void addHandler(char const* dstFileName)
  159 {
  160     if (UTIL_isRegularFile(dstFileName)) {
  161         g_artefact = dstFileName;
  162         signal(SIGINT, INThandler);
  163     } else {
  164         g_artefact = NULL;
  165     }
  166 }
  167 /* Idempotent */
  168 static void clearHandler(void)
  169 {
  170     if (g_artefact) signal(SIGINT, SIG_DFL);
  171     g_artefact = NULL;
  172 }
  173 
  174 
  175 /*-*********************************************************
  176 *  Termination signal trapping (Print debug stack trace)
  177 ***********************************************************/
  178 #if defined(__has_feature) && !defined(BACKTRACE_ENABLE) /* Clang compiler */
  179 #  if (__has_feature(address_sanitizer))
  180 #    define BACKTRACE_ENABLE 0
  181 #  endif /* __has_feature(address_sanitizer) */
  182 #elif defined(__SANITIZE_ADDRESS__) && !defined(BACKTRACE_ENABLE) /* GCC compiler */
  183 #  define BACKTRACE_ENABLE 0
  184 #endif
  185 
  186 #if !defined(BACKTRACE_ENABLE)
  187 /* automatic detector : backtrace enabled by default on linux+glibc and osx */
  188 #  if (defined(__linux__) && (defined(__GLIBC__) && !defined(__UCLIBC__))) \
  189      || (defined(__APPLE__) && defined(__MACH__))
  190 #    define BACKTRACE_ENABLE 1
  191 #  else
  192 #    define BACKTRACE_ENABLE 0
  193 #  endif
  194 #endif
  195 
  196 /* note : after this point, BACKTRACE_ENABLE is necessarily defined */
  197 
  198 
  199 #if BACKTRACE_ENABLE
  200 
  201 #include <execinfo.h>   /* backtrace, backtrace_symbols */
  202 
  203 #define MAX_STACK_FRAMES    50
  204 
  205 static void ABRThandler(int sig) {
  206     const char* name;
  207     void* addrlist[MAX_STACK_FRAMES];
  208     char** symbollist;
  209     int addrlen, i;
  210 
  211     switch (sig) {
  212         case SIGABRT: name = "SIGABRT"; break;
  213         case SIGFPE: name = "SIGFPE"; break;
  214         case SIGILL: name = "SIGILL"; break;
  215         case SIGINT: name = "SIGINT"; break;
  216         case SIGSEGV: name = "SIGSEGV"; break;
  217         default: name = "UNKNOWN";
  218     }
  219 
  220     DISPLAY("Caught %s signal, printing stack:\n", name);
  221     /* Retrieve current stack addresses. */
  222     addrlen = backtrace(addrlist, MAX_STACK_FRAMES);
  223     if (addrlen == 0) {
  224         DISPLAY("\n");
  225         return;
  226     }
  227     /* Create readable strings to each frame. */
  228     symbollist = backtrace_symbols(addrlist, addrlen);
  229     /* Print the stack trace, excluding calls handling the signal. */
  230     for (i = ZSTD_START_SYMBOLLIST_FRAME; i < addrlen; i++) {
  231         DISPLAY("%s\n", symbollist[i]);
  232     }
  233     free(symbollist);
  234     /* Reset and raise the signal so default handler runs. */
  235     signal(sig, SIG_DFL);
  236     raise(sig);
  237 }
  238 #endif
  239 
  240 void FIO_addAbortHandler()
  241 {
  242 #if BACKTRACE_ENABLE
  243     signal(SIGABRT, ABRThandler);
  244     signal(SIGFPE, ABRThandler);
  245     signal(SIGILL, ABRThandler);
  246     signal(SIGSEGV, ABRThandler);
  247     signal(SIGBUS, ABRThandler);
  248 #endif
  249 }
  250 
  251 
  252 /*-************************************************************
  253 * Avoid fseek()'s 2GiB barrier with MSVC, macOS, *BSD, MinGW
  254 ***************************************************************/
  255 #if defined(_MSC_VER) && _MSC_VER >= 1400
  256 #   define LONG_SEEK _fseeki64
  257 #   define LONG_TELL _ftelli64
  258 #elif !defined(__64BIT__) && (PLATFORM_POSIX_VERSION >= 200112L) /* No point defining Large file for 64 bit */
  259 #  define LONG_SEEK fseeko
  260 #  define LONG_TELL ftello
  261 #elif defined(__MINGW32__) && !defined(__STRICT_ANSI__) && !defined(__NO_MINGW_LFS) && defined(__MSVCRT__)
  262 #   define LONG_SEEK fseeko64
  263 #   define LONG_TELL ftello64
  264 #elif defined(_WIN32) && !defined(__DJGPP__)
  265 #   include <windows.h>
  266     static int LONG_SEEK(FILE* file, __int64 offset, int origin) {
  267         LARGE_INTEGER off;
  268         DWORD method;
  269         off.QuadPart = offset;
  270         if (origin == SEEK_END)
  271             method = FILE_END;
  272         else if (origin == SEEK_CUR)
  273             method = FILE_CURRENT;
  274         else
  275             method = FILE_BEGIN;
  276 
  277         if (SetFilePointerEx((HANDLE) _get_osfhandle(_fileno(file)), off, NULL, method))
  278             return 0;
  279         else
  280             return -1;
  281     }
  282     static __int64 LONG_TELL(FILE* file) {
  283         LARGE_INTEGER off, newOff;
  284         off.QuadPart = 0;
  285         newOff.QuadPart = 0;
  286         SetFilePointerEx((HANDLE) _get_osfhandle(_fileno(file)), off, &newOff, FILE_CURRENT);
  287         return newOff.QuadPart;
  288     }
  289 #else
  290 #   define LONG_SEEK fseek
  291 #   define LONG_TELL ftell
  292 #endif
  293 
  294 
  295 /*-*************************************
  296 *  Parameters: FIO_prefs_t
  297 ***************************************/
  298 
  299 /* typedef'd to FIO_prefs_t within fileio.h */
  300 struct FIO_prefs_s {
  301 
  302     /* Algorithm preferences */
  303     FIO_compressionType_t compressionType;
  304     U32 sparseFileSupport;   /* 0: no sparse allowed; 1: auto (file yes, stdout no); 2: force sparse */
  305     int dictIDFlag;
  306     int checksumFlag;
  307     int blockSize;
  308     int overlapLog;
  309     U32 adaptiveMode;
  310     U32 useRowMatchFinder;
  311     int rsyncable;
  312     int minAdaptLevel;
  313     int maxAdaptLevel;
  314     int ldmFlag;
  315     int ldmHashLog;
  316     int ldmMinMatch;
  317     int ldmBucketSizeLog;
  318     int ldmHashRateLog;
  319     size_t streamSrcSize;
  320     size_t targetCBlockSize;
  321     int srcSizeHint;
  322     int testMode;
  323     ZSTD_paramSwitch_e literalCompressionMode;
  324 
  325     /* IO preferences */
  326     U32 removeSrcFile;
  327     U32 overwrite;
  328 
  329     /* Computation resources preferences */
  330     unsigned memLimit;
  331     int nbWorkers;
  332 
  333     int excludeCompressedFiles;
  334     int patchFromMode;
  335     int contentSize;
  336     int allowBlockDevices;
  337 };
  338 
  339 /*-*************************************
  340 *  Parameters: FIO_ctx_t
  341 ***************************************/
  342 
  343 /* typedef'd to FIO_ctx_t within fileio.h */
  344 struct FIO_ctx_s {
  345 
  346     /* file i/o info */
  347     int nbFilesTotal;
  348     int hasStdinInput;
  349     int hasStdoutOutput;
  350 
  351     /* file i/o state */
  352     int currFileIdx;
  353     int nbFilesProcessed;
  354     size_t totalBytesInput;
  355     size_t totalBytesOutput;
  356 };
  357 
  358 
  359 /*-*************************************
  360 *  Parameters: Initialization
  361 ***************************************/
  362 
  363 #define FIO_OVERLAP_LOG_NOTSET 9999
  364 #define FIO_LDM_PARAM_NOTSET 9999
  365 
  366 
  367 FIO_prefs_t* FIO_createPreferences(void)
  368 {
  369     FIO_prefs_t* const ret = (FIO_prefs_t*)malloc(sizeof(FIO_prefs_t));
  370     if (!ret) EXM_THROW(21, "Allocation error : not enough memory");
  371 
  372     ret->compressionType = FIO_zstdCompression;
  373     ret->overwrite = 0;
  374     ret->sparseFileSupport = ZSTD_SPARSE_DEFAULT;
  375     ret->dictIDFlag = 1;
  376     ret->checksumFlag = 1;
  377     ret->removeSrcFile = 0;
  378     ret->memLimit = 0;
  379     ret->nbWorkers = 1;
  380     ret->blockSize = 0;
  381     ret->overlapLog = FIO_OVERLAP_LOG_NOTSET;
  382     ret->adaptiveMode = 0;
  383     ret->rsyncable = 0;
  384     ret->minAdaptLevel = -50;   /* initializing this value requires a constant, so ZSTD_minCLevel() doesn't work */
  385     ret->maxAdaptLevel = 22;   /* initializing this value requires a constant, so ZSTD_maxCLevel() doesn't work */
  386     ret->ldmFlag = 0;
  387     ret->ldmHashLog = 0;
  388     ret->ldmMinMatch = 0;
  389     ret->ldmBucketSizeLog = FIO_LDM_PARAM_NOTSET;
  390     ret->ldmHashRateLog = FIO_LDM_PARAM_NOTSET;
  391     ret->streamSrcSize = 0;
  392     ret->targetCBlockSize = 0;
  393     ret->srcSizeHint = 0;
  394     ret->testMode = 0;
  395     ret->literalCompressionMode = ZSTD_ps_auto;
  396     ret->excludeCompressedFiles = 0;
  397     ret->allowBlockDevices = 0;
  398     return ret;
  399 }
  400 
  401 FIO_ctx_t* FIO_createContext(void)
  402 {
  403     FIO_ctx_t* const ret = (FIO_ctx_t*)malloc(sizeof(FIO_ctx_t));
  404     if (!ret) EXM_THROW(21, "Allocation error : not enough memory");
  405 
  406     ret->currFileIdx = 0;
  407     ret->hasStdinInput = 0;
  408     ret->hasStdoutOutput = 0;
  409     ret->nbFilesTotal = 1;
  410     ret->nbFilesProcessed = 0;
  411     ret->totalBytesInput = 0;
  412     ret->totalBytesOutput = 0;
  413     return ret;
  414 }
  415 
  416 void FIO_freePreferences(FIO_prefs_t* const prefs)
  417 {
  418     free(prefs);
  419 }
  420 
  421 void FIO_freeContext(FIO_ctx_t* const fCtx)
  422 {
  423     free(fCtx);
  424 }
  425 
  426 
  427 /*-*************************************
  428 *  Parameters: Display Options
  429 ***************************************/
  430 
  431 void FIO_setNotificationLevel(int level) { g_display_prefs.displayLevel=level; }
  432 
  433 void FIO_setProgressSetting(FIO_progressSetting_e setting) { g_display_prefs.progressSetting = setting; }
  434 
  435 
  436 /*-*************************************
  437 *  Parameters: Setters
  438 ***************************************/
  439 
  440 /* FIO_prefs_t functions */
  441 
  442 void FIO_setCompressionType(FIO_prefs_t* const prefs, FIO_compressionType_t compressionType) { prefs->compressionType = compressionType; }
  443 
  444 void FIO_overwriteMode(FIO_prefs_t* const prefs) { prefs->overwrite = 1; }
  445 
  446 void FIO_setSparseWrite(FIO_prefs_t* const prefs, unsigned sparse) { prefs->sparseFileSupport = sparse; }
  447 
  448 void FIO_setDictIDFlag(FIO_prefs_t* const prefs, int dictIDFlag) { prefs->dictIDFlag = dictIDFlag; }
  449 
  450 void FIO_setChecksumFlag(FIO_prefs_t* const prefs, int checksumFlag) { prefs->checksumFlag = checksumFlag; }
  451 
  452 void FIO_setRemoveSrcFile(FIO_prefs_t* const prefs, unsigned flag) { prefs->removeSrcFile = (flag>0); }
  453 
  454 void FIO_setMemLimit(FIO_prefs_t* const prefs, unsigned memLimit) { prefs->memLimit = memLimit; }
  455 
  456 void FIO_setNbWorkers(FIO_prefs_t* const prefs, int nbWorkers) {
  457 #ifndef ZSTD_MULTITHREAD
  458     if (nbWorkers > 0) DISPLAYLEVEL(2, "Note : multi-threading is disabled \n");
  459 #endif
  460     prefs->nbWorkers = nbWorkers;
  461 }
  462 
  463 void FIO_setExcludeCompressedFile(FIO_prefs_t* const prefs, int excludeCompressedFiles) { prefs->excludeCompressedFiles = excludeCompressedFiles; }
  464 
  465 void FIO_setAllowBlockDevices(FIO_prefs_t* const prefs, int allowBlockDevices) { prefs->allowBlockDevices = allowBlockDevices; }
  466 
  467 void FIO_setBlockSize(FIO_prefs_t* const prefs, int blockSize) {
  468     if (blockSize && prefs->nbWorkers==0)
  469         DISPLAYLEVEL(2, "Setting block size is useless in single-thread mode \n");
  470     prefs->blockSize = blockSize;
  471 }
  472 
  473 void FIO_setOverlapLog(FIO_prefs_t* const prefs, int overlapLog){
  474     if (overlapLog && prefs->nbWorkers==0)
  475         DISPLAYLEVEL(2, "Setting overlapLog is useless in single-thread mode \n");
  476     prefs->overlapLog = overlapLog;
  477 }
  478 
  479 void FIO_setAdaptiveMode(FIO_prefs_t* const prefs, unsigned adapt) {
  480     if ((adapt>0) && (prefs->nbWorkers==0))
  481         EXM_THROW(1, "Adaptive mode is not compatible with single thread mode \n");
  482     prefs->adaptiveMode = adapt;
  483 }
  484 
  485 void FIO_setUseRowMatchFinder(FIO_prefs_t* const prefs, int useRowMatchFinder) {
  486     prefs->useRowMatchFinder = useRowMatchFinder;
  487 }
  488 
  489 void FIO_setRsyncable(FIO_prefs_t* const prefs, int rsyncable) {
  490     if ((rsyncable>0) && (prefs->nbWorkers==0))
  491         EXM_THROW(1, "Rsyncable mode is not compatible with single thread mode \n");
  492     prefs->rsyncable = rsyncable;
  493 }
  494 
  495 void FIO_setStreamSrcSize(FIO_prefs_t* const prefs, size_t streamSrcSize) {
  496     prefs->streamSrcSize = streamSrcSize;
  497 }
  498 
  499 void FIO_setTargetCBlockSize(FIO_prefs_t* const prefs, size_t targetCBlockSize) {
  500     prefs->targetCBlockSize = targetCBlockSize;
  501 }
  502 
  503 void FIO_setSrcSizeHint(FIO_prefs_t* const prefs, size_t srcSizeHint) {
  504     prefs->srcSizeHint = (int)MIN((size_t)INT_MAX, srcSizeHint);
  505 }
  506 
  507 void FIO_setTestMode(FIO_prefs_t* const prefs, int testMode) {
  508     prefs->testMode = (testMode!=0);
  509 }
  510 
  511 void FIO_setLiteralCompressionMode(
  512         FIO_prefs_t* const prefs,
  513         ZSTD_paramSwitch_e mode) {
  514     prefs->literalCompressionMode = mode;
  515 }
  516 
  517 void FIO_setAdaptMin(FIO_prefs_t* const prefs, int minCLevel)
  518 {
  519 #ifndef ZSTD_NOCOMPRESS
  520     assert(minCLevel >= ZSTD_minCLevel());
  521 #endif
  522     prefs->minAdaptLevel = minCLevel;
  523 }
  524 
  525 void FIO_setAdaptMax(FIO_prefs_t* const prefs, int maxCLevel)
  526 {
  527     prefs->maxAdaptLevel = maxCLevel;
  528 }
  529 
  530 void FIO_setLdmFlag(FIO_prefs_t* const prefs, unsigned ldmFlag) {
  531     prefs->ldmFlag = (ldmFlag>0);
  532 }
  533 
  534 void FIO_setLdmHashLog(FIO_prefs_t* const prefs, int ldmHashLog) {
  535     prefs->ldmHashLog = ldmHashLog;
  536 }
  537 
  538 void FIO_setLdmMinMatch(FIO_prefs_t* const prefs, int ldmMinMatch) {
  539     prefs->ldmMinMatch = ldmMinMatch;
  540 }
  541 
  542 void FIO_setLdmBucketSizeLog(FIO_prefs_t* const prefs, int ldmBucketSizeLog) {
  543     prefs->ldmBucketSizeLog = ldmBucketSizeLog;
  544 }
  545 
  546 
  547 void FIO_setLdmHashRateLog(FIO_prefs_t* const prefs, int ldmHashRateLog) {
  548     prefs->ldmHashRateLog = ldmHashRateLog;
  549 }
  550 
  551 void FIO_setPatchFromMode(FIO_prefs_t* const prefs, int value)
  552 {
  553     prefs->patchFromMode = value != 0;
  554 }
  555 
  556 void FIO_setContentSize(FIO_prefs_t* const prefs, int value)
  557 {
  558     prefs->contentSize = value != 0;
  559 }
  560 
  561 /* FIO_ctx_t functions */
  562 
  563 void FIO_setHasStdoutOutput(FIO_ctx_t* const fCtx, int value) {
  564     fCtx->hasStdoutOutput = value;
  565 }
  566 
  567 void FIO_setNbFilesTotal(FIO_ctx_t* const fCtx, int value)
  568 {
  569     fCtx->nbFilesTotal = value;
  570 }
  571 
  572 void FIO_determineHasStdinInput(FIO_ctx_t* const fCtx, const FileNamesTable* const filenames) {
  573     size_t i = 0;
  574     for ( ; i < filenames->tableSize; ++i) {
  575         if (!strcmp(stdinmark, filenames->fileNames[i])) {
  576             fCtx->hasStdinInput = 1;
  577             return;
  578         }
  579     }
  580 }
  581 
  582 /*-*************************************
  583 *  Functions
  584 ***************************************/
  585 /** FIO_removeFile() :
  586  * @result : Unlink `fileName`, even if it's read-only */
  587 static int FIO_removeFile(const char* path)
  588 {
  589     stat_t statbuf;
  590     if (!UTIL_stat(path, &statbuf)) {
  591         DISPLAYLEVEL(2, "zstd: Failed to stat %s while trying to remove it\n", path);
  592         return 0;
  593     }
  594     if (!UTIL_isRegularFileStat(&statbuf)) {
  595         DISPLAYLEVEL(2, "zstd: Refusing to remove non-regular file %s\n", path);
  596         return 0;
  597     }
  598 #if defined(_WIN32) || defined(WIN32)
  599     /* windows doesn't allow remove read-only files,
  600      * so try to make it writable first */
  601     if (!(statbuf.st_mode & _S_IWRITE)) {
  602         UTIL_chmod(path, &statbuf, _S_IWRITE);
  603     }
  604 #endif
  605     return remove(path);
  606 }
  607 
  608 /** FIO_openSrcFile() :
  609  *  condition : `srcFileName` must be non-NULL. `prefs` may be NULL.
  610  * @result : FILE* to `srcFileName`, or NULL if it fails */
  611 static FILE* FIO_openSrcFile(const FIO_prefs_t* const prefs, const char* srcFileName)
  612 {
  613     stat_t statbuf;
  614     int allowBlockDevices = prefs != NULL ? prefs->allowBlockDevices : 0;
  615     assert(srcFileName != NULL);
  616     if (!strcmp (srcFileName, stdinmark)) {
  617         DISPLAYLEVEL(4,"Using stdin for input \n");
  618         SET_BINARY_MODE(stdin);
  619         return stdin;
  620     }
  621 
  622     if (!UTIL_stat(srcFileName, &statbuf)) {
  623         DISPLAYLEVEL(1, "zstd: can't stat %s : %s -- ignored \n",
  624                         srcFileName, strerror(errno));
  625         return NULL;
  626     }
  627 
  628     if (!UTIL_isRegularFileStat(&statbuf)
  629      && !UTIL_isFIFOStat(&statbuf)
  630      && !(allowBlockDevices && UTIL_isBlockDevStat(&statbuf))
  631     ) {
  632         DISPLAYLEVEL(1, "zstd: %s is not a regular file -- ignored \n",
  633                         srcFileName);
  634         return NULL;
  635     }
  636 
  637     {   FILE* const f = fopen(srcFileName, "rb");
  638         if (f == NULL)
  639             DISPLAYLEVEL(1, "zstd: %s: %s \n", srcFileName, strerror(errno));
  640         return f;
  641     }
  642 }
  643 
  644 /** FIO_openDstFile() :
  645  *  condition : `dstFileName` must be non-NULL.
  646  * @result : FILE* to `dstFileName`, or NULL if it fails */
  647 static FILE*
  648 FIO_openDstFile(FIO_ctx_t* fCtx, FIO_prefs_t* const prefs,
  649                 const char* srcFileName, const char* dstFileName,
  650                 const int mode)
  651 {
  652     if (prefs->testMode) return NULL;  /* do not open file in test mode */
  653 
  654     assert(dstFileName != NULL);
  655     if (!strcmp (dstFileName, stdoutmark)) {
  656         DISPLAYLEVEL(4,"Using stdout for output \n");
  657         SET_BINARY_MODE(stdout);
  658         if (prefs->sparseFileSupport == 1) {
  659             prefs->sparseFileSupport = 0;
  660             DISPLAYLEVEL(4, "Sparse File Support is automatically disabled on stdout ; try --sparse \n");
  661         }
  662         return stdout;
  663     }
  664 
  665     /* ensure dst is not the same as src */
  666     if (srcFileName != NULL && UTIL_isSameFile(srcFileName, dstFileName)) {
  667         DISPLAYLEVEL(1, "zstd: Refusing to open an output file which will overwrite the input file \n");
  668         return NULL;
  669     }
  670 
  671     if (prefs->sparseFileSupport == 1) {
  672         prefs->sparseFileSupport = ZSTD_SPARSE_DEFAULT;
  673     }
  674 
  675     if (UTIL_isRegularFile(dstFileName)) {
  676         /* Check if destination file already exists */
  677 #if !defined(_WIN32)
  678         /* this test does not work on Windows :
  679          * `NUL` and `nul` are detected as regular files */
  680         if (!strcmp(dstFileName, nulmark)) {
  681             EXM_THROW(40, "%s is unexpectedly categorized as a regular file",
  682                         dstFileName);
  683         }
  684 #endif
  685         if (!prefs->overwrite) {
  686             if (g_display_prefs.displayLevel <= 1) {
  687                 /* No interaction possible */
  688                 DISPLAY("zstd: %s already exists; not overwritten  \n",
  689                         dstFileName);
  690                 return NULL;
  691             }
  692             DISPLAY("zstd: %s already exists; ", dstFileName);
  693             if (UTIL_requireUserConfirmation("overwrite (y/n) ? ", "Not overwritten  \n", "yY", fCtx->hasStdinInput))
  694                 return NULL;
  695         }
  696         /* need to unlink */
  697         FIO_removeFile(dstFileName);
  698     }
  699 
  700     {
  701 #if defined(_WIN32)
  702         /* Windows requires opening the file as a "binary" file to avoid
  703          * mangling. This macro doesn't exist on unix. */
  704         const int openflags = O_WRONLY|O_CREAT|O_TRUNC|O_BINARY;
  705         const int fd = _open(dstFileName, openflags, mode);
  706         FILE* f = NULL;
  707         if (fd != -1) {
  708             f = _fdopen(fd, "wb");
  709         }
  710 #else
  711         const int openflags = O_WRONLY|O_CREAT|O_TRUNC;
  712         const int fd = open(dstFileName, openflags, mode);
  713         FILE* f = NULL;
  714         if (fd != -1) {
  715             f = fdopen(fd, "wb");
  716         }
  717 #endif
  718         if (f == NULL) {
  719             DISPLAYLEVEL(1, "zstd: %s: %s\n", dstFileName, strerror(errno));
  720         }
  721         return f;
  722     }
  723 }
  724 
  725 /*! FIO_createDictBuffer() :
  726  *  creates a buffer, pointed by `*bufferPtr`,
  727  *  loads `filename` content into it, up to DICTSIZE_MAX bytes.
  728  * @return : loaded size
  729  *  if fileName==NULL, returns 0 and a NULL pointer
  730  */
  731 static size_t FIO_createDictBuffer(void** bufferPtr, const char* fileName, FIO_prefs_t* const prefs)
  732 {
  733     FILE* fileHandle;
  734     U64 fileSize;
  735     stat_t statbuf;
  736 
  737     assert(bufferPtr != NULL);
  738     *bufferPtr = NULL;
  739     if (fileName == NULL) return 0;
  740 
  741     DISPLAYLEVEL(4,"Loading %s as dictionary \n", fileName);
  742 
  743     if (!UTIL_stat(fileName, &statbuf)) {
  744         EXM_THROW(31, "Stat failed on dictionary file %s: %s", fileName, strerror(errno));
  745     }
  746 
  747     if (!UTIL_isRegularFileStat(&statbuf)) {
  748         EXM_THROW(32, "Dictionary %s must be a regular file.", fileName);
  749     }
  750 
  751     fileHandle = fopen(fileName, "rb");
  752 
  753     if (fileHandle == NULL) {
  754         EXM_THROW(33, "Couldn't open dictionary %s: %s", fileName, strerror(errno));
  755     }
  756 
  757     fileSize = UTIL_getFileSizeStat(&statbuf);
  758     {
  759         size_t const dictSizeMax = prefs->patchFromMode ? prefs->memLimit : DICTSIZE_MAX;
  760         if (fileSize >  dictSizeMax) {
  761             EXM_THROW(34, "Dictionary file %s is too large (> %u bytes)",
  762                             fileName,  (unsigned)dictSizeMax);   /* avoid extreme cases */
  763         }
  764     }
  765     *bufferPtr = malloc((size_t)fileSize);
  766     if (*bufferPtr==NULL) EXM_THROW(34, "%s", strerror(errno));
  767     {   size_t const readSize = fread(*bufferPtr, 1, (size_t)fileSize, fileHandle);
  768         if (readSize != fileSize) {
  769             EXM_THROW(35, "Error reading dictionary file %s : %s",
  770                     fileName, strerror(errno));
  771         }
  772     }
  773     fclose(fileHandle);
  774     return (size_t)fileSize;
  775 }
  776 
  777 
  778 
  779 /* FIO_checkFilenameCollisions() :
  780  * Checks for and warns if there are any files that would have the same output path
  781  */
  782 int FIO_checkFilenameCollisions(const char** filenameTable, unsigned nbFiles) {
  783     const char **filenameTableSorted, *prevElem, *filename;
  784     unsigned u;
  785 
  786     filenameTableSorted = (const char**) malloc(sizeof(char*) * nbFiles);
  787     if (!filenameTableSorted) {
  788         DISPLAY("Unable to malloc new str array, not checking for name collisions\n");
  789         return 1;
  790     }
  791 
  792     for (u = 0; u < nbFiles; ++u) {
  793         filename = strrchr(filenameTable[u], PATH_SEP);
  794         if (filename == NULL) {
  795             filenameTableSorted[u] = filenameTable[u];
  796         } else {
  797             filenameTableSorted[u] = filename+1;
  798         }
  799     }
  800 
  801     qsort((void*)filenameTableSorted, nbFiles, sizeof(char*), UTIL_compareStr);
  802     prevElem = filenameTableSorted[0];
  803     for (u = 1; u < nbFiles; ++u) {
  804         if (strcmp(prevElem, filenameTableSorted[u]) == 0) {
  805             DISPLAY("WARNING: Two files have same filename: %s\n", prevElem);
  806         }
  807         prevElem = filenameTableSorted[u];
  808     }
  809 
  810     free((void*)filenameTableSorted);
  811     return 0;
  812 }
  813 
  814 static const char*
  815 extractFilename(const char* path, char separator)
  816 {
  817     const char* search = strrchr(path, separator);
  818     if (search == NULL) return path;
  819     return search+1;
  820 }
  821 
  822 /* FIO_createFilename_fromOutDir() :
  823  * Takes a source file name and specified output directory, and
  824  * allocates memory for and returns a pointer to final path.
  825  * This function never returns an error (it may abort() in case of pb)
  826  */
  827 static char*
  828 FIO_createFilename_fromOutDir(const char* path, const char* outDirName, const size_t suffixLen)
  829 {
  830     const char* filenameStart;
  831     char separator;
  832     char* result;
  833 
  834 #if defined(_MSC_VER) || defined(__MINGW32__) || defined (__MSVCRT__) /* windows support */
  835     separator = '\\';
  836 #else
  837     separator = '/';
  838 #endif
  839 
  840     filenameStart = extractFilename(path, separator);
  841 #if defined(_MSC_VER) || defined(__MINGW32__) || defined (__MSVCRT__) /* windows support */
  842     filenameStart = extractFilename(filenameStart, '/');  /* sometimes, '/' separator is also used on Windows (mingw+msys2) */
  843 #endif
  844 
  845     result = (char*) calloc(1, strlen(outDirName) + 1 + strlen(filenameStart) + suffixLen + 1);
  846     if (!result) {
  847         EXM_THROW(30, "zstd: FIO_createFilename_fromOutDir: %s", strerror(errno));
  848     }
  849 
  850     memcpy(result, outDirName, strlen(outDirName));
  851     if (outDirName[strlen(outDirName)-1] == separator) {
  852         memcpy(result + strlen(outDirName), filenameStart, strlen(filenameStart));
  853     } else {
  854         memcpy(result + strlen(outDirName), &separator, 1);
  855         memcpy(result + strlen(outDirName) + 1, filenameStart, strlen(filenameStart));
  856     }
  857 
  858     return result;
  859 }
  860 
  861 /* FIO_highbit64() :
  862  * gives position of highest bit.
  863  * note : only works for v > 0 !
  864  */
  865 static unsigned FIO_highbit64(unsigned long long v)
  866 {
  867     unsigned count = 0;
  868     assert(v != 0);
  869     v >>= 1;
  870     while (v) { v >>= 1; count++; }
  871     return count;
  872 }
  873 
  874 static void FIO_adjustMemLimitForPatchFromMode(FIO_prefs_t* const prefs,
  875                                     unsigned long long const dictSize,
  876                                     unsigned long long const maxSrcFileSize)
  877 {
  878     unsigned long long maxSize = MAX(prefs->memLimit, MAX(dictSize, maxSrcFileSize));
  879     unsigned const maxWindowSize = (1U << ZSTD_WINDOWLOG_MAX);
  880     if (maxSize == UTIL_FILESIZE_UNKNOWN)
  881         EXM_THROW(42, "Using --patch-from with stdin requires --stream-size");
  882     assert(maxSize != UTIL_FILESIZE_UNKNOWN);
  883     if (maxSize > maxWindowSize)
  884         EXM_THROW(42, "Can't handle files larger than %u GB\n", maxWindowSize/(1 GB));
  885     FIO_setMemLimit(prefs, (unsigned)maxSize);
  886 }
  887 
  888 /* FIO_removeMultiFilesWarning() :
  889  * Returns 1 if the console should abort, 0 if console should proceed.
  890  * This function handles logic when processing multiple files with -o, displaying the appropriate warnings/prompts.
  891  *
  892  * If -f is specified, or there is just 1 file, zstd will always proceed as usual.
  893  * If --rm is specified, there will be a prompt asking for user confirmation.
  894  *         If -f is specified with --rm, zstd will proceed as usual
  895  *         If -q is specified with --rm, zstd will abort pre-emptively
  896  *         If neither flag is specified, zstd will prompt the user for confirmation to proceed.
  897  * If --rm is not specified, then zstd will print a warning to the user (which can be silenced with -q).
  898  * However, if the output is stdout, we will always abort rather than displaying the warning prompt.
  899  */
  900 static int FIO_removeMultiFilesWarning(FIO_ctx_t* const fCtx, const FIO_prefs_t* const prefs, const char* outFileName, int displayLevelCutoff)
  901 {
  902     int error = 0;
  903     if (fCtx->nbFilesTotal > 1 && !prefs->overwrite) {
  904         if (g_display_prefs.displayLevel <= displayLevelCutoff) {
  905             if (prefs->removeSrcFile) {
  906                 DISPLAYLEVEL(1, "zstd: Aborting... not deleting files and processing into dst: %s\n", outFileName);
  907                 error =  1;
  908             }
  909         } else {
  910             if (!strcmp(outFileName, stdoutmark)) {
  911                 DISPLAYLEVEL(2, "zstd: WARNING: all input files will be processed and concatenated into stdout. \n");
  912             } else {
  913                 DISPLAYLEVEL(2, "zstd: WARNING: all input files will be processed and concatenated into a single output file: %s \n", outFileName);
  914             }
  915             DISPLAYLEVEL(2, "The concatenated output CANNOT regenerate the original directory tree. \n")
  916             if (prefs->removeSrcFile) {
  917                 if (fCtx->hasStdoutOutput) {
  918                     DISPLAYLEVEL(1, "Aborting. Use -f if you really want to delete the files and output to stdout\n");
  919                     error = 1;
  920                 } else {
  921                     error = g_display_prefs.displayLevel > displayLevelCutoff && UTIL_requireUserConfirmation("This is a destructive operation. Proceed? (y/n): ", "Aborting...", "yY", fCtx->hasStdinInput);
  922                 }
  923             }
  924         }
  925     }
  926     return error;
  927 }
  928 
  929 #ifndef ZSTD_NOCOMPRESS
  930 
  931 /* **********************************************************************
  932  *  Compression
  933  ************************************************************************/
  934 typedef struct {
  935     FILE* srcFile;
  936     FILE* dstFile;
  937     void*  srcBuffer;
  938     size_t srcBufferSize;
  939     void*  dstBuffer;
  940     size_t dstBufferSize;
  941     void* dictBuffer;
  942     size_t dictBufferSize;
  943     const char* dictFileName;
  944     ZSTD_CStream* cctx;
  945 } cRess_t;
  946 
  947 /** ZSTD_cycleLog() :
  948  *  condition for correct operation : hashLog > 1 */
  949 static U32 ZSTD_cycleLog(U32 hashLog, ZSTD_strategy strat)
  950 {
  951     U32 const btScale = ((U32)strat >= (U32)ZSTD_btlazy2);
  952     assert(hashLog > 1);
  953     return hashLog - btScale;
  954 }
  955 
  956 static void FIO_adjustParamsForPatchFromMode(FIO_prefs_t* const prefs,
  957                                     ZSTD_compressionParameters* comprParams,
  958                                     unsigned long long const dictSize,
  959                                     unsigned long long const maxSrcFileSize,
  960                                     int cLevel)
  961 {
  962     unsigned const fileWindowLog = FIO_highbit64(maxSrcFileSize) + 1;
  963     ZSTD_compressionParameters const cParams = ZSTD_getCParams(cLevel, (size_t)maxSrcFileSize, (size_t)dictSize);
  964     FIO_adjustMemLimitForPatchFromMode(prefs, dictSize, maxSrcFileSize);
  965     if (fileWindowLog > ZSTD_WINDOWLOG_MAX)
  966         DISPLAYLEVEL(1, "Max window log exceeded by file (compression ratio will suffer)\n");
  967     comprParams->windowLog = MAX(ZSTD_WINDOWLOG_MIN, MIN(ZSTD_WINDOWLOG_MAX, fileWindowLog));
  968     if (fileWindowLog > ZSTD_cycleLog(cParams.chainLog, cParams.strategy)) {
  969         if (!prefs->ldmFlag)
  970             DISPLAYLEVEL(1, "long mode automatically triggered\n");
  971         FIO_setLdmFlag(prefs, 1);
  972     }
  973     if (cParams.strategy >= ZSTD_btopt) {
  974         DISPLAYLEVEL(1, "[Optimal parser notes] Consider the following to improve patch size at the cost of speed:\n");
  975         DISPLAYLEVEL(1, "- Use --single-thread mode in the zstd cli\n");
  976         DISPLAYLEVEL(1, "- Set a larger targetLength (eg. --zstd=targetLength=4096)\n");
  977         DISPLAYLEVEL(1, "- Set a larger chainLog (eg. --zstd=chainLog=%u)\n", ZSTD_CHAINLOG_MAX);
  978         DISPLAYLEVEL(1, "Also consider playing around with searchLog and hashLog\n");
  979     }
  980 }
  981 
  982 static cRess_t FIO_createCResources(FIO_prefs_t* const prefs,
  983                                     const char* dictFileName, unsigned long long const maxSrcFileSize,
  984                                     int cLevel, ZSTD_compressionParameters comprParams) {
  985     cRess_t ress;
  986     memset(&ress, 0, sizeof(ress));
  987 
  988     DISPLAYLEVEL(6, "FIO_createCResources \n");
  989     ress.cctx = ZSTD_createCCtx();
  990     if (ress.cctx == NULL)
  991         EXM_THROW(30, "allocation error (%s): can't create ZSTD_CCtx",
  992                     strerror(errno));
  993     ress.srcBufferSize = ZSTD_CStreamInSize();
  994     ress.srcBuffer = malloc(ress.srcBufferSize);
  995     ress.dstBufferSize = ZSTD_CStreamOutSize();
  996 
  997     /* need to update memLimit before calling createDictBuffer
  998      * because of memLimit check inside it */
  999     if (prefs->patchFromMode) {
 1000         unsigned long long const ssSize = (unsigned long long)prefs->streamSrcSize;
 1001         FIO_adjustParamsForPatchFromMode(prefs, &comprParams, UTIL_getFileSize(dictFileName), ssSize > 0 ? ssSize : maxSrcFileSize, cLevel);
 1002     }
 1003     ress.dstBuffer = malloc(ress.dstBufferSize);
 1004     ress.dictBufferSize = FIO_createDictBuffer(&ress.dictBuffer, dictFileName, prefs);   /* works with dictFileName==NULL */
 1005     if (!ress.srcBuffer || !ress.dstBuffer)
 1006         EXM_THROW(31, "allocation error : not enough memory");
 1007 
 1008     /* Advanced parameters, including dictionary */
 1009     if (dictFileName && (ress.dictBuffer==NULL))
 1010         EXM_THROW(32, "allocation error : can't create dictBuffer");
 1011     ress.dictFileName = dictFileName;
 1012 
 1013     if (prefs->adaptiveMode && !prefs->ldmFlag && !comprParams.windowLog)
 1014         comprParams.windowLog = ADAPT_WINDOWLOG_DEFAULT;
 1015 
 1016     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_contentSizeFlag, prefs->contentSize) );  /* always enable content size when available (note: supposed to be default) */
 1017     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_dictIDFlag, prefs->dictIDFlag) );
 1018     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_checksumFlag, prefs->checksumFlag) );
 1019     /* compression level */
 1020     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_compressionLevel, cLevel) );
 1021     /* max compressed block size */
 1022     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_targetCBlockSize, (int)prefs->targetCBlockSize) );
 1023     /* source size hint */
 1024     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_srcSizeHint, (int)prefs->srcSizeHint) );
 1025     /* long distance matching */
 1026     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_enableLongDistanceMatching, prefs->ldmFlag) );
 1027     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_ldmHashLog, prefs->ldmHashLog) );
 1028     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_ldmMinMatch, prefs->ldmMinMatch) );
 1029     if (prefs->ldmBucketSizeLog != FIO_LDM_PARAM_NOTSET) {
 1030         CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_ldmBucketSizeLog, prefs->ldmBucketSizeLog) );
 1031     }
 1032     if (prefs->ldmHashRateLog != FIO_LDM_PARAM_NOTSET) {
 1033         CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_ldmHashRateLog, prefs->ldmHashRateLog) );
 1034     }
 1035     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_useRowMatchFinder, prefs->useRowMatchFinder));
 1036     /* compression parameters */
 1037     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_windowLog, (int)comprParams.windowLog) );
 1038     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_chainLog, (int)comprParams.chainLog) );
 1039     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_hashLog, (int)comprParams.hashLog) );
 1040     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_searchLog, (int)comprParams.searchLog) );
 1041     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_minMatch, (int)comprParams.minMatch) );
 1042     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_targetLength, (int)comprParams.targetLength) );
 1043     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_strategy, (int)comprParams.strategy) );
 1044     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_literalCompressionMode, (int)prefs->literalCompressionMode) );
 1045     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_enableDedicatedDictSearch, 1) );
 1046     /* multi-threading */
 1047 #ifdef ZSTD_MULTITHREAD
 1048     DISPLAYLEVEL(5,"set nb workers = %u \n", prefs->nbWorkers);
 1049     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_nbWorkers, prefs->nbWorkers) );
 1050     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_jobSize, prefs->blockSize) );
 1051     if (prefs->overlapLog != FIO_OVERLAP_LOG_NOTSET) {
 1052         DISPLAYLEVEL(3,"set overlapLog = %u \n", prefs->overlapLog);
 1053         CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_overlapLog, prefs->overlapLog) );
 1054     }
 1055     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_rsyncable, prefs->rsyncable) );
 1056 #endif
 1057     /* dictionary */
 1058     if (prefs->patchFromMode) {
 1059         CHECK( ZSTD_CCtx_refPrefix(ress.cctx, ress.dictBuffer, ress.dictBufferSize) );
 1060     } else {
 1061         CHECK( ZSTD_CCtx_loadDictionary(ress.cctx, ress.dictBuffer, ress.dictBufferSize) );
 1062     }
 1063 
 1064     return ress;
 1065 }
 1066 
 1067 static void FIO_freeCResources(const cRess_t* const ress)
 1068 {
 1069     free(ress->srcBuffer);
 1070     free(ress->dstBuffer);
 1071     free(ress->dictBuffer);
 1072     ZSTD_freeCStream(ress->cctx);   /* never fails */
 1073 }
 1074 
 1075 
 1076 #ifdef ZSTD_GZCOMPRESS
 1077 static unsigned long long
 1078 FIO_compressGzFrame(const cRess_t* ress,  /* buffers & handlers are used, but not changed */
 1079                     const char* srcFileName, U64 const srcFileSize,
 1080                     int compressionLevel, U64* readsize)
 1081 {
 1082     unsigned long long inFileSize = 0, outFileSize = 0;
 1083     z_stream strm;
 1084 
 1085     if (compressionLevel > Z_BEST_COMPRESSION)
 1086         compressionLevel = Z_BEST_COMPRESSION;
 1087 
 1088     strm.zalloc = Z_NULL;
 1089     strm.zfree = Z_NULL;
 1090     strm.opaque = Z_NULL;
 1091 
 1092     {   int const ret = deflateInit2(&strm, compressionLevel, Z_DEFLATED,
 1093                         15 /* maxWindowLogSize */ + 16 /* gzip only */,
 1094                         8, Z_DEFAULT_STRATEGY); /* see http://www.zlib.net/manual.html */
 1095         if (ret != Z_OK) {
 1096             EXM_THROW(71, "zstd: %s: deflateInit2 error %d \n", srcFileName, ret);
 1097     }   }
 1098 
 1099     strm.next_in = 0;
 1100     strm.avail_in = 0;
 1101     strm.next_out = (Bytef*)ress->dstBuffer;
 1102     strm.avail_out = (uInt)ress->dstBufferSize;
 1103 
 1104     while (1) {
 1105         int ret;
 1106         if (strm.avail_in == 0) {
 1107             size_t const inSize = fread(ress->srcBuffer, 1, ress->srcBufferSize, ress->srcFile);
 1108             if (inSize == 0) break;
 1109             inFileSize += inSize;
 1110             strm.next_in = (z_const unsigned char*)ress->srcBuffer;
 1111             strm.avail_in = (uInt)inSize;
 1112         }
 1113         ret = deflate(&strm, Z_NO_FLUSH);
 1114         if (ret != Z_OK)
 1115             EXM_THROW(72, "zstd: %s: deflate error %d \n", srcFileName, ret);
 1116         {   size_t const cSize = ress->dstBufferSize - strm.avail_out;
 1117             if (cSize) {
 1118                 if (fwrite(ress->dstBuffer, 1, cSize, ress->dstFile) != cSize)
 1119                     EXM_THROW(73, "Write error : cannot write to output file : %s ", strerror(errno));
 1120                 outFileSize += cSize;
 1121                 strm.next_out = (Bytef*)ress->dstBuffer;
 1122                 strm.avail_out = (uInt)ress->dstBufferSize;
 1123         }   }
 1124         if (srcFileSize == UTIL_FILESIZE_UNKNOWN) {
 1125             DISPLAYUPDATE(2, "\rRead : %u MB ==> %.2f%% ",
 1126                             (unsigned)(inFileSize>>20),
 1127                             (double)outFileSize/inFileSize*100)
 1128         } else {
 1129             DISPLAYUPDATE(2, "\rRead : %u / %u MB ==> %.2f%% ",
 1130                             (unsigned)(inFileSize>>20), (unsigned)(srcFileSize>>20),
 1131                             (double)outFileSize/inFileSize*100);
 1132     }   }
 1133 
 1134     while (1) {
 1135         int const ret = deflate(&strm, Z_FINISH);
 1136         {   size_t const cSize = ress->dstBufferSize - strm.avail_out;
 1137             if (cSize) {
 1138                 if (fwrite(ress->dstBuffer, 1, cSize, ress->dstFile) != cSize)
 1139                     EXM_THROW(75, "Write error : %s ", strerror(errno));
 1140                 outFileSize += cSize;
 1141                 strm.next_out = (Bytef*)ress->dstBuffer;
 1142                 strm.avail_out = (uInt)ress->dstBufferSize;
 1143         }   }
 1144         if (ret == Z_STREAM_END) break;
 1145         if (ret != Z_BUF_ERROR)
 1146             EXM_THROW(77, "zstd: %s: deflate error %d \n", srcFileName, ret);
 1147     }
 1148 
 1149     {   int const ret = deflateEnd(&strm);
 1150         if (ret != Z_OK) {
 1151             EXM_THROW(79, "zstd: %s: deflateEnd error %d \n", srcFileName, ret);
 1152     }   }
 1153     *readsize = inFileSize;
 1154     return outFileSize;
 1155 }
 1156 #endif
 1157 
 1158 
 1159 #ifdef ZSTD_LZMACOMPRESS
 1160 static unsigned long long
 1161 FIO_compressLzmaFrame(cRess_t* ress,
 1162                       const char* srcFileName, U64 const srcFileSize,
 1163                       int compressionLevel, U64* readsize, int plain_lzma)
 1164 {
 1165     unsigned long long inFileSize = 0, outFileSize = 0;
 1166     lzma_stream strm = LZMA_STREAM_INIT;
 1167     lzma_action action = LZMA_RUN;
 1168     lzma_ret ret;
 1169 
 1170     if (compressionLevel < 0) compressionLevel = 0;
 1171     if (compressionLevel > 9) compressionLevel = 9;
 1172 
 1173     if (plain_lzma) {
 1174         lzma_options_lzma opt_lzma;
 1175         if (lzma_lzma_preset(&opt_lzma, compressionLevel))
 1176             EXM_THROW(81, "zstd: %s: lzma_lzma_preset error", srcFileName);
 1177         ret = lzma_alone_encoder(&strm, &opt_lzma); /* LZMA */
 1178         if (ret != LZMA_OK)
 1179             EXM_THROW(82, "zstd: %s: lzma_alone_encoder error %d", srcFileName, ret);
 1180     } else {
 1181         ret = lzma_easy_encoder(&strm, compressionLevel, LZMA_CHECK_CRC64); /* XZ */
 1182         if (ret != LZMA_OK)
 1183             EXM_THROW(83, "zstd: %s: lzma_easy_encoder error %d", srcFileName, ret);
 1184     }
 1185 
 1186     strm.next_in = 0;
 1187     strm.avail_in = 0;
 1188     strm.next_out = (BYTE*)ress->dstBuffer;
 1189     strm.avail_out = ress->dstBufferSize;
 1190 
 1191     while (1) {
 1192         if (strm.avail_in == 0) {
 1193             size_t const inSize = fread(ress->srcBuffer, 1, ress->srcBufferSize, ress->srcFile);
 1194             if (inSize == 0) action = LZMA_FINISH;
 1195             inFileSize += inSize;
 1196             strm.next_in = (BYTE const*)ress->srcBuffer;
 1197             strm.avail_in = inSize;
 1198         }
 1199 
 1200         ret = lzma_code(&strm, action);
 1201 
 1202         if (ret != LZMA_OK && ret != LZMA_STREAM_END)
 1203             EXM_THROW(84, "zstd: %s: lzma_code encoding error %d", srcFileName, ret);
 1204         {   size_t const compBytes = ress->dstBufferSize - strm.avail_out;
 1205             if (compBytes) {
 1206                 if (fwrite(ress->dstBuffer, 1, compBytes, ress->dstFile) != compBytes)
 1207                     EXM_THROW(85, "Write error : %s", strerror(errno));
 1208                 outFileSize += compBytes;
 1209                 strm.next_out = (BYTE*)ress->dstBuffer;
 1210                 strm.avail_out = ress->dstBufferSize;
 1211         }   }
 1212         if (srcFileSize == UTIL_FILESIZE_UNKNOWN)
 1213             DISPLAYUPDATE(2, "\rRead : %u MB ==> %.2f%%",
 1214                             (unsigned)(inFileSize>>20),
 1215                             (double)outFileSize/inFileSize*100)
 1216         else
 1217             DISPLAYUPDATE(2, "\rRead : %u / %u MB ==> %.2f%%",
 1218                             (unsigned)(inFileSize>>20), (unsigned)(srcFileSize>>20),
 1219                             (double)outFileSize/inFileSize*100);
 1220         if (ret == LZMA_STREAM_END) break;
 1221     }
 1222 
 1223     lzma_end(&strm);
 1224     *readsize = inFileSize;
 1225 
 1226     return outFileSize;
 1227 }
 1228 #endif
 1229 
 1230 #ifdef ZSTD_LZ4COMPRESS
 1231 
 1232 #if LZ4_VERSION_NUMBER <= 10600
 1233 #define LZ4F_blockLinked blockLinked
 1234 #define LZ4F_max64KB max64KB
 1235 #endif
 1236 
 1237 static int FIO_LZ4_GetBlockSize_FromBlockId (int id) { return (1 << (8 + (2 * id))); }
 1238 
 1239 static unsigned long long
 1240 FIO_compressLz4Frame(cRess_t* ress,
 1241                      const char* srcFileName, U64 const srcFileSize,
 1242                      int compressionLevel, int checksumFlag,
 1243                      U64* readsize)
 1244 {
 1245     const size_t blockSize = FIO_LZ4_GetBlockSize_FromBlockId(LZ4F_max64KB);
 1246     unsigned long long inFileSize = 0, outFileSize = 0;
 1247 
 1248     LZ4F_preferences_t prefs;
 1249     LZ4F_compressionContext_t ctx;
 1250 
 1251     LZ4F_errorCode_t const errorCode = LZ4F_createCompressionContext(&ctx, LZ4F_VERSION);
 1252     if (LZ4F_isError(errorCode))
 1253         EXM_THROW(31, "zstd: failed to create lz4 compression context");
 1254 
 1255     memset(&prefs, 0, sizeof(prefs));
 1256 
 1257     assert(blockSize <= ress->srcBufferSize);
 1258 
 1259     prefs.autoFlush = 1;
 1260     prefs.compressionLevel = compressionLevel;
 1261     prefs.frameInfo.blockMode = LZ4F_blockLinked;
 1262     prefs.frameInfo.blockSizeID = LZ4F_max64KB;
 1263     prefs.frameInfo.contentChecksumFlag = (contentChecksum_t)checksumFlag;
 1264 #if LZ4_VERSION_NUMBER >= 10600
 1265     prefs.frameInfo.contentSize = (srcFileSize==UTIL_FILESIZE_UNKNOWN) ? 0 : srcFileSize;
 1266 #endif
 1267     assert(LZ4F_compressBound(blockSize, &prefs) <= ress->dstBufferSize);
 1268 
 1269     {
 1270         size_t readSize;
 1271         size_t headerSize = LZ4F_compressBegin(ctx, ress->dstBuffer, ress->dstBufferSize, &prefs);
 1272         if (LZ4F_isError(headerSize))
 1273             EXM_THROW(33, "File header generation failed : %s",
 1274                             LZ4F_getErrorName(headerSize));
 1275         if (fwrite(ress->dstBuffer, 1, headerSize, ress->dstFile) != headerSize)
 1276             EXM_THROW(34, "Write error : %s (cannot write header)", strerror(errno));
 1277         outFileSize += headerSize;
 1278 
 1279         /* Read first block */
 1280         readSize  = fread(ress->srcBuffer, (size_t)1, (size_t)blockSize, ress->srcFile);
 1281         inFileSize += readSize;
 1282 
 1283         /* Main Loop */
 1284         while (readSize>0) {
 1285             size_t const outSize = LZ4F_compressUpdate(ctx,
 1286                                         ress->dstBuffer, ress->dstBufferSize,
 1287                                         ress->srcBuffer, readSize, NULL);
 1288             if (LZ4F_isError(outSize))
 1289                 EXM_THROW(35, "zstd: %s: lz4 compression failed : %s",
 1290                             srcFileName, LZ4F_getErrorName(outSize));
 1291             outFileSize += outSize;
 1292             if (srcFileSize == UTIL_FILESIZE_UNKNOWN) {
 1293                 DISPLAYUPDATE(2, "\rRead : %u MB ==> %.2f%%",
 1294                                 (unsigned)(inFileSize>>20),
 1295                                 (double)outFileSize/inFileSize*100)
 1296             } else {
 1297                 DISPLAYUPDATE(2, "\rRead : %u / %u MB ==> %.2f%%",
 1298                                 (unsigned)(inFileSize>>20), (unsigned)(srcFileSize>>20),
 1299                                 (double)outFileSize/inFileSize*100);
 1300             }
 1301 
 1302             /* Write Block */
 1303             {   size_t const sizeCheck = fwrite(ress->dstBuffer, 1, outSize, ress->dstFile);
 1304                 if (sizeCheck != outSize)
 1305                     EXM_THROW(36, "Write error : %s", strerror(errno));
 1306             }
 1307 
 1308             /* Read next block */
 1309             readSize  = fread(ress->srcBuffer, (size_t)1, (size_t)blockSize, ress->srcFile);
 1310             inFileSize += readSize;
 1311         }
 1312         if (ferror(ress->srcFile)) EXM_THROW(37, "Error reading %s ", srcFileName);
 1313 
 1314         /* End of Stream mark */
 1315         headerSize = LZ4F_compressEnd(ctx, ress->dstBuffer, ress->dstBufferSize, NULL);
 1316         if (LZ4F_isError(headerSize))
 1317             EXM_THROW(38, "zstd: %s: lz4 end of file generation failed : %s",
 1318                         srcFileName, LZ4F_getErrorName(headerSize));
 1319 
 1320         {   size_t const sizeCheck = fwrite(ress->dstBuffer, 1, headerSize, ress->dstFile);
 1321             if (sizeCheck != headerSize)
 1322                 EXM_THROW(39, "Write error : %s (cannot write end of stream)",
 1323                             strerror(errno));
 1324         }
 1325         outFileSize += headerSize;
 1326     }
 1327 
 1328     *readsize = inFileSize;
 1329     LZ4F_freeCompressionContext(ctx);
 1330 
 1331     return outFileSize;
 1332 }
 1333 #endif
 1334 
 1335 
 1336 static unsigned long long
 1337 FIO_compressZstdFrame(FIO_ctx_t* const fCtx,
 1338                       FIO_prefs_t* const prefs,
 1339                       const cRess_t* ressPtr,
 1340                       const char* srcFileName, U64 fileSize,
 1341                       int compressionLevel, U64* readsize)
 1342 {
 1343     cRess_t const ress = *ressPtr;
 1344     FILE* const srcFile = ress.srcFile;
 1345     FILE* const dstFile = ress.dstFile;
 1346     U64 compressedfilesize = 0;
 1347     ZSTD_EndDirective directive = ZSTD_e_continue;
 1348     U64 pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN;
 1349 
 1350     /* stats */
 1351     ZSTD_frameProgression previous_zfp_update = { 0, 0, 0, 0, 0, 0 };
 1352     ZSTD_frameProgression previous_zfp_correction = { 0, 0, 0, 0, 0, 0 };
 1353     typedef enum { noChange, slower, faster } speedChange_e;
 1354     speedChange_e speedChange = noChange;
 1355     unsigned flushWaiting = 0;
 1356     unsigned inputPresented = 0;
 1357     unsigned inputBlocked = 0;
 1358     unsigned lastJobID = 0;
 1359     UTIL_HumanReadableSize_t const file_hrs = UTIL_makeHumanReadableSize(fileSize);
 1360 
 1361     DISPLAYLEVEL(6, "compression using zstd format \n");
 1362 
 1363     /* init */
 1364     if (fileSize != UTIL_FILESIZE_UNKNOWN) {
 1365         pledgedSrcSize = fileSize;
 1366         CHECK(ZSTD_CCtx_setPledgedSrcSize(ress.cctx, fileSize));
 1367     } else if (prefs->streamSrcSize > 0) {
 1368       /* unknown source size; use the declared stream size */
 1369       pledgedSrcSize = prefs->streamSrcSize;
 1370       CHECK( ZSTD_CCtx_setPledgedSrcSize(ress.cctx, prefs->streamSrcSize) );
 1371     }
 1372 
 1373     {
 1374         int windowLog;
 1375         UTIL_HumanReadableSize_t windowSize;
 1376         CHECK(ZSTD_CCtx_getParameter(ress.cctx, ZSTD_c_windowLog, &windowLog));
 1377         if (windowLog == 0) {
 1378             const ZSTD_compressionParameters cParams = ZSTD_getCParams(compressionLevel, fileSize, 0);
 1379             windowLog = cParams.windowLog;
 1380         }
 1381         windowSize = UTIL_makeHumanReadableSize(MAX(1ULL, MIN(1ULL << windowLog, pledgedSrcSize)));
 1382         DISPLAYLEVEL(4, "Decompression will require %.*f%s of memory\n", windowSize.precision, windowSize.value, windowSize.suffix);
 1383     }
 1384     (void)srcFileName;
 1385 
 1386     /* Main compression loop */
 1387     do {
 1388         size_t stillToFlush;
 1389         /* Fill input Buffer */
 1390         size_t const inSize = fread(ress.srcBuffer, (size_t)1, ress.srcBufferSize, srcFile);
 1391         ZSTD_inBuffer inBuff = { ress.srcBuffer, inSize, 0 };
 1392         DISPLAYLEVEL(6, "fread %u bytes from source \n", (unsigned)inSize);
 1393         *readsize += inSize;
 1394 
 1395         if ((inSize == 0) || (*readsize == fileSize))
 1396             directive = ZSTD_e_end;
 1397 
 1398         stillToFlush = 1;
 1399         while ((inBuff.pos != inBuff.size)   /* input buffer must be entirely ingested */
 1400             || (directive == ZSTD_e_end && stillToFlush != 0) ) {
 1401 
 1402             size_t const oldIPos = inBuff.pos;
 1403             ZSTD_outBuffer outBuff = { ress.dstBuffer, ress.dstBufferSize, 0 };
 1404             size_t const toFlushNow = ZSTD_toFlushNow(ress.cctx);
 1405             CHECK_V(stillToFlush, ZSTD_compressStream2(ress.cctx, &outBuff, &inBuff, directive));
 1406 
 1407             /* count stats */
 1408             inputPresented++;
 1409             if (oldIPos == inBuff.pos) inputBlocked++;  /* input buffer is full and can't take any more : input speed is faster than consumption rate */
 1410             if (!toFlushNow) flushWaiting = 1;
 1411 
 1412             /* Write compressed stream */
 1413             DISPLAYLEVEL(6, "ZSTD_compress_generic(end:%u) => input pos(%u)<=(%u)size ; output generated %u bytes \n",
 1414                             (unsigned)directive, (unsigned)inBuff.pos, (unsigned)inBuff.size, (unsigned)outBuff.pos);
 1415             if (outBuff.pos) {
 1416                 size_t const sizeCheck = fwrite(ress.dstBuffer, 1, outBuff.pos, dstFile);
 1417                 if (sizeCheck != outBuff.pos)
 1418                     EXM_THROW(25, "Write error : %s (cannot write compressed block)",
 1419                                     strerror(errno));
 1420                 compressedfilesize += outBuff.pos;
 1421             }
 1422 
 1423             /* display notification; and adapt compression level */
 1424             if (READY_FOR_UPDATE()) {
 1425                 ZSTD_frameProgression const zfp = ZSTD_getFrameProgression(ress.cctx);
 1426                 double const cShare = (double)zfp.produced / (double)(zfp.consumed + !zfp.consumed/*avoid div0*/) * 100;
 1427                 UTIL_HumanReadableSize_t const buffered_hrs = UTIL_makeHumanReadableSize(zfp.ingested - zfp.consumed);
 1428                 UTIL_HumanReadableSize_t const consumed_hrs = UTIL_makeHumanReadableSize(zfp.consumed);
 1429                 UTIL_HumanReadableSize_t const produced_hrs = UTIL_makeHumanReadableSize(zfp.produced);
 1430 
 1431                 /* display progress notifications */
 1432                 if (g_display_prefs.displayLevel >= 3) {
 1433                     DISPLAYUPDATE(3, "\r(L%i) Buffered :%6.*f%4s - Consumed :%6.*f%4s - Compressed :%6.*f%4s => %.2f%% ",
 1434                                 compressionLevel,
 1435                                 buffered_hrs.precision, buffered_hrs.value, buffered_hrs.suffix,
 1436                                 consumed_hrs.precision, consumed_hrs.value, consumed_hrs.suffix,
 1437                                 produced_hrs.precision, produced_hrs.value, produced_hrs.suffix,
 1438                                 cShare );
 1439                 } else if (g_display_prefs.displayLevel >= 2 || g_display_prefs.progressSetting == FIO_ps_always) {
 1440                     /* Require level 2 or forcibly displayed progress counter for summarized updates */
 1441                     DISPLAYLEVEL(1, "\r%79s\r", "");    /* Clear out the current displayed line */
 1442                     if (fCtx->nbFilesTotal > 1) {
 1443                         size_t srcFileNameSize = strlen(srcFileName);
 1444                         /* Ensure that the string we print is roughly the same size each time */
 1445                         if (srcFileNameSize > 18) {
 1446                             const char* truncatedSrcFileName = srcFileName + srcFileNameSize - 15;
 1447                             DISPLAYLEVEL(1, "Compress: %u/%u files. Current: ...%s ",
 1448                                         fCtx->currFileIdx+1, fCtx->nbFilesTotal, truncatedSrcFileName);
 1449                         } else {
 1450                             DISPLAYLEVEL(1, "Compress: %u/%u files. Current: %*s ",
 1451                                         fCtx->currFileIdx+1, fCtx->nbFilesTotal, (int)(18-srcFileNameSize), srcFileName);
 1452                         }
 1453                     }
 1454                     DISPLAYLEVEL(1, "Read:%6.*f%4s ", consumed_hrs.precision, consumed_hrs.value, consumed_hrs.suffix);
 1455                     if (fileSize != UTIL_FILESIZE_UNKNOWN)
 1456                         DISPLAYLEVEL(2, "/%6.*f%4s", file_hrs.precision, file_hrs.value, file_hrs.suffix);
 1457                     DISPLAYLEVEL(1, " ==> %2.f%%", cShare);
 1458                     DELAY_NEXT_UPDATE();
 1459                 }
 1460 
 1461                 /* adaptive mode : statistics measurement and speed correction */
 1462                 if (prefs->adaptiveMode) {
 1463 
 1464                     /* check output speed */
 1465                     if (zfp.currentJobID > 1) {  /* only possible if nbWorkers >= 1 */
 1466 
 1467                         unsigned long long newlyProduced = zfp.produced - previous_zfp_update.produced;
 1468                         unsigned long long newlyFlushed = zfp.flushed - previous_zfp_update.flushed;
 1469                         assert(zfp.produced >= previous_zfp_update.produced);
 1470                         assert(prefs->nbWorkers >= 1);
 1471 
 1472                         /* test if compression is blocked
 1473                          * either because output is slow and all buffers are full
 1474                          * or because input is slow and no job can start while waiting for at least one buffer to be filled.
 1475                          * note : exclude starting part, since currentJobID > 1 */
 1476                         if ( (zfp.consumed == previous_zfp_update.consumed)   /* no data compressed : no data available, or no more buffer to compress to, OR compression is really slow (compression of a single block is slower than update rate)*/
 1477                           && (zfp.nbActiveWorkers == 0)                       /* confirmed : no compression ongoing */
 1478                           ) {
 1479                             DISPLAYLEVEL(6, "all buffers full : compression stopped => slow down \n")
 1480                             speedChange = slower;
 1481                         }
 1482 
 1483                         previous_zfp_update = zfp;
 1484 
 1485                         if ( (newlyProduced > (newlyFlushed * 9 / 8))   /* compression produces more data than output can flush (though production can be spiky, due to work unit : (N==4)*block sizes) */
 1486                           && (flushWaiting == 0)                        /* flush speed was never slowed by lack of production, so it's operating at max capacity */
 1487                           ) {
 1488                             DISPLAYLEVEL(6, "compression faster than flush (%llu > %llu), and flushed was never slowed down by lack of production => slow down \n", newlyProduced, newlyFlushed);
 1489                             speedChange = slower;
 1490                         }
 1491                         flushWaiting = 0;
 1492                     }
 1493 
 1494                     /* course correct only if there is at least one new job completed */
 1495                     if (zfp.currentJobID > lastJobID) {
 1496                         DISPLAYLEVEL(6, "compression level adaptation check \n")
 1497 
 1498                         /* check input speed */
 1499                         if (zfp.currentJobID > (unsigned)(prefs->nbWorkers+1)) {   /* warm up period, to fill all workers */
 1500                             if (inputBlocked <= 0) {
 1501                                 DISPLAYLEVEL(6, "input is never blocked => input is slower than ingestion \n");
 1502                                 speedChange = slower;
 1503                             } else if (speedChange == noChange) {
 1504                                 unsigned long long newlyIngested = zfp.ingested - previous_zfp_correction.ingested;
 1505                                 unsigned long long newlyConsumed = zfp.consumed - previous_zfp_correction.consumed;
 1506                                 unsigned long long newlyProduced = zfp.produced - previous_zfp_correction.produced;
 1507                                 unsigned long long newlyFlushed  = zfp.flushed  - previous_zfp_correction.flushed;
 1508                                 previous_zfp_correction = zfp;
 1509                                 assert(inputPresented > 0);
 1510                                 DISPLAYLEVEL(6, "input blocked %u/%u(%.2f) - ingested:%u vs %u:consumed - flushed:%u vs %u:produced \n",
 1511                                                 inputBlocked, inputPresented, (double)inputBlocked/inputPresented*100,
 1512                                                 (unsigned)newlyIngested, (unsigned)newlyConsumed,
 1513                                                 (unsigned)newlyFlushed, (unsigned)newlyProduced);
 1514                                 if ( (inputBlocked > inputPresented / 8)     /* input is waiting often, because input buffers is full : compression or output too slow */
 1515                                   && (newlyFlushed * 33 / 32 > newlyProduced)  /* flush everything that is produced */
 1516                                   && (newlyIngested * 33 / 32 > newlyConsumed) /* input speed as fast or faster than compression speed */
 1517                                 ) {
 1518                                     DISPLAYLEVEL(6, "recommend faster as in(%llu) >= (%llu)comp(%llu) <= out(%llu) \n",
 1519                                                     newlyIngested, newlyConsumed, newlyProduced, newlyFlushed);
 1520                                     speedChange = faster;
 1521                                 }
 1522                             }
 1523                             inputBlocked = 0;
 1524                             inputPresented = 0;
 1525                         }
 1526 
 1527                         if (speedChange == slower) {
 1528                             DISPLAYLEVEL(6, "slower speed , higher compression \n")
 1529                             compressionLevel ++;
 1530                             if (compressionLevel > ZSTD_maxCLevel()) compressionLevel = ZSTD_maxCLevel();
 1531                             if (compressionLevel > prefs->maxAdaptLevel) compressionLevel = prefs->maxAdaptLevel;
 1532                             compressionLevel += (compressionLevel == 0);   /* skip 0 */
 1533                             ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_compressionLevel, compressionLevel);
 1534                         }
 1535                         if (speedChange == faster) {
 1536                             DISPLAYLEVEL(6, "faster speed , lighter compression \n")
 1537                             compressionLevel --;
 1538                             if (compressionLevel < prefs->minAdaptLevel) compressionLevel = prefs->minAdaptLevel;
 1539                             compressionLevel -= (compressionLevel == 0);   /* skip 0 */
 1540                             ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_compressionLevel, compressionLevel);
 1541                         }
 1542                         speedChange = noChange;
 1543 
 1544                         lastJobID = zfp.currentJobID;
 1545                     }  /* if (zfp.currentJobID > lastJobID) */
 1546                 }  /* if (g_adaptiveMode) */
 1547             }  /* if (READY_FOR_UPDATE()) */
 1548         }  /* while ((inBuff.pos != inBuff.size) */
 1549     } while (directive != ZSTD_e_end);
 1550 
 1551     if (ferror(srcFile)) {
 1552         EXM_THROW(26, "Read error : I/O error");
 1553     }
 1554     if (fileSize != UTIL_FILESIZE_UNKNOWN && *readsize != fileSize) {
 1555         EXM_THROW(27, "Read error : Incomplete read : %llu / %llu B",
 1556                 (unsigned long long)*readsize, (unsigned long long)fileSize);
 1557     }
 1558 
 1559     return compressedfilesize;
 1560 }
 1561 
 1562 /*! FIO_compressFilename_internal() :
 1563  *  same as FIO_compressFilename_extRess(), with `ress.desFile` already opened.
 1564  *  @return : 0 : compression completed correctly,
 1565  *            1 : missing or pb opening srcFileName
 1566  */
 1567 static int
 1568 FIO_compressFilename_internal(FIO_ctx_t* const fCtx,
 1569                               FIO_prefs_t* const prefs,
 1570                               cRess_t ress,
 1571                               const char* dstFileName, const char* srcFileName,
 1572                               int compressionLevel)
 1573 {
 1574     UTIL_time_t const timeStart = UTIL_getTime();
 1575     clock_t const cpuStart = clock();
 1576     U64 readsize = 0;
 1577     U64 compressedfilesize = 0;
 1578     U64 const fileSize = UTIL_getFileSize(srcFileName);
 1579     DISPLAYLEVEL(5, "%s: %llu bytes \n", srcFileName, (unsigned long long)fileSize);
 1580 
 1581     /* compression format selection */
 1582     switch (prefs->compressionType) {
 1583         default:
 1584         case FIO_zstdCompression:
 1585             compressedfilesize = FIO_compressZstdFrame(fCtx, prefs, &ress, srcFileName, fileSize, compressionLevel, &readsize);
 1586             break;
 1587 
 1588         case FIO_gzipCompression:
 1589 #ifdef ZSTD_GZCOMPRESS
 1590             compressedfilesize = FIO_compressGzFrame(&ress, srcFileName, fileSize, compressionLevel, &readsize);
 1591 #else
 1592             (void)compressionLevel;
 1593             EXM_THROW(20, "zstd: %s: file cannot be compressed as gzip (zstd compiled without ZSTD_GZCOMPRESS) -- ignored \n",
 1594                             srcFileName);
 1595 #endif
 1596             break;
 1597 
 1598         case FIO_xzCompression:
 1599         case FIO_lzmaCompression:
 1600 #ifdef ZSTD_LZMACOMPRESS
 1601             compressedfilesize = FIO_compressLzmaFrame(&ress, srcFileName, fileSize, compressionLevel, &readsize, prefs->compressionType==FIO_lzmaCompression);
 1602 #else
 1603             (void)compressionLevel;
 1604             EXM_THROW(20, "zstd: %s: file cannot be compressed as xz/lzma (zstd compiled without ZSTD_LZMACOMPRESS) -- ignored \n",
 1605                             srcFileName);
 1606 #endif
 1607             break;
 1608 
 1609         case FIO_lz4Compression:
 1610 #ifdef ZSTD_LZ4COMPRESS
 1611             compressedfilesize = FIO_compressLz4Frame(&ress, srcFileName, fileSize, compressionLevel, prefs->checksumFlag, &readsize);
 1612 #else
 1613             (void)compressionLevel;
 1614             EXM_THROW(20, "zstd: %s: file cannot be compressed as lz4 (zstd compiled without ZSTD_LZ4COMPRESS) -- ignored \n",
 1615                             srcFileName);
 1616 #endif
 1617             break;
 1618     }
 1619 
 1620     /* Status */
 1621     fCtx->totalBytesInput += (size_t)readsize;
 1622     fCtx->totalBytesOutput += (size_t)compressedfilesize;
 1623     DISPLAYLEVEL(2, "\r%79s\r", "");
 1624     if (g_display_prefs.displayLevel >= 2 &&
 1625         !fCtx->hasStdoutOutput &&
 1626         (g_display_prefs.displayLevel >= 3 || fCtx->nbFilesTotal <= 1)) {
 1627         UTIL_HumanReadableSize_t hr_isize = UTIL_makeHumanReadableSize((U64) readsize);
 1628         UTIL_HumanReadableSize_t hr_osize = UTIL_makeHumanReadableSize((U64) compressedfilesize);
 1629         if (readsize == 0) {
 1630             DISPLAYLEVEL(2,"%-20s :  (%6.*f%4s => %6.*f%4s, %s) \n",
 1631                 srcFileName,
 1632                 hr_isize.precision, hr_isize.value, hr_isize.suffix,
 1633                 hr_osize.precision, hr_osize.value, hr_osize.suffix,
 1634                 dstFileName);
 1635         } else {
 1636             DISPLAYLEVEL(2,"%-20s :%6.2f%%   (%6.*f%4s => %6.*f%4s, %s) \n",
 1637                 srcFileName,
 1638                 (double)compressedfilesize / (double)readsize * 100,
 1639                 hr_isize.precision, hr_isize.value, hr_isize.suffix,
 1640                 hr_osize.precision, hr_osize.value, hr_osize.suffix,
 1641                 dstFileName);
 1642         }
 1643     }
 1644 
 1645     /* Elapsed Time and CPU Load */
 1646     {   clock_t const cpuEnd = clock();
 1647         double const cpuLoad_s = (double)(cpuEnd - cpuStart) / CLOCKS_PER_SEC;
 1648         U64 const timeLength_ns = UTIL_clockSpanNano(timeStart);
 1649         double const timeLength_s = (double)timeLength_ns / 1000000000;
 1650         double const cpuLoad_pct = (cpuLoad_s / timeLength_s) * 100;
 1651         DISPLAYLEVEL(4, "%-20s : Completed in %.2f sec  (cpu load : %.0f%%)\n",
 1652                         srcFileName, timeLength_s, cpuLoad_pct);
 1653     }
 1654     return 0;
 1655 }
 1656 
 1657 
 1658 /*! FIO_compressFilename_dstFile() :
 1659  *  open dstFileName, or pass-through if ress.dstFile != NULL,
 1660  *  then start compression with FIO_compressFilename_internal().
 1661  *  Manages source removal (--rm) and file permissions transfer.
 1662  *  note : ress.srcFile must be != NULL,
 1663  *  so reach this function through FIO_compressFilename_srcFile().
 1664  *  @return : 0 : compression completed correctly,
 1665  *            1 : pb
 1666  */
 1667 static int FIO_compressFilename_dstFile(FIO_ctx_t* const fCtx,
 1668                                         FIO_prefs_t* const prefs,
 1669                                         cRess_t ress,
 1670                                         const char* dstFileName,
 1671                                         const char* srcFileName,
 1672                                         int compressionLevel)
 1673 {
 1674     int closeDstFile = 0;
 1675     int result;
 1676     stat_t statbuf;
 1677     int transferMTime = 0;
 1678     assert(ress.srcFile != NULL);
 1679     if (ress.dstFile == NULL) {
 1680         int dstFilePermissions = DEFAULT_FILE_PERMISSIONS;
 1681         if ( strcmp (srcFileName, stdinmark)
 1682           && strcmp (dstFileName, stdoutmark)
 1683           && UTIL_stat(srcFileName, &statbuf)
 1684           && UTIL_isRegularFileStat(&statbuf) ) {
 1685             dstFilePermissions = statbuf.st_mode;
 1686             transferMTime = 1;
 1687         }
 1688 
 1689         closeDstFile = 1;
 1690         DISPLAYLEVEL(6, "FIO_compressFilename_dstFile: opening dst: %s \n", dstFileName);
 1691         ress.dstFile = FIO_openDstFile(fCtx, prefs, srcFileName, dstFileName, dstFilePermissions);
 1692         if (ress.dstFile==NULL) return 1;  /* could not open dstFileName */
 1693         /* Must only be added after FIO_openDstFile() succeeds.
 1694          * Otherwise we may delete the destination file if it already exists,
 1695          * and the user presses Ctrl-C when asked if they wish to overwrite.
 1696          */
 1697         addHandler(dstFileName);
 1698     }
 1699 
 1700     result = FIO_compressFilename_internal(fCtx, prefs, ress, dstFileName, srcFileName, compressionLevel);
 1701 
 1702     if (closeDstFile) {
 1703         FILE* const dstFile = ress.dstFile;
 1704         ress.dstFile = NULL;
 1705 
 1706         clearHandler();
 1707 
 1708         DISPLAYLEVEL(6, "FIO_compressFilename_dstFile: closing dst: %s \n", dstFileName);
 1709         if (fclose(dstFile)) { /* error closing dstFile */
 1710             DISPLAYLEVEL(1, "zstd: %s: %s \n", dstFileName, strerror(errno));
 1711             result=1;
 1712         }
 1713         if (transferMTime) {
 1714             UTIL_utime(dstFileName, &statbuf);
 1715         }
 1716         if ( (result != 0)  /* operation failure */
 1717           && strcmp(dstFileName, stdoutmark)  /* special case : don't remove() stdout */
 1718           ) {
 1719             FIO_removeFile(dstFileName); /* remove compression artefact; note don't do anything special if remove() fails */
 1720         }
 1721     }
 1722 
 1723     return result;
 1724 }
 1725 
 1726 /* List used to compare file extensions (used with --exclude-compressed flag)
 1727 * Different from the suffixList and should only apply to ZSTD compress operationResult
 1728 */
 1729 static const char *compressedFileExtensions[] = {
 1730     ZSTD_EXTENSION,
 1731     TZSTD_EXTENSION,
 1732     GZ_EXTENSION,
 1733     TGZ_EXTENSION,
 1734     LZMA_EXTENSION,
 1735     XZ_EXTENSION,
 1736     TXZ_EXTENSION,
 1737     LZ4_EXTENSION,
 1738     TLZ4_EXTENSION,
 1739     NULL
 1740 };
 1741 
 1742 /*! FIO_compressFilename_srcFile() :
 1743  *  @return : 0 : compression completed correctly,
 1744  *            1 : missing or pb opening srcFileName
 1745  */
 1746 static int
 1747 FIO_compressFilename_srcFile(FIO_ctx_t* const fCtx,
 1748                              FIO_prefs_t* const prefs,
 1749                              cRess_t ress,
 1750                              const char* dstFileName,
 1751                              const char* srcFileName,
 1752                              int compressionLevel)
 1753 {
 1754     int result;
 1755     DISPLAYLEVEL(6, "FIO_compressFilename_srcFile: %s \n", srcFileName);
 1756 
 1757     /* ensure src is not a directory */
 1758     if (UTIL_isDirectory(srcFileName)) {
 1759         DISPLAYLEVEL(1, "zstd: %s is a directory -- ignored \n", srcFileName);
 1760         return 1;
 1761     }
 1762 
 1763     /* ensure src is not the same as dict (if present) */
 1764     if (ress.dictFileName != NULL && UTIL_isSameFile(srcFileName, ress.dictFileName)) {
 1765         DISPLAYLEVEL(1, "zstd: cannot use %s as an input file and dictionary \n", srcFileName);
 1766         return 1;
 1767     }
 1768 
 1769     /* Check if "srcFile" is compressed. Only done if --exclude-compressed flag is used
 1770     * YES => ZSTD will skip compression of the file and will return 0.
 1771     * NO => ZSTD will resume with compress operation.
 1772     */
 1773     if (prefs->excludeCompressedFiles == 1 && UTIL_isCompressedFile(srcFileName, compressedFileExtensions)) {
 1774         DISPLAYLEVEL(4, "File is already compressed : %s \n", srcFileName);
 1775         return 0;
 1776     }
 1777 
 1778     ress.srcFile = FIO_openSrcFile(prefs, srcFileName);
 1779     if (ress.srcFile == NULL) return 1;   /* srcFile could not be opened */
 1780 
 1781     result = FIO_compressFilename_dstFile(fCtx, prefs, ress, dstFileName, srcFileName, compressionLevel);
 1782 
 1783     fclose(ress.srcFile);
 1784     ress.srcFile = NULL;
 1785     if ( prefs->removeSrcFile   /* --rm */
 1786       && result == 0       /* success */
 1787       && strcmp(srcFileName, stdinmark)   /* exception : don't erase stdin */
 1788       ) {
 1789         /* We must clear the handler, since after this point calling it would
 1790          * delete both the source and destination files.
 1791          */
 1792         clearHandler();
 1793         if (FIO_removeFile(srcFileName))
 1794             EXM_THROW(1, "zstd: %s: %s", srcFileName, strerror(errno));
 1795     }
 1796     return result;
 1797 }
 1798 
 1799 static const char* checked_index(const char* options[], size_t length, size_t index) {
 1800     assert(index < length);
 1801     // Necessary to avoid warnings since -O3 will omit the above `assert`
 1802     (void) length;
 1803     return options[index];
 1804 }
 1805 
 1806 #define INDEX(options, index) checked_index((options), sizeof(options)  / sizeof(char*), (index))
 1807 
 1808 void FIO_displayCompressionParameters(const FIO_prefs_t* prefs) {
 1809     static const char* formatOptions[5] = {ZSTD_EXTENSION, GZ_EXTENSION, XZ_EXTENSION,
 1810                                            LZMA_EXTENSION, LZ4_EXTENSION};
 1811     static const char* sparseOptions[3] = {" --no-sparse", "", " --sparse"};
 1812     static const char* checkSumOptions[3] = {" --no-check", "", " --check"};
 1813     static const char* rowMatchFinderOptions[3] = {"", " --no-row-match-finder", " --row-match-finder"};
 1814     static const char* compressLiteralsOptions[3] = {"", " --compress-literals", " --no-compress-literals"};
 1815 
 1816     assert(g_display_prefs.displayLevel >= 4);
 1817 
 1818     DISPLAY("--format=%s", formatOptions[prefs->compressionType]);
 1819     DISPLAY("%s", INDEX(sparseOptions, prefs->sparseFileSupport));
 1820     DISPLAY("%s", prefs->dictIDFlag ? "" : " --no-dictID");
 1821     DISPLAY("%s", INDEX(checkSumOptions, prefs->checksumFlag));
 1822     DISPLAY(" --block-size=%d", prefs->blockSize);
 1823     if (prefs->adaptiveMode)
 1824         DISPLAY(" --adapt=min=%d,max=%d", prefs->minAdaptLevel, prefs->maxAdaptLevel);
 1825     DISPLAY("%s", INDEX(rowMatchFinderOptions, prefs->useRowMatchFinder));
 1826     DISPLAY("%s", prefs->rsyncable ? " --rsyncable" : "");
 1827     if (prefs->streamSrcSize)
 1828         DISPLAY(" --stream-size=%u", (unsigned) prefs->streamSrcSize);
 1829     if (prefs->srcSizeHint)
 1830         DISPLAY(" --size-hint=%d", prefs->srcSizeHint);
 1831     if (prefs->targetCBlockSize)
 1832         DISPLAY(" --target-compressed-block-size=%u", (unsigned) prefs->targetCBlockSize);
 1833     DISPLAY("%s", INDEX(compressLiteralsOptions, prefs->literalCompressionMode));
 1834     DISPLAY(" --memory=%u", prefs->memLimit ? prefs->memLimit : 128 MB);
 1835     DISPLAY(" --threads=%d", prefs->nbWorkers);
 1836     DISPLAY("%s", prefs->excludeCompressedFiles ? " --exclude-compressed" : "");
 1837     DISPLAY(" --%scontent-size", prefs->contentSize ? "" : "no-");
 1838     DISPLAY("\n");
 1839 }
 1840 
 1841 #undef INDEX
 1842 
 1843 int FIO_compressFilename(FIO_ctx_t* const fCtx, FIO_prefs_t* const prefs, const char* dstFileName,
 1844                          const char* srcFileName, const char* dictFileName,
 1845                          int compressionLevel, ZSTD_compressionParameters comprParams)
 1846 {
 1847     cRess_t const ress = FIO_createCResources(prefs, dictFileName, UTIL_getFileSize(srcFileName), compressionLevel, comprParams);
 1848     int const result = FIO_compressFilename_srcFile(fCtx, prefs, ress, dstFileName, srcFileName, compressionLevel);
 1849 
 1850 #define DISPLAY_LEVEL_DEFAULT 2
 1851 
 1852     FIO_freeCResources(&ress);
 1853     return result;
 1854 }
 1855 
 1856 /* FIO_determineCompressedName() :
 1857  * create a destination filename for compressed srcFileName.
 1858  * @return a pointer to it.
 1859  * This function never returns an error (it may abort() in case of pb)
 1860  */
 1861 static const char*
 1862 FIO_determineCompressedName(const char* srcFileName, const char* outDirName, const char* suffix)
 1863 {
 1864     static size_t dfnbCapacity = 0;
 1865     static char* dstFileNameBuffer = NULL;   /* using static allocation : this function cannot be multi-threaded */
 1866     char* outDirFilename = NULL;
 1867     size_t sfnSize = strlen(srcFileName);
 1868     size_t const srcSuffixLen = strlen(suffix);
 1869     if (outDirName) {
 1870         outDirFilename = FIO_createFilename_fromOutDir(srcFileName, outDirName, srcSuffixLen);
 1871         sfnSize = strlen(outDirFilename);
 1872         assert(outDirFilename != NULL);
 1873     }
 1874 
 1875     if (dfnbCapacity <= sfnSize+srcSuffixLen+1) {
 1876         /* resize buffer for dstName */
 1877         free(dstFileNameBuffer);
 1878         dfnbCapacity = sfnSize + srcSuffixLen + 30;
 1879         dstFileNameBuffer = (char*)malloc(dfnbCapacity);
 1880         if (!dstFileNameBuffer) {
 1881             EXM_THROW(30, "zstd: %s", strerror(errno));
 1882         }
 1883     }
 1884     assert(dstFileNameBuffer != NULL);
 1885 
 1886     if (outDirFilename) {
 1887         memcpy(dstFileNameBuffer, outDirFilename, sfnSize);
 1888         free(outDirFilename);
 1889     } else {
 1890         memcpy(dstFileNameBuffer, srcFileName, sfnSize);
 1891     }
 1892     memcpy(dstFileNameBuffer+sfnSize, suffix, srcSuffixLen+1 /* Include terminating null */);
 1893     return dstFileNameBuffer;
 1894 }
 1895 
 1896 static unsigned long long FIO_getLargestFileSize(const char** inFileNames, unsigned nbFiles)
 1897 {
 1898     size_t i;
 1899     unsigned long long fileSize, maxFileSize = 0;
 1900     for (i = 0; i < nbFiles; i++) {
 1901         fileSize = UTIL_getFileSize(inFileNames[i]);
 1902         maxFileSize = fileSize > maxFileSize ? fileSize : maxFileSize;
 1903     }
 1904     return maxFileSize;
 1905 }
 1906 
 1907 /* FIO_compressMultipleFilenames() :
 1908  * compress nbFiles files
 1909  * into either one destination (outFileName),
 1910  * or into one file each (outFileName == NULL, but suffix != NULL),
 1911  * or into a destination folder (specified with -O)
 1912  */
 1913 int FIO_compressMultipleFilenames(FIO_ctx_t* const fCtx,
 1914                                   FIO_prefs_t* const prefs,
 1915                                   const char** inFileNamesTable,
 1916                                   const char* outMirroredRootDirName,
 1917                                   const char* outDirName,
 1918                                   const char* outFileName, const char* suffix,
 1919                                   const char* dictFileName, int compressionLevel,
 1920                                   ZSTD_compressionParameters comprParams)
 1921 {
 1922     int status;
 1923     int error = 0;
 1924     cRess_t ress = FIO_createCResources(prefs, dictFileName,
 1925         FIO_getLargestFileSize(inFileNamesTable, (unsigned)fCtx->nbFilesTotal),
 1926         compressionLevel, comprParams);
 1927 
 1928     /* init */
 1929     assert(outFileName != NULL || suffix != NULL);
 1930     if (outFileName != NULL) {   /* output into a single destination (stdout typically) */
 1931         if (FIO_removeMultiFilesWarning(fCtx, prefs, outFileName, 1 /* displayLevelCutoff */)) {
 1932             FIO_freeCResources(&ress);
 1933             return 1;
 1934         }
 1935         ress.dstFile = FIO_openDstFile(fCtx, prefs, NULL, outFileName, DEFAULT_FILE_PERMISSIONS);
 1936         if (ress.dstFile == NULL) {  /* could not open outFileName */
 1937             error = 1;
 1938         } else {
 1939             for (; fCtx->currFileIdx < fCtx->nbFilesTotal; ++fCtx->currFileIdx) {
 1940                 status = FIO_compressFilename_srcFile(fCtx, prefs, ress, outFileName, inFileNamesTable[fCtx->currFileIdx], compressionLevel);
 1941                 if (!status) fCtx->nbFilesProcessed++;
 1942                 error |= status;
 1943             }
 1944             if (fclose(ress.dstFile))
 1945                 EXM_THROW(29, "Write error (%s) : cannot properly close %s",
 1946                             strerror(errno), outFileName);
 1947             ress.dstFile = NULL;
 1948         }
 1949     } else {
 1950         if (outMirroredRootDirName)
 1951             UTIL_mirrorSourceFilesDirectories(inFileNamesTable, (unsigned)fCtx->nbFilesTotal, outMirroredRootDirName);
 1952 
 1953         for (; fCtx->currFileIdx < fCtx->nbFilesTotal; ++fCtx->currFileIdx) {
 1954             const char* const srcFileName = inFileNamesTable[fCtx->currFileIdx];
 1955             const char* dstFileName = NULL;
 1956             if (outMirroredRootDirName) {
 1957                 char* validMirroredDirName = UTIL_createMirroredDestDirName(srcFileName, outMirroredRootDirName);
 1958                 if (validMirroredDirName) {
 1959                     dstFileName = FIO_determineCompressedName(srcFileName, validMirroredDirName, suffix);
 1960                     free(validMirroredDirName);
 1961                 } else {
 1962                     DISPLAYLEVEL(2, "zstd: --output-dir-mirror cannot compress '%s' into '%s' \n", srcFileName, outMirroredRootDirName);
 1963                     error=1;
 1964                     continue;
 1965                 }
 1966             } else {
 1967                 dstFileName = FIO_determineCompressedName(srcFileName, outDirName, suffix);  /* cannot fail */
 1968             }
 1969             status = FIO_compressFilename_srcFile(fCtx, prefs, ress, dstFileName, srcFileName, compressionLevel);
 1970             if (!status) fCtx->nbFilesProcessed++;
 1971             error |= status;
 1972         }
 1973 
 1974         if (outDirName)
 1975             FIO_checkFilenameCollisions(inFileNamesTable , (unsigned)fCtx->nbFilesTotal);
 1976     }
 1977 
 1978     if (fCtx->nbFilesProcessed >= 1 && fCtx->nbFilesTotal > 1 && fCtx->totalBytesInput != 0) {
 1979         UTIL_HumanReadableSize_t hr_isize = UTIL_makeHumanReadableSize((U64) fCtx->totalBytesInput);
 1980         UTIL_HumanReadableSize_t hr_osize = UTIL_makeHumanReadableSize((U64) fCtx->totalBytesOutput);
 1981 
 1982         DISPLAYLEVEL(2, "\r%79s\r", "");
 1983         DISPLAYLEVEL(2, "%3d files compressed :%.2f%%   (%6.*f%4s => %6.*f%4s)\n",
 1984                         fCtx->nbFilesProcessed,
 1985                         (double)fCtx->totalBytesOutput/((double)fCtx->totalBytesInput)*100,
 1986                         hr_isize.precision, hr_isize.value, hr_isize.suffix,
 1987                         hr_osize.precision, hr_osize.value, hr_osize.suffix);
 1988     }
 1989 
 1990     FIO_freeCResources(&ress);
 1991     return error;
 1992 }
 1993 
 1994 #endif /* #ifndef ZSTD_NOCOMPRESS */
 1995 
 1996 
 1997 
 1998 #ifndef ZSTD_NODECOMPRESS
 1999 
 2000 /* **************************************************************************
 2001  *  Decompression
 2002  ***************************************************************************/
 2003 typedef struct {
 2004     void*  srcBuffer;
 2005     size_t srcBufferSize;
 2006     size_t srcBufferLoaded;
 2007     void*  dstBuffer;
 2008     size_t dstBufferSize;
 2009     ZSTD_DStream* dctx;
 2010     FILE*  dstFile;
 2011 } dRess_t;
 2012 
 2013 static dRess_t FIO_createDResources(FIO_prefs_t* const prefs, const char* dictFileName)
 2014 {
 2015     dRess_t ress;
 2016     memset(&ress, 0, sizeof(ress));
 2017 
 2018     if (prefs->patchFromMode)
 2019         FIO_adjustMemLimitForPatchFromMode(prefs, UTIL_getFileSize(dictFileName), 0 /* just use the dict size */);
 2020 
 2021     /* Allocation */
 2022     ress.dctx = ZSTD_createDStream();
 2023     if (ress.dctx==NULL)
 2024         EXM_THROW(60, "Error: %s : can't create ZSTD_DStream", strerror(errno));
 2025     CHECK( ZSTD_DCtx_setMaxWindowSize(ress.dctx, prefs->memLimit) );
 2026     CHECK( ZSTD_DCtx_setParameter(ress.dctx, ZSTD_d_forceIgnoreChecksum, !prefs->checksumFlag));
 2027 
 2028     ress.srcBufferSize = ZSTD_DStreamInSize();
 2029     ress.srcBuffer = malloc(ress.srcBufferSize);
 2030     ress.dstBufferSize = ZSTD_DStreamOutSize();
 2031     ress.dstBuffer = malloc(ress.dstBufferSize);
 2032     if (!ress.srcBuffer || !ress.dstBuffer)
 2033         EXM_THROW(61, "Allocation error : not enough memory");
 2034 
 2035     /* dictionary */
 2036     {   void* dictBuffer;
 2037         size_t const dictBufferSize = FIO_createDictBuffer(&dictBuffer, dictFileName, prefs);
 2038         CHECK( ZSTD_initDStream_usingDict(ress.dctx, dictBuffer, dictBufferSize) );
 2039         free(dictBuffer);
 2040     }
 2041 
 2042     return ress;
 2043 }
 2044 
 2045 static void FIO_freeDResources(dRess_t ress)
 2046 {
 2047     CHECK( ZSTD_freeDStream(ress.dctx) );
 2048     free(ress.srcBuffer);
 2049     free(ress.dstBuffer);
 2050 }
 2051 
 2052 
 2053 /** FIO_fwriteSparse() :
 2054 *  @return : storedSkips,
 2055 *            argument for next call to FIO_fwriteSparse() or FIO_fwriteSparseEnd() */
 2056 static unsigned
 2057 FIO_fwriteSparse(FILE* file,
 2058                  const void* buffer, size_t bufferSize,
 2059                  const FIO_prefs_t* const prefs,
 2060                  unsigned storedSkips)
 2061 {
 2062     const size_t* const bufferT = (const size_t*)buffer;   /* Buffer is supposed malloc'ed, hence aligned on size_t */
 2063     size_t bufferSizeT = bufferSize / sizeof(size_t);
 2064     const size_t* const bufferTEnd = bufferT + bufferSizeT;
 2065     const size_t* ptrT = bufferT;
 2066     static const size_t segmentSizeT = (32 KB) / sizeof(size_t);   /* check every 32 KB */
 2067 
 2068     if (prefs->testMode) return 0;  /* do not output anything in test mode */
 2069 
 2070     if (!prefs->sparseFileSupport) {  /* normal write */
 2071         size_t const sizeCheck = fwrite(buffer, 1, bufferSize, file);
 2072         if (sizeCheck != bufferSize)
 2073             EXM_THROW(70, "Write error : cannot write decoded block : %s",
 2074                             strerror(errno));
 2075         return 0;
 2076     }
 2077 
 2078     /* avoid int overflow */
 2079     if (storedSkips > 1 GB) {
 2080         if (LONG_SEEK(file, 1 GB, SEEK_CUR) != 0)
 2081             EXM_THROW(91, "1 GB skip error (sparse file support)");
 2082         storedSkips -= 1 GB;
 2083     }
 2084 
 2085     while (ptrT < bufferTEnd) {
 2086         size_t nb0T;
 2087 
 2088         /* adjust last segment if < 32 KB */
 2089         size_t seg0SizeT = segmentSizeT;
 2090         if (seg0SizeT > bufferSizeT) seg0SizeT = bufferSizeT;
 2091         bufferSizeT -= seg0SizeT;
 2092 
 2093         /* count leading zeroes */
 2094         for (nb0T=0; (nb0T < seg0SizeT) && (ptrT[nb0T] == 0); nb0T++) ;
 2095         storedSkips += (unsigned)(nb0T * sizeof(size_t));
 2096 
 2097         if (nb0T != seg0SizeT) {   /* not all 0s */
 2098             size_t const nbNon0ST = seg0SizeT - nb0T;
 2099             /* skip leading zeros */
 2100             if (LONG_SEEK(file, storedSkips, SEEK_CUR) != 0)
 2101                 EXM_THROW(92, "Sparse skip error ; try --no-sparse");
 2102             storedSkips = 0;
 2103             /* write the rest */
 2104             if (fwrite(ptrT + nb0T, sizeof(size_t), nbNon0ST, file) != nbNon0ST)
 2105                 EXM_THROW(93, "Write error : cannot write decoded block : %s",
 2106                             strerror(errno));
 2107         }
 2108         ptrT += seg0SizeT;
 2109     }
 2110 
 2111     {   static size_t const maskT = sizeof(size_t)-1;
 2112         if (bufferSize & maskT) {
 2113             /* size not multiple of sizeof(size_t) : implies end of block */
 2114             const char* const restStart = (const char*)bufferTEnd;
 2115             const char* restPtr = restStart;
 2116             const char* const restEnd = (const char*)buffer + bufferSize;
 2117             assert(restEnd > restStart && restEnd < restStart + sizeof(size_t));
 2118             for ( ; (restPtr < restEnd) && (*restPtr == 0); restPtr++) ;
 2119             storedSkips += (unsigned) (restPtr - restStart);
 2120             if (restPtr != restEnd) {
 2121                 /* not all remaining bytes are 0 */
 2122                 size_t const restSize = (size_t)(restEnd - restPtr);
 2123                 if (LONG_SEEK(file, storedSkips, SEEK_CUR) != 0)
 2124                     EXM_THROW(92, "Sparse skip error ; try --no-sparse");
 2125                 if (fwrite(restPtr, 1, restSize, file) != restSize)
 2126                     EXM_THROW(95, "Write error : cannot write end of decoded block : %s",
 2127                         strerror(errno));
 2128                 storedSkips = 0;
 2129     }   }   }
 2130 
 2131     return storedSkips;
 2132 }
 2133 
 2134 static void
 2135 FIO_fwriteSparseEnd(const FIO_prefs_t* const prefs, FILE* file, unsigned storedSkips)
 2136 {
 2137     if (prefs->testMode) assert(storedSkips == 0);
 2138     if (storedSkips>0) {
 2139         assert(prefs->sparseFileSupport > 0);  /* storedSkips>0 implies sparse support is enabled */
 2140         (void)prefs;   /* assert can be disabled, in which case prefs becomes unused */
 2141         if (LONG_SEEK(file, storedSkips-1, SEEK_CUR) != 0)
 2142             EXM_THROW(69, "Final skip error (sparse file support)");
 2143         /* last zero must be explicitly written,
 2144          * so that skipped ones get implicitly translated as zero by FS */
 2145         {   const char lastZeroByte[1] = { 0 };
 2146             if (fwrite(lastZeroByte, 1, 1, file) != 1)
 2147                 EXM_THROW(69, "Write error : cannot write last zero : %s", strerror(errno));
 2148     }   }
 2149 }
 2150 
 2151 
 2152 /** FIO_passThrough() : just copy input into output, for compatibility with gzip -df mode
 2153     @return : 0 (no error) */
 2154 static int FIO_passThrough(const FIO_prefs_t* const prefs,
 2155                            FILE* foutput, FILE* finput,
 2156                            void* buffer, size_t bufferSize,
 2157                            size_t alreadyLoaded)
 2158 {
 2159     size_t const blockSize = MIN(64 KB, bufferSize);
 2160     size_t readFromInput;
 2161     unsigned storedSkips = 0;
 2162 
 2163     /* assumption : ress->srcBufferLoaded bytes already loaded and stored within buffer */
 2164     {   size_t const sizeCheck = fwrite(buffer, 1, alreadyLoaded, foutput);
 2165         if (sizeCheck != alreadyLoaded) {
 2166             DISPLAYLEVEL(1, "Pass-through write error : %s\n", strerror(errno));
 2167             return 1;
 2168     }   }
 2169 
 2170     do {
 2171         readFromInput = fread(buffer, 1, blockSize, finput);
 2172         storedSkips = FIO_fwriteSparse(foutput, buffer, readFromInput, prefs, storedSkips);
 2173     } while (readFromInput == blockSize);
 2174     if (ferror(finput)) {
 2175         DISPLAYLEVEL(1, "Pass-through read error : %s\n", strerror(errno));
 2176         return 1;
 2177     }
 2178     assert(feof(finput));
 2179 
 2180     FIO_fwriteSparseEnd(prefs, foutput, storedSkips);
 2181     return 0;
 2182 }
 2183 
 2184 /* FIO_zstdErrorHelp() :
 2185  * detailed error message when requested window size is too large */
 2186 static void
 2187 FIO_zstdErrorHelp(const FIO_prefs_t* const prefs,
 2188                   const dRess_t* ress,
 2189                   size_t err, const char* srcFileName)
 2190 {
 2191     ZSTD_frameHeader header;
 2192 
 2193     /* Help message only for one specific error */
 2194     if (ZSTD_getErrorCode(err) != ZSTD_error_frameParameter_windowTooLarge)
 2195         return;
 2196 
 2197     /* Try to decode the frame header */
 2198     err = ZSTD_getFrameHeader(&header, ress->srcBuffer, ress->srcBufferLoaded);
 2199     if (err == 0) {
 2200         unsigned long long const windowSize = header.windowSize;
 2201         unsigned const windowLog = FIO_highbit64(windowSize) + ((windowSize & (windowSize - 1)) != 0);
 2202         assert(prefs->memLimit > 0);
 2203         DISPLAYLEVEL(1, "%s : Window size larger than maximum : %llu > %u \n",
 2204                         srcFileName, windowSize, prefs->memLimit);
 2205         if (windowLog <= ZSTD_WINDOWLOG_MAX) {
 2206             unsigned const windowMB = (unsigned)((windowSize >> 20) + ((windowSize & ((1 MB) - 1)) != 0));
 2207             assert(windowSize < (U64)(1ULL << 52));   /* ensure now overflow for windowMB */
 2208             DISPLAYLEVEL(1, "%s : Use --long=%u or --memory=%uMB \n",
 2209                             srcFileName, windowLog, windowMB);
 2210             return;
 2211     }   }
 2212     DISPLAYLEVEL(1, "%s : Window log larger than ZSTD_WINDOWLOG_MAX=%u; not supported \n",
 2213                     srcFileName, ZSTD_WINDOWLOG_MAX);
 2214 }
 2215 
 2216 /** FIO_decompressFrame() :
 2217  *  @return : size of decoded zstd frame, or an error code
 2218  */
 2219 #define FIO_ERROR_FRAME_DECODING   ((unsigned long long)(-2))
 2220 static unsigned long long
 2221 FIO_decompressZstdFrame(FIO_ctx_t* const fCtx, dRess_t* ress, FILE* finput,
 2222                         const FIO_prefs_t* const prefs,
 2223                         const char* srcFileName,
 2224                         U64 alreadyDecoded)  /* for multi-frames streams */
 2225 {
 2226     U64 frameSize = 0;
 2227     U32 storedSkips = 0;
 2228 
 2229     /* display last 20 characters only */
 2230     {   size_t const srcFileLength = strlen(srcFileName);
 2231         if (srcFileLength>20) srcFileName += srcFileLength-20;
 2232     }
 2233 
 2234     ZSTD_DCtx_reset(ress->dctx, ZSTD_reset_session_only);
 2235 
 2236     /* Header loading : ensures ZSTD_getFrameHeader() will succeed */
 2237     {   size_t const toDecode = ZSTD_FRAMEHEADERSIZE_MAX;
 2238         if (ress->srcBufferLoaded < toDecode) {
 2239             size_t const toRead = toDecode - ress->srcBufferLoaded;
 2240             void* const startPosition = (char*)ress->srcBuffer + ress->srcBufferLoaded;
 2241             ress->srcBufferLoaded += fread(startPosition, 1, toRead, finput);
 2242     }   }
 2243 
 2244     /* Main decompression Loop */
 2245     while (1) {
 2246         ZSTD_inBuffer  inBuff = { ress->srcBuffer, ress->srcBufferLoaded, 0 };
 2247         ZSTD_outBuffer outBuff= { ress->dstBuffer, ress->dstBufferSize, 0 };
 2248         size_t const readSizeHint = ZSTD_decompressStream(ress->dctx, &outBuff, &inBuff);
 2249         const int displayLevel = (g_display_prefs.progressSetting == FIO_ps_always) ? 1 : 2;
 2250         UTIL_HumanReadableSize_t const hrs = UTIL_makeHumanReadableSize(alreadyDecoded+frameSize);
 2251         if (ZSTD_isError(readSizeHint)) {
 2252             DISPLAYLEVEL(1, "%s : Decoding error (36) : %s \n",
 2253                             srcFileName, ZSTD_getErrorName(readSizeHint));
 2254             FIO_zstdErrorHelp(prefs, ress, readSizeHint, srcFileName);
 2255             return FIO_ERROR_FRAME_DECODING;
 2256         }
 2257 
 2258         /* Write block */
 2259         storedSkips = FIO_fwriteSparse(ress->dstFile, ress->dstBuffer, outBuff.pos, prefs, storedSkips);
 2260         frameSize += outBuff.pos;
 2261         if (fCtx->nbFilesTotal > 1) {
 2262             size_t srcFileNameSize = strlen(srcFileName);
 2263             if (srcFileNameSize > 18) {
 2264                 const char* truncatedSrcFileName = srcFileName + srcFileNameSize - 15;
 2265                 DISPLAYUPDATE(displayLevel, "\rDecompress: %2u/%2u files. Current: ...%s : %.*f%s...    ",
 2266                                 fCtx->currFileIdx+1, fCtx->nbFilesTotal, truncatedSrcFileName, hrs.precision, hrs.value, hrs.suffix);
 2267             } else {
 2268                 DISPLAYUPDATE(displayLevel, "\rDecompress: %2u/%2u files. Current: %s : %.*f%s...    ",
 2269                             fCtx->currFileIdx+1, fCtx->nbFilesTotal, srcFileName, hrs.precision, hrs.value, hrs.suffix);
 2270             }
 2271         } else {
 2272             DISPLAYUPDATE(displayLevel, "\r%-20.20s : %.*f%s...     ",
 2273                             srcFileName, hrs.precision, hrs.value, hrs.suffix);
 2274         }
 2275 
 2276         if (inBuff.pos > 0) {
 2277             memmove(ress->srcBuffer, (char*)ress->srcBuffer + inBuff.pos, inBuff.size - inBuff.pos);
 2278             ress->srcBufferLoaded -= inBuff.pos;
 2279         }
 2280 
 2281         if (readSizeHint == 0) break;   /* end of frame */
 2282 
 2283         /* Fill input buffer */
 2284         {   size_t const toDecode = MIN(readSizeHint, ress->srcBufferSize);  /* support large skippable frames */
 2285             if (ress->srcBufferLoaded < toDecode) {
 2286                 size_t const toRead = toDecode - ress->srcBufferLoaded;   /* > 0 */
 2287                 void* const startPosition = (char*)ress->srcBuffer + ress->srcBufferLoaded;
 2288                 size_t const readSize = fread(startPosition, 1, toRead, finput);
 2289                 if (readSize==0) {
 2290                     DISPLAYLEVEL(1, "%s : Read error (39) : premature end \n",
 2291                                     srcFileName);
 2292                     return FIO_ERROR_FRAME_DECODING;
 2293                 }
 2294                 ress->srcBufferLoaded += readSize;
 2295     }   }   }
 2296 
 2297     FIO_fwriteSparseEnd(prefs, ress->dstFile, storedSkips);
 2298 
 2299     return frameSize;
 2300 }
 2301 
 2302 
 2303 #ifdef ZSTD_GZDECOMPRESS
 2304 static unsigned long long
 2305 FIO_decompressGzFrame(dRess_t* ress, FILE* srcFile,
 2306                       const FIO_prefs_t* const prefs,
 2307                       const char* srcFileName)
 2308 {
 2309     unsigned long long outFileSize = 0;
 2310     z_stream strm;
 2311     int flush = Z_NO_FLUSH;
 2312     int decodingError = 0;
 2313     unsigned storedSkips = 0;
 2314 
 2315     strm.zalloc = Z_NULL;
 2316     strm.zfree = Z_NULL;
 2317     strm.opaque = Z_NULL;
 2318     strm.next_in = 0;
 2319     strm.avail_in = 0;
 2320     /* see http://www.zlib.net/manual.html */
 2321     if (inflateInit2(&strm, 15 /* maxWindowLogSize */ + 16 /* gzip only */) != Z_OK)
 2322         return FIO_ERROR_FRAME_DECODING;
 2323 
 2324     strm.next_out = (Bytef*)ress->dstBuffer;
 2325     strm.avail_out = (uInt)ress->dstBufferSize;
 2326     strm.avail_in = (uInt)ress->srcBufferLoaded;
 2327     strm.next_in = (z_const unsigned char*)ress->srcBuffer;
 2328 
 2329     for ( ; ; ) {
 2330         int ret;
 2331         if (strm.avail_in == 0) {
 2332             ress->srcBufferLoaded = fread(ress->srcBuffer, 1, ress->srcBufferSize, srcFile);
 2333             if (ress->srcBufferLoaded == 0) flush = Z_FINISH;
 2334             strm.next_in = (z_const unsigned char*)ress->srcBuffer;
 2335             strm.avail_in = (uInt)ress->srcBufferLoaded;
 2336         }
 2337         ret = inflate(&strm, flush);
 2338         if (ret == Z_BUF_ERROR) {
 2339             DISPLAYLEVEL(1, "zstd: %s: premature gz end \n", srcFileName);
 2340             decodingError = 1; break;
 2341         }
 2342         if (ret != Z_OK && ret != Z_STREAM_END) {
 2343             DISPLAYLEVEL(1, "zstd: %s: inflate error %d \n", srcFileName, ret);
 2344             decodingError = 1; break;
 2345         }
 2346         {   size_t const decompBytes = ress->dstBufferSize - strm.avail_out;
 2347             if (decompBytes) {
 2348                 storedSkips = FIO_fwriteSparse(ress->dstFile, ress->dstBuffer, decompBytes, prefs, storedSkips);
 2349                 outFileSize += decompBytes;
 2350                 strm.next_out = (Bytef*)ress->dstBuffer;
 2351                 strm.avail_out = (uInt)ress->dstBufferSize;
 2352             }
 2353         }
 2354         if (ret == Z_STREAM_END) break;
 2355     }
 2356 
 2357     if (strm.avail_in > 0)
 2358         memmove(ress->srcBuffer, strm.next_in, strm.avail_in);
 2359     ress->srcBufferLoaded = strm.avail_in;
 2360     if ( (inflateEnd(&strm) != Z_OK)  /* release resources ; error detected */
 2361       && (decodingError==0) ) {
 2362         DISPLAYLEVEL(1, "zstd: %s: inflateEnd error \n", srcFileName);
 2363         decodingError = 1;
 2364     }
 2365     FIO_fwriteSparseEnd(prefs, ress->dstFile, storedSkips);
 2366     return decodingError ? FIO_ERROR_FRAME_DECODING : outFileSize;
 2367 }
 2368 #endif
 2369 
 2370 
 2371 #ifdef ZSTD_LZMADECOMPRESS
 2372 static unsigned long long
 2373 FIO_decompressLzmaFrame(dRess_t* ress, FILE* srcFile,
 2374                         const FIO_prefs_t* const prefs,
 2375                         const char* srcFileName, int plain_lzma)
 2376 {
 2377     unsigned long long outFileSize = 0;
 2378     lzma_stream strm = LZMA_STREAM_INIT;
 2379     lzma_action action = LZMA_RUN;
 2380     lzma_ret initRet;
 2381     int decodingError = 0;
 2382     unsigned storedSkips = 0;
 2383 
 2384     strm.next_in = 0;
 2385     strm.avail_in = 0;
 2386     if (plain_lzma) {
 2387         initRet = lzma_alone_decoder(&strm, UINT64_MAX); /* LZMA */
 2388     } else {
 2389         initRet = lzma_stream_decoder(&strm, UINT64_MAX, 0); /* XZ */
 2390     }
 2391 
 2392     if (initRet != LZMA_OK) {
 2393         DISPLAYLEVEL(1, "zstd: %s: %s error %d \n",
 2394                         plain_lzma ? "lzma_alone_decoder" : "lzma_stream_decoder",
 2395                         srcFileName, initRet);
 2396         return FIO_ERROR_FRAME_DECODING;
 2397     }
 2398 
 2399     strm.next_out = (BYTE*)ress->dstBuffer;
 2400     strm.avail_out = ress->dstBufferSize;
 2401     strm.next_in = (BYTE const*)ress->srcBuffer;
 2402     strm.avail_in = ress->srcBufferLoaded;
 2403 
 2404     for ( ; ; ) {
 2405         lzma_ret ret;
 2406         if (strm.avail_in == 0) {
 2407             ress->srcBufferLoaded = fread(ress->srcBuffer, 1, ress->srcBufferSize, srcFile);
 2408             if (ress->srcBufferLoaded == 0) action = LZMA_FINISH;
 2409             strm.next_in = (BYTE const*)ress->srcBuffer;
 2410             strm.avail_in = ress->srcBufferLoaded;
 2411         }
 2412         ret = lzma_code(&strm, action);
 2413 
 2414         if (ret == LZMA_BUF_ERROR) {
 2415             DISPLAYLEVEL(1, "zstd: %s: premature lzma end \n", srcFileName);
 2416             decodingError = 1; break;
 2417         }
 2418         if (ret != LZMA_OK && ret != LZMA_STREAM_END) {
 2419             DISPLAYLEVEL(1, "zstd: %s: lzma_code decoding error %d \n",
 2420                             srcFileName, ret);
 2421             decodingError = 1; break;
 2422         }
 2423         {   size_t const decompBytes = ress->dstBufferSize - strm.avail_out;
 2424             if (decompBytes) {
 2425                 storedSkips = FIO_fwriteSparse(ress->dstFile, ress->dstBuffer, decompBytes, prefs, storedSkips);
 2426                 outFileSize += decompBytes;
 2427                 strm.next_out = (BYTE*)ress->dstBuffer;
 2428                 strm.avail_out = ress->dstBufferSize;
 2429         }   }
 2430         if (ret == LZMA_STREAM_END) break;
 2431     }
 2432 
 2433     if (strm.avail_in > 0)
 2434         memmove(ress->srcBuffer, strm.next_in, strm.avail_in);
 2435     ress->srcBufferLoaded = strm.avail_in;
 2436     lzma_end(&strm);
 2437     FIO_fwriteSparseEnd(prefs, ress->dstFile, storedSkips);
 2438     return decodingError ? FIO_ERROR_FRAME_DECODING : outFileSize;
 2439 }
 2440 #endif
 2441 
 2442 #ifdef ZSTD_LZ4DECOMPRESS
 2443 static unsigned long long
 2444 FIO_decompressLz4Frame(dRess_t* ress, FILE* srcFile,
 2445                        const FIO_prefs_t* const prefs,
 2446                        const char* srcFileName)
 2447 {
 2448     unsigned long long filesize = 0;
 2449     LZ4F_errorCode_t nextToLoad;
 2450     LZ4F_decompressionContext_t dCtx;
 2451     LZ4F_errorCode_t const errorCode = LZ4F_createDecompressionContext(&dCtx, LZ4F_VERSION);
 2452     int decodingError = 0;
 2453     unsigned storedSkips = 0;
 2454 
 2455     if (LZ4F_isError(errorCode)) {
 2456         DISPLAYLEVEL(1, "zstd: failed to create lz4 decompression context \n");
 2457         return FIO_ERROR_FRAME_DECODING;
 2458     }
 2459 
 2460     /* Init feed with magic number (already consumed from FILE* sFile) */
 2461     {   size_t inSize = 4;
 2462         size_t outSize= 0;
 2463         MEM_writeLE32(ress->srcBuffer, LZ4_MAGICNUMBER);
 2464         nextToLoad = LZ4F_decompress(dCtx, ress->dstBuffer, &outSize, ress->srcBuffer, &inSize, NULL);
 2465         if (LZ4F_isError(nextToLoad)) {
 2466             DISPLAYLEVEL(1, "zstd: %s: lz4 header error : %s \n",
 2467                             srcFileName, LZ4F_getErrorName(nextToLoad));
 2468             LZ4F_freeDecompressionContext(dCtx);
 2469             return FIO_ERROR_FRAME_DECODING;
 2470     }   }
 2471 
 2472     /* Main Loop */
 2473     for (;nextToLoad;) {
 2474         size_t readSize;
 2475         size_t pos = 0;
 2476         size_t decodedBytes = ress->dstBufferSize;
 2477 
 2478         /* Read input */
 2479         if (nextToLoad > ress->srcBufferSize) nextToLoad = ress->srcBufferSize;
 2480         readSize = fread(ress->srcBuffer, 1, nextToLoad, srcFile);
 2481         if (!readSize) break;   /* reached end of file or stream */
 2482 
 2483         while ((pos < readSize) || (decodedBytes == ress->dstBufferSize)) {  /* still to read, or still to flush */
 2484             /* Decode Input (at least partially) */
 2485             size_t remaining = readSize - pos;
 2486             decodedBytes = ress->dstBufferSize;
 2487             nextToLoad = LZ4F_decompress(dCtx, ress->dstBuffer, &decodedBytes, (char*)(ress->srcBuffer)+pos, &remaining, NULL);
 2488             if (LZ4F_isError(nextToLoad)) {
 2489                 DISPLAYLEVEL(1, "zstd: %s: lz4 decompression error : %s \n",
 2490                                 srcFileName, LZ4F_getErrorName(nextToLoad));
 2491                 decodingError = 1; nextToLoad = 0; break;
 2492             }
 2493             pos += remaining;
 2494 
 2495             /* Write Block */
 2496             if (decodedBytes) {
 2497                 UTIL_HumanReadableSize_t hrs;
 2498                 storedSkips = FIO_fwriteSparse(ress->dstFile, ress->dstBuffer, decodedBytes, prefs, storedSkips);
 2499                 filesize += decodedBytes;
 2500                 hrs = UTIL_makeHumanReadableSize(filesize);
 2501                 DISPLAYUPDATE(2, "\rDecompressed : %.*f%s  ", hrs.precision, hrs.value, hrs.suffix);
 2502             }
 2503 
 2504             if (!nextToLoad) break;
 2505         }
 2506     }
 2507     /* can be out because readSize == 0, which could be an fread() error */
 2508     if (ferror(srcFile)) {
 2509         DISPLAYLEVEL(1, "zstd: %s: read error \n", srcFileName);
 2510         decodingError=1;
 2511     }
 2512 
 2513     if (nextToLoad!=0) {
 2514         DISPLAYLEVEL(1, "zstd: %s: unfinished lz4 stream \n", srcFileName);
 2515         decodingError=1;
 2516     }
 2517 
 2518     LZ4F_freeDecompressionContext(dCtx);
 2519     ress->srcBufferLoaded = 0; /* LZ4F will reach exact frame boundary */
 2520     FIO_fwriteSparseEnd(prefs, ress->dstFile, storedSkips);
 2521 
 2522     return decodingError ? FIO_ERROR_FRAME_DECODING : filesize;
 2523 }
 2524 #endif
 2525 
 2526 
 2527 
 2528 /** FIO_decompressFrames() :
 2529  *  Find and decode frames inside srcFile
 2530  *  srcFile presumed opened and valid
 2531  * @return : 0 : OK
 2532  *           1 : error
 2533  */
 2534 static int FIO_decompressFrames(FIO_ctx_t* const fCtx,
 2535                           dRess_t ress, FILE* srcFile,
 2536                           const FIO_prefs_t* const prefs,
 2537                           const char* dstFileName, const char* srcFileName)
 2538 {
 2539     unsigned readSomething = 0;
 2540     unsigned long long filesize = 0;
 2541     assert(srcFile != NULL);
 2542 
 2543     /* for each frame */
 2544     for ( ; ; ) {
 2545         /* check magic number -> version */
 2546         size_t const toRead = 4;
 2547         const BYTE* const buf = (const BYTE*)ress.srcBuffer;
 2548         if (ress.srcBufferLoaded < toRead)  /* load up to 4 bytes for header */
 2549             ress.srcBufferLoaded += fread((char*)ress.srcBuffer + ress.srcBufferLoaded,
 2550                                           (size_t)1, toRead - ress.srcBufferLoaded, srcFile);
 2551         if (ress.srcBufferLoaded==0) {
 2552             if (readSomething==0) {  /* srcFile is empty (which is invalid) */
 2553                 DISPLAYLEVEL(1, "zstd: %s: unexpected end of file \n", srcFileName);
 2554                 return 1;
 2555             }  /* else, just reached frame boundary */
 2556             break;   /* no more input */
 2557         }
 2558         readSomething = 1;   /* there is at least 1 byte in srcFile */
 2559         if (ress.srcBufferLoaded < toRead) {
 2560             DISPLAYLEVEL(1, "zstd: %s: unknown header \n", srcFileName);
 2561             return 1;
 2562         }
 2563         if (ZSTD_isFrame(buf, ress.srcBufferLoaded)) {
 2564             unsigned long long const frameSize = FIO_decompressZstdFrame(fCtx, &ress, srcFile, prefs, srcFileName, filesize);
 2565             if (frameSize == FIO_ERROR_FRAME_DECODING) return 1;
 2566             filesize += frameSize;
 2567         } else if (buf[0] == 31 && buf[1] == 139) { /* gz magic number */
 2568 #ifdef ZSTD_GZDECOMPRESS
 2569             unsigned long long const frameSize = FIO_decompressGzFrame(&ress, srcFile, prefs, srcFileName);
 2570             if (frameSize == FIO_ERROR_FRAME_DECODING) return 1;
 2571             filesize += frameSize;
 2572 #else
 2573             DISPLAYLEVEL(1, "zstd: %s: gzip file cannot be uncompressed (zstd compiled without HAVE_ZLIB) -- ignored \n", srcFileName);
 2574             return 1;
 2575 #endif
 2576         } else if ((buf[0] == 0xFD && buf[1] == 0x37)  /* xz magic number */
 2577                 || (buf[0] == 0x5D && buf[1] == 0x00)) { /* lzma header (no magic number) */
 2578 #ifdef ZSTD_LZMADECOMPRESS
 2579             unsigned long long const frameSize = FIO_decompressLzmaFrame(&ress, srcFile, prefs, srcFileName, buf[0] != 0xFD);
 2580             if (frameSize == FIO_ERROR_FRAME_DECODING) return 1;
 2581             filesize += frameSize;
 2582 #else
 2583             DISPLAYLEVEL(1, "zstd: %s: xz/lzma file cannot be uncompressed (zstd compiled without HAVE_LZMA) -- ignored \n", srcFileName);
 2584             return 1;
 2585 #endif
 2586         } else if (MEM_readLE32(buf) == LZ4_MAGICNUMBER) {
 2587 #ifdef ZSTD_LZ4DECOMPRESS
 2588             unsigned long long const frameSize = FIO_decompressLz4Frame(&ress, srcFile, prefs, srcFileName);
 2589             if (frameSize == FIO_ERROR_FRAME_DECODING) return 1;
 2590             filesize += frameSize;
 2591 #else
 2592             DISPLAYLEVEL(1, "zstd: %s: lz4 file cannot be uncompressed (zstd compiled without HAVE_LZ4) -- ignored \n", srcFileName);
 2593             return 1;
 2594 #endif
 2595         } else if ((prefs->overwrite) && !strcmp (dstFileName, stdoutmark)) {  /* pass-through mode */
 2596             return FIO_passThrough(prefs,
 2597                                    ress.dstFile, srcFile,
 2598                                    ress.srcBuffer, ress.srcBufferSize,
 2599                                    ress.srcBufferLoaded);
 2600         } else {
 2601             DISPLAYLEVEL(1, "zstd: %s: unsupported format \n", srcFileName);
 2602             return 1;
 2603     }   }  /* for each frame */
 2604 
 2605     /* Final Status */
 2606     fCtx->totalBytesOutput += (size_t)filesize;
 2607     DISPLAYLEVEL(2, "\r%79s\r", "");
 2608     /* No status message in pipe mode (stdin - stdout) or multi-files mode */
 2609     if ((g_display_prefs.displayLevel >= 2 && fCtx->nbFilesTotal <= 1) ||
 2610         g_display_prefs.displayLevel >= 3 ||
 2611         g_display_prefs.progressSetting == FIO_ps_always) {
 2612         DISPLAYLEVEL(1, "\r%-20s: %llu bytes \n", srcFileName, filesize);
 2613     }
 2614 
 2615     return 0;
 2616 }
 2617 
 2618 /** FIO_decompressDstFile() :
 2619     open `dstFileName`,
 2620     or path-through if ress.dstFile is already != 0,
 2621     then start decompression process (FIO_decompressFrames()).
 2622     @return : 0 : OK
 2623               1 : operation aborted
 2624 */
 2625 static int FIO_decompressDstFile(FIO_ctx_t* const fCtx,
 2626                                  FIO_prefs_t* const prefs,
 2627                                  dRess_t ress, FILE* srcFile,
 2628                                  const char* dstFileName, const char* srcFileName)
 2629 {
 2630     int result;
 2631     stat_t statbuf;
 2632     int releaseDstFile = 0;
 2633     int transferMTime = 0;
 2634 
 2635     if ((ress.dstFile == NULL) && (prefs->testMode==0)) {
 2636         int dstFilePermissions = DEFAULT_FILE_PERMISSIONS;
 2637         if ( strcmp(srcFileName, stdinmark)   /* special case : don't transfer permissions from stdin */
 2638           && strcmp(dstFileName, stdoutmark)
 2639           && UTIL_stat(srcFileName, &statbuf)
 2640           && UTIL_isRegularFileStat(&statbuf) ) {
 2641             dstFilePermissions = statbuf.st_mode;
 2642             transferMTime = 1;
 2643         }
 2644 
 2645         releaseDstFile = 1;
 2646 
 2647         ress.dstFile = FIO_openDstFile(fCtx, prefs, srcFileName, dstFileName, dstFilePermissions);
 2648         if (ress.dstFile==NULL) return 1;
 2649 
 2650         /* Must only be added after FIO_openDstFile() succeeds.
 2651          * Otherwise we may delete the destination file if it already exists,
 2652          * and the user presses Ctrl-C when asked if they wish to overwrite.
 2653          */
 2654         addHandler(dstFileName);
 2655     }
 2656 
 2657     result = FIO_decompressFrames(fCtx, ress, srcFile, prefs, dstFileName, srcFileName);
 2658 
 2659     if (releaseDstFile) {
 2660         FILE* const dstFile = ress.dstFile;
 2661         clearHandler();
 2662         ress.dstFile = NULL;
 2663         if (fclose(dstFile)) {
 2664             DISPLAYLEVEL(1, "zstd: %s: %s \n", dstFileName, strerror(errno));
 2665             result = 1;
 2666         }
 2667 
 2668         if (transferMTime) {
 2669             UTIL_utime(dstFileName, &statbuf);
 2670         }
 2671 
 2672         if ( (result != 0)  /* operation failure */
 2673           && strcmp(dstFileName, stdoutmark)  /* special case : don't remove() stdout */
 2674           ) {
 2675             FIO_removeFile(dstFileName);  /* remove decompression artefact; note: don't do anything special if remove() fails */
 2676         }
 2677     }
 2678 
 2679     return result;
 2680 }
 2681 
 2682 
 2683 /** FIO_decompressSrcFile() :
 2684     Open `srcFileName`, transfer control to decompressDstFile()
 2685     @return : 0 : OK
 2686               1 : error
 2687 */
 2688 static int FIO_decompressSrcFile(FIO_ctx_t* const fCtx, FIO_prefs_t* const prefs, dRess_t ress, const char* dstFileName, const char* srcFileName)
 2689 {
 2690     FILE* srcFile;
 2691     int result;
 2692 
 2693     if (UTIL_isDirectory(srcFileName)) {
 2694         DISPLAYLEVEL(1, "zstd: %s is a directory -- ignored \n", srcFileName);
 2695         return 1;
 2696     }
 2697 
 2698     srcFile = FIO_openSrcFile(prefs, srcFileName);
 2699     if (srcFile==NULL) return 1;
 2700     ress.srcBufferLoaded = 0;
 2701 
 2702     result = FIO_decompressDstFile(fCtx, prefs, ress, srcFile, dstFileName, srcFileName);
 2703 
 2704     /* Close file */
 2705     if (fclose(srcFile)) {
 2706         DISPLAYLEVEL(1, "zstd: %s: %s \n", srcFileName, strerror(errno));  /* error should not happen */
 2707         return 1;
 2708     }
 2709     if ( prefs->removeSrcFile  /* --rm */
 2710       && (result==0)      /* decompression successful */
 2711       && strcmp(srcFileName, stdinmark) ) /* not stdin */ {
 2712         /* We must clear the handler, since after this point calling it would
 2713          * delete both the source and destination files.
 2714          */
 2715         clearHandler();
 2716         if (FIO_removeFile(srcFileName)) {
 2717             /* failed to remove src file */
 2718             DISPLAYLEVEL(1, "zstd: %s: %s \n", srcFileName, strerror(errno));
 2719             return 1;
 2720     }   }
 2721     return result;
 2722 }
 2723 
 2724 
 2725 
 2726 int FIO_decompressFilename(FIO_ctx_t* const fCtx, FIO_prefs_t* const prefs,
 2727                            const char* dstFileName, const char* srcFileName,
 2728                            const char* dictFileName)
 2729 {
 2730     dRess_t const ress = FIO_createDResources(prefs, dictFileName);
 2731 
 2732     int const decodingError = FIO_decompressSrcFile(fCtx, prefs, ress, dstFileName, srcFileName);
 2733 
 2734     FIO_freeDResources(ress);
 2735     return decodingError;
 2736 }
 2737 
 2738 static const char *suffixList[] = {
 2739     ZSTD_EXTENSION,
 2740     TZSTD_EXTENSION,
 2741 #ifndef ZSTD_NODECOMPRESS
 2742     ZSTD_ALT_EXTENSION,
 2743 #endif
 2744 #ifdef ZSTD_GZDECOMPRESS
 2745     GZ_EXTENSION,
 2746     TGZ_EXTENSION,
 2747 #endif
 2748 #ifdef ZSTD_LZMADECOMPRESS
 2749     LZMA_EXTENSION,
 2750     XZ_EXTENSION,
 2751     TXZ_EXTENSION,
 2752 #endif
 2753 #ifdef ZSTD_LZ4DECOMPRESS
 2754     LZ4_EXTENSION,
 2755     TLZ4_EXTENSION,
 2756 #endif
 2757     NULL
 2758 };
 2759 
 2760 static const char *suffixListStr =
 2761     ZSTD_EXTENSION "/" TZSTD_EXTENSION
 2762 #ifdef ZSTD_GZDECOMPRESS
 2763     "/" GZ_EXTENSION "/" TGZ_EXTENSION
 2764 #endif
 2765 #ifdef ZSTD_LZMADECOMPRESS
 2766     "/" LZMA_EXTENSION "/" XZ_EXTENSION "/" TXZ_EXTENSION
 2767 #endif
 2768 #ifdef ZSTD_LZ4DECOMPRESS
 2769     "/" LZ4_EXTENSION "/" TLZ4_EXTENSION
 2770 #endif
 2771 ;
 2772 
 2773 /* FIO_determineDstName() :
 2774  * create a destination filename from a srcFileName.
 2775  * @return a pointer to it.
 2776  * @return == NULL if there is an error */
 2777 static const char*
 2778 FIO_determineDstName(const char* srcFileName, const char* outDirName)
 2779 {
 2780     static size_t dfnbCapacity = 0;
 2781     static char* dstFileNameBuffer = NULL;   /* using static allocation : this function cannot be multi-threaded */
 2782     size_t dstFileNameEndPos;
 2783     char* outDirFilename = NULL;
 2784     const char* dstSuffix = "";
 2785     size_t dstSuffixLen = 0;
 2786 
 2787     size_t sfnSize = strlen(srcFileName);
 2788 
 2789     size_t srcSuffixLen;
 2790     const char* const srcSuffix = strrchr(srcFileName, '.');
 2791     if (srcSuffix == NULL) {
 2792         DISPLAYLEVEL(1,
 2793             "zstd: %s: unknown suffix (%s expected). "
 2794             "Can't derive the output file name. "
 2795             "Specify it with -o dstFileName. Ignoring.\n",
 2796             srcFileName, suffixListStr);
 2797         return NULL;
 2798     }
 2799     srcSuffixLen = strlen(srcSuffix);
 2800 
 2801     {
 2802         const char** matchedSuffixPtr;
 2803         for (matchedSuffixPtr = suffixList; *matchedSuffixPtr != NULL; matchedSuffixPtr++) {
 2804             if (!strcmp(*matchedSuffixPtr, srcSuffix)) {
 2805                 break;
 2806             }
 2807         }
 2808 
 2809         /* check suffix is authorized */
 2810         if (sfnSize <= srcSuffixLen || *matchedSuffixPtr == NULL) {
 2811             DISPLAYLEVEL(1,
 2812                 "zstd: %s: unknown suffix (%s expected). "
 2813                 "Can't derive the output file name. "
 2814                 "Specify it with -o dstFileName. Ignoring.\n",
 2815                 srcFileName, suffixListStr);
 2816             return NULL;
 2817         }
 2818 
 2819         if ((*matchedSuffixPtr)[1] == 't') {
 2820             dstSuffix = ".tar";
 2821             dstSuffixLen = strlen(dstSuffix);
 2822         }
 2823     }
 2824 
 2825     if (outDirName) {
 2826         outDirFilename = FIO_createFilename_fromOutDir(srcFileName, outDirName, 0);
 2827         sfnSize = strlen(outDirFilename);
 2828         assert(outDirFilename != NULL);
 2829     }
 2830 
 2831     if (dfnbCapacity+srcSuffixLen <= sfnSize+1+dstSuffixLen) {
 2832         /* allocate enough space to write dstFilename into it */
 2833         free(dstFileNameBuffer);
 2834         dfnbCapacity = sfnSize + 20;
 2835         dstFileNameBuffer = (char*)malloc(dfnbCapacity);
 2836         if (dstFileNameBuffer==NULL)
 2837             EXM_THROW(74, "%s : not enough memory for dstFileName",
 2838                       strerror(errno));
 2839     }
 2840 
 2841     /* return dst name == src name truncated from suffix */
 2842     assert(dstFileNameBuffer != NULL);
 2843     dstFileNameEndPos = sfnSize - srcSuffixLen;
 2844     if (outDirFilename) {
 2845         memcpy(dstFileNameBuffer, outDirFilename, dstFileNameEndPos);
 2846         free(outDirFilename);
 2847     } else {
 2848         memcpy(dstFileNameBuffer, srcFileName, dstFileNameEndPos);
 2849     }
 2850 
 2851     /* The short tar extensions tzst, tgz, txz and tlz4 files should have "tar"
 2852      * extension on decompression. Also writes terminating null. */
 2853     strcpy(dstFileNameBuffer + dstFileNameEndPos, dstSuffix);
 2854     return dstFileNameBuffer;
 2855 
 2856     /* note : dstFileNameBuffer memory is not going to be free */
 2857 }
 2858 
 2859 int
 2860 FIO_decompressMultipleFilenames(FIO_ctx_t* const fCtx,
 2861                                 FIO_prefs_t* const prefs,
 2862                                 const char** srcNamesTable,
 2863                                 const char* outMirroredRootDirName,
 2864                                 const char* outDirName, const char* outFileName,
 2865                                 const char* dictFileName)
 2866 {
 2867     int status;
 2868     int error = 0;
 2869     dRess_t ress = FIO_createDResources(prefs, dictFileName);
 2870 
 2871     if (outFileName) {
 2872         if (FIO_removeMultiFilesWarning(fCtx, prefs, outFileName, 1 /* displayLevelCutoff */)) {
 2873             FIO_freeDResources(ress);
 2874             return 1;
 2875         }
 2876         if (!prefs->testMode) {
 2877             ress.dstFile = FIO_openDstFile(fCtx, prefs, NULL, outFileName, DEFAULT_FILE_PERMISSIONS);
 2878             if (ress.dstFile == 0) EXM_THROW(19, "cannot open %s", outFileName);
 2879         }
 2880         for (; fCtx->currFileIdx < fCtx->nbFilesTotal; fCtx->currFileIdx++) {
 2881             status = FIO_decompressSrcFile(fCtx, prefs, ress, outFileName, srcNamesTable[fCtx->currFileIdx]);
 2882             if (!status) fCtx->nbFilesProcessed++;
 2883             error |= status;
 2884         }
 2885         if ((!prefs->testMode) && (fclose(ress.dstFile)))
 2886             EXM_THROW(72, "Write error : %s : cannot properly close output file",
 2887                         strerror(errno));
 2888     } else {
 2889         if (outMirroredRootDirName)
 2890             UTIL_mirrorSourceFilesDirectories(srcNamesTable, (unsigned)fCtx->nbFilesTotal, outMirroredRootDirName);
 2891 
 2892         for (; fCtx->currFileIdx < fCtx->nbFilesTotal; fCtx->currFileIdx++) {   /* create dstFileName */
 2893             const char* const srcFileName = srcNamesTable[fCtx->currFileIdx];
 2894             const char* dstFileName = NULL;
 2895             if (outMirroredRootDirName) {
 2896                 char* validMirroredDirName = UTIL_createMirroredDestDirName(srcFileName, outMirroredRootDirName);
 2897                 if (validMirroredDirName) {
 2898                     dstFileName = FIO_determineDstName(srcFileName, validMirroredDirName);
 2899                     free(validMirroredDirName);
 2900                 } else {
 2901                     DISPLAYLEVEL(2, "zstd: --output-dir-mirror cannot decompress '%s' into '%s'\n", srcFileName, outMirroredRootDirName);
 2902                 }
 2903             } else {
 2904                 dstFileName = FIO_determineDstName(srcFileName, outDirName);
 2905             }
 2906             if (dstFileName == NULL) { error=1; continue; }
 2907             status = FIO_decompressSrcFile(fCtx, prefs, ress, dstFileName, srcFileName);
 2908             if (!status) fCtx->nbFilesProcessed++;
 2909             error |= status;
 2910         }
 2911         if (outDirName)
 2912             FIO_checkFilenameCollisions(srcNamesTable , (unsigned)fCtx->nbFilesTotal);
 2913     }
 2914 
 2915     if (fCtx->nbFilesProcessed >= 1  && fCtx->nbFilesTotal > 1 && fCtx->totalBytesOutput != 0)
 2916         DISPLAYLEVEL(2, "%d files decompressed : %6zu bytes total \n", fCtx->nbFilesProcessed, fCtx->totalBytesOutput);
 2917 
 2918     FIO_freeDResources(ress);
 2919     return error;
 2920 }
 2921 
 2922 /* **************************************************************************
 2923  *  .zst file info (--list command)
 2924  ***************************************************************************/
 2925 
 2926 typedef struct {
 2927     U64 decompressedSize;
 2928     U64 compressedSize;
 2929     U64 windowSize;
 2930     int numActualFrames;
 2931     int numSkippableFrames;
 2932     int decompUnavailable;
 2933     int usesCheck;
 2934     U32 nbFiles;
 2935 } fileInfo_t;
 2936 
 2937 typedef enum {
 2938   info_success=0,
 2939   info_frame_error=1,
 2940   info_not_zstd=2,
 2941   info_file_error=3,
 2942   info_truncated_input=4,
 2943 } InfoError;
 2944 
 2945 #define ERROR_IF(c,n,...) {             \
 2946     if (c) {                           \
 2947         DISPLAYLEVEL(1, __VA_ARGS__);  \
 2948         DISPLAYLEVEL(1, " \n");        \
 2949         return n;                      \
 2950     }                                  \
 2951 }
 2952 
 2953 static InfoError
 2954 FIO_analyzeFrames(fileInfo_t* info, FILE* const srcFile)
 2955 {
 2956     /* begin analyzing frame */
 2957     for ( ; ; ) {
 2958         BYTE headerBuffer[ZSTD_FRAMEHEADERSIZE_MAX];
 2959         size_t const numBytesRead = fread(headerBuffer, 1, sizeof(headerBuffer), srcFile);
 2960         if (numBytesRead < ZSTD_FRAMEHEADERSIZE_MIN(ZSTD_f_zstd1)) {
 2961             if ( feof(srcFile)
 2962               && (numBytesRead == 0)
 2963               && (info->compressedSize > 0)
 2964               && (info->compressedSize != UTIL_FILESIZE_UNKNOWN) ) {
 2965                 unsigned long long file_position = (unsigned long long) LONG_TELL(srcFile);
 2966                 unsigned long long file_size = (unsigned long long) info->compressedSize;
 2967                 ERROR_IF(file_position != file_size, info_truncated_input,
 2968                   "Error: seeked to position %llu, which is beyond file size of %llu\n",
 2969                   file_position,
 2970                   file_size);
 2971                 break;  /* correct end of file => success */
 2972             }
 2973             ERROR_IF(feof(srcFile), info_not_zstd, "Error: reached end of file with incomplete frame");
 2974             ERROR_IF(1, info_frame_error, "Error: did not reach end of file but ran out of frames");
 2975         }
 2976         {   U32 const magicNumber = MEM_readLE32(headerBuffer);
 2977             /* Zstandard frame */
 2978             if (magicNumber == ZSTD_MAGICNUMBER) {
 2979                 ZSTD_frameHeader header;
 2980                 U64 const frameContentSize = ZSTD_getFrameContentSize(headerBuffer, numBytesRead);
 2981                 if ( frameContentSize == ZSTD_CONTENTSIZE_ERROR
 2982                   || frameContentSize == ZSTD_CONTENTSIZE_UNKNOWN ) {
 2983                     info->decompUnavailable = 1;
 2984                 } else {
 2985                     info->decompressedSize += frameContentSize;
 2986                 }
 2987                 ERROR_IF(ZSTD_getFrameHeader(&header, headerBuffer, numBytesRead) != 0,
 2988                         info_frame_error, "Error: could not decode frame header");
 2989                 info->windowSize = header.windowSize;
 2990                 /* move to the end of the frame header */
 2991                 {   size_t const headerSize = ZSTD_frameHeaderSize(headerBuffer, numBytesRead);
 2992                     ERROR_IF(ZSTD_isError(headerSize), info_frame_error, "Error: could not determine frame header size");
 2993                     ERROR_IF(fseek(srcFile, ((long)headerSize)-((long)numBytesRead), SEEK_CUR) != 0,
 2994                             info_frame_error, "Error: could not move to end of frame header");
 2995                 }
 2996 
 2997                 /* skip all blocks in the frame */
 2998                 {   int lastBlock = 0;
 2999                     do {
 3000                         BYTE blockHeaderBuffer[3];
 3001                         ERROR_IF(fread(blockHeaderBuffer, 1, 3, srcFile) != 3,
 3002                                 info_frame_error, "Error while reading block header");
 3003                         {   U32 const blockHeader = MEM_readLE24(blockHeaderBuffer);
 3004                             U32 const blockTypeID = (blockHeader >> 1) & 3;
 3005                             U32 const isRLE = (blockTypeID == 1);
 3006                             U32 const isWrongBlock = (blockTypeID == 3);
 3007                             long const blockSize = isRLE ? 1 : (long)(blockHeader >> 3);
 3008                             ERROR_IF(isWrongBlock, info_frame_error, "Error: unsupported block type");
 3009                             lastBlock = blockHeader & 1;
 3010                             ERROR_IF(fseek(srcFile, blockSize, SEEK_CUR) != 0,
 3011                                     info_frame_error, "Error: could not skip to end of block");
 3012                         }
 3013                     } while (lastBlock != 1);
 3014                 }
 3015 
 3016                 /* check if checksum is used */
 3017                 {   BYTE const frameHeaderDescriptor = headerBuffer[4];
 3018                     int const contentChecksumFlag = (frameHeaderDescriptor & (1 << 2)) >> 2;
 3019                     if (contentChecksumFlag) {
 3020                         info->usesCheck = 1;
 3021                         ERROR_IF(fseek(srcFile, 4, SEEK_CUR) != 0,
 3022                                 info_frame_error, "Error: could not skip past checksum");
 3023                 }   }
 3024                 info->numActualFrames++;
 3025             }
 3026             /* Skippable frame */
 3027             else if ((magicNumber & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) {
 3028                 U32 const frameSize = MEM_readLE32(headerBuffer + 4);
 3029                 long const seek = (long)(8 + frameSize - numBytesRead);
 3030                 ERROR_IF(LONG_SEEK(srcFile, seek, SEEK_CUR) != 0,
 3031                         info_frame_error, "Error: could not find end of skippable frame");
 3032                 info->numSkippableFrames++;
 3033             }
 3034             /* unknown content */
 3035             else {
 3036                 return info_not_zstd;
 3037             }
 3038         }  /* magic number analysis */
 3039     }  /* end analyzing frames */
 3040     return info_success;
 3041 }
 3042 
 3043 
 3044 static InfoError
 3045 getFileInfo_fileConfirmed(fileInfo_t* info, const char* inFileName)
 3046 {
 3047     InfoError status;
 3048     FILE* const srcFile = FIO_openSrcFile(NULL, inFileName);
 3049     ERROR_IF(srcFile == NULL, info_file_error, "Error: could not open source file %s", inFileName);
 3050 
 3051     info->compressedSize = UTIL_getFileSize(inFileName);
 3052     status = FIO_analyzeFrames(info, srcFile);
 3053 
 3054     fclose(srcFile);
 3055     info->nbFiles = 1;
 3056     return status;
 3057 }
 3058 
 3059 
 3060 /** getFileInfo() :
 3061  *  Reads information from file, stores in *info
 3062  * @return : InfoError status
 3063  */
 3064 static InfoError
 3065 getFileInfo(fileInfo_t* info, const char* srcFileName)
 3066 {
 3067     ERROR_IF(!UTIL_isRegularFile(srcFileName),
 3068             info_file_error, "Error : %s is not a file", srcFileName);
 3069     return getFileInfo_fileConfirmed(info, srcFileName);
 3070 }
 3071 
 3072 
 3073 static void
 3074 displayInfo(const char* inFileName, const fileInfo_t* info, int displayLevel)
 3075 {
 3076     UTIL_HumanReadableSize_t const window_hrs = UTIL_makeHumanReadableSize(info->windowSize);
 3077     UTIL_HumanReadableSize_t const compressed_hrs = UTIL_makeHumanReadableSize(info->compressedSize);
 3078     UTIL_HumanReadableSize_t const decompressed_hrs = UTIL_makeHumanReadableSize(info->decompressedSize);
 3079     double const ratio = (info->compressedSize == 0) ? 0 : ((double)info->decompressedSize)/(double)info->compressedSize;
 3080     const char* const checkString = (info->usesCheck ? "XXH64" : "None");
 3081     if (displayLevel <= 2) {
 3082         if (!info->decompUnavailable) {
 3083             DISPLAYOUT("%6d  %5d  %6.*f%4s  %8.*f%4s  %5.3f  %5s  %s\n",
 3084                     info->numSkippableFrames + info->numActualFrames,
 3085                     info->numSkippableFrames,
 3086                     compressed_hrs.precision, compressed_hrs.value, compressed_hrs.suffix,
 3087                     decompressed_hrs.precision, decompressed_hrs.value, decompressed_hrs.suffix,
 3088                     ratio, checkString, inFileName);
 3089         } else {
 3090             DISPLAYOUT("%6d  %5d  %6.*f%4s                       %5s  %s\n",
 3091                     info->numSkippableFrames + info->numActualFrames,
 3092                     info->numSkippableFrames,
 3093                     compressed_hrs.precision, compressed_hrs.value, compressed_hrs.suffix,
 3094                     checkString, inFileName);
 3095         }
 3096     } else {
 3097         DISPLAYOUT("%s \n", inFileName);
 3098         DISPLAYOUT("# Zstandard Frames: %d\n", info->numActualFrames);
 3099         if (info->numSkippableFrames)
 3100             DISPLAYOUT("# Skippable Frames: %d\n", info->numSkippableFrames);
 3101         DISPLAYOUT("Window Size: %.*f%s (%llu B)\n",
 3102                    window_hrs.precision, window_hrs.value, window_hrs.suffix,
 3103                    (unsigned long long)info->windowSize);
 3104         DISPLAYOUT("Compressed Size: %.*f%s (%llu B)\n",
 3105                     compressed_hrs.precision, compressed_hrs.value, compressed_hrs.suffix,
 3106                     (unsigned long long)info->compressedSize);
 3107         if (!info->decompUnavailable) {
 3108             DISPLAYOUT("Decompressed Size: %.*f%s (%llu B)\n",
 3109                     decompressed_hrs.precision, decompressed_hrs.value, decompressed_hrs.suffix,
 3110                     (unsigned long long)info->decompressedSize);
 3111             DISPLAYOUT("Ratio: %.4f\n", ratio);
 3112         }
 3113         DISPLAYOUT("Check: %s\n", checkString);
 3114         DISPLAYOUT("\n");
 3115     }
 3116 }
 3117 
 3118 static fileInfo_t FIO_addFInfo(fileInfo_t fi1, fileInfo_t fi2)
 3119 {
 3120     fileInfo_t total;
 3121     memset(&total, 0, sizeof(total));
 3122     total.numActualFrames = fi1.numActualFrames + fi2.numActualFrames;
 3123     total.numSkippableFrames = fi1.numSkippableFrames + fi2.numSkippableFrames;
 3124     total.compressedSize = fi1.compressedSize + fi2.compressedSize;
 3125     total.decompressedSize = fi1.decompressedSize + fi2.decompressedSize;
 3126     total.decompUnavailable = fi1.decompUnavailable | fi2.decompUnavailable;
 3127     total.usesCheck = fi1.usesCheck & fi2.usesCheck;
 3128     total.nbFiles = fi1.nbFiles + fi2.nbFiles;
 3129     return total;
 3130 }
 3131 
 3132 static int
 3133 FIO_listFile(fileInfo_t* total, const char* inFileName, int displayLevel)
 3134 {
 3135     fileInfo_t info;
 3136     memset(&info, 0, sizeof(info));
 3137     {   InfoError const error = getFileInfo(&info, inFileName);
 3138         switch (error) {
 3139             case info_frame_error:
 3140                 /* display error, but provide output */
 3141                 DISPLAYLEVEL(1, "Error while parsing \"%s\" \n", inFileName);
 3142                 break;
 3143             case info_not_zstd:
 3144                 DISPLAYOUT("File \"%s\" not compressed by zstd \n", inFileName);
 3145                 if (displayLevel > 2) DISPLAYOUT("\n");
 3146                 return 1;
 3147             case info_file_error:
 3148                 /* error occurred while opening the file */
 3149                 if (displayLevel > 2) DISPLAYOUT("\n");
 3150                 return 1;
 3151             case info_truncated_input:
 3152                 DISPLAYOUT("File \"%s\" is truncated \n", inFileName);
 3153                 if (displayLevel > 2) DISPLAYOUT("\n");
 3154                 return 1;
 3155             case info_success:
 3156             default:
 3157                 break;
 3158         }
 3159 
 3160         displayInfo(inFileName, &info, displayLevel);
 3161         *total = FIO_addFInfo(*total, info);
 3162         assert(error == info_success || error == info_frame_error);
 3163         return (int)error;
 3164     }
 3165 }
 3166 
 3167 int FIO_listMultipleFiles(unsigned numFiles, const char** filenameTable, int displayLevel)
 3168 {
 3169     /* ensure no specified input is stdin (needs fseek() capability) */
 3170     {   unsigned u;
 3171         for (u=0; u<numFiles;u++) {
 3172             ERROR_IF(!strcmp (filenameTable[u], stdinmark),
 3173                     1, "zstd: --list does not support reading from standard input");
 3174     }   }
 3175 
 3176     if (numFiles == 0) {
 3177         if (!IS_CONSOLE(stdin)) {
 3178             DISPLAYLEVEL(1, "zstd: --list does not support reading from standard input \n");
 3179         }
 3180         DISPLAYLEVEL(1, "No files given \n");
 3181         return 1;
 3182     }
 3183 
 3184     if (displayLevel <= 2) {
 3185         DISPLAYOUT("Frames  Skips  Compressed  Uncompressed  Ratio  Check  Filename\n");
 3186     }
 3187     {   int error = 0;
 3188         fileInfo_t total;
 3189         memset(&total, 0, sizeof(total));
 3190         total.usesCheck = 1;
 3191         /* --list each file, and check for any error */
 3192         {   unsigned u;
 3193             for (u=0; u<numFiles;u++) {
 3194                 error |= FIO_listFile(&total, filenameTable[u], displayLevel);
 3195         }   }
 3196         if (numFiles > 1 && displayLevel <= 2) {   /* display total */
 3197             UTIL_HumanReadableSize_t const compressed_hrs = UTIL_makeHumanReadableSize(total.compressedSize);
 3198             UTIL_HumanReadableSize_t const decompressed_hrs = UTIL_makeHumanReadableSize(total.decompressedSize);
 3199             double const ratio = (total.compressedSize == 0) ? 0 : ((double)total.decompressedSize)/(double)total.compressedSize;
 3200             const char* const checkString = (total.usesCheck ? "XXH64" : "");
 3201             DISPLAYOUT("----------------------------------------------------------------- \n");
 3202             if (total.decompUnavailable) {
 3203                 DISPLAYOUT("%6d  %5d  %6.*f%4s                       %5s  %u files\n",
 3204                         total.numSkippableFrames + total.numActualFrames,
 3205                         total.numSkippableFrames,
 3206                         compressed_hrs.precision, compressed_hrs.value, compressed_hrs.suffix,
 3207                         checkString, (unsigned)total.nbFiles);
 3208             } else {
 3209                 DISPLAYOUT("%6d  %5d  %6.*f%4s  %8.*f%4s  %5.3f  %5s  %u files\n",
 3210                         total.numSkippableFrames + total.numActualFrames,
 3211                         total.numSkippableFrames,
 3212                         compressed_hrs.precision, compressed_hrs.value, compressed_hrs.suffix,
 3213                         decompressed_hrs.precision, decompressed_hrs.value, decompressed_hrs.suffix,
 3214                         ratio, checkString, (unsigned)total.nbFiles);
 3215         }   }
 3216         return error;
 3217     }
 3218 }
 3219 
 3220 
 3221 #endif /* #ifndef ZSTD_NODECOMPRESS */

Cache object: 9251f6752b5a27a4a19962a616f86254


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.