xref: /freebsd/sys/contrib/zstd/programs/fileio.c (revision 5ff13fbc199bdf5f0572845351c68ee5ca828e71)
10c16b537SWarner Losh /*
2*5ff13fbcSAllan Jude  * Copyright (c) Yann Collet, Facebook, Inc.
30c16b537SWarner Losh  * All rights reserved.
40c16b537SWarner Losh  *
50c16b537SWarner Losh  * This source code is licensed under both the BSD-style license (found in the
60c16b537SWarner Losh  * LICENSE file in the root directory of this source tree) and the GPLv2 (found
70c16b537SWarner Losh  * in the COPYING file in the root directory of this source tree).
80c16b537SWarner Losh  * You may select, at your option, one of the above-listed licenses.
90c16b537SWarner Losh  */
100c16b537SWarner Losh 
110c16b537SWarner Losh 
120c16b537SWarner Losh /* *************************************
130c16b537SWarner Losh *  Compiler Options
140c16b537SWarner Losh ***************************************/
150c16b537SWarner Losh #ifdef _MSC_VER   /* Visual */
160c16b537SWarner Losh #  pragma warning(disable : 4127)  /* disable: C4127: conditional expression is constant */
170c16b537SWarner Losh #  pragma warning(disable : 4204)  /* non-constant aggregate initializer */
180c16b537SWarner Losh #endif
190c16b537SWarner Losh #if defined(__MINGW32__) && !defined(_POSIX_SOURCE)
200c16b537SWarner Losh #  define _POSIX_SOURCE 1          /* disable %llu warnings with MinGW on Windows */
210c16b537SWarner Losh #endif
220c16b537SWarner Losh 
230c16b537SWarner Losh /*-*************************************
240c16b537SWarner Losh *  Includes
250c16b537SWarner Losh ***************************************/
260c16b537SWarner Losh #include "platform.h"   /* Large Files support, SET_BINARY_MODE */
272b9c00cbSConrad Meyer #include "util.h"       /* UTIL_getFileSize, UTIL_isRegularFile, UTIL_isSameFile */
28*5ff13fbcSAllan Jude #include <stdio.h>      /* fprintf, open, fdopen, fread, _fileno, stdin, stdout */
290c16b537SWarner Losh #include <stdlib.h>     /* malloc, free */
300c16b537SWarner Losh #include <string.h>     /* strcmp, strlen */
31*5ff13fbcSAllan Jude #include <fcntl.h>      /* O_WRONLY */
320f743729SConrad Meyer #include <assert.h>
330c16b537SWarner Losh #include <errno.h>      /* errno */
349cbefe25SConrad Meyer #include <limits.h>     /* INT_MAX */
350f743729SConrad Meyer #include <signal.h>
362b9c00cbSConrad Meyer #include "timefn.h"     /* UTIL_getTime, UTIL_clockSpanMicro */
370c16b537SWarner Losh 
380c16b537SWarner Losh #if defined (_MSC_VER)
390c16b537SWarner Losh #  include <sys/stat.h>
400c16b537SWarner Losh #  include <io.h>
410c16b537SWarner Losh #endif
420c16b537SWarner Losh 
4337f1f268SConrad Meyer #include "../lib/common/mem.h"     /* U32, U64 */
440c16b537SWarner Losh #include "fileio.h"
4519fcbaf1SConrad Meyer 
460c16b537SWarner Losh #define ZSTD_STATIC_LINKING_ONLY   /* ZSTD_magicNumber, ZSTD_frameHeaderSize_max */
4737f1f268SConrad Meyer #include "../lib/zstd.h"
48*5ff13fbcSAllan Jude #include "../lib/zstd_errors.h"  /* ZSTD_error_frameParameter_windowTooLarge */
4919fcbaf1SConrad Meyer 
500c16b537SWarner Losh #if defined(ZSTD_GZCOMPRESS) || defined(ZSTD_GZDECOMPRESS)
510c16b537SWarner Losh #  include <zlib.h>
520c16b537SWarner Losh #  if !defined(z_const)
530c16b537SWarner Losh #    define z_const
540c16b537SWarner Losh #  endif
550c16b537SWarner Losh #endif
5619fcbaf1SConrad Meyer 
570c16b537SWarner Losh #if defined(ZSTD_LZMACOMPRESS) || defined(ZSTD_LZMADECOMPRESS)
580c16b537SWarner Losh #  include <lzma.h>
590c16b537SWarner Losh #endif
600c16b537SWarner Losh 
610c16b537SWarner Losh #define LZ4_MAGICNUMBER 0x184D2204
620c16b537SWarner Losh #if defined(ZSTD_LZ4COMPRESS) || defined(ZSTD_LZ4DECOMPRESS)
630c16b537SWarner Losh #  define LZ4F_ENABLE_OBSOLETE_ENUMS
640c16b537SWarner Losh #  include <lz4frame.h>
650c16b537SWarner Losh #  include <lz4.h>
660c16b537SWarner Losh #endif
670c16b537SWarner Losh 
680c16b537SWarner Losh 
690c16b537SWarner Losh /*-*************************************
700c16b537SWarner Losh *  Constants
710c16b537SWarner Losh ***************************************/
720f743729SConrad Meyer #define ADAPT_WINDOWLOG_DEFAULT 23   /* 8 MB */
730c16b537SWarner Losh #define DICTSIZE_MAX (32 MB)   /* protection against large input (attack scenario) */
740c16b537SWarner Losh 
750c16b537SWarner Losh #define FNSPACE 30
760c16b537SWarner Losh 
77*5ff13fbcSAllan Jude /* Default file permissions 0666 (modulated by umask) */
78*5ff13fbcSAllan Jude #if !defined(_WIN32)
79*5ff13fbcSAllan Jude /* These macros aren't defined on windows. */
80*5ff13fbcSAllan Jude #define DEFAULT_FILE_PERMISSIONS (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
81*5ff13fbcSAllan Jude #else
82*5ff13fbcSAllan Jude #define DEFAULT_FILE_PERMISSIONS (0666)
83*5ff13fbcSAllan Jude #endif
84*5ff13fbcSAllan Jude 
850c16b537SWarner Losh /*-*************************************
860c16b537SWarner Losh *  Macros
870c16b537SWarner Losh ***************************************/
88*5ff13fbcSAllan Jude #define KB *(1 <<10)
89*5ff13fbcSAllan Jude #define MB *(1 <<20)
90*5ff13fbcSAllan Jude #define GB *(1U<<30)
91*5ff13fbcSAllan Jude #undef MAX
92*5ff13fbcSAllan Jude #define MAX(a,b) ((a)>(b) ? (a) : (b))
932b9c00cbSConrad Meyer 
942b9c00cbSConrad Meyer struct FIO_display_prefs_s {
952b9c00cbSConrad Meyer     int displayLevel;   /* 0 : no display;  1: errors;  2: + result + interaction + warnings;  3: + progression;  4: + information */
96*5ff13fbcSAllan Jude     FIO_progressSetting_e progressSetting;
972b9c00cbSConrad Meyer };
982b9c00cbSConrad Meyer 
99*5ff13fbcSAllan Jude static FIO_display_prefs_t g_display_prefs = {2, FIO_ps_auto};
1002b9c00cbSConrad Meyer 
1010c16b537SWarner Losh #define DISPLAY(...)         fprintf(stderr, __VA_ARGS__)
1020c16b537SWarner Losh #define DISPLAYOUT(...)      fprintf(stdout, __VA_ARGS__)
1032b9c00cbSConrad Meyer #define DISPLAYLEVEL(l, ...) { if (g_display_prefs.displayLevel>=l) { DISPLAY(__VA_ARGS__); } }
1040c16b537SWarner Losh 
105052d3c12SConrad Meyer static const U64 g_refreshRate = SEC_TO_MICRO / 6;
106052d3c12SConrad Meyer static UTIL_time_t g_displayClock = UTIL_TIME_INITIALIZER;
107052d3c12SConrad Meyer 
108*5ff13fbcSAllan Jude #define READY_FOR_UPDATE() ((g_display_prefs.progressSetting != FIO_ps_never) && UTIL_clockSpanMicro(g_displayClock) > g_refreshRate)
10919fcbaf1SConrad Meyer #define DELAY_NEXT_UPDATE() { g_displayClock = UTIL_getTime(); }
11019fcbaf1SConrad Meyer #define DISPLAYUPDATE(l, ...) {                              \
111*5ff13fbcSAllan Jude         if (g_display_prefs.displayLevel>=l && (g_display_prefs.progressSetting != FIO_ps_never)) { \
1122b9c00cbSConrad Meyer             if (READY_FOR_UPDATE() || (g_display_prefs.displayLevel>=4)) { \
11319fcbaf1SConrad Meyer                 DELAY_NEXT_UPDATE();                         \
11419fcbaf1SConrad Meyer                 DISPLAY(__VA_ARGS__);                        \
1152b9c00cbSConrad Meyer                 if (g_display_prefs.displayLevel>=4) fflush(stderr);       \
11619fcbaf1SConrad Meyer     }   }   }
1170c16b537SWarner Losh 
1180c16b537SWarner Losh #undef MIN  /* in case it would be already defined */
1190c16b537SWarner Losh #define MIN(a,b)    ((a) < (b) ? (a) : (b))
1200c16b537SWarner Losh 
1210c16b537SWarner Losh 
1220c16b537SWarner Losh #define EXM_THROW(error, ...)                                             \
1230c16b537SWarner Losh {                                                                         \
1240c16b537SWarner Losh     DISPLAYLEVEL(1, "zstd: ");                                            \
1250f743729SConrad Meyer     DISPLAYLEVEL(5, "Error defined at %s, line %i : \n", __FILE__, __LINE__); \
1260c16b537SWarner Losh     DISPLAYLEVEL(1, "error %i : ", error);                                \
1270c16b537SWarner Losh     DISPLAYLEVEL(1, __VA_ARGS__);                                         \
1280c16b537SWarner Losh     DISPLAYLEVEL(1, " \n");                                               \
1290c16b537SWarner Losh     exit(error);                                                          \
1300c16b537SWarner Losh }
1310c16b537SWarner Losh 
132052d3c12SConrad Meyer #define CHECK_V(v, f)                                \
133052d3c12SConrad Meyer     v = f;                                           \
134052d3c12SConrad Meyer     if (ZSTD_isError(v)) {                           \
1350f743729SConrad Meyer         DISPLAYLEVEL(5, "%s \n", #f);                \
136052d3c12SConrad Meyer         EXM_THROW(11, "%s", ZSTD_getErrorName(v));   \
137052d3c12SConrad Meyer     }
138052d3c12SConrad Meyer #define CHECK(f) { size_t err; CHECK_V(err, f); }
1390c16b537SWarner Losh 
1400c16b537SWarner Losh 
1410c16b537SWarner Losh /*-************************************
1420c16b537SWarner Losh *  Signal (Ctrl-C trapping)
1430c16b537SWarner Losh **************************************/
1440c16b537SWarner Losh static const char* g_artefact = NULL;
INThandler(int sig)1450c16b537SWarner Losh static void INThandler(int sig)
1460c16b537SWarner Losh {
1470c16b537SWarner Losh     assert(sig==SIGINT); (void)sig;
1480c16b537SWarner Losh #if !defined(_MSC_VER)
1490c16b537SWarner Losh     signal(sig, SIG_IGN);  /* this invocation generates a buggy warning in Visual Studio */
1500c16b537SWarner Losh #endif
15119fcbaf1SConrad Meyer     if (g_artefact) {
15219fcbaf1SConrad Meyer         assert(UTIL_isRegularFile(g_artefact));
15319fcbaf1SConrad Meyer         remove(g_artefact);
15419fcbaf1SConrad Meyer     }
1550c16b537SWarner Losh     DISPLAY("\n");
1560c16b537SWarner Losh     exit(2);
1570c16b537SWarner Losh }
addHandler(char const * dstFileName)158052d3c12SConrad Meyer static void addHandler(char const* dstFileName)
159052d3c12SConrad Meyer {
160052d3c12SConrad Meyer     if (UTIL_isRegularFile(dstFileName)) {
161052d3c12SConrad Meyer         g_artefact = dstFileName;
162052d3c12SConrad Meyer         signal(SIGINT, INThandler);
163052d3c12SConrad Meyer     } else {
164052d3c12SConrad Meyer         g_artefact = NULL;
165052d3c12SConrad Meyer     }
166052d3c12SConrad Meyer }
167052d3c12SConrad Meyer /* Idempotent */
clearHandler(void)168052d3c12SConrad Meyer static void clearHandler(void)
169052d3c12SConrad Meyer {
170052d3c12SConrad Meyer     if (g_artefact) signal(SIGINT, SIG_DFL);
171052d3c12SConrad Meyer     g_artefact = NULL;
172052d3c12SConrad Meyer }
1730c16b537SWarner Losh 
1740c16b537SWarner Losh 
1750f743729SConrad Meyer /*-*********************************************************
1760f743729SConrad Meyer *  Termination signal trapping (Print debug stack trace)
1770f743729SConrad Meyer ***********************************************************/
1780f743729SConrad Meyer #if defined(__has_feature) && !defined(BACKTRACE_ENABLE) /* Clang compiler */
1790f743729SConrad Meyer #  if (__has_feature(address_sanitizer))
1800f743729SConrad Meyer #    define BACKTRACE_ENABLE 0
1810f743729SConrad Meyer #  endif /* __has_feature(address_sanitizer) */
1820f743729SConrad Meyer #elif defined(__SANITIZE_ADDRESS__) && !defined(BACKTRACE_ENABLE) /* GCC compiler */
1830f743729SConrad Meyer #  define BACKTRACE_ENABLE 0
1840f743729SConrad Meyer #endif
1850f743729SConrad Meyer 
1860f743729SConrad Meyer #if !defined(BACKTRACE_ENABLE)
1870f743729SConrad Meyer /* automatic detector : backtrace enabled by default on linux+glibc and osx */
1884d3f1eafSConrad Meyer #  if (defined(__linux__) && (defined(__GLIBC__) && !defined(__UCLIBC__))) \
1890f743729SConrad Meyer      || (defined(__APPLE__) && defined(__MACH__))
1900f743729SConrad Meyer #    define BACKTRACE_ENABLE 1
1910f743729SConrad Meyer #  else
1920f743729SConrad Meyer #    define BACKTRACE_ENABLE 0
1930f743729SConrad Meyer #  endif
1940f743729SConrad Meyer #endif
1950f743729SConrad Meyer 
1960f743729SConrad Meyer /* note : after this point, BACKTRACE_ENABLE is necessarily defined */
1970f743729SConrad Meyer 
1980f743729SConrad Meyer 
1990f743729SConrad Meyer #if BACKTRACE_ENABLE
2000f743729SConrad Meyer 
2010f743729SConrad Meyer #include <execinfo.h>   /* backtrace, backtrace_symbols */
2020f743729SConrad Meyer 
2030f743729SConrad Meyer #define MAX_STACK_FRAMES    50
2040f743729SConrad Meyer 
ABRThandler(int sig)2050f743729SConrad Meyer static void ABRThandler(int sig) {
2060f743729SConrad Meyer     const char* name;
2070f743729SConrad Meyer     void* addrlist[MAX_STACK_FRAMES];
2080f743729SConrad Meyer     char** symbollist;
2092b9c00cbSConrad Meyer     int addrlen, i;
2100f743729SConrad Meyer 
2110f743729SConrad Meyer     switch (sig) {
2120f743729SConrad Meyer         case SIGABRT: name = "SIGABRT"; break;
2130f743729SConrad Meyer         case SIGFPE: name = "SIGFPE"; break;
2140f743729SConrad Meyer         case SIGILL: name = "SIGILL"; break;
2150f743729SConrad Meyer         case SIGINT: name = "SIGINT"; break;
2160f743729SConrad Meyer         case SIGSEGV: name = "SIGSEGV"; break;
2170f743729SConrad Meyer         default: name = "UNKNOWN";
2180f743729SConrad Meyer     }
2190f743729SConrad Meyer 
2200f743729SConrad Meyer     DISPLAY("Caught %s signal, printing stack:\n", name);
2210f743729SConrad Meyer     /* Retrieve current stack addresses. */
2220f743729SConrad Meyer     addrlen = backtrace(addrlist, MAX_STACK_FRAMES);
2230f743729SConrad Meyer     if (addrlen == 0) {
2240f743729SConrad Meyer         DISPLAY("\n");
2250f743729SConrad Meyer         return;
2260f743729SConrad Meyer     }
2270f743729SConrad Meyer     /* Create readable strings to each frame. */
2280f743729SConrad Meyer     symbollist = backtrace_symbols(addrlist, addrlen);
2290f743729SConrad Meyer     /* Print the stack trace, excluding calls handling the signal. */
2300f743729SConrad Meyer     for (i = ZSTD_START_SYMBOLLIST_FRAME; i < addrlen; i++) {
2310f743729SConrad Meyer         DISPLAY("%s\n", symbollist[i]);
2320f743729SConrad Meyer     }
2330f743729SConrad Meyer     free(symbollist);
2340f743729SConrad Meyer     /* Reset and raise the signal so default handler runs. */
2350f743729SConrad Meyer     signal(sig, SIG_DFL);
2360f743729SConrad Meyer     raise(sig);
2370f743729SConrad Meyer }
2380f743729SConrad Meyer #endif
2390f743729SConrad Meyer 
FIO_addAbortHandler()2400f743729SConrad Meyer void FIO_addAbortHandler()
2410f743729SConrad Meyer {
2420f743729SConrad Meyer #if BACKTRACE_ENABLE
2430f743729SConrad Meyer     signal(SIGABRT, ABRThandler);
2440f743729SConrad Meyer     signal(SIGFPE, ABRThandler);
2450f743729SConrad Meyer     signal(SIGILL, ABRThandler);
2460f743729SConrad Meyer     signal(SIGSEGV, ABRThandler);
2470f743729SConrad Meyer     signal(SIGBUS, ABRThandler);
2480f743729SConrad Meyer #endif
2490f743729SConrad Meyer }
2500f743729SConrad Meyer 
2510f743729SConrad Meyer 
2520f743729SConrad Meyer /*-************************************************************
2530f743729SConrad Meyer * Avoid fseek()'s 2GiB barrier with MSVC, macOS, *BSD, MinGW
2540c16b537SWarner Losh ***************************************************************/
2550c16b537SWarner Losh #if defined(_MSC_VER) && _MSC_VER >= 1400
2560c16b537SWarner Losh #   define LONG_SEEK _fseeki64
2572b9c00cbSConrad Meyer #   define LONG_TELL _ftelli64
2580c16b537SWarner Losh #elif !defined(__64BIT__) && (PLATFORM_POSIX_VERSION >= 200112L) /* No point defining Large file for 64 bit */
2590c16b537SWarner Losh #  define LONG_SEEK fseeko
2602b9c00cbSConrad Meyer #  define LONG_TELL ftello
2610c16b537SWarner Losh #elif defined(__MINGW32__) && !defined(__STRICT_ANSI__) && !defined(__NO_MINGW_LFS) && defined(__MSVCRT__)
2620c16b537SWarner Losh #   define LONG_SEEK fseeko64
2632b9c00cbSConrad Meyer #   define LONG_TELL ftello64
2640c16b537SWarner Losh #elif defined(_WIN32) && !defined(__DJGPP__)
2650c16b537SWarner Losh #   include <windows.h>
LONG_SEEK(FILE * file,__int64 offset,int origin)2660c16b537SWarner Losh     static int LONG_SEEK(FILE* file, __int64 offset, int origin) {
2670c16b537SWarner Losh         LARGE_INTEGER off;
2680c16b537SWarner Losh         DWORD method;
2690c16b537SWarner Losh         off.QuadPart = offset;
2700c16b537SWarner Losh         if (origin == SEEK_END)
2710c16b537SWarner Losh             method = FILE_END;
2720c16b537SWarner Losh         else if (origin == SEEK_CUR)
2730c16b537SWarner Losh             method = FILE_CURRENT;
2740c16b537SWarner Losh         else
2750c16b537SWarner Losh             method = FILE_BEGIN;
2760c16b537SWarner Losh 
2770c16b537SWarner Losh         if (SetFilePointerEx((HANDLE) _get_osfhandle(_fileno(file)), off, NULL, method))
2780c16b537SWarner Losh             return 0;
2790c16b537SWarner Losh         else
2800c16b537SWarner Losh             return -1;
2810c16b537SWarner Losh     }
LONG_TELL(FILE * file)2824d3f1eafSConrad Meyer     static __int64 LONG_TELL(FILE* file) {
2834d3f1eafSConrad Meyer         LARGE_INTEGER off, newOff;
2844d3f1eafSConrad Meyer         off.QuadPart = 0;
2854d3f1eafSConrad Meyer         newOff.QuadPart = 0;
2864d3f1eafSConrad Meyer         SetFilePointerEx((HANDLE) _get_osfhandle(_fileno(file)), off, &newOff, FILE_CURRENT);
2874d3f1eafSConrad Meyer         return newOff.QuadPart;
2884d3f1eafSConrad Meyer     }
2890c16b537SWarner Losh #else
2900c16b537SWarner Losh #   define LONG_SEEK fseek
2912b9c00cbSConrad Meyer #   define LONG_TELL ftell
2920c16b537SWarner Losh #endif
2930c16b537SWarner Losh 
2940c16b537SWarner Losh 
2950c16b537SWarner Losh /*-*************************************
2969cbefe25SConrad Meyer *  Parameters: FIO_prefs_t
2970c16b537SWarner Losh ***************************************/
2982b9c00cbSConrad Meyer 
2999cbefe25SConrad Meyer /* typedef'd to FIO_prefs_t within fileio.h */
3002b9c00cbSConrad Meyer struct FIO_prefs_s {
3012b9c00cbSConrad Meyer 
3022b9c00cbSConrad Meyer     /* Algorithm preferences */
3032b9c00cbSConrad Meyer     FIO_compressionType_t compressionType;
3042b9c00cbSConrad Meyer     U32 sparseFileSupport;   /* 0: no sparse allowed; 1: auto (file yes, stdout no); 2: force sparse */
3052b9c00cbSConrad Meyer     int dictIDFlag;
3062b9c00cbSConrad Meyer     int checksumFlag;
3072b9c00cbSConrad Meyer     int blockSize;
3082b9c00cbSConrad Meyer     int overlapLog;
3092b9c00cbSConrad Meyer     U32 adaptiveMode;
310*5ff13fbcSAllan Jude     U32 useRowMatchFinder;
3112b9c00cbSConrad Meyer     int rsyncable;
3122b9c00cbSConrad Meyer     int minAdaptLevel;
3132b9c00cbSConrad Meyer     int maxAdaptLevel;
3142b9c00cbSConrad Meyer     int ldmFlag;
3152b9c00cbSConrad Meyer     int ldmHashLog;
3162b9c00cbSConrad Meyer     int ldmMinMatch;
3172b9c00cbSConrad Meyer     int ldmBucketSizeLog;
3182b9c00cbSConrad Meyer     int ldmHashRateLog;
3199cbefe25SConrad Meyer     size_t streamSrcSize;
3204d3f1eafSConrad Meyer     size_t targetCBlockSize;
3219cbefe25SConrad Meyer     int srcSizeHint;
3229cbefe25SConrad Meyer     int testMode;
323*5ff13fbcSAllan Jude     ZSTD_paramSwitch_e literalCompressionMode;
3242b9c00cbSConrad Meyer 
3252b9c00cbSConrad Meyer     /* IO preferences */
3262b9c00cbSConrad Meyer     U32 removeSrcFile;
3272b9c00cbSConrad Meyer     U32 overwrite;
3282b9c00cbSConrad Meyer 
3292b9c00cbSConrad Meyer     /* Computation resources preferences */
3302b9c00cbSConrad Meyer     unsigned memLimit;
3312b9c00cbSConrad Meyer     int nbWorkers;
3329cbefe25SConrad Meyer 
3339cbefe25SConrad Meyer     int excludeCompressedFiles;
33437f1f268SConrad Meyer     int patchFromMode;
33537f1f268SConrad Meyer     int contentSize;
336*5ff13fbcSAllan Jude     int allowBlockDevices;
3372b9c00cbSConrad Meyer };
3382b9c00cbSConrad Meyer 
339f7cd7fe5SConrad Meyer /*-*************************************
340f7cd7fe5SConrad Meyer *  Parameters: FIO_ctx_t
341f7cd7fe5SConrad Meyer ***************************************/
342f7cd7fe5SConrad Meyer 
343f7cd7fe5SConrad Meyer /* typedef'd to FIO_ctx_t within fileio.h */
344f7cd7fe5SConrad Meyer struct FIO_ctx_s {
345f7cd7fe5SConrad Meyer 
346f7cd7fe5SConrad Meyer     /* file i/o info */
347f7cd7fe5SConrad Meyer     int nbFilesTotal;
348f7cd7fe5SConrad Meyer     int hasStdinInput;
349f7cd7fe5SConrad Meyer     int hasStdoutOutput;
350f7cd7fe5SConrad Meyer 
351f7cd7fe5SConrad Meyer     /* file i/o state */
352f7cd7fe5SConrad Meyer     int currFileIdx;
353f7cd7fe5SConrad Meyer     int nbFilesProcessed;
354f7cd7fe5SConrad Meyer     size_t totalBytesInput;
355f7cd7fe5SConrad Meyer     size_t totalBytesOutput;
356f7cd7fe5SConrad Meyer };
357f7cd7fe5SConrad Meyer 
3582b9c00cbSConrad Meyer 
3592b9c00cbSConrad Meyer /*-*************************************
3602b9c00cbSConrad Meyer *  Parameters: Initialization
3612b9c00cbSConrad Meyer ***************************************/
3622b9c00cbSConrad Meyer 
3632b9c00cbSConrad Meyer #define FIO_OVERLAP_LOG_NOTSET 9999
3642b9c00cbSConrad Meyer #define FIO_LDM_PARAM_NOTSET 9999
3652b9c00cbSConrad Meyer 
3662b9c00cbSConrad Meyer 
FIO_createPreferences(void)3672b9c00cbSConrad Meyer FIO_prefs_t* FIO_createPreferences(void)
3682b9c00cbSConrad Meyer {
3692b9c00cbSConrad Meyer     FIO_prefs_t* const ret = (FIO_prefs_t*)malloc(sizeof(FIO_prefs_t));
3702b9c00cbSConrad Meyer     if (!ret) EXM_THROW(21, "Allocation error : not enough memory");
3712b9c00cbSConrad Meyer 
3722b9c00cbSConrad Meyer     ret->compressionType = FIO_zstdCompression;
3732b9c00cbSConrad Meyer     ret->overwrite = 0;
3742b9c00cbSConrad Meyer     ret->sparseFileSupport = ZSTD_SPARSE_DEFAULT;
3752b9c00cbSConrad Meyer     ret->dictIDFlag = 1;
3762b9c00cbSConrad Meyer     ret->checksumFlag = 1;
3772b9c00cbSConrad Meyer     ret->removeSrcFile = 0;
3782b9c00cbSConrad Meyer     ret->memLimit = 0;
3792b9c00cbSConrad Meyer     ret->nbWorkers = 1;
3802b9c00cbSConrad Meyer     ret->blockSize = 0;
3812b9c00cbSConrad Meyer     ret->overlapLog = FIO_OVERLAP_LOG_NOTSET;
3822b9c00cbSConrad Meyer     ret->adaptiveMode = 0;
3832b9c00cbSConrad Meyer     ret->rsyncable = 0;
3842b9c00cbSConrad Meyer     ret->minAdaptLevel = -50;   /* initializing this value requires a constant, so ZSTD_minCLevel() doesn't work */
3852b9c00cbSConrad Meyer     ret->maxAdaptLevel = 22;   /* initializing this value requires a constant, so ZSTD_maxCLevel() doesn't work */
3862b9c00cbSConrad Meyer     ret->ldmFlag = 0;
3872b9c00cbSConrad Meyer     ret->ldmHashLog = 0;
3882b9c00cbSConrad Meyer     ret->ldmMinMatch = 0;
3892b9c00cbSConrad Meyer     ret->ldmBucketSizeLog = FIO_LDM_PARAM_NOTSET;
3902b9c00cbSConrad Meyer     ret->ldmHashRateLog = FIO_LDM_PARAM_NOTSET;
3919cbefe25SConrad Meyer     ret->streamSrcSize = 0;
3924d3f1eafSConrad Meyer     ret->targetCBlockSize = 0;
3939cbefe25SConrad Meyer     ret->srcSizeHint = 0;
3949cbefe25SConrad Meyer     ret->testMode = 0;
395*5ff13fbcSAllan Jude     ret->literalCompressionMode = ZSTD_ps_auto;
3969cbefe25SConrad Meyer     ret->excludeCompressedFiles = 0;
397*5ff13fbcSAllan Jude     ret->allowBlockDevices = 0;
3982b9c00cbSConrad Meyer     return ret;
3992b9c00cbSConrad Meyer }
4002b9c00cbSConrad Meyer 
FIO_createContext(void)401f7cd7fe5SConrad Meyer FIO_ctx_t* FIO_createContext(void)
402f7cd7fe5SConrad Meyer {
403f7cd7fe5SConrad Meyer     FIO_ctx_t* const ret = (FIO_ctx_t*)malloc(sizeof(FIO_ctx_t));
404f7cd7fe5SConrad Meyer     if (!ret) EXM_THROW(21, "Allocation error : not enough memory");
405f7cd7fe5SConrad Meyer 
406f7cd7fe5SConrad Meyer     ret->currFileIdx = 0;
407f7cd7fe5SConrad Meyer     ret->hasStdinInput = 0;
408f7cd7fe5SConrad Meyer     ret->hasStdoutOutput = 0;
409f7cd7fe5SConrad Meyer     ret->nbFilesTotal = 1;
410f7cd7fe5SConrad Meyer     ret->nbFilesProcessed = 0;
411f7cd7fe5SConrad Meyer     ret->totalBytesInput = 0;
412f7cd7fe5SConrad Meyer     ret->totalBytesOutput = 0;
413f7cd7fe5SConrad Meyer     return ret;
414f7cd7fe5SConrad Meyer }
415f7cd7fe5SConrad Meyer 
FIO_freePreferences(FIO_prefs_t * const prefs)4162b9c00cbSConrad Meyer void FIO_freePreferences(FIO_prefs_t* const prefs)
4172b9c00cbSConrad Meyer {
4182b9c00cbSConrad Meyer     free(prefs);
4192b9c00cbSConrad Meyer }
4202b9c00cbSConrad Meyer 
FIO_freeContext(FIO_ctx_t * const fCtx)421f7cd7fe5SConrad Meyer void FIO_freeContext(FIO_ctx_t* const fCtx)
422f7cd7fe5SConrad Meyer {
423f7cd7fe5SConrad Meyer     free(fCtx);
424f7cd7fe5SConrad Meyer }
425f7cd7fe5SConrad Meyer 
4262b9c00cbSConrad Meyer 
4272b9c00cbSConrad Meyer /*-*************************************
4282b9c00cbSConrad Meyer *  Parameters: Display Options
4292b9c00cbSConrad Meyer ***************************************/
4302b9c00cbSConrad Meyer 
FIO_setNotificationLevel(int level)4312b9c00cbSConrad Meyer void FIO_setNotificationLevel(int level) { g_display_prefs.displayLevel=level; }
4322b9c00cbSConrad Meyer 
FIO_setProgressSetting(FIO_progressSetting_e setting)433*5ff13fbcSAllan Jude void FIO_setProgressSetting(FIO_progressSetting_e setting) { g_display_prefs.progressSetting = setting; }
4342b9c00cbSConrad Meyer 
4352b9c00cbSConrad Meyer 
4362b9c00cbSConrad Meyer /*-*************************************
4372b9c00cbSConrad Meyer *  Parameters: Setters
4382b9c00cbSConrad Meyer ***************************************/
4392b9c00cbSConrad Meyer 
440f7cd7fe5SConrad Meyer /* FIO_prefs_t functions */
441f7cd7fe5SConrad Meyer 
FIO_setCompressionType(FIO_prefs_t * const prefs,FIO_compressionType_t compressionType)4422b9c00cbSConrad Meyer void FIO_setCompressionType(FIO_prefs_t* const prefs, FIO_compressionType_t compressionType) { prefs->compressionType = compressionType; }
4432b9c00cbSConrad Meyer 
FIO_overwriteMode(FIO_prefs_t * const prefs)4442b9c00cbSConrad Meyer void FIO_overwriteMode(FIO_prefs_t* const prefs) { prefs->overwrite = 1; }
4452b9c00cbSConrad Meyer 
FIO_setSparseWrite(FIO_prefs_t * const prefs,unsigned sparse)4462b9c00cbSConrad Meyer void FIO_setSparseWrite(FIO_prefs_t* const prefs, unsigned sparse) { prefs->sparseFileSupport = sparse; }
4472b9c00cbSConrad Meyer 
FIO_setDictIDFlag(FIO_prefs_t * const prefs,int dictIDFlag)4482b9c00cbSConrad Meyer void FIO_setDictIDFlag(FIO_prefs_t* const prefs, int dictIDFlag) { prefs->dictIDFlag = dictIDFlag; }
4492b9c00cbSConrad Meyer 
FIO_setChecksumFlag(FIO_prefs_t * const prefs,int checksumFlag)4502b9c00cbSConrad Meyer void FIO_setChecksumFlag(FIO_prefs_t* const prefs, int checksumFlag) { prefs->checksumFlag = checksumFlag; }
4512b9c00cbSConrad Meyer 
FIO_setRemoveSrcFile(FIO_prefs_t * const prefs,unsigned flag)4522b9c00cbSConrad Meyer void FIO_setRemoveSrcFile(FIO_prefs_t* const prefs, unsigned flag) { prefs->removeSrcFile = (flag>0); }
4532b9c00cbSConrad Meyer 
FIO_setMemLimit(FIO_prefs_t * const prefs,unsigned memLimit)4542b9c00cbSConrad Meyer void FIO_setMemLimit(FIO_prefs_t* const prefs, unsigned memLimit) { prefs->memLimit = memLimit; }
4552b9c00cbSConrad Meyer 
FIO_setNbWorkers(FIO_prefs_t * const prefs,int nbWorkers)4562b9c00cbSConrad Meyer void FIO_setNbWorkers(FIO_prefs_t* const prefs, int nbWorkers) {
4570c16b537SWarner Losh #ifndef ZSTD_MULTITHREAD
45819fcbaf1SConrad Meyer     if (nbWorkers > 0) DISPLAYLEVEL(2, "Note : multi-threading is disabled \n");
4590c16b537SWarner Losh #endif
4602b9c00cbSConrad Meyer     prefs->nbWorkers = nbWorkers;
4610c16b537SWarner Losh }
4622b9c00cbSConrad Meyer 
FIO_setExcludeCompressedFile(FIO_prefs_t * const prefs,int excludeCompressedFiles)4639cbefe25SConrad Meyer void FIO_setExcludeCompressedFile(FIO_prefs_t* const prefs, int excludeCompressedFiles) { prefs->excludeCompressedFiles = excludeCompressedFiles; }
4649cbefe25SConrad Meyer 
FIO_setAllowBlockDevices(FIO_prefs_t * const prefs,int allowBlockDevices)465*5ff13fbcSAllan Jude void FIO_setAllowBlockDevices(FIO_prefs_t* const prefs, int allowBlockDevices) { prefs->allowBlockDevices = allowBlockDevices; }
466*5ff13fbcSAllan Jude 
FIO_setBlockSize(FIO_prefs_t * const prefs,int blockSize)4672b9c00cbSConrad Meyer void FIO_setBlockSize(FIO_prefs_t* const prefs, int blockSize) {
4682b9c00cbSConrad Meyer     if (blockSize && prefs->nbWorkers==0)
4690c16b537SWarner Losh         DISPLAYLEVEL(2, "Setting block size is useless in single-thread mode \n");
4702b9c00cbSConrad Meyer     prefs->blockSize = blockSize;
4710c16b537SWarner Losh }
4722b9c00cbSConrad Meyer 
FIO_setOverlapLog(FIO_prefs_t * const prefs,int overlapLog)4732b9c00cbSConrad Meyer void FIO_setOverlapLog(FIO_prefs_t* const prefs, int overlapLog){
4742b9c00cbSConrad Meyer     if (overlapLog && prefs->nbWorkers==0)
4750c16b537SWarner Losh         DISPLAYLEVEL(2, "Setting overlapLog is useless in single-thread mode \n");
4762b9c00cbSConrad Meyer     prefs->overlapLog = overlapLog;
4770c16b537SWarner Losh }
4782b9c00cbSConrad Meyer 
FIO_setAdaptiveMode(FIO_prefs_t * const prefs,unsigned adapt)4792b9c00cbSConrad Meyer void FIO_setAdaptiveMode(FIO_prefs_t* const prefs, unsigned adapt) {
4802b9c00cbSConrad Meyer     if ((adapt>0) && (prefs->nbWorkers==0))
4810f743729SConrad Meyer         EXM_THROW(1, "Adaptive mode is not compatible with single thread mode \n");
4822b9c00cbSConrad Meyer     prefs->adaptiveMode = adapt;
4830f743729SConrad Meyer }
4842b9c00cbSConrad Meyer 
FIO_setUseRowMatchFinder(FIO_prefs_t * const prefs,int useRowMatchFinder)485*5ff13fbcSAllan Jude void FIO_setUseRowMatchFinder(FIO_prefs_t* const prefs, int useRowMatchFinder) {
486*5ff13fbcSAllan Jude     prefs->useRowMatchFinder = useRowMatchFinder;
487*5ff13fbcSAllan Jude }
488*5ff13fbcSAllan Jude 
FIO_setRsyncable(FIO_prefs_t * const prefs,int rsyncable)4892b9c00cbSConrad Meyer void FIO_setRsyncable(FIO_prefs_t* const prefs, int rsyncable) {
4902b9c00cbSConrad Meyer     if ((rsyncable>0) && (prefs->nbWorkers==0))
491a0483764SConrad Meyer         EXM_THROW(1, "Rsyncable mode is not compatible with single thread mode \n");
4922b9c00cbSConrad Meyer     prefs->rsyncable = rsyncable;
493a0483764SConrad Meyer }
4942b9c00cbSConrad Meyer 
FIO_setStreamSrcSize(FIO_prefs_t * const prefs,size_t streamSrcSize)4959cbefe25SConrad Meyer void FIO_setStreamSrcSize(FIO_prefs_t* const prefs, size_t streamSrcSize) {
4969cbefe25SConrad Meyer     prefs->streamSrcSize = streamSrcSize;
4979cbefe25SConrad Meyer }
4989cbefe25SConrad Meyer 
FIO_setTargetCBlockSize(FIO_prefs_t * const prefs,size_t targetCBlockSize)4994d3f1eafSConrad Meyer void FIO_setTargetCBlockSize(FIO_prefs_t* const prefs, size_t targetCBlockSize) {
5004d3f1eafSConrad Meyer     prefs->targetCBlockSize = targetCBlockSize;
5014d3f1eafSConrad Meyer }
5024d3f1eafSConrad Meyer 
FIO_setSrcSizeHint(FIO_prefs_t * const prefs,size_t srcSizeHint)5039cbefe25SConrad Meyer void FIO_setSrcSizeHint(FIO_prefs_t* const prefs, size_t srcSizeHint) {
5049cbefe25SConrad Meyer     prefs->srcSizeHint = (int)MIN((size_t)INT_MAX, srcSizeHint);
5059cbefe25SConrad Meyer }
5069cbefe25SConrad Meyer 
FIO_setTestMode(FIO_prefs_t * const prefs,int testMode)5079cbefe25SConrad Meyer void FIO_setTestMode(FIO_prefs_t* const prefs, int testMode) {
5089cbefe25SConrad Meyer     prefs->testMode = (testMode!=0);
5099cbefe25SConrad Meyer }
5109cbefe25SConrad Meyer 
FIO_setLiteralCompressionMode(FIO_prefs_t * const prefs,ZSTD_paramSwitch_e mode)5112b9c00cbSConrad Meyer void FIO_setLiteralCompressionMode(
5122b9c00cbSConrad Meyer         FIO_prefs_t* const prefs,
513*5ff13fbcSAllan Jude         ZSTD_paramSwitch_e mode) {
5142b9c00cbSConrad Meyer     prefs->literalCompressionMode = mode;
5152b9c00cbSConrad Meyer }
5162b9c00cbSConrad Meyer 
FIO_setAdaptMin(FIO_prefs_t * const prefs,int minCLevel)5172b9c00cbSConrad Meyer void FIO_setAdaptMin(FIO_prefs_t* const prefs, int minCLevel)
5180f743729SConrad Meyer {
5190f743729SConrad Meyer #ifndef ZSTD_NOCOMPRESS
5200f743729SConrad Meyer     assert(minCLevel >= ZSTD_minCLevel());
5210f743729SConrad Meyer #endif
5222b9c00cbSConrad Meyer     prefs->minAdaptLevel = minCLevel;
5230f743729SConrad Meyer }
5242b9c00cbSConrad Meyer 
FIO_setAdaptMax(FIO_prefs_t * const prefs,int maxCLevel)5252b9c00cbSConrad Meyer void FIO_setAdaptMax(FIO_prefs_t* const prefs, int maxCLevel)
5260f743729SConrad Meyer {
5272b9c00cbSConrad Meyer     prefs->maxAdaptLevel = maxCLevel;
5280f743729SConrad Meyer }
5290f743729SConrad Meyer 
FIO_setLdmFlag(FIO_prefs_t * const prefs,unsigned ldmFlag)5302b9c00cbSConrad Meyer void FIO_setLdmFlag(FIO_prefs_t* const prefs, unsigned ldmFlag) {
5312b9c00cbSConrad Meyer     prefs->ldmFlag = (ldmFlag>0);
5320c16b537SWarner Losh }
5330c16b537SWarner Losh 
FIO_setLdmHashLog(FIO_prefs_t * const prefs,int ldmHashLog)5342b9c00cbSConrad Meyer void FIO_setLdmHashLog(FIO_prefs_t* const prefs, int ldmHashLog) {
5352b9c00cbSConrad Meyer     prefs->ldmHashLog = ldmHashLog;
5360c16b537SWarner Losh }
5370c16b537SWarner Losh 
FIO_setLdmMinMatch(FIO_prefs_t * const prefs,int ldmMinMatch)5382b9c00cbSConrad Meyer void FIO_setLdmMinMatch(FIO_prefs_t* const prefs, int ldmMinMatch) {
5392b9c00cbSConrad Meyer     prefs->ldmMinMatch = ldmMinMatch;
5400c16b537SWarner Losh }
5410c16b537SWarner Losh 
FIO_setLdmBucketSizeLog(FIO_prefs_t * const prefs,int ldmBucketSizeLog)5422b9c00cbSConrad Meyer void FIO_setLdmBucketSizeLog(FIO_prefs_t* const prefs, int ldmBucketSizeLog) {
5432b9c00cbSConrad Meyer     prefs->ldmBucketSizeLog = ldmBucketSizeLog;
5442b9c00cbSConrad Meyer }
5452b9c00cbSConrad Meyer 
5462b9c00cbSConrad Meyer 
FIO_setLdmHashRateLog(FIO_prefs_t * const prefs,int ldmHashRateLog)5472b9c00cbSConrad Meyer void FIO_setLdmHashRateLog(FIO_prefs_t* const prefs, int ldmHashRateLog) {
5482b9c00cbSConrad Meyer     prefs->ldmHashRateLog = ldmHashRateLog;
5492b9c00cbSConrad Meyer }
5500c16b537SWarner Losh 
FIO_setPatchFromMode(FIO_prefs_t * const prefs,int value)55137f1f268SConrad Meyer void FIO_setPatchFromMode(FIO_prefs_t* const prefs, int value)
55237f1f268SConrad Meyer {
55337f1f268SConrad Meyer     prefs->patchFromMode = value != 0;
55437f1f268SConrad Meyer }
55537f1f268SConrad Meyer 
FIO_setContentSize(FIO_prefs_t * const prefs,int value)55637f1f268SConrad Meyer void FIO_setContentSize(FIO_prefs_t* const prefs, int value)
55737f1f268SConrad Meyer {
55837f1f268SConrad Meyer     prefs->contentSize = value != 0;
55937f1f268SConrad Meyer }
5600c16b537SWarner Losh 
561f7cd7fe5SConrad Meyer /* FIO_ctx_t functions */
562f7cd7fe5SConrad Meyer 
FIO_setHasStdoutOutput(FIO_ctx_t * const fCtx,int value)563f7cd7fe5SConrad Meyer void FIO_setHasStdoutOutput(FIO_ctx_t* const fCtx, int value) {
564f7cd7fe5SConrad Meyer     fCtx->hasStdoutOutput = value;
565f7cd7fe5SConrad Meyer }
566f7cd7fe5SConrad Meyer 
FIO_setNbFilesTotal(FIO_ctx_t * const fCtx,int value)567f7cd7fe5SConrad Meyer void FIO_setNbFilesTotal(FIO_ctx_t* const fCtx, int value)
568f7cd7fe5SConrad Meyer {
569f7cd7fe5SConrad Meyer     fCtx->nbFilesTotal = value;
570f7cd7fe5SConrad Meyer }
571f7cd7fe5SConrad Meyer 
FIO_determineHasStdinInput(FIO_ctx_t * const fCtx,const FileNamesTable * const filenames)572f7cd7fe5SConrad Meyer void FIO_determineHasStdinInput(FIO_ctx_t* const fCtx, const FileNamesTable* const filenames) {
573f7cd7fe5SConrad Meyer     size_t i = 0;
574f7cd7fe5SConrad Meyer     for ( ; i < filenames->tableSize; ++i) {
575f7cd7fe5SConrad Meyer         if (!strcmp(stdinmark, filenames->fileNames[i])) {
576f7cd7fe5SConrad Meyer             fCtx->hasStdinInput = 1;
577f7cd7fe5SConrad Meyer             return;
578f7cd7fe5SConrad Meyer         }
579f7cd7fe5SConrad Meyer     }
580f7cd7fe5SConrad Meyer }
581f7cd7fe5SConrad Meyer 
5820c16b537SWarner Losh /*-*************************************
5830c16b537SWarner Losh *  Functions
5840c16b537SWarner Losh ***************************************/
585f7cd7fe5SConrad Meyer /** FIO_removeFile() :
5860c16b537SWarner Losh  * @result : Unlink `fileName`, even if it's read-only */
FIO_removeFile(const char * path)587f7cd7fe5SConrad Meyer static int FIO_removeFile(const char* path)
5880c16b537SWarner Losh {
589f7cd7fe5SConrad Meyer     stat_t statbuf;
590f7cd7fe5SConrad Meyer     if (!UTIL_stat(path, &statbuf)) {
591f7cd7fe5SConrad Meyer         DISPLAYLEVEL(2, "zstd: Failed to stat %s while trying to remove it\n", path);
592f7cd7fe5SConrad Meyer         return 0;
593f7cd7fe5SConrad Meyer     }
594f7cd7fe5SConrad Meyer     if (!UTIL_isRegularFileStat(&statbuf)) {
595052d3c12SConrad Meyer         DISPLAYLEVEL(2, "zstd: Refusing to remove non-regular file %s\n", path);
596052d3c12SConrad Meyer         return 0;
597052d3c12SConrad Meyer     }
5980c16b537SWarner Losh #if defined(_WIN32) || defined(WIN32)
5990c16b537SWarner Losh     /* windows doesn't allow remove read-only files,
6000c16b537SWarner Losh      * so try to make it writable first */
601f7cd7fe5SConrad Meyer     if (!(statbuf.st_mode & _S_IWRITE)) {
602f7cd7fe5SConrad Meyer         UTIL_chmod(path, &statbuf, _S_IWRITE);
603f7cd7fe5SConrad Meyer     }
6040c16b537SWarner Losh #endif
6050c16b537SWarner Losh     return remove(path);
6060c16b537SWarner Losh }
6070c16b537SWarner Losh 
6080c16b537SWarner Losh /** FIO_openSrcFile() :
609*5ff13fbcSAllan Jude  *  condition : `srcFileName` must be non-NULL. `prefs` may be NULL.
610052d3c12SConrad Meyer  * @result : FILE* to `srcFileName`, or NULL if it fails */
FIO_openSrcFile(const FIO_prefs_t * const prefs,const char * srcFileName)611*5ff13fbcSAllan Jude static FILE* FIO_openSrcFile(const FIO_prefs_t* const prefs, const char* srcFileName)
6120c16b537SWarner Losh {
613f7cd7fe5SConrad Meyer     stat_t statbuf;
614*5ff13fbcSAllan Jude     int allowBlockDevices = prefs != NULL ? prefs->allowBlockDevices : 0;
615052d3c12SConrad Meyer     assert(srcFileName != NULL);
6160c16b537SWarner Losh     if (!strcmp (srcFileName, stdinmark)) {
6170c16b537SWarner Losh         DISPLAYLEVEL(4,"Using stdin for input \n");
6180c16b537SWarner Losh         SET_BINARY_MODE(stdin);
619052d3c12SConrad Meyer         return stdin;
620052d3c12SConrad Meyer     }
621052d3c12SConrad Meyer 
622f7cd7fe5SConrad Meyer     if (!UTIL_stat(srcFileName, &statbuf)) {
623a0483764SConrad Meyer         DISPLAYLEVEL(1, "zstd: can't stat %s : %s -- ignored \n",
624a0483764SConrad Meyer                         srcFileName, strerror(errno));
625a0483764SConrad Meyer         return NULL;
626a0483764SConrad Meyer     }
627a0483764SConrad Meyer 
628f7cd7fe5SConrad Meyer     if (!UTIL_isRegularFileStat(&statbuf)
629f7cd7fe5SConrad Meyer      && !UTIL_isFIFOStat(&statbuf)
630*5ff13fbcSAllan Jude      && !(allowBlockDevices && UTIL_isBlockDevStat(&statbuf))
6319cbefe25SConrad Meyer     ) {
6320c16b537SWarner Losh         DISPLAYLEVEL(1, "zstd: %s is not a regular file -- ignored \n",
6330c16b537SWarner Losh                         srcFileName);
6340c16b537SWarner Losh         return NULL;
6350c16b537SWarner Losh     }
636052d3c12SConrad Meyer 
637052d3c12SConrad Meyer     {   FILE* const f = fopen(srcFileName, "rb");
6380c16b537SWarner Losh         if (f == NULL)
6390c16b537SWarner Losh             DISPLAYLEVEL(1, "zstd: %s: %s \n", srcFileName, strerror(errno));
6400c16b537SWarner Losh         return f;
6410c16b537SWarner Losh     }
642052d3c12SConrad Meyer }
6430c16b537SWarner Losh 
6440c16b537SWarner Losh /** FIO_openDstFile() :
6450c16b537SWarner Losh  *  condition : `dstFileName` must be non-NULL.
6460c16b537SWarner Losh  * @result : FILE* to `dstFileName`, or NULL if it fails */
6479cbefe25SConrad Meyer static FILE*
FIO_openDstFile(FIO_ctx_t * fCtx,FIO_prefs_t * const prefs,const char * srcFileName,const char * dstFileName,const int mode)648f7cd7fe5SConrad Meyer FIO_openDstFile(FIO_ctx_t* fCtx, FIO_prefs_t* const prefs,
649*5ff13fbcSAllan Jude                 const char* srcFileName, const char* dstFileName,
650*5ff13fbcSAllan Jude                 const int mode)
6510c16b537SWarner Losh {
6529cbefe25SConrad Meyer     if (prefs->testMode) return NULL;  /* do not open file in test mode */
6539cbefe25SConrad Meyer 
654052d3c12SConrad Meyer     assert(dstFileName != NULL);
6550c16b537SWarner Losh     if (!strcmp (dstFileName, stdoutmark)) {
6560c16b537SWarner Losh         DISPLAYLEVEL(4,"Using stdout for output \n");
6570c16b537SWarner Losh         SET_BINARY_MODE(stdout);
6582b9c00cbSConrad Meyer         if (prefs->sparseFileSupport == 1) {
6592b9c00cbSConrad Meyer             prefs->sparseFileSupport = 0;
6600c16b537SWarner Losh             DISPLAYLEVEL(4, "Sparse File Support is automatically disabled on stdout ; try --sparse \n");
6610c16b537SWarner Losh         }
662052d3c12SConrad Meyer         return stdout;
663052d3c12SConrad Meyer     }
664052d3c12SConrad Meyer 
6652b9c00cbSConrad Meyer     /* ensure dst is not the same as src */
6662b9c00cbSConrad Meyer     if (srcFileName != NULL && UTIL_isSameFile(srcFileName, dstFileName)) {
6672b9c00cbSConrad Meyer         DISPLAYLEVEL(1, "zstd: Refusing to open an output file which will overwrite the input file \n");
668a0483764SConrad Meyer         return NULL;
669a0483764SConrad Meyer     }
670a0483764SConrad Meyer 
6712b9c00cbSConrad Meyer     if (prefs->sparseFileSupport == 1) {
6722b9c00cbSConrad Meyer         prefs->sparseFileSupport = ZSTD_SPARSE_DEFAULT;
6730c16b537SWarner Losh     }
674052d3c12SConrad Meyer 
675052d3c12SConrad Meyer     if (UTIL_isRegularFile(dstFileName)) {
6760c16b537SWarner Losh         /* Check if destination file already exists */
6779cbefe25SConrad Meyer #if !defined(_WIN32)
6789cbefe25SConrad Meyer         /* this test does not work on Windows :
6799cbefe25SConrad Meyer          * `NUL` and `nul` are detected as regular files */
680a0483764SConrad Meyer         if (!strcmp(dstFileName, nulmark)) {
681a0483764SConrad Meyer             EXM_THROW(40, "%s is unexpectedly categorized as a regular file",
682a0483764SConrad Meyer                         dstFileName);
683a0483764SConrad Meyer         }
6849cbefe25SConrad Meyer #endif
6852b9c00cbSConrad Meyer         if (!prefs->overwrite) {
6862b9c00cbSConrad Meyer             if (g_display_prefs.displayLevel <= 1) {
6870c16b537SWarner Losh                 /* No interaction possible */
6880c16b537SWarner Losh                 DISPLAY("zstd: %s already exists; not overwritten  \n",
6890c16b537SWarner Losh                         dstFileName);
6900c16b537SWarner Losh                 return NULL;
6910c16b537SWarner Losh             }
692f7cd7fe5SConrad Meyer             DISPLAY("zstd: %s already exists; ", dstFileName);
693f7cd7fe5SConrad Meyer             if (UTIL_requireUserConfirmation("overwrite (y/n) ? ", "Not overwritten  \n", "yY", fCtx->hasStdinInput))
6940c16b537SWarner Losh                 return NULL;
6950c16b537SWarner Losh         }
6960c16b537SWarner Losh         /* need to unlink */
697f7cd7fe5SConrad Meyer         FIO_removeFile(dstFileName);
698*5ff13fbcSAllan Jude     }
6990c16b537SWarner Losh 
700*5ff13fbcSAllan Jude     {
701*5ff13fbcSAllan Jude #if defined(_WIN32)
702*5ff13fbcSAllan Jude         /* Windows requires opening the file as a "binary" file to avoid
703*5ff13fbcSAllan Jude          * mangling. This macro doesn't exist on unix. */
704*5ff13fbcSAllan Jude         const int openflags = O_WRONLY|O_CREAT|O_TRUNC|O_BINARY;
705*5ff13fbcSAllan Jude         const int fd = _open(dstFileName, openflags, mode);
706*5ff13fbcSAllan Jude         FILE* f = NULL;
707*5ff13fbcSAllan Jude         if (fd != -1) {
708*5ff13fbcSAllan Jude             f = _fdopen(fd, "wb");
709*5ff13fbcSAllan Jude         }
710*5ff13fbcSAllan Jude #else
711*5ff13fbcSAllan Jude         const int openflags = O_WRONLY|O_CREAT|O_TRUNC;
712*5ff13fbcSAllan Jude         const int fd = open(dstFileName, openflags, mode);
713*5ff13fbcSAllan Jude         FILE* f = NULL;
714*5ff13fbcSAllan Jude         if (fd != -1) {
715*5ff13fbcSAllan Jude             f = fdopen(fd, "wb");
716*5ff13fbcSAllan Jude         }
717*5ff13fbcSAllan Jude #endif
7184d3f1eafSConrad Meyer         if (f == NULL) {
719052d3c12SConrad Meyer             DISPLAYLEVEL(1, "zstd: %s: %s\n", dstFileName, strerror(errno));
7204d3f1eafSConrad Meyer         }
7210c16b537SWarner Losh         return f;
7220c16b537SWarner Losh     }
723052d3c12SConrad Meyer }
7240c16b537SWarner Losh 
7250c16b537SWarner Losh /*! FIO_createDictBuffer() :
7260c16b537SWarner Losh  *  creates a buffer, pointed by `*bufferPtr`,
7270c16b537SWarner Losh  *  loads `filename` content into it, up to DICTSIZE_MAX bytes.
7280c16b537SWarner Losh  * @return : loaded size
7290c16b537SWarner Losh  *  if fileName==NULL, returns 0 and a NULL pointer
7300c16b537SWarner Losh  */
FIO_createDictBuffer(void ** bufferPtr,const char * fileName,FIO_prefs_t * const prefs)73137f1f268SConrad Meyer static size_t FIO_createDictBuffer(void** bufferPtr, const char* fileName, FIO_prefs_t* const prefs)
7320c16b537SWarner Losh {
7330c16b537SWarner Losh     FILE* fileHandle;
7340c16b537SWarner Losh     U64 fileSize;
735*5ff13fbcSAllan Jude     stat_t statbuf;
7360c16b537SWarner Losh 
737052d3c12SConrad Meyer     assert(bufferPtr != NULL);
7380c16b537SWarner Losh     *bufferPtr = NULL;
7390c16b537SWarner Losh     if (fileName == NULL) return 0;
7400c16b537SWarner Losh 
7410c16b537SWarner Losh     DISPLAYLEVEL(4,"Loading %s as dictionary \n", fileName);
742a0483764SConrad Meyer 
743*5ff13fbcSAllan Jude     if (!UTIL_stat(fileName, &statbuf)) {
744*5ff13fbcSAllan Jude         EXM_THROW(31, "Stat failed on dictionary file %s: %s", fileName, strerror(errno));
745*5ff13fbcSAllan Jude     }
746*5ff13fbcSAllan Jude 
747*5ff13fbcSAllan Jude     if (!UTIL_isRegularFileStat(&statbuf)) {
748*5ff13fbcSAllan Jude         EXM_THROW(32, "Dictionary %s must be a regular file.", fileName);
749*5ff13fbcSAllan Jude     }
750*5ff13fbcSAllan Jude 
751*5ff13fbcSAllan Jude     fileHandle = fopen(fileName, "rb");
752*5ff13fbcSAllan Jude 
753*5ff13fbcSAllan Jude     if (fileHandle == NULL) {
754*5ff13fbcSAllan Jude         EXM_THROW(33, "Couldn't open dictionary %s: %s", fileName, strerror(errno));
755*5ff13fbcSAllan Jude     }
756*5ff13fbcSAllan Jude 
757*5ff13fbcSAllan Jude     fileSize = UTIL_getFileSizeStat(&statbuf);
75837f1f268SConrad Meyer     {
75937f1f268SConrad Meyer         size_t const dictSizeMax = prefs->patchFromMode ? prefs->memLimit : DICTSIZE_MAX;
76037f1f268SConrad Meyer         if (fileSize >  dictSizeMax) {
761*5ff13fbcSAllan Jude             EXM_THROW(34, "Dictionary file %s is too large (> %u bytes)",
76237f1f268SConrad Meyer                             fileName,  (unsigned)dictSizeMax);   /* avoid extreme cases */
76337f1f268SConrad Meyer         }
7640c16b537SWarner Losh     }
7650c16b537SWarner Losh     *bufferPtr = malloc((size_t)fileSize);
7660c16b537SWarner Losh     if (*bufferPtr==NULL) EXM_THROW(34, "%s", strerror(errno));
7670c16b537SWarner Losh     {   size_t const readSize = fread(*bufferPtr, 1, (size_t)fileSize, fileHandle);
768*5ff13fbcSAllan Jude         if (readSize != fileSize) {
769a0483764SConrad Meyer             EXM_THROW(35, "Error reading dictionary file %s : %s",
770a0483764SConrad Meyer                     fileName, strerror(errno));
7710c16b537SWarner Losh         }
772*5ff13fbcSAllan Jude     }
7730c16b537SWarner Losh     fclose(fileHandle);
7740c16b537SWarner Losh     return (size_t)fileSize;
7750c16b537SWarner Losh }
7760c16b537SWarner Losh 
7779cbefe25SConrad Meyer 
7789cbefe25SConrad Meyer 
7799cbefe25SConrad Meyer /* FIO_checkFilenameCollisions() :
7809cbefe25SConrad Meyer  * Checks for and warns if there are any files that would have the same output path
7819cbefe25SConrad Meyer  */
FIO_checkFilenameCollisions(const char ** filenameTable,unsigned nbFiles)7829cbefe25SConrad Meyer int FIO_checkFilenameCollisions(const char** filenameTable, unsigned nbFiles) {
783f7cd7fe5SConrad Meyer     const char **filenameTableSorted, *prevElem, *filename;
7849cbefe25SConrad Meyer     unsigned u;
7859cbefe25SConrad Meyer 
7869cbefe25SConrad Meyer     filenameTableSorted = (const char**) malloc(sizeof(char*) * nbFiles);
7879cbefe25SConrad Meyer     if (!filenameTableSorted) {
7889cbefe25SConrad Meyer         DISPLAY("Unable to malloc new str array, not checking for name collisions\n");
7899cbefe25SConrad Meyer         return 1;
7909cbefe25SConrad Meyer     }
7919cbefe25SConrad Meyer 
7929cbefe25SConrad Meyer     for (u = 0; u < nbFiles; ++u) {
793f7cd7fe5SConrad Meyer         filename = strrchr(filenameTable[u], PATH_SEP);
7949cbefe25SConrad Meyer         if (filename == NULL) {
7959cbefe25SConrad Meyer             filenameTableSorted[u] = filenameTable[u];
7969cbefe25SConrad Meyer         } else {
7979cbefe25SConrad Meyer             filenameTableSorted[u] = filename+1;
7989cbefe25SConrad Meyer         }
7999cbefe25SConrad Meyer     }
8009cbefe25SConrad Meyer 
8019cbefe25SConrad Meyer     qsort((void*)filenameTableSorted, nbFiles, sizeof(char*), UTIL_compareStr);
8029cbefe25SConrad Meyer     prevElem = filenameTableSorted[0];
8039cbefe25SConrad Meyer     for (u = 1; u < nbFiles; ++u) {
8049cbefe25SConrad Meyer         if (strcmp(prevElem, filenameTableSorted[u]) == 0) {
8059cbefe25SConrad Meyer             DISPLAY("WARNING: Two files have same filename: %s\n", prevElem);
8069cbefe25SConrad Meyer         }
8079cbefe25SConrad Meyer         prevElem = filenameTableSorted[u];
8089cbefe25SConrad Meyer     }
8099cbefe25SConrad Meyer 
8109cbefe25SConrad Meyer     free((void*)filenameTableSorted);
8119cbefe25SConrad Meyer     return 0;
8129cbefe25SConrad Meyer }
8139cbefe25SConrad Meyer 
8149cbefe25SConrad Meyer static const char*
extractFilename(const char * path,char separator)8159cbefe25SConrad Meyer extractFilename(const char* path, char separator)
8169cbefe25SConrad Meyer {
8179cbefe25SConrad Meyer     const char* search = strrchr(path, separator);
8189cbefe25SConrad Meyer     if (search == NULL) return path;
8199cbefe25SConrad Meyer     return search+1;
8209cbefe25SConrad Meyer }
8219cbefe25SConrad Meyer 
8229cbefe25SConrad Meyer /* FIO_createFilename_fromOutDir() :
8239cbefe25SConrad Meyer  * Takes a source file name and specified output directory, and
8249cbefe25SConrad Meyer  * allocates memory for and returns a pointer to final path.
8259cbefe25SConrad Meyer  * This function never returns an error (it may abort() in case of pb)
8269cbefe25SConrad Meyer  */
8279cbefe25SConrad Meyer static char*
FIO_createFilename_fromOutDir(const char * path,const char * outDirName,const size_t suffixLen)8289cbefe25SConrad Meyer FIO_createFilename_fromOutDir(const char* path, const char* outDirName, const size_t suffixLen)
8299cbefe25SConrad Meyer {
8309cbefe25SConrad Meyer     const char* filenameStart;
8319cbefe25SConrad Meyer     char separator;
8329cbefe25SConrad Meyer     char* result;
8339cbefe25SConrad Meyer 
8349cbefe25SConrad Meyer #if defined(_MSC_VER) || defined(__MINGW32__) || defined (__MSVCRT__) /* windows support */
8359cbefe25SConrad Meyer     separator = '\\';
8369cbefe25SConrad Meyer #else
8379cbefe25SConrad Meyer     separator = '/';
8389cbefe25SConrad Meyer #endif
8399cbefe25SConrad Meyer 
8409cbefe25SConrad Meyer     filenameStart = extractFilename(path, separator);
8419cbefe25SConrad Meyer #if defined(_MSC_VER) || defined(__MINGW32__) || defined (__MSVCRT__) /* windows support */
8429cbefe25SConrad Meyer     filenameStart = extractFilename(filenameStart, '/');  /* sometimes, '/' separator is also used on Windows (mingw+msys2) */
8439cbefe25SConrad Meyer #endif
8449cbefe25SConrad Meyer 
8459cbefe25SConrad Meyer     result = (char*) calloc(1, strlen(outDirName) + 1 + strlen(filenameStart) + suffixLen + 1);
8469cbefe25SConrad Meyer     if (!result) {
8479cbefe25SConrad Meyer         EXM_THROW(30, "zstd: FIO_createFilename_fromOutDir: %s", strerror(errno));
8489cbefe25SConrad Meyer     }
8499cbefe25SConrad Meyer 
8509cbefe25SConrad Meyer     memcpy(result, outDirName, strlen(outDirName));
8519cbefe25SConrad Meyer     if (outDirName[strlen(outDirName)-1] == separator) {
8529cbefe25SConrad Meyer         memcpy(result + strlen(outDirName), filenameStart, strlen(filenameStart));
8539cbefe25SConrad Meyer     } else {
8549cbefe25SConrad Meyer         memcpy(result + strlen(outDirName), &separator, 1);
8559cbefe25SConrad Meyer         memcpy(result + strlen(outDirName) + 1, filenameStart, strlen(filenameStart));
8569cbefe25SConrad Meyer     }
8579cbefe25SConrad Meyer 
8589cbefe25SConrad Meyer     return result;
8599cbefe25SConrad Meyer }
8609cbefe25SConrad Meyer 
86137f1f268SConrad Meyer /* FIO_highbit64() :
86237f1f268SConrad Meyer  * gives position of highest bit.
86337f1f268SConrad Meyer  * note : only works for v > 0 !
86437f1f268SConrad Meyer  */
FIO_highbit64(unsigned long long v)86537f1f268SConrad Meyer static unsigned FIO_highbit64(unsigned long long v)
86637f1f268SConrad Meyer {
86737f1f268SConrad Meyer     unsigned count = 0;
86837f1f268SConrad Meyer     assert(v != 0);
86937f1f268SConrad Meyer     v >>= 1;
87037f1f268SConrad Meyer     while (v) { v >>= 1; count++; }
87137f1f268SConrad Meyer     return count;
87237f1f268SConrad Meyer }
87337f1f268SConrad Meyer 
FIO_adjustMemLimitForPatchFromMode(FIO_prefs_t * const prefs,unsigned long long const dictSize,unsigned long long const maxSrcFileSize)87437f1f268SConrad Meyer static void FIO_adjustMemLimitForPatchFromMode(FIO_prefs_t* const prefs,
87537f1f268SConrad Meyer                                     unsigned long long const dictSize,
87637f1f268SConrad Meyer                                     unsigned long long const maxSrcFileSize)
87737f1f268SConrad Meyer {
87837f1f268SConrad Meyer     unsigned long long maxSize = MAX(prefs->memLimit, MAX(dictSize, maxSrcFileSize));
879f7cd7fe5SConrad Meyer     unsigned const maxWindowSize = (1U << ZSTD_WINDOWLOG_MAX);
880f7cd7fe5SConrad Meyer     if (maxSize == UTIL_FILESIZE_UNKNOWN)
881f7cd7fe5SConrad Meyer         EXM_THROW(42, "Using --patch-from with stdin requires --stream-size");
88237f1f268SConrad Meyer     assert(maxSize != UTIL_FILESIZE_UNKNOWN);
883f7cd7fe5SConrad Meyer     if (maxSize > maxWindowSize)
884f7cd7fe5SConrad Meyer         EXM_THROW(42, "Can't handle files larger than %u GB\n", maxWindowSize/(1 GB));
88537f1f268SConrad Meyer     FIO_setMemLimit(prefs, (unsigned)maxSize);
88637f1f268SConrad Meyer }
88737f1f268SConrad Meyer 
888f7cd7fe5SConrad Meyer /* FIO_removeMultiFilesWarning() :
889f7cd7fe5SConrad Meyer  * Returns 1 if the console should abort, 0 if console should proceed.
890f7cd7fe5SConrad Meyer  * This function handles logic when processing multiple files with -o, displaying the appropriate warnings/prompts.
891f7cd7fe5SConrad Meyer  *
892f7cd7fe5SConrad Meyer  * If -f is specified, or there is just 1 file, zstd will always proceed as usual.
893f7cd7fe5SConrad Meyer  * If --rm is specified, there will be a prompt asking for user confirmation.
894f7cd7fe5SConrad Meyer  *         If -f is specified with --rm, zstd will proceed as usual
895f7cd7fe5SConrad Meyer  *         If -q is specified with --rm, zstd will abort pre-emptively
896f7cd7fe5SConrad Meyer  *         If neither flag is specified, zstd will prompt the user for confirmation to proceed.
897f7cd7fe5SConrad Meyer  * If --rm is not specified, then zstd will print a warning to the user (which can be silenced with -q).
898f7cd7fe5SConrad Meyer  * However, if the output is stdout, we will always abort rather than displaying the warning prompt.
899f7cd7fe5SConrad Meyer  */
FIO_removeMultiFilesWarning(FIO_ctx_t * const fCtx,const FIO_prefs_t * const prefs,const char * outFileName,int displayLevelCutoff)900f7cd7fe5SConrad Meyer static int FIO_removeMultiFilesWarning(FIO_ctx_t* const fCtx, const FIO_prefs_t* const prefs, const char* outFileName, int displayLevelCutoff)
901f7cd7fe5SConrad Meyer {
902f7cd7fe5SConrad Meyer     int error = 0;
903f7cd7fe5SConrad Meyer     if (fCtx->nbFilesTotal > 1 && !prefs->overwrite) {
904f7cd7fe5SConrad Meyer         if (g_display_prefs.displayLevel <= displayLevelCutoff) {
905f7cd7fe5SConrad Meyer             if (prefs->removeSrcFile) {
906*5ff13fbcSAllan Jude                 DISPLAYLEVEL(1, "zstd: Aborting... not deleting files and processing into dst: %s\n", outFileName);
907f7cd7fe5SConrad Meyer                 error =  1;
908f7cd7fe5SConrad Meyer             }
909f7cd7fe5SConrad Meyer         } else {
910f7cd7fe5SConrad Meyer             if (!strcmp(outFileName, stdoutmark)) {
911*5ff13fbcSAllan Jude                 DISPLAYLEVEL(2, "zstd: WARNING: all input files will be processed and concatenated into stdout. \n");
912f7cd7fe5SConrad Meyer             } else {
913*5ff13fbcSAllan Jude                 DISPLAYLEVEL(2, "zstd: WARNING: all input files will be processed and concatenated into a single output file: %s \n", outFileName);
914f7cd7fe5SConrad Meyer             }
915*5ff13fbcSAllan Jude             DISPLAYLEVEL(2, "The concatenated output CANNOT regenerate the original directory tree. \n")
916f7cd7fe5SConrad Meyer             if (prefs->removeSrcFile) {
917f7cd7fe5SConrad Meyer                 if (fCtx->hasStdoutOutput) {
918*5ff13fbcSAllan Jude                     DISPLAYLEVEL(1, "Aborting. Use -f if you really want to delete the files and output to stdout\n");
919f7cd7fe5SConrad Meyer                     error = 1;
920f7cd7fe5SConrad Meyer                 } else {
921f7cd7fe5SConrad Meyer                     error = g_display_prefs.displayLevel > displayLevelCutoff && UTIL_requireUserConfirmation("This is a destructive operation. Proceed? (y/n): ", "Aborting...", "yY", fCtx->hasStdinInput);
922f7cd7fe5SConrad Meyer                 }
923f7cd7fe5SConrad Meyer             }
924f7cd7fe5SConrad Meyer         }
925f7cd7fe5SConrad Meyer     }
926f7cd7fe5SConrad Meyer     return error;
927f7cd7fe5SConrad Meyer }
928f7cd7fe5SConrad Meyer 
9290c16b537SWarner Losh #ifndef ZSTD_NOCOMPRESS
9300c16b537SWarner Losh 
93119fcbaf1SConrad Meyer /* **********************************************************************
9320c16b537SWarner Losh  *  Compression
9330c16b537SWarner Losh  ************************************************************************/
9340c16b537SWarner Losh typedef struct {
9350c16b537SWarner Losh     FILE* srcFile;
9360c16b537SWarner Losh     FILE* dstFile;
9370c16b537SWarner Losh     void*  srcBuffer;
9380c16b537SWarner Losh     size_t srcBufferSize;
9390c16b537SWarner Losh     void*  dstBuffer;
9400c16b537SWarner Losh     size_t dstBufferSize;
94137f1f268SConrad Meyer     void* dictBuffer;
94237f1f268SConrad Meyer     size_t dictBufferSize;
9432b9c00cbSConrad Meyer     const char* dictFileName;
9440c16b537SWarner Losh     ZSTD_CStream* cctx;
9450c16b537SWarner Losh } cRess_t;
9460c16b537SWarner Losh 
947*5ff13fbcSAllan Jude /** ZSTD_cycleLog() :
948*5ff13fbcSAllan Jude  *  condition for correct operation : hashLog > 1 */
ZSTD_cycleLog(U32 hashLog,ZSTD_strategy strat)949*5ff13fbcSAllan Jude static U32 ZSTD_cycleLog(U32 hashLog, ZSTD_strategy strat)
950*5ff13fbcSAllan Jude {
951*5ff13fbcSAllan Jude     U32 const btScale = ((U32)strat >= (U32)ZSTD_btlazy2);
952*5ff13fbcSAllan Jude     assert(hashLog > 1);
953*5ff13fbcSAllan Jude     return hashLog - btScale;
954*5ff13fbcSAllan Jude }
955*5ff13fbcSAllan Jude 
FIO_adjustParamsForPatchFromMode(FIO_prefs_t * const prefs,ZSTD_compressionParameters * comprParams,unsigned long long const dictSize,unsigned long long const maxSrcFileSize,int cLevel)95637f1f268SConrad Meyer static void FIO_adjustParamsForPatchFromMode(FIO_prefs_t* const prefs,
95737f1f268SConrad Meyer                                     ZSTD_compressionParameters* comprParams,
95837f1f268SConrad Meyer                                     unsigned long long const dictSize,
95937f1f268SConrad Meyer                                     unsigned long long const maxSrcFileSize,
96037f1f268SConrad Meyer                                     int cLevel)
96137f1f268SConrad Meyer {
96237f1f268SConrad Meyer     unsigned const fileWindowLog = FIO_highbit64(maxSrcFileSize) + 1;
96337f1f268SConrad Meyer     ZSTD_compressionParameters const cParams = ZSTD_getCParams(cLevel, (size_t)maxSrcFileSize, (size_t)dictSize);
96437f1f268SConrad Meyer     FIO_adjustMemLimitForPatchFromMode(prefs, dictSize, maxSrcFileSize);
96537f1f268SConrad Meyer     if (fileWindowLog > ZSTD_WINDOWLOG_MAX)
96637f1f268SConrad Meyer         DISPLAYLEVEL(1, "Max window log exceeded by file (compression ratio will suffer)\n");
967*5ff13fbcSAllan Jude     comprParams->windowLog = MAX(ZSTD_WINDOWLOG_MIN, MIN(ZSTD_WINDOWLOG_MAX, fileWindowLog));
968f7cd7fe5SConrad Meyer     if (fileWindowLog > ZSTD_cycleLog(cParams.chainLog, cParams.strategy)) {
96937f1f268SConrad Meyer         if (!prefs->ldmFlag)
970f7cd7fe5SConrad Meyer             DISPLAYLEVEL(1, "long mode automatically triggered\n");
97137f1f268SConrad Meyer         FIO_setLdmFlag(prefs, 1);
97237f1f268SConrad Meyer     }
97337f1f268SConrad Meyer     if (cParams.strategy >= ZSTD_btopt) {
97437f1f268SConrad Meyer         DISPLAYLEVEL(1, "[Optimal parser notes] Consider the following to improve patch size at the cost of speed:\n");
97537f1f268SConrad Meyer         DISPLAYLEVEL(1, "- Use --single-thread mode in the zstd cli\n");
97637f1f268SConrad Meyer         DISPLAYLEVEL(1, "- Set a larger targetLength (eg. --zstd=targetLength=4096)\n");
97737f1f268SConrad Meyer         DISPLAYLEVEL(1, "- Set a larger chainLog (eg. --zstd=chainLog=%u)\n", ZSTD_CHAINLOG_MAX);
978*5ff13fbcSAllan Jude         DISPLAYLEVEL(1, "Also consider playing around with searchLog and hashLog\n");
97937f1f268SConrad Meyer     }
98037f1f268SConrad Meyer }
98137f1f268SConrad Meyer 
FIO_createCResources(FIO_prefs_t * const prefs,const char * dictFileName,unsigned long long const maxSrcFileSize,int cLevel,ZSTD_compressionParameters comprParams)9822b9c00cbSConrad Meyer static cRess_t FIO_createCResources(FIO_prefs_t* const prefs,
98337f1f268SConrad Meyer                                     const char* dictFileName, unsigned long long const maxSrcFileSize,
98437f1f268SConrad Meyer                                     int cLevel, ZSTD_compressionParameters comprParams) {
9850c16b537SWarner Losh     cRess_t ress;
9860c16b537SWarner Losh     memset(&ress, 0, sizeof(ress));
9870c16b537SWarner Losh 
98819fcbaf1SConrad Meyer     DISPLAYLEVEL(6, "FIO_createCResources \n");
9890c16b537SWarner Losh     ress.cctx = ZSTD_createCCtx();
9900c16b537SWarner Losh     if (ress.cctx == NULL)
991a0483764SConrad Meyer         EXM_THROW(30, "allocation error (%s): can't create ZSTD_CCtx",
992a0483764SConrad Meyer                     strerror(errno));
9930c16b537SWarner Losh     ress.srcBufferSize = ZSTD_CStreamInSize();
9940c16b537SWarner Losh     ress.srcBuffer = malloc(ress.srcBufferSize);
9950c16b537SWarner Losh     ress.dstBufferSize = ZSTD_CStreamOutSize();
99637f1f268SConrad Meyer 
99737f1f268SConrad Meyer     /* need to update memLimit before calling createDictBuffer
99837f1f268SConrad Meyer      * because of memLimit check inside it */
999f7cd7fe5SConrad Meyer     if (prefs->patchFromMode) {
1000f7cd7fe5SConrad Meyer         unsigned long long const ssSize = (unsigned long long)prefs->streamSrcSize;
1001f7cd7fe5SConrad Meyer         FIO_adjustParamsForPatchFromMode(prefs, &comprParams, UTIL_getFileSize(dictFileName), ssSize > 0 ? ssSize : maxSrcFileSize, cLevel);
1002f7cd7fe5SConrad Meyer     }
10030c16b537SWarner Losh     ress.dstBuffer = malloc(ress.dstBufferSize);
100437f1f268SConrad Meyer     ress.dictBufferSize = FIO_createDictBuffer(&ress.dictBuffer, dictFileName, prefs);   /* works with dictFileName==NULL */
10050c16b537SWarner Losh     if (!ress.srcBuffer || !ress.dstBuffer)
10060c16b537SWarner Losh         EXM_THROW(31, "allocation error : not enough memory");
10070c16b537SWarner Losh 
100819fcbaf1SConrad Meyer     /* Advanced parameters, including dictionary */
100937f1f268SConrad Meyer     if (dictFileName && (ress.dictBuffer==NULL))
10100c16b537SWarner Losh         EXM_THROW(32, "allocation error : can't create dictBuffer");
10112b9c00cbSConrad Meyer     ress.dictFileName = dictFileName;
10120c16b537SWarner Losh 
10132b9c00cbSConrad Meyer     if (prefs->adaptiveMode && !prefs->ldmFlag && !comprParams.windowLog)
10140f743729SConrad Meyer         comprParams.windowLog = ADAPT_WINDOWLOG_DEFAULT;
10150f743729SConrad Meyer 
101637f1f268SConrad Meyer     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_contentSizeFlag, prefs->contentSize) );  /* always enable content size when available (note: supposed to be default) */
10172b9c00cbSConrad Meyer     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_dictIDFlag, prefs->dictIDFlag) );
10182b9c00cbSConrad Meyer     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_checksumFlag, prefs->checksumFlag) );
10190c16b537SWarner Losh     /* compression level */
1020a0483764SConrad Meyer     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_compressionLevel, cLevel) );
10214d3f1eafSConrad Meyer     /* max compressed block size */
10224d3f1eafSConrad Meyer     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_targetCBlockSize, (int)prefs->targetCBlockSize) );
10239cbefe25SConrad Meyer     /* source size hint */
10249cbefe25SConrad Meyer     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_srcSizeHint, (int)prefs->srcSizeHint) );
10250c16b537SWarner Losh     /* long distance matching */
10262b9c00cbSConrad Meyer     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_enableLongDistanceMatching, prefs->ldmFlag) );
10272b9c00cbSConrad Meyer     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_ldmHashLog, prefs->ldmHashLog) );
10282b9c00cbSConrad Meyer     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_ldmMinMatch, prefs->ldmMinMatch) );
10292b9c00cbSConrad Meyer     if (prefs->ldmBucketSizeLog != FIO_LDM_PARAM_NOTSET) {
10302b9c00cbSConrad Meyer         CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_ldmBucketSizeLog, prefs->ldmBucketSizeLog) );
10310c16b537SWarner Losh     }
10322b9c00cbSConrad Meyer     if (prefs->ldmHashRateLog != FIO_LDM_PARAM_NOTSET) {
10332b9c00cbSConrad Meyer         CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_ldmHashRateLog, prefs->ldmHashRateLog) );
10340c16b537SWarner Losh     }
1035*5ff13fbcSAllan Jude     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_useRowMatchFinder, prefs->useRowMatchFinder));
10360c16b537SWarner Losh     /* compression parameters */
10372b9c00cbSConrad Meyer     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_windowLog, (int)comprParams.windowLog) );
10382b9c00cbSConrad Meyer     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_chainLog, (int)comprParams.chainLog) );
10392b9c00cbSConrad Meyer     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_hashLog, (int)comprParams.hashLog) );
10402b9c00cbSConrad Meyer     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_searchLog, (int)comprParams.searchLog) );
10412b9c00cbSConrad Meyer     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_minMatch, (int)comprParams.minMatch) );
10422b9c00cbSConrad Meyer     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_targetLength, (int)comprParams.targetLength) );
1043*5ff13fbcSAllan Jude     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_strategy, (int)comprParams.strategy) );
10442b9c00cbSConrad Meyer     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_literalCompressionMode, (int)prefs->literalCompressionMode) );
1045f7cd7fe5SConrad Meyer     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_enableDedicatedDictSearch, 1) );
10460c16b537SWarner Losh     /* multi-threading */
104719fcbaf1SConrad Meyer #ifdef ZSTD_MULTITHREAD
10482b9c00cbSConrad Meyer     DISPLAYLEVEL(5,"set nb workers = %u \n", prefs->nbWorkers);
10492b9c00cbSConrad Meyer     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_nbWorkers, prefs->nbWorkers) );
10502b9c00cbSConrad Meyer     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_jobSize, prefs->blockSize) );
10512b9c00cbSConrad Meyer     if (prefs->overlapLog != FIO_OVERLAP_LOG_NOTSET) {
10522b9c00cbSConrad Meyer         DISPLAYLEVEL(3,"set overlapLog = %u \n", prefs->overlapLog);
10532b9c00cbSConrad Meyer         CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_overlapLog, prefs->overlapLog) );
10540f743729SConrad Meyer     }
10552b9c00cbSConrad Meyer     CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_rsyncable, prefs->rsyncable) );
105619fcbaf1SConrad Meyer #endif
10570c16b537SWarner Losh     /* dictionary */
105837f1f268SConrad Meyer     if (prefs->patchFromMode) {
105937f1f268SConrad Meyer         CHECK( ZSTD_CCtx_refPrefix(ress.cctx, ress.dictBuffer, ress.dictBufferSize) );
106037f1f268SConrad Meyer     } else {
106137f1f268SConrad Meyer         CHECK( ZSTD_CCtx_loadDictionary(ress.cctx, ress.dictBuffer, ress.dictBufferSize) );
10620c16b537SWarner Losh     }
10630c16b537SWarner Losh 
10640c16b537SWarner Losh     return ress;
10650c16b537SWarner Losh }
10660c16b537SWarner Losh 
FIO_freeCResources(const cRess_t * const ress)1067f7cd7fe5SConrad Meyer static void FIO_freeCResources(const cRess_t* const ress)
10680c16b537SWarner Losh {
1069f7cd7fe5SConrad Meyer     free(ress->srcBuffer);
1070f7cd7fe5SConrad Meyer     free(ress->dstBuffer);
1071f7cd7fe5SConrad Meyer     free(ress->dictBuffer);
1072f7cd7fe5SConrad Meyer     ZSTD_freeCStream(ress->cctx);   /* never fails */
10730c16b537SWarner Losh }
10740c16b537SWarner Losh 
10750c16b537SWarner Losh 
10760c16b537SWarner Losh #ifdef ZSTD_GZCOMPRESS
10770f743729SConrad Meyer static unsigned long long
FIO_compressGzFrame(const cRess_t * ress,const char * srcFileName,U64 const srcFileSize,int compressionLevel,U64 * readsize)10789cbefe25SConrad Meyer FIO_compressGzFrame(const cRess_t* ress,  /* buffers & handlers are used, but not changed */
10790c16b537SWarner Losh                     const char* srcFileName, U64 const srcFileSize,
10800c16b537SWarner Losh                     int compressionLevel, U64* readsize)
10810c16b537SWarner Losh {
10820c16b537SWarner Losh     unsigned long long inFileSize = 0, outFileSize = 0;
10830c16b537SWarner Losh     z_stream strm;
10840c16b537SWarner Losh 
10850c16b537SWarner Losh     if (compressionLevel > Z_BEST_COMPRESSION)
10860c16b537SWarner Losh         compressionLevel = Z_BEST_COMPRESSION;
10870c16b537SWarner Losh 
10880c16b537SWarner Losh     strm.zalloc = Z_NULL;
10890c16b537SWarner Losh     strm.zfree = Z_NULL;
10900c16b537SWarner Losh     strm.opaque = Z_NULL;
10910c16b537SWarner Losh 
10929cbefe25SConrad Meyer     {   int const ret = deflateInit2(&strm, compressionLevel, Z_DEFLATED,
10930c16b537SWarner Losh                         15 /* maxWindowLogSize */ + 16 /* gzip only */,
10940c16b537SWarner Losh                         8, Z_DEFAULT_STRATEGY); /* see http://www.zlib.net/manual.html */
10959cbefe25SConrad Meyer         if (ret != Z_OK) {
10960c16b537SWarner Losh             EXM_THROW(71, "zstd: %s: deflateInit2 error %d \n", srcFileName, ret);
10979cbefe25SConrad Meyer     }   }
10980c16b537SWarner Losh 
10990c16b537SWarner Losh     strm.next_in = 0;
11000c16b537SWarner Losh     strm.avail_in = 0;
11010c16b537SWarner Losh     strm.next_out = (Bytef*)ress->dstBuffer;
11020c16b537SWarner Losh     strm.avail_out = (uInt)ress->dstBufferSize;
11030c16b537SWarner Losh 
11040c16b537SWarner Losh     while (1) {
11059cbefe25SConrad Meyer         int ret;
11060c16b537SWarner Losh         if (strm.avail_in == 0) {
11070c16b537SWarner Losh             size_t const inSize = fread(ress->srcBuffer, 1, ress->srcBufferSize, ress->srcFile);
11080c16b537SWarner Losh             if (inSize == 0) break;
11090c16b537SWarner Losh             inFileSize += inSize;
11100c16b537SWarner Losh             strm.next_in = (z_const unsigned char*)ress->srcBuffer;
11110c16b537SWarner Losh             strm.avail_in = (uInt)inSize;
11120c16b537SWarner Losh         }
11130c16b537SWarner Losh         ret = deflate(&strm, Z_NO_FLUSH);
11140c16b537SWarner Losh         if (ret != Z_OK)
11150c16b537SWarner Losh             EXM_THROW(72, "zstd: %s: deflate error %d \n", srcFileName, ret);
11169cbefe25SConrad Meyer         {   size_t const cSize = ress->dstBufferSize - strm.avail_out;
11179cbefe25SConrad Meyer             if (cSize) {
11189cbefe25SConrad Meyer                 if (fwrite(ress->dstBuffer, 1, cSize, ress->dstFile) != cSize)
11199cbefe25SConrad Meyer                     EXM_THROW(73, "Write error : cannot write to output file : %s ", strerror(errno));
11209cbefe25SConrad Meyer                 outFileSize += cSize;
11210c16b537SWarner Losh                 strm.next_out = (Bytef*)ress->dstBuffer;
11220c16b537SWarner Losh                 strm.avail_out = (uInt)ress->dstBufferSize;
11239cbefe25SConrad Meyer         }   }
11249cbefe25SConrad Meyer         if (srcFileSize == UTIL_FILESIZE_UNKNOWN) {
11250c16b537SWarner Losh             DISPLAYUPDATE(2, "\rRead : %u MB ==> %.2f%% ",
1126a0483764SConrad Meyer                             (unsigned)(inFileSize>>20),
11270c16b537SWarner Losh                             (double)outFileSize/inFileSize*100)
11289cbefe25SConrad Meyer         } else {
11290c16b537SWarner Losh             DISPLAYUPDATE(2, "\rRead : %u / %u MB ==> %.2f%% ",
1130a0483764SConrad Meyer                             (unsigned)(inFileSize>>20), (unsigned)(srcFileSize>>20),
11310c16b537SWarner Losh                             (double)outFileSize/inFileSize*100);
11329cbefe25SConrad Meyer     }   }
11330c16b537SWarner Losh 
11340c16b537SWarner Losh     while (1) {
11359cbefe25SConrad Meyer         int const ret = deflate(&strm, Z_FINISH);
11369cbefe25SConrad Meyer         {   size_t const cSize = ress->dstBufferSize - strm.avail_out;
11379cbefe25SConrad Meyer             if (cSize) {
11389cbefe25SConrad Meyer                 if (fwrite(ress->dstBuffer, 1, cSize, ress->dstFile) != cSize)
1139a0483764SConrad Meyer                     EXM_THROW(75, "Write error : %s ", strerror(errno));
11409cbefe25SConrad Meyer                 outFileSize += cSize;
11410c16b537SWarner Losh                 strm.next_out = (Bytef*)ress->dstBuffer;
11420c16b537SWarner Losh                 strm.avail_out = (uInt)ress->dstBufferSize;
11430c16b537SWarner Losh         }   }
11440c16b537SWarner Losh         if (ret == Z_STREAM_END) break;
11450c16b537SWarner Losh         if (ret != Z_BUF_ERROR)
11460c16b537SWarner Losh             EXM_THROW(77, "zstd: %s: deflate error %d \n", srcFileName, ret);
11470c16b537SWarner Losh     }
11480c16b537SWarner Losh 
11499cbefe25SConrad Meyer     {   int const ret = deflateEnd(&strm);
11509cbefe25SConrad Meyer         if (ret != Z_OK) {
11510c16b537SWarner Losh             EXM_THROW(79, "zstd: %s: deflateEnd error %d \n", srcFileName, ret);
11529cbefe25SConrad Meyer     }   }
11530c16b537SWarner Losh     *readsize = inFileSize;
11540c16b537SWarner Losh     return outFileSize;
11550c16b537SWarner Losh }
11560c16b537SWarner Losh #endif
11570c16b537SWarner Losh 
11580c16b537SWarner Losh 
11590c16b537SWarner Losh #ifdef ZSTD_LZMACOMPRESS
11600f743729SConrad Meyer static unsigned long long
FIO_compressLzmaFrame(cRess_t * ress,const char * srcFileName,U64 const srcFileSize,int compressionLevel,U64 * readsize,int plain_lzma)11610f743729SConrad Meyer FIO_compressLzmaFrame(cRess_t* ress,
11620c16b537SWarner Losh                       const char* srcFileName, U64 const srcFileSize,
11630c16b537SWarner Losh                       int compressionLevel, U64* readsize, int plain_lzma)
11640c16b537SWarner Losh {
11650c16b537SWarner Losh     unsigned long long inFileSize = 0, outFileSize = 0;
11660c16b537SWarner Losh     lzma_stream strm = LZMA_STREAM_INIT;
11670c16b537SWarner Losh     lzma_action action = LZMA_RUN;
11680c16b537SWarner Losh     lzma_ret ret;
11690c16b537SWarner Losh 
11700c16b537SWarner Losh     if (compressionLevel < 0) compressionLevel = 0;
11710c16b537SWarner Losh     if (compressionLevel > 9) compressionLevel = 9;
11720c16b537SWarner Losh 
11730c16b537SWarner Losh     if (plain_lzma) {
11740c16b537SWarner Losh         lzma_options_lzma opt_lzma;
11750c16b537SWarner Losh         if (lzma_lzma_preset(&opt_lzma, compressionLevel))
11769cbefe25SConrad Meyer             EXM_THROW(81, "zstd: %s: lzma_lzma_preset error", srcFileName);
11770c16b537SWarner Losh         ret = lzma_alone_encoder(&strm, &opt_lzma); /* LZMA */
11780c16b537SWarner Losh         if (ret != LZMA_OK)
11799cbefe25SConrad Meyer             EXM_THROW(82, "zstd: %s: lzma_alone_encoder error %d", srcFileName, ret);
11800c16b537SWarner Losh     } else {
11810c16b537SWarner Losh         ret = lzma_easy_encoder(&strm, compressionLevel, LZMA_CHECK_CRC64); /* XZ */
11820c16b537SWarner Losh         if (ret != LZMA_OK)
11839cbefe25SConrad Meyer             EXM_THROW(83, "zstd: %s: lzma_easy_encoder error %d", srcFileName, ret);
11840c16b537SWarner Losh     }
11850c16b537SWarner Losh 
11860c16b537SWarner Losh     strm.next_in = 0;
11870c16b537SWarner Losh     strm.avail_in = 0;
11880c16b537SWarner Losh     strm.next_out = (BYTE*)ress->dstBuffer;
11890c16b537SWarner Losh     strm.avail_out = ress->dstBufferSize;
11900c16b537SWarner Losh 
11910c16b537SWarner Losh     while (1) {
11920c16b537SWarner Losh         if (strm.avail_in == 0) {
11930c16b537SWarner Losh             size_t const inSize = fread(ress->srcBuffer, 1, ress->srcBufferSize, ress->srcFile);
11940c16b537SWarner Losh             if (inSize == 0) action = LZMA_FINISH;
11950c16b537SWarner Losh             inFileSize += inSize;
11960c16b537SWarner Losh             strm.next_in = (BYTE const*)ress->srcBuffer;
11970c16b537SWarner Losh             strm.avail_in = inSize;
11980c16b537SWarner Losh         }
11990c16b537SWarner Losh 
12000c16b537SWarner Losh         ret = lzma_code(&strm, action);
12010c16b537SWarner Losh 
12020c16b537SWarner Losh         if (ret != LZMA_OK && ret != LZMA_STREAM_END)
12039cbefe25SConrad Meyer             EXM_THROW(84, "zstd: %s: lzma_code encoding error %d", srcFileName, ret);
12040c16b537SWarner Losh         {   size_t const compBytes = ress->dstBufferSize - strm.avail_out;
12050c16b537SWarner Losh             if (compBytes) {
12060c16b537SWarner Losh                 if (fwrite(ress->dstBuffer, 1, compBytes, ress->dstFile) != compBytes)
12079cbefe25SConrad Meyer                     EXM_THROW(85, "Write error : %s", strerror(errno));
12080c16b537SWarner Losh                 outFileSize += compBytes;
12090c16b537SWarner Losh                 strm.next_out = (BYTE*)ress->dstBuffer;
12100c16b537SWarner Losh                 strm.avail_out = ress->dstBufferSize;
12110c16b537SWarner Losh         }   }
1212052d3c12SConrad Meyer         if (srcFileSize == UTIL_FILESIZE_UNKNOWN)
12130c16b537SWarner Losh             DISPLAYUPDATE(2, "\rRead : %u MB ==> %.2f%%",
1214a0483764SConrad Meyer                             (unsigned)(inFileSize>>20),
12150c16b537SWarner Losh                             (double)outFileSize/inFileSize*100)
12160c16b537SWarner Losh         else
12170c16b537SWarner Losh             DISPLAYUPDATE(2, "\rRead : %u / %u MB ==> %.2f%%",
1218a0483764SConrad Meyer                             (unsigned)(inFileSize>>20), (unsigned)(srcFileSize>>20),
12190c16b537SWarner Losh                             (double)outFileSize/inFileSize*100);
12200c16b537SWarner Losh         if (ret == LZMA_STREAM_END) break;
12210c16b537SWarner Losh     }
12220c16b537SWarner Losh 
12230c16b537SWarner Losh     lzma_end(&strm);
12240c16b537SWarner Losh     *readsize = inFileSize;
12250c16b537SWarner Losh 
12260c16b537SWarner Losh     return outFileSize;
12270c16b537SWarner Losh }
12280c16b537SWarner Losh #endif
12290c16b537SWarner Losh 
12300c16b537SWarner Losh #ifdef ZSTD_LZ4COMPRESS
12312b9c00cbSConrad Meyer 
1232052d3c12SConrad Meyer #if LZ4_VERSION_NUMBER <= 10600
1233052d3c12SConrad Meyer #define LZ4F_blockLinked blockLinked
1234052d3c12SConrad Meyer #define LZ4F_max64KB max64KB
1235052d3c12SConrad Meyer #endif
12362b9c00cbSConrad Meyer 
FIO_LZ4_GetBlockSize_FromBlockId(int id)12370c16b537SWarner Losh static int FIO_LZ4_GetBlockSize_FromBlockId (int id) { return (1 << (8 + (2 * id))); }
12382b9c00cbSConrad Meyer 
12390f743729SConrad Meyer static unsigned long long
FIO_compressLz4Frame(cRess_t * ress,const char * srcFileName,U64 const srcFileSize,int compressionLevel,int checksumFlag,U64 * readsize)12400f743729SConrad Meyer FIO_compressLz4Frame(cRess_t* ress,
12410c16b537SWarner Losh                      const char* srcFileName, U64 const srcFileSize,
12422b9c00cbSConrad Meyer                      int compressionLevel, int checksumFlag,
12432b9c00cbSConrad Meyer                      U64* readsize)
12440c16b537SWarner Losh {
1245052d3c12SConrad Meyer     const size_t blockSize = FIO_LZ4_GetBlockSize_FromBlockId(LZ4F_max64KB);
12460c16b537SWarner Losh     unsigned long long inFileSize = 0, outFileSize = 0;
12470c16b537SWarner Losh 
12480c16b537SWarner Losh     LZ4F_preferences_t prefs;
12490c16b537SWarner Losh     LZ4F_compressionContext_t ctx;
12500c16b537SWarner Losh 
12510c16b537SWarner Losh     LZ4F_errorCode_t const errorCode = LZ4F_createCompressionContext(&ctx, LZ4F_VERSION);
12520c16b537SWarner Losh     if (LZ4F_isError(errorCode))
12530c16b537SWarner Losh         EXM_THROW(31, "zstd: failed to create lz4 compression context");
12540c16b537SWarner Losh 
12550c16b537SWarner Losh     memset(&prefs, 0, sizeof(prefs));
12560c16b537SWarner Losh 
1257052d3c12SConrad Meyer     assert(blockSize <= ress->srcBufferSize);
12580c16b537SWarner Losh 
12590c16b537SWarner Losh     prefs.autoFlush = 1;
12600c16b537SWarner Losh     prefs.compressionLevel = compressionLevel;
1261052d3c12SConrad Meyer     prefs.frameInfo.blockMode = LZ4F_blockLinked;
1262052d3c12SConrad Meyer     prefs.frameInfo.blockSizeID = LZ4F_max64KB;
12632b9c00cbSConrad Meyer     prefs.frameInfo.contentChecksumFlag = (contentChecksum_t)checksumFlag;
12640c16b537SWarner Losh #if LZ4_VERSION_NUMBER >= 10600
1265052d3c12SConrad Meyer     prefs.frameInfo.contentSize = (srcFileSize==UTIL_FILESIZE_UNKNOWN) ? 0 : srcFileSize;
12660c16b537SWarner Losh #endif
1267052d3c12SConrad Meyer     assert(LZ4F_compressBound(blockSize, &prefs) <= ress->dstBufferSize);
12680c16b537SWarner Losh 
12690c16b537SWarner Losh     {
12700c16b537SWarner Losh         size_t readSize;
12710c16b537SWarner Losh         size_t headerSize = LZ4F_compressBegin(ctx, ress->dstBuffer, ress->dstBufferSize, &prefs);
12720c16b537SWarner Losh         if (LZ4F_isError(headerSize))
12730c16b537SWarner Losh             EXM_THROW(33, "File header generation failed : %s",
12740c16b537SWarner Losh                             LZ4F_getErrorName(headerSize));
1275052d3c12SConrad Meyer         if (fwrite(ress->dstBuffer, 1, headerSize, ress->dstFile) != headerSize)
1276a0483764SConrad Meyer             EXM_THROW(34, "Write error : %s (cannot write header)", strerror(errno));
12770c16b537SWarner Losh         outFileSize += headerSize;
12780c16b537SWarner Losh 
12790c16b537SWarner Losh         /* Read first block */
12800c16b537SWarner Losh         readSize  = fread(ress->srcBuffer, (size_t)1, (size_t)blockSize, ress->srcFile);
12810c16b537SWarner Losh         inFileSize += readSize;
12820c16b537SWarner Losh 
12830c16b537SWarner Losh         /* Main Loop */
12840c16b537SWarner Losh         while (readSize>0) {
1285a0483764SConrad Meyer             size_t const outSize = LZ4F_compressUpdate(ctx,
1286a0483764SConrad Meyer                                         ress->dstBuffer, ress->dstBufferSize,
1287a0483764SConrad Meyer                                         ress->srcBuffer, readSize, NULL);
12880c16b537SWarner Losh             if (LZ4F_isError(outSize))
12890c16b537SWarner Losh                 EXM_THROW(35, "zstd: %s: lz4 compression failed : %s",
12900c16b537SWarner Losh                             srcFileName, LZ4F_getErrorName(outSize));
12910c16b537SWarner Losh             outFileSize += outSize;
1292a0483764SConrad Meyer             if (srcFileSize == UTIL_FILESIZE_UNKNOWN) {
12930c16b537SWarner Losh                 DISPLAYUPDATE(2, "\rRead : %u MB ==> %.2f%%",
1294a0483764SConrad Meyer                                 (unsigned)(inFileSize>>20),
12950c16b537SWarner Losh                                 (double)outFileSize/inFileSize*100)
1296a0483764SConrad Meyer             } else {
12970c16b537SWarner Losh                 DISPLAYUPDATE(2, "\rRead : %u / %u MB ==> %.2f%%",
1298a0483764SConrad Meyer                                 (unsigned)(inFileSize>>20), (unsigned)(srcFileSize>>20),
12990c16b537SWarner Losh                                 (double)outFileSize/inFileSize*100);
1300a0483764SConrad Meyer             }
13010c16b537SWarner Losh 
13020c16b537SWarner Losh             /* Write Block */
13030c16b537SWarner Losh             {   size_t const sizeCheck = fwrite(ress->dstBuffer, 1, outSize, ress->dstFile);
1304a0483764SConrad Meyer                 if (sizeCheck != outSize)
1305a0483764SConrad Meyer                     EXM_THROW(36, "Write error : %s", strerror(errno));
1306a0483764SConrad Meyer             }
13070c16b537SWarner Losh 
13080c16b537SWarner Losh             /* Read next block */
13090c16b537SWarner Losh             readSize  = fread(ress->srcBuffer, (size_t)1, (size_t)blockSize, ress->srcFile);
13100c16b537SWarner Losh             inFileSize += readSize;
13110c16b537SWarner Losh         }
13120c16b537SWarner Losh         if (ferror(ress->srcFile)) EXM_THROW(37, "Error reading %s ", srcFileName);
13130c16b537SWarner Losh 
13140c16b537SWarner Losh         /* End of Stream mark */
13150c16b537SWarner Losh         headerSize = LZ4F_compressEnd(ctx, ress->dstBuffer, ress->dstBufferSize, NULL);
13160c16b537SWarner Losh         if (LZ4F_isError(headerSize))
13170c16b537SWarner Losh             EXM_THROW(38, "zstd: %s: lz4 end of file generation failed : %s",
13180c16b537SWarner Losh                         srcFileName, LZ4F_getErrorName(headerSize));
13190c16b537SWarner Losh 
13200c16b537SWarner Losh         {   size_t const sizeCheck = fwrite(ress->dstBuffer, 1, headerSize, ress->dstFile);
1321a0483764SConrad Meyer             if (sizeCheck != headerSize)
1322a0483764SConrad Meyer                 EXM_THROW(39, "Write error : %s (cannot write end of stream)",
1323a0483764SConrad Meyer                             strerror(errno));
1324a0483764SConrad Meyer         }
13250c16b537SWarner Losh         outFileSize += headerSize;
13260c16b537SWarner Losh     }
13270c16b537SWarner Losh 
13280c16b537SWarner Losh     *readsize = inFileSize;
13290c16b537SWarner Losh     LZ4F_freeCompressionContext(ctx);
13300c16b537SWarner Losh 
13310c16b537SWarner Losh     return outFileSize;
13320c16b537SWarner Losh }
13330c16b537SWarner Losh #endif
13340c16b537SWarner Losh 
13350c16b537SWarner Losh 
133619fcbaf1SConrad Meyer static unsigned long long
FIO_compressZstdFrame(FIO_ctx_t * const fCtx,FIO_prefs_t * const prefs,const cRess_t * ressPtr,const char * srcFileName,U64 fileSize,int compressionLevel,U64 * readsize)1337f7cd7fe5SConrad Meyer FIO_compressZstdFrame(FIO_ctx_t* const fCtx,
1338f7cd7fe5SConrad Meyer                       FIO_prefs_t* const prefs,
13392b9c00cbSConrad Meyer                       const cRess_t* ressPtr,
134019fcbaf1SConrad Meyer                       const char* srcFileName, U64 fileSize,
134119fcbaf1SConrad Meyer                       int compressionLevel, U64* readsize)
13420c16b537SWarner Losh {
134319fcbaf1SConrad Meyer     cRess_t const ress = *ressPtr;
13440c16b537SWarner Losh     FILE* const srcFile = ress.srcFile;
13450c16b537SWarner Losh     FILE* const dstFile = ress.dstFile;
134619fcbaf1SConrad Meyer     U64 compressedfilesize = 0;
134719fcbaf1SConrad Meyer     ZSTD_EndDirective directive = ZSTD_e_continue;
1348*5ff13fbcSAllan Jude     U64 pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN;
13490f743729SConrad Meyer 
13500f743729SConrad Meyer     /* stats */
13510f743729SConrad Meyer     ZSTD_frameProgression previous_zfp_update = { 0, 0, 0, 0, 0, 0 };
13520f743729SConrad Meyer     ZSTD_frameProgression previous_zfp_correction = { 0, 0, 0, 0, 0, 0 };
13530f743729SConrad Meyer     typedef enum { noChange, slower, faster } speedChange_e;
13540f743729SConrad Meyer     speedChange_e speedChange = noChange;
13550f743729SConrad Meyer     unsigned flushWaiting = 0;
13560f743729SConrad Meyer     unsigned inputPresented = 0;
13570f743729SConrad Meyer     unsigned inputBlocked = 0;
13580f743729SConrad Meyer     unsigned lastJobID = 0;
1359*5ff13fbcSAllan Jude     UTIL_HumanReadableSize_t const file_hrs = UTIL_makeHumanReadableSize(fileSize);
13600f743729SConrad Meyer 
136119fcbaf1SConrad Meyer     DISPLAYLEVEL(6, "compression using zstd format \n");
136219fcbaf1SConrad Meyer 
136319fcbaf1SConrad Meyer     /* init */
13640f743729SConrad Meyer     if (fileSize != UTIL_FILESIZE_UNKNOWN) {
1365*5ff13fbcSAllan Jude         pledgedSrcSize = fileSize;
13660f743729SConrad Meyer         CHECK(ZSTD_CCtx_setPledgedSrcSize(ress.cctx, fileSize));
13679cbefe25SConrad Meyer     } else if (prefs->streamSrcSize > 0) {
13689cbefe25SConrad Meyer       /* unknown source size; use the declared stream size */
1369*5ff13fbcSAllan Jude       pledgedSrcSize = prefs->streamSrcSize;
13709cbefe25SConrad Meyer       CHECK( ZSTD_CCtx_setPledgedSrcSize(ress.cctx, prefs->streamSrcSize) );
13710f743729SConrad Meyer     }
1372*5ff13fbcSAllan Jude 
1373*5ff13fbcSAllan Jude     {
1374*5ff13fbcSAllan Jude         int windowLog;
1375*5ff13fbcSAllan Jude         UTIL_HumanReadableSize_t windowSize;
1376*5ff13fbcSAllan Jude         CHECK(ZSTD_CCtx_getParameter(ress.cctx, ZSTD_c_windowLog, &windowLog));
1377*5ff13fbcSAllan Jude         if (windowLog == 0) {
1378*5ff13fbcSAllan Jude             const ZSTD_compressionParameters cParams = ZSTD_getCParams(compressionLevel, fileSize, 0);
1379*5ff13fbcSAllan Jude             windowLog = cParams.windowLog;
1380*5ff13fbcSAllan Jude         }
1381*5ff13fbcSAllan Jude         windowSize = UTIL_makeHumanReadableSize(MAX(1ULL, MIN(1ULL << windowLog, pledgedSrcSize)));
1382*5ff13fbcSAllan Jude         DISPLAYLEVEL(4, "Decompression will require %.*f%s of memory\n", windowSize.precision, windowSize.value, windowSize.suffix);
1383*5ff13fbcSAllan Jude     }
13840f743729SConrad Meyer     (void)srcFileName;
138519fcbaf1SConrad Meyer 
138619fcbaf1SConrad Meyer     /* Main compression loop */
138719fcbaf1SConrad Meyer     do {
13880f743729SConrad Meyer         size_t stillToFlush;
138919fcbaf1SConrad Meyer         /* Fill input Buffer */
139019fcbaf1SConrad Meyer         size_t const inSize = fread(ress.srcBuffer, (size_t)1, ress.srcBufferSize, srcFile);
139119fcbaf1SConrad Meyer         ZSTD_inBuffer inBuff = { ress.srcBuffer, inSize, 0 };
1392a0483764SConrad Meyer         DISPLAYLEVEL(6, "fread %u bytes from source \n", (unsigned)inSize);
139319fcbaf1SConrad Meyer         *readsize += inSize;
139419fcbaf1SConrad Meyer 
139519fcbaf1SConrad Meyer         if ((inSize == 0) || (*readsize == fileSize))
139619fcbaf1SConrad Meyer             directive = ZSTD_e_end;
139719fcbaf1SConrad Meyer 
13980f743729SConrad Meyer         stillToFlush = 1;
13990f743729SConrad Meyer         while ((inBuff.pos != inBuff.size)   /* input buffer must be entirely ingested */
14000f743729SConrad Meyer             || (directive == ZSTD_e_end && stillToFlush != 0) ) {
14010f743729SConrad Meyer 
14020f743729SConrad Meyer             size_t const oldIPos = inBuff.pos;
140319fcbaf1SConrad Meyer             ZSTD_outBuffer outBuff = { ress.dstBuffer, ress.dstBufferSize, 0 };
14040f743729SConrad Meyer             size_t const toFlushNow = ZSTD_toFlushNow(ress.cctx);
1405a0483764SConrad Meyer             CHECK_V(stillToFlush, ZSTD_compressStream2(ress.cctx, &outBuff, &inBuff, directive));
14060f743729SConrad Meyer 
14070f743729SConrad Meyer             /* count stats */
14080f743729SConrad Meyer             inputPresented++;
14090f743729SConrad Meyer             if (oldIPos == inBuff.pos) inputBlocked++;  /* input buffer is full and can't take any more : input speed is faster than consumption rate */
14100f743729SConrad Meyer             if (!toFlushNow) flushWaiting = 1;
141119fcbaf1SConrad Meyer 
141219fcbaf1SConrad Meyer             /* Write compressed stream */
14130f743729SConrad Meyer             DISPLAYLEVEL(6, "ZSTD_compress_generic(end:%u) => input pos(%u)<=(%u)size ; output generated %u bytes \n",
1414a0483764SConrad Meyer                             (unsigned)directive, (unsigned)inBuff.pos, (unsigned)inBuff.size, (unsigned)outBuff.pos);
141519fcbaf1SConrad Meyer             if (outBuff.pos) {
141619fcbaf1SConrad Meyer                 size_t const sizeCheck = fwrite(ress.dstBuffer, 1, outBuff.pos, dstFile);
141719fcbaf1SConrad Meyer                 if (sizeCheck != outBuff.pos)
1418a0483764SConrad Meyer                     EXM_THROW(25, "Write error : %s (cannot write compressed block)",
1419a0483764SConrad Meyer                                     strerror(errno));
142019fcbaf1SConrad Meyer                 compressedfilesize += outBuff.pos;
142119fcbaf1SConrad Meyer             }
14220f743729SConrad Meyer 
14230f743729SConrad Meyer             /* display notification; and adapt compression level */
142419fcbaf1SConrad Meyer             if (READY_FOR_UPDATE()) {
142519fcbaf1SConrad Meyer                 ZSTD_frameProgression const zfp = ZSTD_getFrameProgression(ress.cctx);
1426*5ff13fbcSAllan Jude                 double const cShare = (double)zfp.produced / (double)(zfp.consumed + !zfp.consumed/*avoid div0*/) * 100;
1427*5ff13fbcSAllan Jude                 UTIL_HumanReadableSize_t const buffered_hrs = UTIL_makeHumanReadableSize(zfp.ingested - zfp.consumed);
1428*5ff13fbcSAllan Jude                 UTIL_HumanReadableSize_t const consumed_hrs = UTIL_makeHumanReadableSize(zfp.consumed);
1429*5ff13fbcSAllan Jude                 UTIL_HumanReadableSize_t const produced_hrs = UTIL_makeHumanReadableSize(zfp.produced);
14300f743729SConrad Meyer 
14310f743729SConrad Meyer                 /* display progress notifications */
14322b9c00cbSConrad Meyer                 if (g_display_prefs.displayLevel >= 3) {
1433*5ff13fbcSAllan Jude                     DISPLAYUPDATE(3, "\r(L%i) Buffered :%6.*f%4s - Consumed :%6.*f%4s - Compressed :%6.*f%4s => %.2f%% ",
143419fcbaf1SConrad Meyer                                 compressionLevel,
1435*5ff13fbcSAllan Jude                                 buffered_hrs.precision, buffered_hrs.value, buffered_hrs.suffix,
1436*5ff13fbcSAllan Jude                                 consumed_hrs.precision, consumed_hrs.value, consumed_hrs.suffix,
1437*5ff13fbcSAllan Jude                                 produced_hrs.precision, produced_hrs.value, produced_hrs.suffix,
143819fcbaf1SConrad Meyer                                 cShare );
1439*5ff13fbcSAllan Jude                 } else if (g_display_prefs.displayLevel >= 2 || g_display_prefs.progressSetting == FIO_ps_always) {
1440*5ff13fbcSAllan Jude                     /* Require level 2 or forcibly displayed progress counter for summarized updates */
1441*5ff13fbcSAllan Jude                     DISPLAYLEVEL(1, "\r%79s\r", "");    /* Clear out the current displayed line */
1442f7cd7fe5SConrad Meyer                     if (fCtx->nbFilesTotal > 1) {
1443f7cd7fe5SConrad Meyer                         size_t srcFileNameSize = strlen(srcFileName);
1444f7cd7fe5SConrad Meyer                         /* Ensure that the string we print is roughly the same size each time */
1445f7cd7fe5SConrad Meyer                         if (srcFileNameSize > 18) {
1446f7cd7fe5SConrad Meyer                             const char* truncatedSrcFileName = srcFileName + srcFileNameSize - 15;
1447*5ff13fbcSAllan Jude                             DISPLAYLEVEL(1, "Compress: %u/%u files. Current: ...%s ",
1448f7cd7fe5SConrad Meyer                                         fCtx->currFileIdx+1, fCtx->nbFilesTotal, truncatedSrcFileName);
1449f7cd7fe5SConrad Meyer                         } else {
1450*5ff13fbcSAllan Jude                             DISPLAYLEVEL(1, "Compress: %u/%u files. Current: %*s ",
1451f7cd7fe5SConrad Meyer                                         fCtx->currFileIdx+1, fCtx->nbFilesTotal, (int)(18-srcFileNameSize), srcFileName);
1452f7cd7fe5SConrad Meyer                         }
1453f7cd7fe5SConrad Meyer                     }
1454*5ff13fbcSAllan Jude                     DISPLAYLEVEL(1, "Read:%6.*f%4s ", consumed_hrs.precision, consumed_hrs.value, consumed_hrs.suffix);
145519fcbaf1SConrad Meyer                     if (fileSize != UTIL_FILESIZE_UNKNOWN)
1456*5ff13fbcSAllan Jude                         DISPLAYLEVEL(2, "/%6.*f%4s", file_hrs.precision, file_hrs.value, file_hrs.suffix);
1457*5ff13fbcSAllan Jude                     DISPLAYLEVEL(1, " ==> %2.f%%", cShare);
145819fcbaf1SConrad Meyer                     DELAY_NEXT_UPDATE();
145919fcbaf1SConrad Meyer                 }
14600f743729SConrad Meyer 
14610f743729SConrad Meyer                 /* adaptive mode : statistics measurement and speed correction */
14622b9c00cbSConrad Meyer                 if (prefs->adaptiveMode) {
14630f743729SConrad Meyer 
14640f743729SConrad Meyer                     /* check output speed */
14650f743729SConrad Meyer                     if (zfp.currentJobID > 1) {  /* only possible if nbWorkers >= 1 */
14660f743729SConrad Meyer 
14670f743729SConrad Meyer                         unsigned long long newlyProduced = zfp.produced - previous_zfp_update.produced;
14680f743729SConrad Meyer                         unsigned long long newlyFlushed = zfp.flushed - previous_zfp_update.flushed;
14690f743729SConrad Meyer                         assert(zfp.produced >= previous_zfp_update.produced);
14702b9c00cbSConrad Meyer                         assert(prefs->nbWorkers >= 1);
14710f743729SConrad Meyer 
14720f743729SConrad Meyer                         /* test if compression is blocked
14730f743729SConrad Meyer                          * either because output is slow and all buffers are full
14740f743729SConrad Meyer                          * or because input is slow and no job can start while waiting for at least one buffer to be filled.
14752b9c00cbSConrad Meyer                          * note : exclude starting part, since currentJobID > 1 */
14760f743729SConrad Meyer                         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)*/
14770f743729SConrad Meyer                           && (zfp.nbActiveWorkers == 0)                       /* confirmed : no compression ongoing */
14780f743729SConrad Meyer                           ) {
14790f743729SConrad Meyer                             DISPLAYLEVEL(6, "all buffers full : compression stopped => slow down \n")
14800f743729SConrad Meyer                             speedChange = slower;
14810f743729SConrad Meyer                         }
14820f743729SConrad Meyer 
14830f743729SConrad Meyer                         previous_zfp_update = zfp;
14840f743729SConrad Meyer 
14850f743729SConrad Meyer                         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) */
14860f743729SConrad Meyer                           && (flushWaiting == 0)                        /* flush speed was never slowed by lack of production, so it's operating at max capacity */
14870f743729SConrad Meyer                           ) {
14880f743729SConrad Meyer                             DISPLAYLEVEL(6, "compression faster than flush (%llu > %llu), and flushed was never slowed down by lack of production => slow down \n", newlyProduced, newlyFlushed);
14890f743729SConrad Meyer                             speedChange = slower;
14900f743729SConrad Meyer                         }
14910f743729SConrad Meyer                         flushWaiting = 0;
14920f743729SConrad Meyer                     }
14930f743729SConrad Meyer 
14940f743729SConrad Meyer                     /* course correct only if there is at least one new job completed */
14950f743729SConrad Meyer                     if (zfp.currentJobID > lastJobID) {
14960f743729SConrad Meyer                         DISPLAYLEVEL(6, "compression level adaptation check \n")
14970f743729SConrad Meyer 
14980f743729SConrad Meyer                         /* check input speed */
14992b9c00cbSConrad Meyer                         if (zfp.currentJobID > (unsigned)(prefs->nbWorkers+1)) {   /* warm up period, to fill all workers */
15000f743729SConrad Meyer                             if (inputBlocked <= 0) {
15010f743729SConrad Meyer                                 DISPLAYLEVEL(6, "input is never blocked => input is slower than ingestion \n");
15020f743729SConrad Meyer                                 speedChange = slower;
15030f743729SConrad Meyer                             } else if (speedChange == noChange) {
15040f743729SConrad Meyer                                 unsigned long long newlyIngested = zfp.ingested - previous_zfp_correction.ingested;
15050f743729SConrad Meyer                                 unsigned long long newlyConsumed = zfp.consumed - previous_zfp_correction.consumed;
15060f743729SConrad Meyer                                 unsigned long long newlyProduced = zfp.produced - previous_zfp_correction.produced;
15070f743729SConrad Meyer                                 unsigned long long newlyFlushed  = zfp.flushed  - previous_zfp_correction.flushed;
15080f743729SConrad Meyer                                 previous_zfp_correction = zfp;
15090f743729SConrad Meyer                                 assert(inputPresented > 0);
15100f743729SConrad Meyer                                 DISPLAYLEVEL(6, "input blocked %u/%u(%.2f) - ingested:%u vs %u:consumed - flushed:%u vs %u:produced \n",
15110f743729SConrad Meyer                                                 inputBlocked, inputPresented, (double)inputBlocked/inputPresented*100,
1512a0483764SConrad Meyer                                                 (unsigned)newlyIngested, (unsigned)newlyConsumed,
1513a0483764SConrad Meyer                                                 (unsigned)newlyFlushed, (unsigned)newlyProduced);
15140f743729SConrad Meyer                                 if ( (inputBlocked > inputPresented / 8)     /* input is waiting often, because input buffers is full : compression or output too slow */
15150f743729SConrad Meyer                                   && (newlyFlushed * 33 / 32 > newlyProduced)  /* flush everything that is produced */
15160f743729SConrad Meyer                                   && (newlyIngested * 33 / 32 > newlyConsumed) /* input speed as fast or faster than compression speed */
15170f743729SConrad Meyer                                 ) {
15180f743729SConrad Meyer                                     DISPLAYLEVEL(6, "recommend faster as in(%llu) >= (%llu)comp(%llu) <= out(%llu) \n",
15190f743729SConrad Meyer                                                     newlyIngested, newlyConsumed, newlyProduced, newlyFlushed);
15200f743729SConrad Meyer                                     speedChange = faster;
152119fcbaf1SConrad Meyer                                 }
152219fcbaf1SConrad Meyer                             }
15230f743729SConrad Meyer                             inputBlocked = 0;
15240f743729SConrad Meyer                             inputPresented = 0;
15250f743729SConrad Meyer                         }
15260f743729SConrad Meyer 
15270f743729SConrad Meyer                         if (speedChange == slower) {
15280f743729SConrad Meyer                             DISPLAYLEVEL(6, "slower speed , higher compression \n")
15290f743729SConrad Meyer                             compressionLevel ++;
15300f743729SConrad Meyer                             if (compressionLevel > ZSTD_maxCLevel()) compressionLevel = ZSTD_maxCLevel();
15312b9c00cbSConrad Meyer                             if (compressionLevel > prefs->maxAdaptLevel) compressionLevel = prefs->maxAdaptLevel;
15320f743729SConrad Meyer                             compressionLevel += (compressionLevel == 0);   /* skip 0 */
1533a0483764SConrad Meyer                             ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_compressionLevel, compressionLevel);
15340f743729SConrad Meyer                         }
15350f743729SConrad Meyer                         if (speedChange == faster) {
15360f743729SConrad Meyer                             DISPLAYLEVEL(6, "faster speed , lighter compression \n")
15370f743729SConrad Meyer                             compressionLevel --;
15382b9c00cbSConrad Meyer                             if (compressionLevel < prefs->minAdaptLevel) compressionLevel = prefs->minAdaptLevel;
15390f743729SConrad Meyer                             compressionLevel -= (compressionLevel == 0);   /* skip 0 */
1540a0483764SConrad Meyer                             ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_compressionLevel, compressionLevel);
15410f743729SConrad Meyer                         }
15420f743729SConrad Meyer                         speedChange = noChange;
15430f743729SConrad Meyer 
15440f743729SConrad Meyer                         lastJobID = zfp.currentJobID;
15450f743729SConrad Meyer                     }  /* if (zfp.currentJobID > lastJobID) */
15460f743729SConrad Meyer                 }  /* if (g_adaptiveMode) */
15470f743729SConrad Meyer             }  /* if (READY_FOR_UPDATE()) */
15480f743729SConrad Meyer         }  /* while ((inBuff.pos != inBuff.size) */
154919fcbaf1SConrad Meyer     } while (directive != ZSTD_e_end);
155019fcbaf1SConrad Meyer 
15510f743729SConrad Meyer     if (ferror(srcFile)) {
15520f743729SConrad Meyer         EXM_THROW(26, "Read error : I/O error");
15530f743729SConrad Meyer     }
15540f743729SConrad Meyer     if (fileSize != UTIL_FILESIZE_UNKNOWN && *readsize != fileSize) {
15550f743729SConrad Meyer         EXM_THROW(27, "Read error : Incomplete read : %llu / %llu B",
15560f743729SConrad Meyer                 (unsigned long long)*readsize, (unsigned long long)fileSize);
15570f743729SConrad Meyer     }
15580f743729SConrad Meyer 
155919fcbaf1SConrad Meyer     return compressedfilesize;
156019fcbaf1SConrad Meyer }
156119fcbaf1SConrad Meyer 
156219fcbaf1SConrad Meyer /*! FIO_compressFilename_internal() :
156319fcbaf1SConrad Meyer  *  same as FIO_compressFilename_extRess(), with `ress.desFile` already opened.
156419fcbaf1SConrad Meyer  *  @return : 0 : compression completed correctly,
156519fcbaf1SConrad Meyer  *            1 : missing or pb opening srcFileName
156619fcbaf1SConrad Meyer  */
156719fcbaf1SConrad Meyer static int
FIO_compressFilename_internal(FIO_ctx_t * const fCtx,FIO_prefs_t * const prefs,cRess_t ress,const char * dstFileName,const char * srcFileName,int compressionLevel)1568f7cd7fe5SConrad Meyer FIO_compressFilename_internal(FIO_ctx_t* const fCtx,
1569f7cd7fe5SConrad Meyer                               FIO_prefs_t* const prefs,
15702b9c00cbSConrad Meyer                               cRess_t ress,
157119fcbaf1SConrad Meyer                               const char* dstFileName, const char* srcFileName,
157219fcbaf1SConrad Meyer                               int compressionLevel)
157319fcbaf1SConrad Meyer {
15744d3f1eafSConrad Meyer     UTIL_time_t const timeStart = UTIL_getTime();
15754d3f1eafSConrad Meyer     clock_t const cpuStart = clock();
15760c16b537SWarner Losh     U64 readsize = 0;
15770c16b537SWarner Losh     U64 compressedfilesize = 0;
15780c16b537SWarner Losh     U64 const fileSize = UTIL_getFileSize(srcFileName);
1579*5ff13fbcSAllan Jude     DISPLAYLEVEL(5, "%s: %llu bytes \n", srcFileName, (unsigned long long)fileSize);
15800c16b537SWarner Losh 
158119fcbaf1SConrad Meyer     /* compression format selection */
15822b9c00cbSConrad Meyer     switch (prefs->compressionType) {
158319fcbaf1SConrad Meyer         default:
15840c16b537SWarner Losh         case FIO_zstdCompression:
1585f7cd7fe5SConrad Meyer             compressedfilesize = FIO_compressZstdFrame(fCtx, prefs, &ress, srcFileName, fileSize, compressionLevel, &readsize);
15860c16b537SWarner Losh             break;
15870c16b537SWarner Losh 
15880c16b537SWarner Losh         case FIO_gzipCompression:
15890c16b537SWarner Losh #ifdef ZSTD_GZCOMPRESS
15900c16b537SWarner Losh             compressedfilesize = FIO_compressGzFrame(&ress, srcFileName, fileSize, compressionLevel, &readsize);
15910c16b537SWarner Losh #else
15920c16b537SWarner Losh             (void)compressionLevel;
15930c16b537SWarner Losh             EXM_THROW(20, "zstd: %s: file cannot be compressed as gzip (zstd compiled without ZSTD_GZCOMPRESS) -- ignored \n",
15940c16b537SWarner Losh                             srcFileName);
15950c16b537SWarner Losh #endif
159619fcbaf1SConrad Meyer             break;
15970c16b537SWarner Losh 
15980c16b537SWarner Losh         case FIO_xzCompression:
15990c16b537SWarner Losh         case FIO_lzmaCompression:
16000c16b537SWarner Losh #ifdef ZSTD_LZMACOMPRESS
16012b9c00cbSConrad Meyer             compressedfilesize = FIO_compressLzmaFrame(&ress, srcFileName, fileSize, compressionLevel, &readsize, prefs->compressionType==FIO_lzmaCompression);
16020c16b537SWarner Losh #else
16030c16b537SWarner Losh             (void)compressionLevel;
16040c16b537SWarner Losh             EXM_THROW(20, "zstd: %s: file cannot be compressed as xz/lzma (zstd compiled without ZSTD_LZMACOMPRESS) -- ignored \n",
16050c16b537SWarner Losh                             srcFileName);
16060c16b537SWarner Losh #endif
160719fcbaf1SConrad Meyer             break;
16080c16b537SWarner Losh 
16090c16b537SWarner Losh         case FIO_lz4Compression:
16100c16b537SWarner Losh #ifdef ZSTD_LZ4COMPRESS
16112b9c00cbSConrad Meyer             compressedfilesize = FIO_compressLz4Frame(&ress, srcFileName, fileSize, compressionLevel, prefs->checksumFlag, &readsize);
16120c16b537SWarner Losh #else
16130c16b537SWarner Losh             (void)compressionLevel;
16140c16b537SWarner Losh             EXM_THROW(20, "zstd: %s: file cannot be compressed as lz4 (zstd compiled without ZSTD_LZ4COMPRESS) -- ignored \n",
16150c16b537SWarner Losh                             srcFileName);
16160c16b537SWarner Losh #endif
161719fcbaf1SConrad Meyer             break;
16180c16b537SWarner Losh     }
16190c16b537SWarner Losh 
16200c16b537SWarner Losh     /* Status */
1621f7cd7fe5SConrad Meyer     fCtx->totalBytesInput += (size_t)readsize;
1622f7cd7fe5SConrad Meyer     fCtx->totalBytesOutput += (size_t)compressedfilesize;
16230c16b537SWarner Losh     DISPLAYLEVEL(2, "\r%79s\r", "");
1624f7cd7fe5SConrad Meyer     if (g_display_prefs.displayLevel >= 2 &&
1625f7cd7fe5SConrad Meyer         !fCtx->hasStdoutOutput &&
1626f7cd7fe5SConrad Meyer         (g_display_prefs.displayLevel >= 3 || fCtx->nbFilesTotal <= 1)) {
1627*5ff13fbcSAllan Jude         UTIL_HumanReadableSize_t hr_isize = UTIL_makeHumanReadableSize((U64) readsize);
1628*5ff13fbcSAllan Jude         UTIL_HumanReadableSize_t hr_osize = UTIL_makeHumanReadableSize((U64) compressedfilesize);
162937f1f268SConrad Meyer         if (readsize == 0) {
1630*5ff13fbcSAllan Jude             DISPLAYLEVEL(2,"%-20s :  (%6.*f%4s => %6.*f%4s, %s) \n",
163119fcbaf1SConrad Meyer                 srcFileName,
1632*5ff13fbcSAllan Jude                 hr_isize.precision, hr_isize.value, hr_isize.suffix,
1633*5ff13fbcSAllan Jude                 hr_osize.precision, hr_osize.value, hr_osize.suffix,
16340c16b537SWarner Losh                 dstFileName);
163537f1f268SConrad Meyer         } else {
1636*5ff13fbcSAllan Jude             DISPLAYLEVEL(2,"%-20s :%6.2f%%   (%6.*f%4s => %6.*f%4s, %s) \n",
163737f1f268SConrad Meyer                 srcFileName,
1638*5ff13fbcSAllan Jude                 (double)compressedfilesize / (double)readsize * 100,
1639*5ff13fbcSAllan Jude                 hr_isize.precision, hr_isize.value, hr_isize.suffix,
1640*5ff13fbcSAllan Jude                 hr_osize.precision, hr_osize.value, hr_osize.suffix,
164137f1f268SConrad Meyer                 dstFileName);
164237f1f268SConrad Meyer         }
1643f7cd7fe5SConrad Meyer     }
16440c16b537SWarner Losh 
16454d3f1eafSConrad Meyer     /* Elapsed Time and CPU Load */
16464d3f1eafSConrad Meyer     {   clock_t const cpuEnd = clock();
16474d3f1eafSConrad Meyer         double const cpuLoad_s = (double)(cpuEnd - cpuStart) / CLOCKS_PER_SEC;
16484d3f1eafSConrad Meyer         U64 const timeLength_ns = UTIL_clockSpanNano(timeStart);
16494d3f1eafSConrad Meyer         double const timeLength_s = (double)timeLength_ns / 1000000000;
16504d3f1eafSConrad Meyer         double const cpuLoad_pct = (cpuLoad_s / timeLength_s) * 100;
16514d3f1eafSConrad Meyer         DISPLAYLEVEL(4, "%-20s : Completed in %.2f sec  (cpu load : %.0f%%)\n",
16524d3f1eafSConrad Meyer                         srcFileName, timeLength_s, cpuLoad_pct);
16534d3f1eafSConrad Meyer     }
16540c16b537SWarner Losh     return 0;
16550c16b537SWarner Losh }
16560c16b537SWarner Losh 
16570c16b537SWarner Losh 
16580f743729SConrad Meyer /*! FIO_compressFilename_dstFile() :
16590f743729SConrad Meyer  *  open dstFileName, or pass-through if ress.dstFile != NULL,
16600f743729SConrad Meyer  *  then start compression with FIO_compressFilename_internal().
16610f743729SConrad Meyer  *  Manages source removal (--rm) and file permissions transfer.
16620f743729SConrad Meyer  *  note : ress.srcFile must be != NULL,
16630f743729SConrad Meyer  *  so reach this function through FIO_compressFilename_srcFile().
16640f743729SConrad Meyer  *  @return : 0 : compression completed correctly,
16650f743729SConrad Meyer  *            1 : pb
16660f743729SConrad Meyer  */
FIO_compressFilename_dstFile(FIO_ctx_t * const fCtx,FIO_prefs_t * const prefs,cRess_t ress,const char * dstFileName,const char * srcFileName,int compressionLevel)1667f7cd7fe5SConrad Meyer static int FIO_compressFilename_dstFile(FIO_ctx_t* const fCtx,
1668f7cd7fe5SConrad Meyer                                         FIO_prefs_t* const prefs,
16692b9c00cbSConrad Meyer                                         cRess_t ress,
16700f743729SConrad Meyer                                         const char* dstFileName,
16710f743729SConrad Meyer                                         const char* srcFileName,
16720f743729SConrad Meyer                                         int compressionLevel)
16730f743729SConrad Meyer {
16740f743729SConrad Meyer     int closeDstFile = 0;
16750f743729SConrad Meyer     int result;
16760f743729SConrad Meyer     stat_t statbuf;
1677*5ff13fbcSAllan Jude     int transferMTime = 0;
16780f743729SConrad Meyer     assert(ress.srcFile != NULL);
16790f743729SConrad Meyer     if (ress.dstFile == NULL) {
1680*5ff13fbcSAllan Jude         int dstFilePermissions = DEFAULT_FILE_PERMISSIONS;
1681*5ff13fbcSAllan Jude         if ( strcmp (srcFileName, stdinmark)
1682*5ff13fbcSAllan Jude           && strcmp (dstFileName, stdoutmark)
1683*5ff13fbcSAllan Jude           && UTIL_stat(srcFileName, &statbuf)
1684*5ff13fbcSAllan Jude           && UTIL_isRegularFileStat(&statbuf) ) {
1685*5ff13fbcSAllan Jude             dstFilePermissions = statbuf.st_mode;
1686*5ff13fbcSAllan Jude             transferMTime = 1;
1687*5ff13fbcSAllan Jude         }
1688*5ff13fbcSAllan Jude 
16890f743729SConrad Meyer         closeDstFile = 1;
169037f1f268SConrad Meyer         DISPLAYLEVEL(6, "FIO_compressFilename_dstFile: opening dst: %s \n", dstFileName);
1691*5ff13fbcSAllan Jude         ress.dstFile = FIO_openDstFile(fCtx, prefs, srcFileName, dstFileName, dstFilePermissions);
16920f743729SConrad Meyer         if (ress.dstFile==NULL) return 1;  /* could not open dstFileName */
16930f743729SConrad Meyer         /* Must only be added after FIO_openDstFile() succeeds.
16940f743729SConrad Meyer          * Otherwise we may delete the destination file if it already exists,
16950f743729SConrad Meyer          * and the user presses Ctrl-C when asked if they wish to overwrite.
16960f743729SConrad Meyer          */
16970f743729SConrad Meyer         addHandler(dstFileName);
16980f743729SConrad Meyer     }
16990f743729SConrad Meyer 
1700f7cd7fe5SConrad Meyer     result = FIO_compressFilename_internal(fCtx, prefs, ress, dstFileName, srcFileName, compressionLevel);
17010f743729SConrad Meyer 
17020f743729SConrad Meyer     if (closeDstFile) {
17030f743729SConrad Meyer         FILE* const dstFile = ress.dstFile;
17040f743729SConrad Meyer         ress.dstFile = NULL;
17050f743729SConrad Meyer 
17060f743729SConrad Meyer         clearHandler();
17070f743729SConrad Meyer 
170837f1f268SConrad Meyer         DISPLAYLEVEL(6, "FIO_compressFilename_dstFile: closing dst: %s \n", dstFileName);
17090f743729SConrad Meyer         if (fclose(dstFile)) { /* error closing dstFile */
17100f743729SConrad Meyer             DISPLAYLEVEL(1, "zstd: %s: %s \n", dstFileName, strerror(errno));
17110f743729SConrad Meyer             result=1;
17120f743729SConrad Meyer         }
1713*5ff13fbcSAllan Jude         if (transferMTime) {
1714*5ff13fbcSAllan Jude             UTIL_utime(dstFileName, &statbuf);
1715*5ff13fbcSAllan Jude         }
17160f743729SConrad Meyer         if ( (result != 0)  /* operation failure */
17170f743729SConrad Meyer           && strcmp(dstFileName, stdoutmark)  /* special case : don't remove() stdout */
17180f743729SConrad Meyer           ) {
1719f7cd7fe5SConrad Meyer             FIO_removeFile(dstFileName); /* remove compression artefact; note don't do anything special if remove() fails */
17200f743729SConrad Meyer         }
17210f743729SConrad Meyer     }
17220f743729SConrad Meyer 
17230f743729SConrad Meyer     return result;
17240f743729SConrad Meyer }
17250f743729SConrad Meyer 
17269cbefe25SConrad Meyer /* List used to compare file extensions (used with --exclude-compressed flag)
17279cbefe25SConrad Meyer * Different from the suffixList and should only apply to ZSTD compress operationResult
17289cbefe25SConrad Meyer */
17299cbefe25SConrad Meyer static const char *compressedFileExtensions[] = {
17309cbefe25SConrad Meyer     ZSTD_EXTENSION,
17319cbefe25SConrad Meyer     TZSTD_EXTENSION,
17329cbefe25SConrad Meyer     GZ_EXTENSION,
17339cbefe25SConrad Meyer     TGZ_EXTENSION,
17349cbefe25SConrad Meyer     LZMA_EXTENSION,
17359cbefe25SConrad Meyer     XZ_EXTENSION,
17369cbefe25SConrad Meyer     TXZ_EXTENSION,
17379cbefe25SConrad Meyer     LZ4_EXTENSION,
17389cbefe25SConrad Meyer     TLZ4_EXTENSION,
17399cbefe25SConrad Meyer     NULL
17409cbefe25SConrad Meyer };
17410f743729SConrad Meyer 
17420c16b537SWarner Losh /*! FIO_compressFilename_srcFile() :
17430c16b537SWarner Losh  *  @return : 0 : compression completed correctly,
17440c16b537SWarner Losh  *            1 : missing or pb opening srcFileName
17450c16b537SWarner Losh  */
17460f743729SConrad Meyer static int
FIO_compressFilename_srcFile(FIO_ctx_t * const fCtx,FIO_prefs_t * const prefs,cRess_t ress,const char * dstFileName,const char * srcFileName,int compressionLevel)1747f7cd7fe5SConrad Meyer FIO_compressFilename_srcFile(FIO_ctx_t* const fCtx,
1748f7cd7fe5SConrad Meyer                              FIO_prefs_t* const prefs,
17492b9c00cbSConrad Meyer                              cRess_t ress,
17500f743729SConrad Meyer                              const char* dstFileName,
17510f743729SConrad Meyer                              const char* srcFileName,
17520c16b537SWarner Losh                              int compressionLevel)
17530c16b537SWarner Losh {
17540c16b537SWarner Losh     int result;
175537f1f268SConrad Meyer     DISPLAYLEVEL(6, "FIO_compressFilename_srcFile: %s \n", srcFileName);
17560c16b537SWarner Losh 
17572b9c00cbSConrad Meyer     /* ensure src is not a directory */
17580c16b537SWarner Losh     if (UTIL_isDirectory(srcFileName)) {
17590c16b537SWarner Losh         DISPLAYLEVEL(1, "zstd: %s is a directory -- ignored \n", srcFileName);
17600c16b537SWarner Losh         return 1;
17610c16b537SWarner Losh     }
17620c16b537SWarner Losh 
17632b9c00cbSConrad Meyer     /* ensure src is not the same as dict (if present) */
17642b9c00cbSConrad Meyer     if (ress.dictFileName != NULL && UTIL_isSameFile(srcFileName, ress.dictFileName)) {
17652b9c00cbSConrad Meyer         DISPLAYLEVEL(1, "zstd: cannot use %s as an input file and dictionary \n", srcFileName);
17662b9c00cbSConrad Meyer         return 1;
17672b9c00cbSConrad Meyer     }
17682b9c00cbSConrad Meyer 
17699cbefe25SConrad Meyer     /* Check if "srcFile" is compressed. Only done if --exclude-compressed flag is used
17709cbefe25SConrad Meyer     * YES => ZSTD will skip compression of the file and will return 0.
17719cbefe25SConrad Meyer     * NO => ZSTD will resume with compress operation.
17729cbefe25SConrad Meyer     */
17739cbefe25SConrad Meyer     if (prefs->excludeCompressedFiles == 1 && UTIL_isCompressedFile(srcFileName, compressedFileExtensions)) {
17749cbefe25SConrad Meyer         DISPLAYLEVEL(4, "File is already compressed : %s \n", srcFileName);
17759cbefe25SConrad Meyer         return 0;
17769cbefe25SConrad Meyer     }
17779cbefe25SConrad Meyer 
1778*5ff13fbcSAllan Jude     ress.srcFile = FIO_openSrcFile(prefs, srcFileName);
17790f743729SConrad Meyer     if (ress.srcFile == NULL) return 1;   /* srcFile could not be opened */
17800c16b537SWarner Losh 
1781f7cd7fe5SConrad Meyer     result = FIO_compressFilename_dstFile(fCtx, prefs, ress, dstFileName, srcFileName, compressionLevel);
17820c16b537SWarner Losh 
17830c16b537SWarner Losh     fclose(ress.srcFile);
17840f743729SConrad Meyer     ress.srcFile = NULL;
17852b9c00cbSConrad Meyer     if ( prefs->removeSrcFile   /* --rm */
17860f743729SConrad Meyer       && result == 0       /* success */
17870f743729SConrad Meyer       && strcmp(srcFileName, stdinmark)   /* exception : don't erase stdin */
17880f743729SConrad Meyer       ) {
1789052d3c12SConrad Meyer         /* We must clear the handler, since after this point calling it would
1790052d3c12SConrad Meyer          * delete both the source and destination files.
1791052d3c12SConrad Meyer          */
1792052d3c12SConrad Meyer         clearHandler();
1793f7cd7fe5SConrad Meyer         if (FIO_removeFile(srcFileName))
17940c16b537SWarner Losh             EXM_THROW(1, "zstd: %s: %s", srcFileName, strerror(errno));
17950c16b537SWarner Losh     }
17960c16b537SWarner Losh     return result;
17970c16b537SWarner Losh }
17980c16b537SWarner Losh 
checked_index(const char * options[],size_t length,size_t index)1799*5ff13fbcSAllan Jude static const char* checked_index(const char* options[], size_t length, size_t index) {
1800*5ff13fbcSAllan Jude     assert(index < length);
1801*5ff13fbcSAllan Jude     // Necessary to avoid warnings since -O3 will omit the above `assert`
1802*5ff13fbcSAllan Jude     (void) length;
1803*5ff13fbcSAllan Jude     return options[index];
1804*5ff13fbcSAllan Jude }
1805*5ff13fbcSAllan Jude 
1806*5ff13fbcSAllan Jude #define INDEX(options, index) checked_index((options), sizeof(options)  / sizeof(char*), (index))
1807*5ff13fbcSAllan Jude 
FIO_displayCompressionParameters(const FIO_prefs_t * prefs)1808*5ff13fbcSAllan Jude void FIO_displayCompressionParameters(const FIO_prefs_t* prefs) {
1809*5ff13fbcSAllan Jude     static const char* formatOptions[5] = {ZSTD_EXTENSION, GZ_EXTENSION, XZ_EXTENSION,
1810*5ff13fbcSAllan Jude                                            LZMA_EXTENSION, LZ4_EXTENSION};
1811*5ff13fbcSAllan Jude     static const char* sparseOptions[3] = {" --no-sparse", "", " --sparse"};
1812*5ff13fbcSAllan Jude     static const char* checkSumOptions[3] = {" --no-check", "", " --check"};
1813*5ff13fbcSAllan Jude     static const char* rowMatchFinderOptions[3] = {"", " --no-row-match-finder", " --row-match-finder"};
1814*5ff13fbcSAllan Jude     static const char* compressLiteralsOptions[3] = {"", " --compress-literals", " --no-compress-literals"};
1815*5ff13fbcSAllan Jude 
1816*5ff13fbcSAllan Jude     assert(g_display_prefs.displayLevel >= 4);
1817*5ff13fbcSAllan Jude 
1818*5ff13fbcSAllan Jude     DISPLAY("--format=%s", formatOptions[prefs->compressionType]);
1819*5ff13fbcSAllan Jude     DISPLAY("%s", INDEX(sparseOptions, prefs->sparseFileSupport));
1820*5ff13fbcSAllan Jude     DISPLAY("%s", prefs->dictIDFlag ? "" : " --no-dictID");
1821*5ff13fbcSAllan Jude     DISPLAY("%s", INDEX(checkSumOptions, prefs->checksumFlag));
1822*5ff13fbcSAllan Jude     DISPLAY(" --block-size=%d", prefs->blockSize);
1823*5ff13fbcSAllan Jude     if (prefs->adaptiveMode)
1824*5ff13fbcSAllan Jude         DISPLAY(" --adapt=min=%d,max=%d", prefs->minAdaptLevel, prefs->maxAdaptLevel);
1825*5ff13fbcSAllan Jude     DISPLAY("%s", INDEX(rowMatchFinderOptions, prefs->useRowMatchFinder));
1826*5ff13fbcSAllan Jude     DISPLAY("%s", prefs->rsyncable ? " --rsyncable" : "");
1827*5ff13fbcSAllan Jude     if (prefs->streamSrcSize)
1828*5ff13fbcSAllan Jude         DISPLAY(" --stream-size=%u", (unsigned) prefs->streamSrcSize);
1829*5ff13fbcSAllan Jude     if (prefs->srcSizeHint)
1830*5ff13fbcSAllan Jude         DISPLAY(" --size-hint=%d", prefs->srcSizeHint);
1831*5ff13fbcSAllan Jude     if (prefs->targetCBlockSize)
1832*5ff13fbcSAllan Jude         DISPLAY(" --target-compressed-block-size=%u", (unsigned) prefs->targetCBlockSize);
1833*5ff13fbcSAllan Jude     DISPLAY("%s", INDEX(compressLiteralsOptions, prefs->literalCompressionMode));
1834*5ff13fbcSAllan Jude     DISPLAY(" --memory=%u", prefs->memLimit ? prefs->memLimit : 128 MB);
1835*5ff13fbcSAllan Jude     DISPLAY(" --threads=%d", prefs->nbWorkers);
1836*5ff13fbcSAllan Jude     DISPLAY("%s", prefs->excludeCompressedFiles ? " --exclude-compressed" : "");
1837*5ff13fbcSAllan Jude     DISPLAY(" --%scontent-size", prefs->contentSize ? "" : "no-");
1838*5ff13fbcSAllan Jude     DISPLAY("\n");
1839*5ff13fbcSAllan Jude }
1840*5ff13fbcSAllan Jude 
1841*5ff13fbcSAllan Jude #undef INDEX
1842*5ff13fbcSAllan Jude 
FIO_compressFilename(FIO_ctx_t * const fCtx,FIO_prefs_t * const prefs,const char * dstFileName,const char * srcFileName,const char * dictFileName,int compressionLevel,ZSTD_compressionParameters comprParams)1843f7cd7fe5SConrad Meyer int FIO_compressFilename(FIO_ctx_t* const fCtx, FIO_prefs_t* const prefs, const char* dstFileName,
18449cbefe25SConrad Meyer                          const char* srcFileName, const char* dictFileName,
18459cbefe25SConrad Meyer                          int compressionLevel, ZSTD_compressionParameters comprParams)
18460c16b537SWarner Losh {
184737f1f268SConrad Meyer     cRess_t const ress = FIO_createCResources(prefs, dictFileName, UTIL_getFileSize(srcFileName), compressionLevel, comprParams);
1848f7cd7fe5SConrad Meyer     int const result = FIO_compressFilename_srcFile(fCtx, prefs, ress, dstFileName, srcFileName, compressionLevel);
18490c16b537SWarner Losh 
1850f7cd7fe5SConrad Meyer #define DISPLAY_LEVEL_DEFAULT 2
18510c16b537SWarner Losh 
1852f7cd7fe5SConrad Meyer     FIO_freeCResources(&ress);
18530c16b537SWarner Losh     return result;
18540c16b537SWarner Losh }
18550c16b537SWarner Losh 
18560f743729SConrad Meyer /* FIO_determineCompressedName() :
18570f743729SConrad Meyer  * create a destination filename for compressed srcFileName.
18580f743729SConrad Meyer  * @return a pointer to it.
18590f743729SConrad Meyer  * This function never returns an error (it may abort() in case of pb)
18600f743729SConrad Meyer  */
18610f743729SConrad Meyer static const char*
FIO_determineCompressedName(const char * srcFileName,const char * outDirName,const char * suffix)18629cbefe25SConrad Meyer FIO_determineCompressedName(const char* srcFileName, const char* outDirName, const char* suffix)
18630f743729SConrad Meyer {
18640f743729SConrad Meyer     static size_t dfnbCapacity = 0;
18650f743729SConrad Meyer     static char* dstFileNameBuffer = NULL;   /* using static allocation : this function cannot be multi-threaded */
18669cbefe25SConrad Meyer     char* outDirFilename = NULL;
18679cbefe25SConrad Meyer     size_t sfnSize = strlen(srcFileName);
18689cbefe25SConrad Meyer     size_t const srcSuffixLen = strlen(suffix);
18699cbefe25SConrad Meyer     if (outDirName) {
18709cbefe25SConrad Meyer         outDirFilename = FIO_createFilename_fromOutDir(srcFileName, outDirName, srcSuffixLen);
18719cbefe25SConrad Meyer         sfnSize = strlen(outDirFilename);
18729cbefe25SConrad Meyer         assert(outDirFilename != NULL);
18739cbefe25SConrad Meyer     }
18740f743729SConrad Meyer 
18759cbefe25SConrad Meyer     if (dfnbCapacity <= sfnSize+srcSuffixLen+1) {
18760f743729SConrad Meyer         /* resize buffer for dstName */
18770f743729SConrad Meyer         free(dstFileNameBuffer);
18789cbefe25SConrad Meyer         dfnbCapacity = sfnSize + srcSuffixLen + 30;
18790f743729SConrad Meyer         dstFileNameBuffer = (char*)malloc(dfnbCapacity);
18800f743729SConrad Meyer         if (!dstFileNameBuffer) {
18810f743729SConrad Meyer             EXM_THROW(30, "zstd: %s", strerror(errno));
18829cbefe25SConrad Meyer         }
18839cbefe25SConrad Meyer     }
18840f743729SConrad Meyer     assert(dstFileNameBuffer != NULL);
18850f743729SConrad Meyer 
18869cbefe25SConrad Meyer     if (outDirFilename) {
18879cbefe25SConrad Meyer         memcpy(dstFileNameBuffer, outDirFilename, sfnSize);
18889cbefe25SConrad Meyer         free(outDirFilename);
18899cbefe25SConrad Meyer     } else {
18909cbefe25SConrad Meyer         memcpy(dstFileNameBuffer, srcFileName, sfnSize);
18919cbefe25SConrad Meyer     }
18929cbefe25SConrad Meyer     memcpy(dstFileNameBuffer+sfnSize, suffix, srcSuffixLen+1 /* Include terminating null */);
18930f743729SConrad Meyer     return dstFileNameBuffer;
18940f743729SConrad Meyer }
18950f743729SConrad Meyer 
FIO_getLargestFileSize(const char ** inFileNames,unsigned nbFiles)189637f1f268SConrad Meyer static unsigned long long FIO_getLargestFileSize(const char** inFileNames, unsigned nbFiles)
189737f1f268SConrad Meyer {
189837f1f268SConrad Meyer     size_t i;
189937f1f268SConrad Meyer     unsigned long long fileSize, maxFileSize = 0;
190037f1f268SConrad Meyer     for (i = 0; i < nbFiles; i++) {
190137f1f268SConrad Meyer         fileSize = UTIL_getFileSize(inFileNames[i]);
190237f1f268SConrad Meyer         maxFileSize = fileSize > maxFileSize ? fileSize : maxFileSize;
190337f1f268SConrad Meyer     }
190437f1f268SConrad Meyer     return maxFileSize;
190537f1f268SConrad Meyer }
19060f743729SConrad Meyer 
19070f743729SConrad Meyer /* FIO_compressMultipleFilenames() :
19080f743729SConrad Meyer  * compress nbFiles files
19099cbefe25SConrad Meyer  * into either one destination (outFileName),
19109cbefe25SConrad Meyer  * or into one file each (outFileName == NULL, but suffix != NULL),
19119cbefe25SConrad Meyer  * or into a destination folder (specified with -O)
19120f743729SConrad Meyer  */
FIO_compressMultipleFilenames(FIO_ctx_t * const fCtx,FIO_prefs_t * const prefs,const char ** inFileNamesTable,const char * outMirroredRootDirName,const char * outDirName,const char * outFileName,const char * suffix,const char * dictFileName,int compressionLevel,ZSTD_compressionParameters comprParams)1913f7cd7fe5SConrad Meyer int FIO_compressMultipleFilenames(FIO_ctx_t* const fCtx,
1914f7cd7fe5SConrad Meyer                                   FIO_prefs_t* const prefs,
1915f7cd7fe5SConrad Meyer                                   const char** inFileNamesTable,
1916f7cd7fe5SConrad Meyer                                   const char* outMirroredRootDirName,
19179cbefe25SConrad Meyer                                   const char* outDirName,
1918052d3c12SConrad Meyer                                   const char* outFileName, const char* suffix,
19190c16b537SWarner Losh                                   const char* dictFileName, int compressionLevel,
19200f743729SConrad Meyer                                   ZSTD_compressionParameters comprParams)
19210c16b537SWarner Losh {
1922f7cd7fe5SConrad Meyer     int status;
19230f743729SConrad Meyer     int error = 0;
192437f1f268SConrad Meyer     cRess_t ress = FIO_createCResources(prefs, dictFileName,
1925*5ff13fbcSAllan Jude         FIO_getLargestFileSize(inFileNamesTable, (unsigned)fCtx->nbFilesTotal),
192637f1f268SConrad Meyer         compressionLevel, comprParams);
19270c16b537SWarner Losh 
19280c16b537SWarner Losh     /* init */
19290f743729SConrad Meyer     assert(outFileName != NULL || suffix != NULL);
19300f743729SConrad Meyer     if (outFileName != NULL) {   /* output into a single destination (stdout typically) */
1931f7cd7fe5SConrad Meyer         if (FIO_removeMultiFilesWarning(fCtx, prefs, outFileName, 1 /* displayLevelCutoff */)) {
1932f7cd7fe5SConrad Meyer             FIO_freeCResources(&ress);
1933f7cd7fe5SConrad Meyer             return 1;
1934f7cd7fe5SConrad Meyer         }
1935*5ff13fbcSAllan Jude         ress.dstFile = FIO_openDstFile(fCtx, prefs, NULL, outFileName, DEFAULT_FILE_PERMISSIONS);
193619fcbaf1SConrad Meyer         if (ress.dstFile == NULL) {  /* could not open outFileName */
19370f743729SConrad Meyer             error = 1;
193819fcbaf1SConrad Meyer         } else {
1939f7cd7fe5SConrad Meyer             for (; fCtx->currFileIdx < fCtx->nbFilesTotal; ++fCtx->currFileIdx) {
1940f7cd7fe5SConrad Meyer                 status = FIO_compressFilename_srcFile(fCtx, prefs, ress, outFileName, inFileNamesTable[fCtx->currFileIdx], compressionLevel);
1941f7cd7fe5SConrad Meyer                 if (!status) fCtx->nbFilesProcessed++;
1942f7cd7fe5SConrad Meyer                 error |= status;
1943f7cd7fe5SConrad Meyer             }
19440c16b537SWarner Losh             if (fclose(ress.dstFile))
1945a0483764SConrad Meyer                 EXM_THROW(29, "Write error (%s) : cannot properly close %s",
1946a0483764SConrad Meyer                             strerror(errno), outFileName);
19470f743729SConrad Meyer             ress.dstFile = NULL;
194819fcbaf1SConrad Meyer         }
19490c16b537SWarner Losh     } else {
1950f7cd7fe5SConrad Meyer         if (outMirroredRootDirName)
1951*5ff13fbcSAllan Jude             UTIL_mirrorSourceFilesDirectories(inFileNamesTable, (unsigned)fCtx->nbFilesTotal, outMirroredRootDirName);
1952f7cd7fe5SConrad Meyer 
1953f7cd7fe5SConrad Meyer         for (; fCtx->currFileIdx < fCtx->nbFilesTotal; ++fCtx->currFileIdx) {
1954f7cd7fe5SConrad Meyer             const char* const srcFileName = inFileNamesTable[fCtx->currFileIdx];
1955f7cd7fe5SConrad Meyer             const char* dstFileName = NULL;
1956f7cd7fe5SConrad Meyer             if (outMirroredRootDirName) {
1957f7cd7fe5SConrad Meyer                 char* validMirroredDirName = UTIL_createMirroredDestDirName(srcFileName, outMirroredRootDirName);
1958f7cd7fe5SConrad Meyer                 if (validMirroredDirName) {
1959f7cd7fe5SConrad Meyer                     dstFileName = FIO_determineCompressedName(srcFileName, validMirroredDirName, suffix);
1960f7cd7fe5SConrad Meyer                     free(validMirroredDirName);
1961f7cd7fe5SConrad Meyer                 } else {
1962f7cd7fe5SConrad Meyer                     DISPLAYLEVEL(2, "zstd: --output-dir-mirror cannot compress '%s' into '%s' \n", srcFileName, outMirroredRootDirName);
1963f7cd7fe5SConrad Meyer                     error=1;
1964f7cd7fe5SConrad Meyer                     continue;
19659cbefe25SConrad Meyer                 }
1966f7cd7fe5SConrad Meyer             } else {
1967f7cd7fe5SConrad Meyer                 dstFileName = FIO_determineCompressedName(srcFileName, outDirName, suffix);  /* cannot fail */
1968f7cd7fe5SConrad Meyer             }
1969f7cd7fe5SConrad Meyer             status = FIO_compressFilename_srcFile(fCtx, prefs, ress, dstFileName, srcFileName, compressionLevel);
1970f7cd7fe5SConrad Meyer             if (!status) fCtx->nbFilesProcessed++;
1971f7cd7fe5SConrad Meyer             error |= status;
19729cbefe25SConrad Meyer         }
19730c16b537SWarner Losh 
1974f7cd7fe5SConrad Meyer         if (outDirName)
1975*5ff13fbcSAllan Jude             FIO_checkFilenameCollisions(inFileNamesTable , (unsigned)fCtx->nbFilesTotal);
1976f7cd7fe5SConrad Meyer     }
1977f7cd7fe5SConrad Meyer 
1978f7cd7fe5SConrad Meyer     if (fCtx->nbFilesProcessed >= 1 && fCtx->nbFilesTotal > 1 && fCtx->totalBytesInput != 0) {
1979*5ff13fbcSAllan Jude         UTIL_HumanReadableSize_t hr_isize = UTIL_makeHumanReadableSize((U64) fCtx->totalBytesInput);
1980*5ff13fbcSAllan Jude         UTIL_HumanReadableSize_t hr_osize = UTIL_makeHumanReadableSize((U64) fCtx->totalBytesOutput);
1981*5ff13fbcSAllan Jude 
1982f7cd7fe5SConrad Meyer         DISPLAYLEVEL(2, "\r%79s\r", "");
1983*5ff13fbcSAllan Jude         DISPLAYLEVEL(2, "%3d files compressed :%.2f%%   (%6.*f%4s => %6.*f%4s)\n",
1984*5ff13fbcSAllan Jude                         fCtx->nbFilesProcessed,
1985f7cd7fe5SConrad Meyer                         (double)fCtx->totalBytesOutput/((double)fCtx->totalBytesInput)*100,
1986*5ff13fbcSAllan Jude                         hr_isize.precision, hr_isize.value, hr_isize.suffix,
1987*5ff13fbcSAllan Jude                         hr_osize.precision, hr_osize.value, hr_osize.suffix);
1988f7cd7fe5SConrad Meyer     }
1989f7cd7fe5SConrad Meyer 
1990f7cd7fe5SConrad Meyer     FIO_freeCResources(&ress);
19910f743729SConrad Meyer     return error;
19920c16b537SWarner Losh }
19930c16b537SWarner Losh 
19940c16b537SWarner Losh #endif /* #ifndef ZSTD_NOCOMPRESS */
19950c16b537SWarner Losh 
19960c16b537SWarner Losh 
19970c16b537SWarner Losh 
19980c16b537SWarner Losh #ifndef ZSTD_NODECOMPRESS
19990c16b537SWarner Losh 
20000c16b537SWarner Losh /* **************************************************************************
20010c16b537SWarner Losh  *  Decompression
20020c16b537SWarner Losh  ***************************************************************************/
20030c16b537SWarner Losh typedef struct {
20040c16b537SWarner Losh     void*  srcBuffer;
20050c16b537SWarner Losh     size_t srcBufferSize;
20060c16b537SWarner Losh     size_t srcBufferLoaded;
20070c16b537SWarner Losh     void*  dstBuffer;
20080c16b537SWarner Losh     size_t dstBufferSize;
20090c16b537SWarner Losh     ZSTD_DStream* dctx;
20100c16b537SWarner Losh     FILE*  dstFile;
20110c16b537SWarner Losh } dRess_t;
20120c16b537SWarner Losh 
FIO_createDResources(FIO_prefs_t * const prefs,const char * dictFileName)20132b9c00cbSConrad Meyer static dRess_t FIO_createDResources(FIO_prefs_t* const prefs, const char* dictFileName)
20140c16b537SWarner Losh {
20150c16b537SWarner Losh     dRess_t ress;
20160c16b537SWarner Losh     memset(&ress, 0, sizeof(ress));
20170c16b537SWarner Losh 
201837f1f268SConrad Meyer     if (prefs->patchFromMode)
201937f1f268SConrad Meyer         FIO_adjustMemLimitForPatchFromMode(prefs, UTIL_getFileSize(dictFileName), 0 /* just use the dict size */);
202037f1f268SConrad Meyer 
20210c16b537SWarner Losh     /* Allocation */
20220c16b537SWarner Losh     ress.dctx = ZSTD_createDStream();
2023a0483764SConrad Meyer     if (ress.dctx==NULL)
2024a0483764SConrad Meyer         EXM_THROW(60, "Error: %s : can't create ZSTD_DStream", strerror(errno));
20252b9c00cbSConrad Meyer     CHECK( ZSTD_DCtx_setMaxWindowSize(ress.dctx, prefs->memLimit) );
2026f7cd7fe5SConrad Meyer     CHECK( ZSTD_DCtx_setParameter(ress.dctx, ZSTD_d_forceIgnoreChecksum, !prefs->checksumFlag));
2027f7cd7fe5SConrad Meyer 
20280c16b537SWarner Losh     ress.srcBufferSize = ZSTD_DStreamInSize();
20290c16b537SWarner Losh     ress.srcBuffer = malloc(ress.srcBufferSize);
20300c16b537SWarner Losh     ress.dstBufferSize = ZSTD_DStreamOutSize();
20310c16b537SWarner Losh     ress.dstBuffer = malloc(ress.dstBufferSize);
20320c16b537SWarner Losh     if (!ress.srcBuffer || !ress.dstBuffer)
20330c16b537SWarner Losh         EXM_THROW(61, "Allocation error : not enough memory");
20340c16b537SWarner Losh 
20350c16b537SWarner Losh     /* dictionary */
20360c16b537SWarner Losh     {   void* dictBuffer;
203737f1f268SConrad Meyer         size_t const dictBufferSize = FIO_createDictBuffer(&dictBuffer, dictFileName, prefs);
20380c16b537SWarner Losh         CHECK( ZSTD_initDStream_usingDict(ress.dctx, dictBuffer, dictBufferSize) );
20390c16b537SWarner Losh         free(dictBuffer);
20400c16b537SWarner Losh     }
20410c16b537SWarner Losh 
20420c16b537SWarner Losh     return ress;
20430c16b537SWarner Losh }
20440c16b537SWarner Losh 
FIO_freeDResources(dRess_t ress)20450c16b537SWarner Losh static void FIO_freeDResources(dRess_t ress)
20460c16b537SWarner Losh {
20470c16b537SWarner Losh     CHECK( ZSTD_freeDStream(ress.dctx) );
20480c16b537SWarner Losh     free(ress.srcBuffer);
20490c16b537SWarner Losh     free(ress.dstBuffer);
20500c16b537SWarner Losh }
20510c16b537SWarner Losh 
20520c16b537SWarner Losh 
20530c16b537SWarner Losh /** FIO_fwriteSparse() :
205437f1f268SConrad Meyer *  @return : storedSkips,
205537f1f268SConrad Meyer *            argument for next call to FIO_fwriteSparse() or FIO_fwriteSparseEnd() */
20569cbefe25SConrad Meyer static unsigned
FIO_fwriteSparse(FILE * file,const void * buffer,size_t bufferSize,const FIO_prefs_t * const prefs,unsigned storedSkips)205737f1f268SConrad Meyer FIO_fwriteSparse(FILE* file,
20589cbefe25SConrad Meyer                  const void* buffer, size_t bufferSize,
205937f1f268SConrad Meyer                  const FIO_prefs_t* const prefs,
20609cbefe25SConrad Meyer                  unsigned storedSkips)
20610c16b537SWarner Losh {
20620c16b537SWarner Losh     const size_t* const bufferT = (const size_t*)buffer;   /* Buffer is supposed malloc'ed, hence aligned on size_t */
20630c16b537SWarner Losh     size_t bufferSizeT = bufferSize / sizeof(size_t);
20640c16b537SWarner Losh     const size_t* const bufferTEnd = bufferT + bufferSizeT;
20650c16b537SWarner Losh     const size_t* ptrT = bufferT;
206637f1f268SConrad Meyer     static const size_t segmentSizeT = (32 KB) / sizeof(size_t);   /* check every 32 KB */
20670c16b537SWarner Losh 
20689cbefe25SConrad Meyer     if (prefs->testMode) return 0;  /* do not output anything in test mode */
20699cbefe25SConrad Meyer 
20702b9c00cbSConrad Meyer     if (!prefs->sparseFileSupport) {  /* normal write */
20710c16b537SWarner Losh         size_t const sizeCheck = fwrite(buffer, 1, bufferSize, file);
2072a0483764SConrad Meyer         if (sizeCheck != bufferSize)
20739cbefe25SConrad Meyer             EXM_THROW(70, "Write error : cannot write decoded block : %s",
2074a0483764SConrad Meyer                             strerror(errno));
20750c16b537SWarner Losh         return 0;
20760c16b537SWarner Losh     }
20770c16b537SWarner Losh 
20780c16b537SWarner Losh     /* avoid int overflow */
20790c16b537SWarner Losh     if (storedSkips > 1 GB) {
208037f1f268SConrad Meyer         if (LONG_SEEK(file, 1 GB, SEEK_CUR) != 0)
20819cbefe25SConrad Meyer             EXM_THROW(91, "1 GB skip error (sparse file support)");
20820c16b537SWarner Losh         storedSkips -= 1 GB;
20830c16b537SWarner Losh     }
20840c16b537SWarner Losh 
20850c16b537SWarner Losh     while (ptrT < bufferTEnd) {
20860c16b537SWarner Losh         size_t nb0T;
20870c16b537SWarner Losh 
208837f1f268SConrad Meyer         /* adjust last segment if < 32 KB */
208937f1f268SConrad Meyer         size_t seg0SizeT = segmentSizeT;
20900c16b537SWarner Losh         if (seg0SizeT > bufferSizeT) seg0SizeT = bufferSizeT;
20910c16b537SWarner Losh         bufferSizeT -= seg0SizeT;
209237f1f268SConrad Meyer 
209337f1f268SConrad Meyer         /* count leading zeroes */
20940c16b537SWarner Losh         for (nb0T=0; (nb0T < seg0SizeT) && (ptrT[nb0T] == 0); nb0T++) ;
20950c16b537SWarner Losh         storedSkips += (unsigned)(nb0T * sizeof(size_t));
20960c16b537SWarner Losh 
20970c16b537SWarner Losh         if (nb0T != seg0SizeT) {   /* not all 0s */
209837f1f268SConrad Meyer             size_t const nbNon0ST = seg0SizeT - nb0T;
209937f1f268SConrad Meyer             /* skip leading zeros */
210037f1f268SConrad Meyer             if (LONG_SEEK(file, storedSkips, SEEK_CUR) != 0)
210137f1f268SConrad Meyer                 EXM_THROW(92, "Sparse skip error ; try --no-sparse");
21020c16b537SWarner Losh             storedSkips = 0;
210337f1f268SConrad Meyer             /* write the rest */
210437f1f268SConrad Meyer             if (fwrite(ptrT + nb0T, sizeof(size_t), nbNon0ST, file) != nbNon0ST)
21059cbefe25SConrad Meyer                 EXM_THROW(93, "Write error : cannot write decoded block : %s",
21069cbefe25SConrad Meyer                             strerror(errno));
210737f1f268SConrad Meyer         }
21080c16b537SWarner Losh         ptrT += seg0SizeT;
21090c16b537SWarner Losh     }
21100c16b537SWarner Losh 
21110c16b537SWarner Losh     {   static size_t const maskT = sizeof(size_t)-1;
21120c16b537SWarner Losh         if (bufferSize & maskT) {
21130c16b537SWarner Losh             /* size not multiple of sizeof(size_t) : implies end of block */
21140c16b537SWarner Losh             const char* const restStart = (const char*)bufferTEnd;
21150c16b537SWarner Losh             const char* restPtr = restStart;
211637f1f268SConrad Meyer             const char* const restEnd = (const char*)buffer + bufferSize;
211737f1f268SConrad Meyer             assert(restEnd > restStart && restEnd < restStart + sizeof(size_t));
21180c16b537SWarner Losh             for ( ; (restPtr < restEnd) && (*restPtr == 0); restPtr++) ;
21190c16b537SWarner Losh             storedSkips += (unsigned) (restPtr - restStart);
21200c16b537SWarner Losh             if (restPtr != restEnd) {
212137f1f268SConrad Meyer                 /* not all remaining bytes are 0 */
212237f1f268SConrad Meyer                 size_t const restSize = (size_t)(restEnd - restPtr);
212337f1f268SConrad Meyer                 if (LONG_SEEK(file, storedSkips, SEEK_CUR) != 0)
212437f1f268SConrad Meyer                     EXM_THROW(92, "Sparse skip error ; try --no-sparse");
212537f1f268SConrad Meyer                 if (fwrite(restPtr, 1, restSize, file) != restSize)
212637f1f268SConrad Meyer                     EXM_THROW(95, "Write error : cannot write end of decoded block : %s",
21279cbefe25SConrad Meyer                         strerror(errno));
212837f1f268SConrad Meyer                 storedSkips = 0;
212937f1f268SConrad Meyer     }   }   }
21300c16b537SWarner Losh 
21310c16b537SWarner Losh     return storedSkips;
21320c16b537SWarner Losh }
21330c16b537SWarner Losh 
21342b9c00cbSConrad Meyer static void
FIO_fwriteSparseEnd(const FIO_prefs_t * const prefs,FILE * file,unsigned storedSkips)21359cbefe25SConrad Meyer FIO_fwriteSparseEnd(const FIO_prefs_t* const prefs, FILE* file, unsigned storedSkips)
21360c16b537SWarner Losh {
21379cbefe25SConrad Meyer     if (prefs->testMode) assert(storedSkips == 0);
2138a0483764SConrad Meyer     if (storedSkips>0) {
21392b9c00cbSConrad Meyer         assert(prefs->sparseFileSupport > 0);  /* storedSkips>0 implies sparse support is enabled */
21402b9c00cbSConrad Meyer         (void)prefs;   /* assert can be disabled, in which case prefs becomes unused */
2141a0483764SConrad Meyer         if (LONG_SEEK(file, storedSkips-1, SEEK_CUR) != 0)
2142a0483764SConrad Meyer             EXM_THROW(69, "Final skip error (sparse file support)");
2143a0483764SConrad Meyer         /* last zero must be explicitly written,
2144a0483764SConrad Meyer          * so that skipped ones get implicitly translated as zero by FS */
21450c16b537SWarner Losh         {   const char lastZeroByte[1] = { 0 };
2146a0483764SConrad Meyer             if (fwrite(lastZeroByte, 1, 1, file) != 1)
21479cbefe25SConrad Meyer                 EXM_THROW(69, "Write error : cannot write last zero : %s", strerror(errno));
21480c16b537SWarner Losh     }   }
21490c16b537SWarner Losh }
21500c16b537SWarner Losh 
21510c16b537SWarner Losh 
21520c16b537SWarner Losh /** FIO_passThrough() : just copy input into output, for compatibility with gzip -df mode
21530c16b537SWarner Losh     @return : 0 (no error) */
FIO_passThrough(const FIO_prefs_t * const prefs,FILE * foutput,FILE * finput,void * buffer,size_t bufferSize,size_t alreadyLoaded)21549cbefe25SConrad Meyer static int FIO_passThrough(const FIO_prefs_t* const prefs,
21552b9c00cbSConrad Meyer                            FILE* foutput, FILE* finput,
21562b9c00cbSConrad Meyer                            void* buffer, size_t bufferSize,
21572b9c00cbSConrad Meyer                            size_t alreadyLoaded)
21580c16b537SWarner Losh {
21590c16b537SWarner Losh     size_t const blockSize = MIN(64 KB, bufferSize);
216037f1f268SConrad Meyer     size_t readFromInput;
21610c16b537SWarner Losh     unsigned storedSkips = 0;
21620c16b537SWarner Losh 
21630c16b537SWarner Losh     /* assumption : ress->srcBufferLoaded bytes already loaded and stored within buffer */
21640c16b537SWarner Losh     {   size_t const sizeCheck = fwrite(buffer, 1, alreadyLoaded, foutput);
21650c16b537SWarner Losh         if (sizeCheck != alreadyLoaded) {
21669cbefe25SConrad Meyer             DISPLAYLEVEL(1, "Pass-through write error : %s\n", strerror(errno));
21670c16b537SWarner Losh             return 1;
21680c16b537SWarner Losh     }   }
21690c16b537SWarner Losh 
217037f1f268SConrad Meyer     do {
21710c16b537SWarner Losh         readFromInput = fread(buffer, 1, blockSize, finput);
217237f1f268SConrad Meyer         storedSkips = FIO_fwriteSparse(foutput, buffer, readFromInput, prefs, storedSkips);
217337f1f268SConrad Meyer     } while (readFromInput == blockSize);
217437f1f268SConrad Meyer     if (ferror(finput)) {
217537f1f268SConrad Meyer         DISPLAYLEVEL(1, "Pass-through read error : %s\n", strerror(errno));
217637f1f268SConrad Meyer         return 1;
21770c16b537SWarner Losh     }
217837f1f268SConrad Meyer     assert(feof(finput));
21790c16b537SWarner Losh 
21802b9c00cbSConrad Meyer     FIO_fwriteSparseEnd(prefs, foutput, storedSkips);
21810c16b537SWarner Losh     return 0;
21820c16b537SWarner Losh }
21830c16b537SWarner Losh 
218419fcbaf1SConrad Meyer /* FIO_zstdErrorHelp() :
218519fcbaf1SConrad Meyer  * detailed error message when requested window size is too large */
21869cbefe25SConrad Meyer static void
FIO_zstdErrorHelp(const FIO_prefs_t * const prefs,const dRess_t * ress,size_t err,const char * srcFileName)21879cbefe25SConrad Meyer FIO_zstdErrorHelp(const FIO_prefs_t* const prefs,
21889cbefe25SConrad Meyer                   const dRess_t* ress,
21899cbefe25SConrad Meyer                   size_t err, const char* srcFileName)
21900c16b537SWarner Losh {
21910c16b537SWarner Losh     ZSTD_frameHeader header;
219219fcbaf1SConrad Meyer 
219319fcbaf1SConrad Meyer     /* Help message only for one specific error */
219419fcbaf1SConrad Meyer     if (ZSTD_getErrorCode(err) != ZSTD_error_frameParameter_windowTooLarge)
21950c16b537SWarner Losh         return;
219619fcbaf1SConrad Meyer 
21970c16b537SWarner Losh     /* Try to decode the frame header */
219819fcbaf1SConrad Meyer     err = ZSTD_getFrameHeader(&header, ress->srcBuffer, ress->srcBufferLoaded);
219919fcbaf1SConrad Meyer     if (err == 0) {
220019fcbaf1SConrad Meyer         unsigned long long const windowSize = header.windowSize;
2201a0483764SConrad Meyer         unsigned const windowLog = FIO_highbit64(windowSize) + ((windowSize & (windowSize - 1)) != 0);
22022b9c00cbSConrad Meyer         assert(prefs->memLimit > 0);
22030c16b537SWarner Losh         DISPLAYLEVEL(1, "%s : Window size larger than maximum : %llu > %u \n",
22042b9c00cbSConrad Meyer                         srcFileName, windowSize, prefs->memLimit);
22050c16b537SWarner Losh         if (windowLog <= ZSTD_WINDOWLOG_MAX) {
2206a0483764SConrad Meyer             unsigned const windowMB = (unsigned)((windowSize >> 20) + ((windowSize & ((1 MB) - 1)) != 0));
22070f743729SConrad Meyer             assert(windowSize < (U64)(1ULL << 52));   /* ensure now overflow for windowMB */
22080c16b537SWarner Losh             DISPLAYLEVEL(1, "%s : Use --long=%u or --memory=%uMB \n",
22090c16b537SWarner Losh                             srcFileName, windowLog, windowMB);
22100c16b537SWarner Losh             return;
221137f1f268SConrad Meyer     }   }
221219fcbaf1SConrad Meyer     DISPLAYLEVEL(1, "%s : Window log larger than ZSTD_WINDOWLOG_MAX=%u; not supported \n",
22130c16b537SWarner Losh                     srcFileName, ZSTD_WINDOWLOG_MAX);
22140c16b537SWarner Losh }
22150c16b537SWarner Losh 
22160c16b537SWarner Losh /** FIO_decompressFrame() :
22170c16b537SWarner Losh  *  @return : size of decoded zstd frame, or an error code
22180c16b537SWarner Losh  */
22190c16b537SWarner Losh #define FIO_ERROR_FRAME_DECODING   ((unsigned long long)(-2))
22209cbefe25SConrad Meyer static unsigned long long
FIO_decompressZstdFrame(FIO_ctx_t * const fCtx,dRess_t * ress,FILE * finput,const FIO_prefs_t * const prefs,const char * srcFileName,U64 alreadyDecoded)2221f7cd7fe5SConrad Meyer FIO_decompressZstdFrame(FIO_ctx_t* const fCtx, dRess_t* ress, FILE* finput,
222237f1f268SConrad Meyer                         const FIO_prefs_t* const prefs,
222337f1f268SConrad Meyer                         const char* srcFileName,
222437f1f268SConrad Meyer                         U64 alreadyDecoded)  /* for multi-frames streams */
22250c16b537SWarner Losh {
22260c16b537SWarner Losh     U64 frameSize = 0;
22270c16b537SWarner Losh     U32 storedSkips = 0;
22280c16b537SWarner Losh 
222937f1f268SConrad Meyer     /* display last 20 characters only */
223037f1f268SConrad Meyer     {   size_t const srcFileLength = strlen(srcFileName);
223137f1f268SConrad Meyer         if (srcFileLength>20) srcFileName += srcFileLength-20;
223237f1f268SConrad Meyer     }
22330c16b537SWarner Losh 
2234*5ff13fbcSAllan Jude     ZSTD_DCtx_reset(ress->dctx, ZSTD_reset_session_only);
22350c16b537SWarner Losh 
22360c16b537SWarner Losh     /* Header loading : ensures ZSTD_getFrameHeader() will succeed */
22370c16b537SWarner Losh     {   size_t const toDecode = ZSTD_FRAMEHEADERSIZE_MAX;
22380c16b537SWarner Losh         if (ress->srcBufferLoaded < toDecode) {
22390c16b537SWarner Losh             size_t const toRead = toDecode - ress->srcBufferLoaded;
22400c16b537SWarner Losh             void* const startPosition = (char*)ress->srcBuffer + ress->srcBufferLoaded;
22410c16b537SWarner Losh             ress->srcBufferLoaded += fread(startPosition, 1, toRead, finput);
22420c16b537SWarner Losh     }   }
22430c16b537SWarner Losh 
22440c16b537SWarner Losh     /* Main decompression Loop */
22450c16b537SWarner Losh     while (1) {
22460c16b537SWarner Losh         ZSTD_inBuffer  inBuff = { ress->srcBuffer, ress->srcBufferLoaded, 0 };
22470c16b537SWarner Losh         ZSTD_outBuffer outBuff= { ress->dstBuffer, ress->dstBufferSize, 0 };
22480c16b537SWarner Losh         size_t const readSizeHint = ZSTD_decompressStream(ress->dctx, &outBuff, &inBuff);
2249*5ff13fbcSAllan Jude         const int displayLevel = (g_display_prefs.progressSetting == FIO_ps_always) ? 1 : 2;
2250*5ff13fbcSAllan Jude         UTIL_HumanReadableSize_t const hrs = UTIL_makeHumanReadableSize(alreadyDecoded+frameSize);
22510c16b537SWarner Losh         if (ZSTD_isError(readSizeHint)) {
22520c16b537SWarner Losh             DISPLAYLEVEL(1, "%s : Decoding error (36) : %s \n",
22530c16b537SWarner Losh                             srcFileName, ZSTD_getErrorName(readSizeHint));
22542b9c00cbSConrad Meyer             FIO_zstdErrorHelp(prefs, ress, readSizeHint, srcFileName);
22550c16b537SWarner Losh             return FIO_ERROR_FRAME_DECODING;
22560c16b537SWarner Losh         }
22570c16b537SWarner Losh 
22580c16b537SWarner Losh         /* Write block */
225937f1f268SConrad Meyer         storedSkips = FIO_fwriteSparse(ress->dstFile, ress->dstBuffer, outBuff.pos, prefs, storedSkips);
22600c16b537SWarner Losh         frameSize += outBuff.pos;
2261f7cd7fe5SConrad Meyer         if (fCtx->nbFilesTotal > 1) {
2262f7cd7fe5SConrad Meyer             size_t srcFileNameSize = strlen(srcFileName);
2263f7cd7fe5SConrad Meyer             if (srcFileNameSize > 18) {
2264f7cd7fe5SConrad Meyer                 const char* truncatedSrcFileName = srcFileName + srcFileNameSize - 15;
2265*5ff13fbcSAllan Jude                 DISPLAYUPDATE(displayLevel, "\rDecompress: %2u/%2u files. Current: ...%s : %.*f%s...    ",
2266*5ff13fbcSAllan Jude                                 fCtx->currFileIdx+1, fCtx->nbFilesTotal, truncatedSrcFileName, hrs.precision, hrs.value, hrs.suffix);
2267f7cd7fe5SConrad Meyer             } else {
2268*5ff13fbcSAllan Jude                 DISPLAYUPDATE(displayLevel, "\rDecompress: %2u/%2u files. Current: %s : %.*f%s...    ",
2269*5ff13fbcSAllan Jude                             fCtx->currFileIdx+1, fCtx->nbFilesTotal, srcFileName, hrs.precision, hrs.value, hrs.suffix);
2270f7cd7fe5SConrad Meyer             }
2271f7cd7fe5SConrad Meyer         } else {
2272*5ff13fbcSAllan Jude             DISPLAYUPDATE(displayLevel, "\r%-20.20s : %.*f%s...     ",
2273*5ff13fbcSAllan Jude                             srcFileName, hrs.precision, hrs.value, hrs.suffix);
2274f7cd7fe5SConrad Meyer         }
22750c16b537SWarner Losh 
22760c16b537SWarner Losh         if (inBuff.pos > 0) {
22770c16b537SWarner Losh             memmove(ress->srcBuffer, (char*)ress->srcBuffer + inBuff.pos, inBuff.size - inBuff.pos);
22780c16b537SWarner Losh             ress->srcBufferLoaded -= inBuff.pos;
22790c16b537SWarner Losh         }
22800c16b537SWarner Losh 
22810c16b537SWarner Losh         if (readSizeHint == 0) break;   /* end of frame */
22820c16b537SWarner Losh 
22830c16b537SWarner Losh         /* Fill input buffer */
22840c16b537SWarner Losh         {   size_t const toDecode = MIN(readSizeHint, ress->srcBufferSize);  /* support large skippable frames */
22850c16b537SWarner Losh             if (ress->srcBufferLoaded < toDecode) {
22860c16b537SWarner Losh                 size_t const toRead = toDecode - ress->srcBufferLoaded;   /* > 0 */
22870c16b537SWarner Losh                 void* const startPosition = (char*)ress->srcBuffer + ress->srcBufferLoaded;
22880c16b537SWarner Losh                 size_t const readSize = fread(startPosition, 1, toRead, finput);
22890c16b537SWarner Losh                 if (readSize==0) {
22900c16b537SWarner Losh                     DISPLAYLEVEL(1, "%s : Read error (39) : premature end \n",
22910c16b537SWarner Losh                                     srcFileName);
22920c16b537SWarner Losh                     return FIO_ERROR_FRAME_DECODING;
22930c16b537SWarner Losh                 }
22940c16b537SWarner Losh                 ress->srcBufferLoaded += readSize;
22950c16b537SWarner Losh     }   }   }
22960c16b537SWarner Losh 
22972b9c00cbSConrad Meyer     FIO_fwriteSparseEnd(prefs, ress->dstFile, storedSkips);
22980c16b537SWarner Losh 
22990c16b537SWarner Losh     return frameSize;
23000c16b537SWarner Losh }
23010c16b537SWarner Losh 
23020c16b537SWarner Losh 
23030c16b537SWarner Losh #ifdef ZSTD_GZDECOMPRESS
23049cbefe25SConrad Meyer static unsigned long long
FIO_decompressGzFrame(dRess_t * ress,FILE * srcFile,const FIO_prefs_t * const prefs,const char * srcFileName)230537f1f268SConrad Meyer FIO_decompressGzFrame(dRess_t* ress, FILE* srcFile,
230637f1f268SConrad Meyer                       const FIO_prefs_t* const prefs,
23079cbefe25SConrad Meyer                       const char* srcFileName)
23080c16b537SWarner Losh {
23090c16b537SWarner Losh     unsigned long long outFileSize = 0;
23100c16b537SWarner Losh     z_stream strm;
23110c16b537SWarner Losh     int flush = Z_NO_FLUSH;
23120c16b537SWarner Losh     int decodingError = 0;
23139cbefe25SConrad Meyer     unsigned storedSkips = 0;
23140c16b537SWarner Losh 
23150c16b537SWarner Losh     strm.zalloc = Z_NULL;
23160c16b537SWarner Losh     strm.zfree = Z_NULL;
23170c16b537SWarner Losh     strm.opaque = Z_NULL;
23180c16b537SWarner Losh     strm.next_in = 0;
23190c16b537SWarner Losh     strm.avail_in = 0;
23200c16b537SWarner Losh     /* see http://www.zlib.net/manual.html */
23210c16b537SWarner Losh     if (inflateInit2(&strm, 15 /* maxWindowLogSize */ + 16 /* gzip only */) != Z_OK)
23220c16b537SWarner Losh         return FIO_ERROR_FRAME_DECODING;
23230c16b537SWarner Losh 
23240c16b537SWarner Losh     strm.next_out = (Bytef*)ress->dstBuffer;
23250c16b537SWarner Losh     strm.avail_out = (uInt)ress->dstBufferSize;
23260c16b537SWarner Losh     strm.avail_in = (uInt)ress->srcBufferLoaded;
23270c16b537SWarner Losh     strm.next_in = (z_const unsigned char*)ress->srcBuffer;
23280c16b537SWarner Losh 
23290c16b537SWarner Losh     for ( ; ; ) {
23300c16b537SWarner Losh         int ret;
23310c16b537SWarner Losh         if (strm.avail_in == 0) {
23320c16b537SWarner Losh             ress->srcBufferLoaded = fread(ress->srcBuffer, 1, ress->srcBufferSize, srcFile);
23330c16b537SWarner Losh             if (ress->srcBufferLoaded == 0) flush = Z_FINISH;
23340c16b537SWarner Losh             strm.next_in = (z_const unsigned char*)ress->srcBuffer;
23350c16b537SWarner Losh             strm.avail_in = (uInt)ress->srcBufferLoaded;
23360c16b537SWarner Losh         }
23370c16b537SWarner Losh         ret = inflate(&strm, flush);
23380c16b537SWarner Losh         if (ret == Z_BUF_ERROR) {
23390c16b537SWarner Losh             DISPLAYLEVEL(1, "zstd: %s: premature gz end \n", srcFileName);
23400c16b537SWarner Losh             decodingError = 1; break;
23410c16b537SWarner Losh         }
23420c16b537SWarner Losh         if (ret != Z_OK && ret != Z_STREAM_END) {
23430c16b537SWarner Losh             DISPLAYLEVEL(1, "zstd: %s: inflate error %d \n", srcFileName, ret);
23440c16b537SWarner Losh             decodingError = 1; break;
23450c16b537SWarner Losh         }
23460c16b537SWarner Losh         {   size_t const decompBytes = ress->dstBufferSize - strm.avail_out;
23470c16b537SWarner Losh             if (decompBytes) {
234837f1f268SConrad Meyer                 storedSkips = FIO_fwriteSparse(ress->dstFile, ress->dstBuffer, decompBytes, prefs, storedSkips);
23490c16b537SWarner Losh                 outFileSize += decompBytes;
23500c16b537SWarner Losh                 strm.next_out = (Bytef*)ress->dstBuffer;
23510c16b537SWarner Losh                 strm.avail_out = (uInt)ress->dstBufferSize;
23520c16b537SWarner Losh             }
23530c16b537SWarner Losh         }
23540c16b537SWarner Losh         if (ret == Z_STREAM_END) break;
23550c16b537SWarner Losh     }
23560c16b537SWarner Losh 
23570c16b537SWarner Losh     if (strm.avail_in > 0)
23580c16b537SWarner Losh         memmove(ress->srcBuffer, strm.next_in, strm.avail_in);
23590c16b537SWarner Losh     ress->srcBufferLoaded = strm.avail_in;
23600c16b537SWarner Losh     if ( (inflateEnd(&strm) != Z_OK)  /* release resources ; error detected */
23610c16b537SWarner Losh       && (decodingError==0) ) {
23620c16b537SWarner Losh         DISPLAYLEVEL(1, "zstd: %s: inflateEnd error \n", srcFileName);
23630c16b537SWarner Losh         decodingError = 1;
23640c16b537SWarner Losh     }
23659cbefe25SConrad Meyer     FIO_fwriteSparseEnd(prefs, ress->dstFile, storedSkips);
23660c16b537SWarner Losh     return decodingError ? FIO_ERROR_FRAME_DECODING : outFileSize;
23670c16b537SWarner Losh }
23680c16b537SWarner Losh #endif
23690c16b537SWarner Losh 
23700c16b537SWarner Losh 
23710c16b537SWarner Losh #ifdef ZSTD_LZMADECOMPRESS
23729cbefe25SConrad Meyer static unsigned long long
FIO_decompressLzmaFrame(dRess_t * ress,FILE * srcFile,const FIO_prefs_t * const prefs,const char * srcFileName,int plain_lzma)237337f1f268SConrad Meyer FIO_decompressLzmaFrame(dRess_t* ress, FILE* srcFile,
237437f1f268SConrad Meyer                         const FIO_prefs_t* const prefs,
23759cbefe25SConrad Meyer                         const char* srcFileName, int plain_lzma)
23760c16b537SWarner Losh {
23770c16b537SWarner Losh     unsigned long long outFileSize = 0;
23780c16b537SWarner Losh     lzma_stream strm = LZMA_STREAM_INIT;
23790c16b537SWarner Losh     lzma_action action = LZMA_RUN;
23800c16b537SWarner Losh     lzma_ret initRet;
23810c16b537SWarner Losh     int decodingError = 0;
23829cbefe25SConrad Meyer     unsigned storedSkips = 0;
23830c16b537SWarner Losh 
23840c16b537SWarner Losh     strm.next_in = 0;
23850c16b537SWarner Losh     strm.avail_in = 0;
23860c16b537SWarner Losh     if (plain_lzma) {
23870c16b537SWarner Losh         initRet = lzma_alone_decoder(&strm, UINT64_MAX); /* LZMA */
23880c16b537SWarner Losh     } else {
23890c16b537SWarner Losh         initRet = lzma_stream_decoder(&strm, UINT64_MAX, 0); /* XZ */
23900c16b537SWarner Losh     }
23910c16b537SWarner Losh 
23920c16b537SWarner Losh     if (initRet != LZMA_OK) {
23930c16b537SWarner Losh         DISPLAYLEVEL(1, "zstd: %s: %s error %d \n",
23940c16b537SWarner Losh                         plain_lzma ? "lzma_alone_decoder" : "lzma_stream_decoder",
23950c16b537SWarner Losh                         srcFileName, initRet);
23960c16b537SWarner Losh         return FIO_ERROR_FRAME_DECODING;
23970c16b537SWarner Losh     }
23980c16b537SWarner Losh 
23990c16b537SWarner Losh     strm.next_out = (BYTE*)ress->dstBuffer;
24000c16b537SWarner Losh     strm.avail_out = ress->dstBufferSize;
24010c16b537SWarner Losh     strm.next_in = (BYTE const*)ress->srcBuffer;
24020c16b537SWarner Losh     strm.avail_in = ress->srcBufferLoaded;
24030c16b537SWarner Losh 
24040c16b537SWarner Losh     for ( ; ; ) {
24050c16b537SWarner Losh         lzma_ret ret;
24060c16b537SWarner Losh         if (strm.avail_in == 0) {
24070c16b537SWarner Losh             ress->srcBufferLoaded = fread(ress->srcBuffer, 1, ress->srcBufferSize, srcFile);
24080c16b537SWarner Losh             if (ress->srcBufferLoaded == 0) action = LZMA_FINISH;
24090c16b537SWarner Losh             strm.next_in = (BYTE const*)ress->srcBuffer;
24100c16b537SWarner Losh             strm.avail_in = ress->srcBufferLoaded;
24110c16b537SWarner Losh         }
24120c16b537SWarner Losh         ret = lzma_code(&strm, action);
24130c16b537SWarner Losh 
24140c16b537SWarner Losh         if (ret == LZMA_BUF_ERROR) {
24150c16b537SWarner Losh             DISPLAYLEVEL(1, "zstd: %s: premature lzma end \n", srcFileName);
24160c16b537SWarner Losh             decodingError = 1; break;
24170c16b537SWarner Losh         }
24180c16b537SWarner Losh         if (ret != LZMA_OK && ret != LZMA_STREAM_END) {
24190c16b537SWarner Losh             DISPLAYLEVEL(1, "zstd: %s: lzma_code decoding error %d \n",
24200c16b537SWarner Losh                             srcFileName, ret);
24210c16b537SWarner Losh             decodingError = 1; break;
24220c16b537SWarner Losh         }
24230c16b537SWarner Losh         {   size_t const decompBytes = ress->dstBufferSize - strm.avail_out;
24240c16b537SWarner Losh             if (decompBytes) {
242537f1f268SConrad Meyer                 storedSkips = FIO_fwriteSparse(ress->dstFile, ress->dstBuffer, decompBytes, prefs, storedSkips);
24260c16b537SWarner Losh                 outFileSize += decompBytes;
24270c16b537SWarner Losh                 strm.next_out = (BYTE*)ress->dstBuffer;
24280c16b537SWarner Losh                 strm.avail_out = ress->dstBufferSize;
24290c16b537SWarner Losh         }   }
24300c16b537SWarner Losh         if (ret == LZMA_STREAM_END) break;
24310c16b537SWarner Losh     }
24320c16b537SWarner Losh 
24330c16b537SWarner Losh     if (strm.avail_in > 0)
24340c16b537SWarner Losh         memmove(ress->srcBuffer, strm.next_in, strm.avail_in);
24350c16b537SWarner Losh     ress->srcBufferLoaded = strm.avail_in;
24360c16b537SWarner Losh     lzma_end(&strm);
24379cbefe25SConrad Meyer     FIO_fwriteSparseEnd(prefs, ress->dstFile, storedSkips);
24380c16b537SWarner Losh     return decodingError ? FIO_ERROR_FRAME_DECODING : outFileSize;
24390c16b537SWarner Losh }
24400c16b537SWarner Losh #endif
24410c16b537SWarner Losh 
24420c16b537SWarner Losh #ifdef ZSTD_LZ4DECOMPRESS
24439cbefe25SConrad Meyer static unsigned long long
FIO_decompressLz4Frame(dRess_t * ress,FILE * srcFile,const FIO_prefs_t * const prefs,const char * srcFileName)244437f1f268SConrad Meyer FIO_decompressLz4Frame(dRess_t* ress, FILE* srcFile,
244537f1f268SConrad Meyer                        const FIO_prefs_t* const prefs,
24469cbefe25SConrad Meyer                        const char* srcFileName)
24470c16b537SWarner Losh {
24480c16b537SWarner Losh     unsigned long long filesize = 0;
24490c16b537SWarner Losh     LZ4F_errorCode_t nextToLoad;
24500c16b537SWarner Losh     LZ4F_decompressionContext_t dCtx;
24510c16b537SWarner Losh     LZ4F_errorCode_t const errorCode = LZ4F_createDecompressionContext(&dCtx, LZ4F_VERSION);
24520c16b537SWarner Losh     int decodingError = 0;
24539cbefe25SConrad Meyer     unsigned storedSkips = 0;
24540c16b537SWarner Losh 
24550c16b537SWarner Losh     if (LZ4F_isError(errorCode)) {
24560c16b537SWarner Losh         DISPLAYLEVEL(1, "zstd: failed to create lz4 decompression context \n");
24570c16b537SWarner Losh         return FIO_ERROR_FRAME_DECODING;
24580c16b537SWarner Losh     }
24590c16b537SWarner Losh 
24600c16b537SWarner Losh     /* Init feed with magic number (already consumed from FILE* sFile) */
24610c16b537SWarner Losh     {   size_t inSize = 4;
24620c16b537SWarner Losh         size_t outSize= 0;
24630c16b537SWarner Losh         MEM_writeLE32(ress->srcBuffer, LZ4_MAGICNUMBER);
24640c16b537SWarner Losh         nextToLoad = LZ4F_decompress(dCtx, ress->dstBuffer, &outSize, ress->srcBuffer, &inSize, NULL);
24650c16b537SWarner Losh         if (LZ4F_isError(nextToLoad)) {
24660c16b537SWarner Losh             DISPLAYLEVEL(1, "zstd: %s: lz4 header error : %s \n",
24670c16b537SWarner Losh                             srcFileName, LZ4F_getErrorName(nextToLoad));
24680c16b537SWarner Losh             LZ4F_freeDecompressionContext(dCtx);
24690c16b537SWarner Losh             return FIO_ERROR_FRAME_DECODING;
24700c16b537SWarner Losh     }   }
24710c16b537SWarner Losh 
24720c16b537SWarner Losh     /* Main Loop */
24730c16b537SWarner Losh     for (;nextToLoad;) {
24740c16b537SWarner Losh         size_t readSize;
24750c16b537SWarner Losh         size_t pos = 0;
24760c16b537SWarner Losh         size_t decodedBytes = ress->dstBufferSize;
24770c16b537SWarner Losh 
24780c16b537SWarner Losh         /* Read input */
24790c16b537SWarner Losh         if (nextToLoad > ress->srcBufferSize) nextToLoad = ress->srcBufferSize;
24800c16b537SWarner Losh         readSize = fread(ress->srcBuffer, 1, nextToLoad, srcFile);
24810c16b537SWarner Losh         if (!readSize) break;   /* reached end of file or stream */
24820c16b537SWarner Losh 
24830c16b537SWarner Losh         while ((pos < readSize) || (decodedBytes == ress->dstBufferSize)) {  /* still to read, or still to flush */
24840c16b537SWarner Losh             /* Decode Input (at least partially) */
24850c16b537SWarner Losh             size_t remaining = readSize - pos;
24860c16b537SWarner Losh             decodedBytes = ress->dstBufferSize;
24870c16b537SWarner Losh             nextToLoad = LZ4F_decompress(dCtx, ress->dstBuffer, &decodedBytes, (char*)(ress->srcBuffer)+pos, &remaining, NULL);
24880c16b537SWarner Losh             if (LZ4F_isError(nextToLoad)) {
24890c16b537SWarner Losh                 DISPLAYLEVEL(1, "zstd: %s: lz4 decompression error : %s \n",
24900c16b537SWarner Losh                                 srcFileName, LZ4F_getErrorName(nextToLoad));
24910f743729SConrad Meyer                 decodingError = 1; nextToLoad = 0; break;
24920c16b537SWarner Losh             }
24930c16b537SWarner Losh             pos += remaining;
24940c16b537SWarner Losh 
24950c16b537SWarner Losh             /* Write Block */
24960c16b537SWarner Losh             if (decodedBytes) {
2497*5ff13fbcSAllan Jude                 UTIL_HumanReadableSize_t hrs;
249837f1f268SConrad Meyer                 storedSkips = FIO_fwriteSparse(ress->dstFile, ress->dstBuffer, decodedBytes, prefs, storedSkips);
24990c16b537SWarner Losh                 filesize += decodedBytes;
2500*5ff13fbcSAllan Jude                 hrs = UTIL_makeHumanReadableSize(filesize);
2501*5ff13fbcSAllan Jude                 DISPLAYUPDATE(2, "\rDecompressed : %.*f%s  ", hrs.precision, hrs.value, hrs.suffix);
25020c16b537SWarner Losh             }
25030c16b537SWarner Losh 
25040c16b537SWarner Losh             if (!nextToLoad) break;
25050c16b537SWarner Losh         }
25060c16b537SWarner Losh     }
25070c16b537SWarner Losh     /* can be out because readSize == 0, which could be an fread() error */
25080c16b537SWarner Losh     if (ferror(srcFile)) {
25090c16b537SWarner Losh         DISPLAYLEVEL(1, "zstd: %s: read error \n", srcFileName);
25100c16b537SWarner Losh         decodingError=1;
25110c16b537SWarner Losh     }
25120c16b537SWarner Losh 
25130c16b537SWarner Losh     if (nextToLoad!=0) {
25140c16b537SWarner Losh         DISPLAYLEVEL(1, "zstd: %s: unfinished lz4 stream \n", srcFileName);
25150c16b537SWarner Losh         decodingError=1;
25160c16b537SWarner Losh     }
25170c16b537SWarner Losh 
25180c16b537SWarner Losh     LZ4F_freeDecompressionContext(dCtx);
25190c16b537SWarner Losh     ress->srcBufferLoaded = 0; /* LZ4F will reach exact frame boundary */
25209cbefe25SConrad Meyer     FIO_fwriteSparseEnd(prefs, ress->dstFile, storedSkips);
25210c16b537SWarner Losh 
25220c16b537SWarner Losh     return decodingError ? FIO_ERROR_FRAME_DECODING : filesize;
25230c16b537SWarner Losh }
25240c16b537SWarner Losh #endif
25250c16b537SWarner Losh 
25260c16b537SWarner Losh 
25270c16b537SWarner Losh 
25280c16b537SWarner Losh /** FIO_decompressFrames() :
25290c16b537SWarner Losh  *  Find and decode frames inside srcFile
25300c16b537SWarner Losh  *  srcFile presumed opened and valid
25310c16b537SWarner Losh  * @return : 0 : OK
25320c16b537SWarner Losh  *           1 : error
25330c16b537SWarner Losh  */
FIO_decompressFrames(FIO_ctx_t * const fCtx,dRess_t ress,FILE * srcFile,const FIO_prefs_t * const prefs,const char * dstFileName,const char * srcFileName)2534f7cd7fe5SConrad Meyer static int FIO_decompressFrames(FIO_ctx_t* const fCtx,
2535f7cd7fe5SConrad Meyer                           dRess_t ress, FILE* srcFile,
253637f1f268SConrad Meyer                           const FIO_prefs_t* const prefs,
25370c16b537SWarner Losh                           const char* dstFileName, const char* srcFileName)
25380c16b537SWarner Losh {
25390c16b537SWarner Losh     unsigned readSomething = 0;
25400c16b537SWarner Losh     unsigned long long filesize = 0;
25410c16b537SWarner Losh     assert(srcFile != NULL);
25420c16b537SWarner Losh 
25430c16b537SWarner Losh     /* for each frame */
25440c16b537SWarner Losh     for ( ; ; ) {
25450c16b537SWarner Losh         /* check magic number -> version */
25460c16b537SWarner Losh         size_t const toRead = 4;
25470c16b537SWarner Losh         const BYTE* const buf = (const BYTE*)ress.srcBuffer;
25480c16b537SWarner Losh         if (ress.srcBufferLoaded < toRead)  /* load up to 4 bytes for header */
25490c16b537SWarner Losh             ress.srcBufferLoaded += fread((char*)ress.srcBuffer + ress.srcBufferLoaded,
25500c16b537SWarner Losh                                           (size_t)1, toRead - ress.srcBufferLoaded, srcFile);
25510c16b537SWarner Losh         if (ress.srcBufferLoaded==0) {
25520c16b537SWarner Losh             if (readSomething==0) {  /* srcFile is empty (which is invalid) */
25530c16b537SWarner Losh                 DISPLAYLEVEL(1, "zstd: %s: unexpected end of file \n", srcFileName);
25540c16b537SWarner Losh                 return 1;
25550c16b537SWarner Losh             }  /* else, just reached frame boundary */
25560c16b537SWarner Losh             break;   /* no more input */
25570c16b537SWarner Losh         }
25580c16b537SWarner Losh         readSomething = 1;   /* there is at least 1 byte in srcFile */
25590c16b537SWarner Losh         if (ress.srcBufferLoaded < toRead) {
25600c16b537SWarner Losh             DISPLAYLEVEL(1, "zstd: %s: unknown header \n", srcFileName);
25610c16b537SWarner Losh             return 1;
25620c16b537SWarner Losh         }
25630c16b537SWarner Losh         if (ZSTD_isFrame(buf, ress.srcBufferLoaded)) {
2564f7cd7fe5SConrad Meyer             unsigned long long const frameSize = FIO_decompressZstdFrame(fCtx, &ress, srcFile, prefs, srcFileName, filesize);
25650c16b537SWarner Losh             if (frameSize == FIO_ERROR_FRAME_DECODING) return 1;
25660c16b537SWarner Losh             filesize += frameSize;
25670c16b537SWarner Losh         } else if (buf[0] == 31 && buf[1] == 139) { /* gz magic number */
25680c16b537SWarner Losh #ifdef ZSTD_GZDECOMPRESS
256937f1f268SConrad Meyer             unsigned long long const frameSize = FIO_decompressGzFrame(&ress, srcFile, prefs, srcFileName);
25700c16b537SWarner Losh             if (frameSize == FIO_ERROR_FRAME_DECODING) return 1;
25710c16b537SWarner Losh             filesize += frameSize;
25720c16b537SWarner Losh #else
25730c16b537SWarner Losh             DISPLAYLEVEL(1, "zstd: %s: gzip file cannot be uncompressed (zstd compiled without HAVE_ZLIB) -- ignored \n", srcFileName);
25740c16b537SWarner Losh             return 1;
25750c16b537SWarner Losh #endif
25760c16b537SWarner Losh         } else if ((buf[0] == 0xFD && buf[1] == 0x37)  /* xz magic number */
25770c16b537SWarner Losh                 || (buf[0] == 0x5D && buf[1] == 0x00)) { /* lzma header (no magic number) */
25780c16b537SWarner Losh #ifdef ZSTD_LZMADECOMPRESS
257937f1f268SConrad Meyer             unsigned long long const frameSize = FIO_decompressLzmaFrame(&ress, srcFile, prefs, srcFileName, buf[0] != 0xFD);
25800c16b537SWarner Losh             if (frameSize == FIO_ERROR_FRAME_DECODING) return 1;
25810c16b537SWarner Losh             filesize += frameSize;
25820c16b537SWarner Losh #else
25830c16b537SWarner Losh             DISPLAYLEVEL(1, "zstd: %s: xz/lzma file cannot be uncompressed (zstd compiled without HAVE_LZMA) -- ignored \n", srcFileName);
25840c16b537SWarner Losh             return 1;
25850c16b537SWarner Losh #endif
25860c16b537SWarner Losh         } else if (MEM_readLE32(buf) == LZ4_MAGICNUMBER) {
25870c16b537SWarner Losh #ifdef ZSTD_LZ4DECOMPRESS
258837f1f268SConrad Meyer             unsigned long long const frameSize = FIO_decompressLz4Frame(&ress, srcFile, prefs, srcFileName);
25890c16b537SWarner Losh             if (frameSize == FIO_ERROR_FRAME_DECODING) return 1;
25900c16b537SWarner Losh             filesize += frameSize;
25910c16b537SWarner Losh #else
25920c16b537SWarner Losh             DISPLAYLEVEL(1, "zstd: %s: lz4 file cannot be uncompressed (zstd compiled without HAVE_LZ4) -- ignored \n", srcFileName);
25930c16b537SWarner Losh             return 1;
25940c16b537SWarner Losh #endif
25952b9c00cbSConrad Meyer         } else if ((prefs->overwrite) && !strcmp (dstFileName, stdoutmark)) {  /* pass-through mode */
25962b9c00cbSConrad Meyer             return FIO_passThrough(prefs,
25972b9c00cbSConrad Meyer                                    ress.dstFile, srcFile,
25982b9c00cbSConrad Meyer                                    ress.srcBuffer, ress.srcBufferSize,
25992b9c00cbSConrad Meyer                                    ress.srcBufferLoaded);
26000c16b537SWarner Losh         } else {
26010c16b537SWarner Losh             DISPLAYLEVEL(1, "zstd: %s: unsupported format \n", srcFileName);
26020c16b537SWarner Losh             return 1;
26030c16b537SWarner Losh     }   }  /* for each frame */
26040c16b537SWarner Losh 
26050c16b537SWarner Losh     /* Final Status */
2606f7cd7fe5SConrad Meyer     fCtx->totalBytesOutput += (size_t)filesize;
26070c16b537SWarner Losh     DISPLAYLEVEL(2, "\r%79s\r", "");
2608f7cd7fe5SConrad Meyer     /* No status message in pipe mode (stdin - stdout) or multi-files mode */
2609*5ff13fbcSAllan Jude     if ((g_display_prefs.displayLevel >= 2 && fCtx->nbFilesTotal <= 1) ||
2610*5ff13fbcSAllan Jude         g_display_prefs.displayLevel >= 3 ||
2611*5ff13fbcSAllan Jude         g_display_prefs.progressSetting == FIO_ps_always) {
2612*5ff13fbcSAllan Jude         DISPLAYLEVEL(1, "\r%-20s: %llu bytes \n", srcFileName, filesize);
2613f7cd7fe5SConrad Meyer     }
26140c16b537SWarner Losh 
26150c16b537SWarner Losh     return 0;
26160c16b537SWarner Losh }
26170c16b537SWarner Losh 
26180f743729SConrad Meyer /** FIO_decompressDstFile() :
26190f743729SConrad Meyer     open `dstFileName`,
26200f743729SConrad Meyer     or path-through if ress.dstFile is already != 0,
26210f743729SConrad Meyer     then start decompression process (FIO_decompressFrames()).
26220f743729SConrad Meyer     @return : 0 : OK
26230f743729SConrad Meyer               1 : operation aborted
26240f743729SConrad Meyer */
FIO_decompressDstFile(FIO_ctx_t * const fCtx,FIO_prefs_t * const prefs,dRess_t ress,FILE * srcFile,const char * dstFileName,const char * srcFileName)2625f7cd7fe5SConrad Meyer static int FIO_decompressDstFile(FIO_ctx_t* const fCtx,
2626f7cd7fe5SConrad Meyer                                  FIO_prefs_t* const prefs,
26272b9c00cbSConrad Meyer                                  dRess_t ress, FILE* srcFile,
26280f743729SConrad Meyer                                  const char* dstFileName, const char* srcFileName)
26290f743729SConrad Meyer {
26300f743729SConrad Meyer     int result;
26310f743729SConrad Meyer     stat_t statbuf;
26320f743729SConrad Meyer     int releaseDstFile = 0;
2633*5ff13fbcSAllan Jude     int transferMTime = 0;
26340f743729SConrad Meyer 
26359cbefe25SConrad Meyer     if ((ress.dstFile == NULL) && (prefs->testMode==0)) {
2636*5ff13fbcSAllan Jude         int dstFilePermissions = DEFAULT_FILE_PERMISSIONS;
2637*5ff13fbcSAllan Jude         if ( strcmp(srcFileName, stdinmark)   /* special case : don't transfer permissions from stdin */
2638*5ff13fbcSAllan Jude           && strcmp(dstFileName, stdoutmark)
2639*5ff13fbcSAllan Jude           && UTIL_stat(srcFileName, &statbuf)
2640*5ff13fbcSAllan Jude           && UTIL_isRegularFileStat(&statbuf) ) {
2641*5ff13fbcSAllan Jude             dstFilePermissions = statbuf.st_mode;
2642*5ff13fbcSAllan Jude             transferMTime = 1;
2643*5ff13fbcSAllan Jude         }
2644*5ff13fbcSAllan Jude 
26450f743729SConrad Meyer         releaseDstFile = 1;
26460f743729SConrad Meyer 
2647*5ff13fbcSAllan Jude         ress.dstFile = FIO_openDstFile(fCtx, prefs, srcFileName, dstFileName, dstFilePermissions);
26489cbefe25SConrad Meyer         if (ress.dstFile==NULL) return 1;
26490f743729SConrad Meyer 
26500f743729SConrad Meyer         /* Must only be added after FIO_openDstFile() succeeds.
26510f743729SConrad Meyer          * Otherwise we may delete the destination file if it already exists,
26520f743729SConrad Meyer          * and the user presses Ctrl-C when asked if they wish to overwrite.
26530f743729SConrad Meyer          */
26540f743729SConrad Meyer         addHandler(dstFileName);
26550f743729SConrad Meyer     }
26560f743729SConrad Meyer 
2657f7cd7fe5SConrad Meyer     result = FIO_decompressFrames(fCtx, ress, srcFile, prefs, dstFileName, srcFileName);
26580f743729SConrad Meyer 
26590f743729SConrad Meyer     if (releaseDstFile) {
26600f743729SConrad Meyer         FILE* const dstFile = ress.dstFile;
26610f743729SConrad Meyer         clearHandler();
26620f743729SConrad Meyer         ress.dstFile = NULL;
26630f743729SConrad Meyer         if (fclose(dstFile)) {
26640f743729SConrad Meyer             DISPLAYLEVEL(1, "zstd: %s: %s \n", dstFileName, strerror(errno));
26650f743729SConrad Meyer             result = 1;
26660f743729SConrad Meyer         }
26670f743729SConrad Meyer 
2668*5ff13fbcSAllan Jude         if (transferMTime) {
2669*5ff13fbcSAllan Jude             UTIL_utime(dstFileName, &statbuf);
2670*5ff13fbcSAllan Jude         }
2671*5ff13fbcSAllan Jude 
26720f743729SConrad Meyer         if ( (result != 0)  /* operation failure */
26730f743729SConrad Meyer           && strcmp(dstFileName, stdoutmark)  /* special case : don't remove() stdout */
26740f743729SConrad Meyer           ) {
2675f7cd7fe5SConrad Meyer             FIO_removeFile(dstFileName);  /* remove decompression artefact; note: don't do anything special if remove() fails */
26760f743729SConrad Meyer         }
26770f743729SConrad Meyer     }
26780f743729SConrad Meyer 
26790f743729SConrad Meyer     return result;
26800f743729SConrad Meyer }
26810f743729SConrad Meyer 
26820c16b537SWarner Losh 
26830c16b537SWarner Losh /** FIO_decompressSrcFile() :
26840f743729SConrad Meyer     Open `srcFileName`, transfer control to decompressDstFile()
26850c16b537SWarner Losh     @return : 0 : OK
26860f743729SConrad Meyer               1 : error
26870c16b537SWarner Losh */
FIO_decompressSrcFile(FIO_ctx_t * const fCtx,FIO_prefs_t * const prefs,dRess_t ress,const char * dstFileName,const char * srcFileName)2688f7cd7fe5SConrad Meyer static int FIO_decompressSrcFile(FIO_ctx_t* const fCtx, FIO_prefs_t* const prefs, dRess_t ress, const char* dstFileName, const char* srcFileName)
26890c16b537SWarner Losh {
26900c16b537SWarner Losh     FILE* srcFile;
26910c16b537SWarner Losh     int result;
26920c16b537SWarner Losh 
26930c16b537SWarner Losh     if (UTIL_isDirectory(srcFileName)) {
26940c16b537SWarner Losh         DISPLAYLEVEL(1, "zstd: %s is a directory -- ignored \n", srcFileName);
26950c16b537SWarner Losh         return 1;
26960c16b537SWarner Losh     }
26970c16b537SWarner Losh 
2698*5ff13fbcSAllan Jude     srcFile = FIO_openSrcFile(prefs, srcFileName);
26990c16b537SWarner Losh     if (srcFile==NULL) return 1;
27000f743729SConrad Meyer     ress.srcBufferLoaded = 0;
27010c16b537SWarner Losh 
2702f7cd7fe5SConrad Meyer     result = FIO_decompressDstFile(fCtx, prefs, ress, srcFile, dstFileName, srcFileName);
27030c16b537SWarner Losh 
27040c16b537SWarner Losh     /* Close file */
27050c16b537SWarner Losh     if (fclose(srcFile)) {
27060c16b537SWarner Losh         DISPLAYLEVEL(1, "zstd: %s: %s \n", srcFileName, strerror(errno));  /* error should not happen */
27070c16b537SWarner Losh         return 1;
27080c16b537SWarner Losh     }
27092b9c00cbSConrad Meyer     if ( prefs->removeSrcFile  /* --rm */
27100c16b537SWarner Losh       && (result==0)      /* decompression successful */
27110c16b537SWarner Losh       && strcmp(srcFileName, stdinmark) ) /* not stdin */ {
2712052d3c12SConrad Meyer         /* We must clear the handler, since after this point calling it would
2713052d3c12SConrad Meyer          * delete both the source and destination files.
2714052d3c12SConrad Meyer          */
2715052d3c12SConrad Meyer         clearHandler();
2716f7cd7fe5SConrad Meyer         if (FIO_removeFile(srcFileName)) {
27170c16b537SWarner Losh             /* failed to remove src file */
27180c16b537SWarner Losh             DISPLAYLEVEL(1, "zstd: %s: %s \n", srcFileName, strerror(errno));
27190c16b537SWarner Losh             return 1;
27200c16b537SWarner Losh     }   }
27210c16b537SWarner Losh     return result;
27220c16b537SWarner Losh }
27230c16b537SWarner Losh 
27240c16b537SWarner Losh 
27250c16b537SWarner Losh 
FIO_decompressFilename(FIO_ctx_t * const fCtx,FIO_prefs_t * const prefs,const char * dstFileName,const char * srcFileName,const char * dictFileName)2726f7cd7fe5SConrad Meyer int FIO_decompressFilename(FIO_ctx_t* const fCtx, FIO_prefs_t* const prefs,
27272b9c00cbSConrad Meyer                            const char* dstFileName, const char* srcFileName,
27280c16b537SWarner Losh                            const char* dictFileName)
27290c16b537SWarner Losh {
27302b9c00cbSConrad Meyer     dRess_t const ress = FIO_createDResources(prefs, dictFileName);
27310c16b537SWarner Losh 
2732f7cd7fe5SConrad Meyer     int const decodingError = FIO_decompressSrcFile(fCtx, prefs, ress, dstFileName, srcFileName);
27330c16b537SWarner Losh 
27340c16b537SWarner Losh     FIO_freeDResources(ress);
27350c16b537SWarner Losh     return decodingError;
27360c16b537SWarner Losh }
27370c16b537SWarner Losh 
27389cbefe25SConrad Meyer static const char *suffixList[] = {
27399cbefe25SConrad Meyer     ZSTD_EXTENSION,
27409cbefe25SConrad Meyer     TZSTD_EXTENSION,
2741f7cd7fe5SConrad Meyer #ifndef ZSTD_NODECOMPRESS
2742f7cd7fe5SConrad Meyer     ZSTD_ALT_EXTENSION,
2743f7cd7fe5SConrad Meyer #endif
27449cbefe25SConrad Meyer #ifdef ZSTD_GZDECOMPRESS
27459cbefe25SConrad Meyer     GZ_EXTENSION,
27469cbefe25SConrad Meyer     TGZ_EXTENSION,
27479cbefe25SConrad Meyer #endif
27489cbefe25SConrad Meyer #ifdef ZSTD_LZMADECOMPRESS
27499cbefe25SConrad Meyer     LZMA_EXTENSION,
27509cbefe25SConrad Meyer     XZ_EXTENSION,
27519cbefe25SConrad Meyer     TXZ_EXTENSION,
27529cbefe25SConrad Meyer #endif
27539cbefe25SConrad Meyer #ifdef ZSTD_LZ4DECOMPRESS
27549cbefe25SConrad Meyer     LZ4_EXTENSION,
27559cbefe25SConrad Meyer     TLZ4_EXTENSION,
27569cbefe25SConrad Meyer #endif
27579cbefe25SConrad Meyer     NULL
27589cbefe25SConrad Meyer };
27599cbefe25SConrad Meyer 
27609cbefe25SConrad Meyer static const char *suffixListStr =
27619cbefe25SConrad Meyer     ZSTD_EXTENSION "/" TZSTD_EXTENSION
27629cbefe25SConrad Meyer #ifdef ZSTD_GZDECOMPRESS
27639cbefe25SConrad Meyer     "/" GZ_EXTENSION "/" TGZ_EXTENSION
27649cbefe25SConrad Meyer #endif
27659cbefe25SConrad Meyer #ifdef ZSTD_LZMADECOMPRESS
27669cbefe25SConrad Meyer     "/" LZMA_EXTENSION "/" XZ_EXTENSION "/" TXZ_EXTENSION
27679cbefe25SConrad Meyer #endif
27689cbefe25SConrad Meyer #ifdef ZSTD_LZ4DECOMPRESS
27699cbefe25SConrad Meyer     "/" LZ4_EXTENSION "/" TLZ4_EXTENSION
27709cbefe25SConrad Meyer #endif
27719cbefe25SConrad Meyer ;
27720c16b537SWarner Losh 
27730f743729SConrad Meyer /* FIO_determineDstName() :
27740f743729SConrad Meyer  * create a destination filename from a srcFileName.
27750f743729SConrad Meyer  * @return a pointer to it.
27760f743729SConrad Meyer  * @return == NULL if there is an error */
27770f743729SConrad Meyer static const char*
FIO_determineDstName(const char * srcFileName,const char * outDirName)27789cbefe25SConrad Meyer FIO_determineDstName(const char* srcFileName, const char* outDirName)
27790f743729SConrad Meyer {
27800f743729SConrad Meyer     static size_t dfnbCapacity = 0;
27810f743729SConrad Meyer     static char* dstFileNameBuffer = NULL;   /* using static allocation : this function cannot be multi-threaded */
27829cbefe25SConrad Meyer     size_t dstFileNameEndPos;
27839cbefe25SConrad Meyer     char* outDirFilename = NULL;
27849cbefe25SConrad Meyer     const char* dstSuffix = "";
27859cbefe25SConrad Meyer     size_t dstSuffixLen = 0;
27860f743729SConrad Meyer 
27879cbefe25SConrad Meyer     size_t sfnSize = strlen(srcFileName);
27889cbefe25SConrad Meyer 
27899cbefe25SConrad Meyer     size_t srcSuffixLen;
27909cbefe25SConrad Meyer     const char* const srcSuffix = strrchr(srcFileName, '.');
27919cbefe25SConrad Meyer     if (srcSuffix == NULL) {
27929cbefe25SConrad Meyer         DISPLAYLEVEL(1,
27939cbefe25SConrad Meyer             "zstd: %s: unknown suffix (%s expected). "
27949cbefe25SConrad Meyer             "Can't derive the output file name. "
27959cbefe25SConrad Meyer             "Specify it with -o dstFileName. Ignoring.\n",
27969cbefe25SConrad Meyer             srcFileName, suffixListStr);
27970f743729SConrad Meyer         return NULL;
27980f743729SConrad Meyer     }
27999cbefe25SConrad Meyer     srcSuffixLen = strlen(srcSuffix);
28009cbefe25SConrad Meyer 
28019cbefe25SConrad Meyer     {
28029cbefe25SConrad Meyer         const char** matchedSuffixPtr;
28039cbefe25SConrad Meyer         for (matchedSuffixPtr = suffixList; *matchedSuffixPtr != NULL; matchedSuffixPtr++) {
28049cbefe25SConrad Meyer             if (!strcmp(*matchedSuffixPtr, srcSuffix)) {
28059cbefe25SConrad Meyer                 break;
28069cbefe25SConrad Meyer             }
28079cbefe25SConrad Meyer         }
28080f743729SConrad Meyer 
28090f743729SConrad Meyer         /* check suffix is authorized */
28109cbefe25SConrad Meyer         if (sfnSize <= srcSuffixLen || *matchedSuffixPtr == NULL) {
28119cbefe25SConrad Meyer             DISPLAYLEVEL(1,
28129cbefe25SConrad Meyer                 "zstd: %s: unknown suffix (%s expected). "
28139cbefe25SConrad Meyer                 "Can't derive the output file name. "
28149cbefe25SConrad Meyer                 "Specify it with -o dstFileName. Ignoring.\n",
28159cbefe25SConrad Meyer                 srcFileName, suffixListStr);
28160f743729SConrad Meyer             return NULL;
28170f743729SConrad Meyer         }
28180f743729SConrad Meyer 
28199cbefe25SConrad Meyer         if ((*matchedSuffixPtr)[1] == 't') {
28209cbefe25SConrad Meyer             dstSuffix = ".tar";
28219cbefe25SConrad Meyer             dstSuffixLen = strlen(dstSuffix);
28229cbefe25SConrad Meyer         }
28239cbefe25SConrad Meyer     }
28249cbefe25SConrad Meyer 
28259cbefe25SConrad Meyer     if (outDirName) {
28269cbefe25SConrad Meyer         outDirFilename = FIO_createFilename_fromOutDir(srcFileName, outDirName, 0);
28279cbefe25SConrad Meyer         sfnSize = strlen(outDirFilename);
28289cbefe25SConrad Meyer         assert(outDirFilename != NULL);
28299cbefe25SConrad Meyer     }
28309cbefe25SConrad Meyer 
28319cbefe25SConrad Meyer     if (dfnbCapacity+srcSuffixLen <= sfnSize+1+dstSuffixLen) {
28320f743729SConrad Meyer         /* allocate enough space to write dstFilename into it */
28330f743729SConrad Meyer         free(dstFileNameBuffer);
28340f743729SConrad Meyer         dfnbCapacity = sfnSize + 20;
28350f743729SConrad Meyer         dstFileNameBuffer = (char*)malloc(dfnbCapacity);
28360f743729SConrad Meyer         if (dstFileNameBuffer==NULL)
28379cbefe25SConrad Meyer             EXM_THROW(74, "%s : not enough memory for dstFileName",
28389cbefe25SConrad Meyer                       strerror(errno));
28390f743729SConrad Meyer     }
28400f743729SConrad Meyer 
28410f743729SConrad Meyer     /* return dst name == src name truncated from suffix */
28420f743729SConrad Meyer     assert(dstFileNameBuffer != NULL);
28439cbefe25SConrad Meyer     dstFileNameEndPos = sfnSize - srcSuffixLen;
28449cbefe25SConrad Meyer     if (outDirFilename) {
28459cbefe25SConrad Meyer         memcpy(dstFileNameBuffer, outDirFilename, dstFileNameEndPos);
28469cbefe25SConrad Meyer         free(outDirFilename);
28479cbefe25SConrad Meyer     } else {
28489cbefe25SConrad Meyer         memcpy(dstFileNameBuffer, srcFileName, dstFileNameEndPos);
28499cbefe25SConrad Meyer     }
28509cbefe25SConrad Meyer 
28519cbefe25SConrad Meyer     /* The short tar extensions tzst, tgz, txz and tlz4 files should have "tar"
28529cbefe25SConrad Meyer      * extension on decompression. Also writes terminating null. */
28539cbefe25SConrad Meyer     strcpy(dstFileNameBuffer + dstFileNameEndPos, dstSuffix);
28540f743729SConrad Meyer     return dstFileNameBuffer;
28550f743729SConrad Meyer 
28560f743729SConrad Meyer     /* note : dstFileNameBuffer memory is not going to be free */
28570f743729SConrad Meyer }
28580f743729SConrad Meyer 
28590f743729SConrad Meyer int
FIO_decompressMultipleFilenames(FIO_ctx_t * const fCtx,FIO_prefs_t * const prefs,const char ** srcNamesTable,const char * outMirroredRootDirName,const char * outDirName,const char * outFileName,const char * dictFileName)2860f7cd7fe5SConrad Meyer FIO_decompressMultipleFilenames(FIO_ctx_t* const fCtx,
2861f7cd7fe5SConrad Meyer                                 FIO_prefs_t* const prefs,
2862f7cd7fe5SConrad Meyer                                 const char** srcNamesTable,
2863f7cd7fe5SConrad Meyer                                 const char* outMirroredRootDirName,
28649cbefe25SConrad Meyer                                 const char* outDirName, const char* outFileName,
28650c16b537SWarner Losh                                 const char* dictFileName)
28660c16b537SWarner Losh {
2867f7cd7fe5SConrad Meyer     int status;
28680f743729SConrad Meyer     int error = 0;
28692b9c00cbSConrad Meyer     dRess_t ress = FIO_createDResources(prefs, dictFileName);
28700c16b537SWarner Losh 
2871052d3c12SConrad Meyer     if (outFileName) {
2872f7cd7fe5SConrad Meyer         if (FIO_removeMultiFilesWarning(fCtx, prefs, outFileName, 1 /* displayLevelCutoff */)) {
2873f7cd7fe5SConrad Meyer             FIO_freeDResources(ress);
2874f7cd7fe5SConrad Meyer             return 1;
2875f7cd7fe5SConrad Meyer         }
28769cbefe25SConrad Meyer         if (!prefs->testMode) {
2877*5ff13fbcSAllan Jude             ress.dstFile = FIO_openDstFile(fCtx, prefs, NULL, outFileName, DEFAULT_FILE_PERMISSIONS);
28789cbefe25SConrad Meyer             if (ress.dstFile == 0) EXM_THROW(19, "cannot open %s", outFileName);
28799cbefe25SConrad Meyer         }
2880f7cd7fe5SConrad Meyer         for (; fCtx->currFileIdx < fCtx->nbFilesTotal; fCtx->currFileIdx++) {
2881f7cd7fe5SConrad Meyer             status = FIO_decompressSrcFile(fCtx, prefs, ress, outFileName, srcNamesTable[fCtx->currFileIdx]);
2882f7cd7fe5SConrad Meyer             if (!status) fCtx->nbFilesProcessed++;
2883f7cd7fe5SConrad Meyer             error |= status;
2884f7cd7fe5SConrad Meyer         }
28859cbefe25SConrad Meyer         if ((!prefs->testMode) && (fclose(ress.dstFile)))
2886a0483764SConrad Meyer             EXM_THROW(72, "Write error : %s : cannot properly close output file",
2887a0483764SConrad Meyer                         strerror(errno));
28880c16b537SWarner Losh     } else {
2889f7cd7fe5SConrad Meyer         if (outMirroredRootDirName)
2890*5ff13fbcSAllan Jude             UTIL_mirrorSourceFilesDirectories(srcNamesTable, (unsigned)fCtx->nbFilesTotal, outMirroredRootDirName);
28910f743729SConrad Meyer 
2892f7cd7fe5SConrad Meyer         for (; fCtx->currFileIdx < fCtx->nbFilesTotal; fCtx->currFileIdx++) {   /* create dstFileName */
2893f7cd7fe5SConrad Meyer             const char* const srcFileName = srcNamesTable[fCtx->currFileIdx];
2894f7cd7fe5SConrad Meyer             const char* dstFileName = NULL;
2895f7cd7fe5SConrad Meyer             if (outMirroredRootDirName) {
2896f7cd7fe5SConrad Meyer                 char* validMirroredDirName = UTIL_createMirroredDestDirName(srcFileName, outMirroredRootDirName);
2897f7cd7fe5SConrad Meyer                 if (validMirroredDirName) {
2898f7cd7fe5SConrad Meyer                     dstFileName = FIO_determineDstName(srcFileName, validMirroredDirName);
2899f7cd7fe5SConrad Meyer                     free(validMirroredDirName);
2900f7cd7fe5SConrad Meyer                 } else {
2901f7cd7fe5SConrad Meyer                     DISPLAYLEVEL(2, "zstd: --output-dir-mirror cannot decompress '%s' into '%s'\n", srcFileName, outMirroredRootDirName);
2902f7cd7fe5SConrad Meyer                 }
2903f7cd7fe5SConrad Meyer             } else {
2904f7cd7fe5SConrad Meyer                 dstFileName = FIO_determineDstName(srcFileName, outDirName);
2905f7cd7fe5SConrad Meyer             }
2906f7cd7fe5SConrad Meyer             if (dstFileName == NULL) { error=1; continue; }
2907f7cd7fe5SConrad Meyer             status = FIO_decompressSrcFile(fCtx, prefs, ress, dstFileName, srcFileName);
2908f7cd7fe5SConrad Meyer             if (!status) fCtx->nbFilesProcessed++;
2909f7cd7fe5SConrad Meyer             error |= status;
29100c16b537SWarner Losh         }
29119cbefe25SConrad Meyer         if (outDirName)
2912*5ff13fbcSAllan Jude             FIO_checkFilenameCollisions(srcNamesTable , (unsigned)fCtx->nbFilesTotal);
29130c16b537SWarner Losh     }
29140c16b537SWarner Losh 
2915f7cd7fe5SConrad Meyer     if (fCtx->nbFilesProcessed >= 1  && fCtx->nbFilesTotal > 1 && fCtx->totalBytesOutput != 0)
2916f7cd7fe5SConrad Meyer         DISPLAYLEVEL(2, "%d files decompressed : %6zu bytes total \n", fCtx->nbFilesProcessed, fCtx->totalBytesOutput);
2917f7cd7fe5SConrad Meyer 
29180c16b537SWarner Losh     FIO_freeDResources(ress);
29190f743729SConrad Meyer     return error;
29200c16b537SWarner Losh }
29210c16b537SWarner Losh 
29220c16b537SWarner Losh /* **************************************************************************
29230c16b537SWarner Losh  *  .zst file info (--list command)
29240c16b537SWarner Losh  ***************************************************************************/
29250c16b537SWarner Losh 
29260c16b537SWarner Losh typedef struct {
29270c16b537SWarner Losh     U64 decompressedSize;
29280c16b537SWarner Losh     U64 compressedSize;
29290c16b537SWarner Losh     U64 windowSize;
29300c16b537SWarner Losh     int numActualFrames;
29310c16b537SWarner Losh     int numSkippableFrames;
29320c16b537SWarner Losh     int decompUnavailable;
29330c16b537SWarner Losh     int usesCheck;
29340c16b537SWarner Losh     U32 nbFiles;
29350c16b537SWarner Losh } fileInfo_t;
29360c16b537SWarner Losh 
29372b9c00cbSConrad Meyer typedef enum {
29382b9c00cbSConrad Meyer   info_success=0,
29392b9c00cbSConrad Meyer   info_frame_error=1,
29402b9c00cbSConrad Meyer   info_not_zstd=2,
29412b9c00cbSConrad Meyer   info_file_error=3,
29422b9c00cbSConrad Meyer   info_truncated_input=4,
29432b9c00cbSConrad Meyer } InfoError;
29440c16b537SWarner Losh 
29450f743729SConrad Meyer #define ERROR_IF(c,n,...) {             \
29460f743729SConrad Meyer     if (c) {                           \
29470f743729SConrad Meyer         DISPLAYLEVEL(1, __VA_ARGS__);  \
29480f743729SConrad Meyer         DISPLAYLEVEL(1, " \n");        \
29490f743729SConrad Meyer         return n;                      \
29500f743729SConrad Meyer     }                                  \
29510f743729SConrad Meyer }
29520f743729SConrad Meyer 
29530f743729SConrad Meyer static InfoError
FIO_analyzeFrames(fileInfo_t * info,FILE * const srcFile)29540f743729SConrad Meyer FIO_analyzeFrames(fileInfo_t* info, FILE* const srcFile)
29550f743729SConrad Meyer {
29560c16b537SWarner Losh     /* begin analyzing frame */
29570c16b537SWarner Losh     for ( ; ; ) {
29580c16b537SWarner Losh         BYTE headerBuffer[ZSTD_FRAMEHEADERSIZE_MAX];
29590c16b537SWarner Losh         size_t const numBytesRead = fread(headerBuffer, 1, sizeof(headerBuffer), srcFile);
29609cbefe25SConrad Meyer         if (numBytesRead < ZSTD_FRAMEHEADERSIZE_MIN(ZSTD_f_zstd1)) {
29610c16b537SWarner Losh             if ( feof(srcFile)
29620c16b537SWarner Losh               && (numBytesRead == 0)
2963052d3c12SConrad Meyer               && (info->compressedSize > 0)
2964052d3c12SConrad Meyer               && (info->compressedSize != UTIL_FILESIZE_UNKNOWN) ) {
29652b9c00cbSConrad Meyer                 unsigned long long file_position = (unsigned long long) LONG_TELL(srcFile);
29662b9c00cbSConrad Meyer                 unsigned long long file_size = (unsigned long long) info->compressedSize;
29672b9c00cbSConrad Meyer                 ERROR_IF(file_position != file_size, info_truncated_input,
29682b9c00cbSConrad Meyer                   "Error: seeked to position %llu, which is beyond file size of %llu\n",
29692b9c00cbSConrad Meyer                   file_position,
29702b9c00cbSConrad Meyer                   file_size);
29710f743729SConrad Meyer                 break;  /* correct end of file => success */
29720c16b537SWarner Losh             }
29730f743729SConrad Meyer             ERROR_IF(feof(srcFile), info_not_zstd, "Error: reached end of file with incomplete frame");
29740f743729SConrad Meyer             ERROR_IF(1, info_frame_error, "Error: did not reach end of file but ran out of frames");
29750c16b537SWarner Losh         }
29760c16b537SWarner Losh         {   U32 const magicNumber = MEM_readLE32(headerBuffer);
29770c16b537SWarner Losh             /* Zstandard frame */
29780c16b537SWarner Losh             if (magicNumber == ZSTD_MAGICNUMBER) {
29790c16b537SWarner Losh                 ZSTD_frameHeader header;
29800c16b537SWarner Losh                 U64 const frameContentSize = ZSTD_getFrameContentSize(headerBuffer, numBytesRead);
29810f743729SConrad Meyer                 if ( frameContentSize == ZSTD_CONTENTSIZE_ERROR
29820f743729SConrad Meyer                   || frameContentSize == ZSTD_CONTENTSIZE_UNKNOWN ) {
29830c16b537SWarner Losh                     info->decompUnavailable = 1;
29840c16b537SWarner Losh                 } else {
29850c16b537SWarner Losh                     info->decompressedSize += frameContentSize;
29860c16b537SWarner Losh                 }
29870f743729SConrad Meyer                 ERROR_IF(ZSTD_getFrameHeader(&header, headerBuffer, numBytesRead) != 0,
29880f743729SConrad Meyer                         info_frame_error, "Error: could not decode frame header");
29890c16b537SWarner Losh                 info->windowSize = header.windowSize;
29900c16b537SWarner Losh                 /* move to the end of the frame header */
29910c16b537SWarner Losh                 {   size_t const headerSize = ZSTD_frameHeaderSize(headerBuffer, numBytesRead);
29920f743729SConrad Meyer                     ERROR_IF(ZSTD_isError(headerSize), info_frame_error, "Error: could not determine frame header size");
29930f743729SConrad Meyer                     ERROR_IF(fseek(srcFile, ((long)headerSize)-((long)numBytesRead), SEEK_CUR) != 0,
29940f743729SConrad Meyer                             info_frame_error, "Error: could not move to end of frame header");
29950c16b537SWarner Losh                 }
29960c16b537SWarner Losh 
29970f743729SConrad Meyer                 /* skip all blocks in the frame */
29980c16b537SWarner Losh                 {   int lastBlock = 0;
29990c16b537SWarner Losh                     do {
30000c16b537SWarner Losh                         BYTE blockHeaderBuffer[3];
30010f743729SConrad Meyer                         ERROR_IF(fread(blockHeaderBuffer, 1, 3, srcFile) != 3,
30020f743729SConrad Meyer                                 info_frame_error, "Error while reading block header");
30030c16b537SWarner Losh                         {   U32 const blockHeader = MEM_readLE24(blockHeaderBuffer);
30040c16b537SWarner Losh                             U32 const blockTypeID = (blockHeader >> 1) & 3;
30050c16b537SWarner Losh                             U32 const isRLE = (blockTypeID == 1);
30060c16b537SWarner Losh                             U32 const isWrongBlock = (blockTypeID == 3);
30070c16b537SWarner Losh                             long const blockSize = isRLE ? 1 : (long)(blockHeader >> 3);
30080f743729SConrad Meyer                             ERROR_IF(isWrongBlock, info_frame_error, "Error: unsupported block type");
30090c16b537SWarner Losh                             lastBlock = blockHeader & 1;
30100f743729SConrad Meyer                             ERROR_IF(fseek(srcFile, blockSize, SEEK_CUR) != 0,
30110f743729SConrad Meyer                                     info_frame_error, "Error: could not skip to end of block");
30120f743729SConrad Meyer                         }
30130c16b537SWarner Losh                     } while (lastBlock != 1);
30140c16b537SWarner Losh                 }
30150c16b537SWarner Losh 
30160c16b537SWarner Losh                 /* check if checksum is used */
30170c16b537SWarner Losh                 {   BYTE const frameHeaderDescriptor = headerBuffer[4];
30180c16b537SWarner Losh                     int const contentChecksumFlag = (frameHeaderDescriptor & (1 << 2)) >> 2;
30190c16b537SWarner Losh                     if (contentChecksumFlag) {
30200c16b537SWarner Losh                         info->usesCheck = 1;
30210f743729SConrad Meyer                         ERROR_IF(fseek(srcFile, 4, SEEK_CUR) != 0,
30220f743729SConrad Meyer                                 info_frame_error, "Error: could not skip past checksum");
30230f743729SConrad Meyer                 }   }
30240c16b537SWarner Losh                 info->numActualFrames++;
30250c16b537SWarner Losh             }
30260c16b537SWarner Losh             /* Skippable frame */
3027a0483764SConrad Meyer             else if ((magicNumber & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) {
30280c16b537SWarner Losh                 U32 const frameSize = MEM_readLE32(headerBuffer + 4);
30290c16b537SWarner Losh                 long const seek = (long)(8 + frameSize - numBytesRead);
30300f743729SConrad Meyer                 ERROR_IF(LONG_SEEK(srcFile, seek, SEEK_CUR) != 0,
30310f743729SConrad Meyer                         info_frame_error, "Error: could not find end of skippable frame");
30320c16b537SWarner Losh                 info->numSkippableFrames++;
30330c16b537SWarner Losh             }
30340c16b537SWarner Losh             /* unknown content */
30350c16b537SWarner Losh             else {
30360f743729SConrad Meyer                 return info_not_zstd;
30370c16b537SWarner Losh             }
30380f743729SConrad Meyer         }  /* magic number analysis */
30390f743729SConrad Meyer     }  /* end analyzing frames */
30400f743729SConrad Meyer     return info_success;
30410c16b537SWarner Losh }
30420c16b537SWarner Losh 
30430f743729SConrad Meyer 
30440f743729SConrad Meyer static InfoError
getFileInfo_fileConfirmed(fileInfo_t * info,const char * inFileName)30450f743729SConrad Meyer getFileInfo_fileConfirmed(fileInfo_t* info, const char* inFileName)
3046052d3c12SConrad Meyer {
30470f743729SConrad Meyer     InfoError status;
3048*5ff13fbcSAllan Jude     FILE* const srcFile = FIO_openSrcFile(NULL, inFileName);
30490f743729SConrad Meyer     ERROR_IF(srcFile == NULL, info_file_error, "Error: could not open source file %s", inFileName);
30500f743729SConrad Meyer 
30510f743729SConrad Meyer     info->compressedSize = UTIL_getFileSize(inFileName);
30520f743729SConrad Meyer     status = FIO_analyzeFrames(info, srcFile);
30530f743729SConrad Meyer 
30540f743729SConrad Meyer     fclose(srcFile);
30550f743729SConrad Meyer     info->nbFiles = 1;
30560f743729SConrad Meyer     return status;
3057052d3c12SConrad Meyer }
30580f743729SConrad Meyer 
30590f743729SConrad Meyer 
30600f743729SConrad Meyer /** getFileInfo() :
30610f743729SConrad Meyer  *  Reads information from file, stores in *info
30620f743729SConrad Meyer  * @return : InfoError status
30630f743729SConrad Meyer  */
30640f743729SConrad Meyer static InfoError
getFileInfo(fileInfo_t * info,const char * srcFileName)30650f743729SConrad Meyer getFileInfo(fileInfo_t* info, const char* srcFileName)
30660f743729SConrad Meyer {
30670f743729SConrad Meyer     ERROR_IF(!UTIL_isRegularFile(srcFileName),
30680f743729SConrad Meyer             info_file_error, "Error : %s is not a file", srcFileName);
3069052d3c12SConrad Meyer     return getFileInfo_fileConfirmed(info, srcFileName);
3070052d3c12SConrad Meyer }
3071052d3c12SConrad Meyer 
3072052d3c12SConrad Meyer 
30730f743729SConrad Meyer static void
displayInfo(const char * inFileName,const fileInfo_t * info,int displayLevel)30740f743729SConrad Meyer displayInfo(const char* inFileName, const fileInfo_t* info, int displayLevel)
30750f743729SConrad Meyer {
3076*5ff13fbcSAllan Jude     UTIL_HumanReadableSize_t const window_hrs = UTIL_makeHumanReadableSize(info->windowSize);
3077*5ff13fbcSAllan Jude     UTIL_HumanReadableSize_t const compressed_hrs = UTIL_makeHumanReadableSize(info->compressedSize);
3078*5ff13fbcSAllan Jude     UTIL_HumanReadableSize_t const decompressed_hrs = UTIL_makeHumanReadableSize(info->decompressedSize);
3079*5ff13fbcSAllan Jude     double const ratio = (info->compressedSize == 0) ? 0 : ((double)info->decompressedSize)/(double)info->compressedSize;
30800c16b537SWarner Losh     const char* const checkString = (info->usesCheck ? "XXH64" : "None");
30810c16b537SWarner Losh     if (displayLevel <= 2) {
30820c16b537SWarner Losh         if (!info->decompUnavailable) {
3083*5ff13fbcSAllan Jude             DISPLAYOUT("%6d  %5d  %6.*f%4s  %8.*f%4s  %5.3f  %5s  %s\n",
30840c16b537SWarner Losh                     info->numSkippableFrames + info->numActualFrames,
30850c16b537SWarner Losh                     info->numSkippableFrames,
3086*5ff13fbcSAllan Jude                     compressed_hrs.precision, compressed_hrs.value, compressed_hrs.suffix,
3087*5ff13fbcSAllan Jude                     decompressed_hrs.precision, decompressed_hrs.value, decompressed_hrs.suffix,
30880c16b537SWarner Losh                     ratio, checkString, inFileName);
30890c16b537SWarner Losh         } else {
3090*5ff13fbcSAllan Jude             DISPLAYOUT("%6d  %5d  %6.*f%4s                       %5s  %s\n",
30910c16b537SWarner Losh                     info->numSkippableFrames + info->numActualFrames,
30920c16b537SWarner Losh                     info->numSkippableFrames,
3093*5ff13fbcSAllan Jude                     compressed_hrs.precision, compressed_hrs.value, compressed_hrs.suffix,
30940c16b537SWarner Losh                     checkString, inFileName);
30950c16b537SWarner Losh         }
30960c16b537SWarner Losh     } else {
3097052d3c12SConrad Meyer         DISPLAYOUT("%s \n", inFileName);
30980c16b537SWarner Losh         DISPLAYOUT("# Zstandard Frames: %d\n", info->numActualFrames);
3099052d3c12SConrad Meyer         if (info->numSkippableFrames)
31000c16b537SWarner Losh             DISPLAYOUT("# Skippable Frames: %d\n", info->numSkippableFrames);
3101*5ff13fbcSAllan Jude         DISPLAYOUT("Window Size: %.*f%s (%llu B)\n",
3102*5ff13fbcSAllan Jude                    window_hrs.precision, window_hrs.value, window_hrs.suffix,
31030c16b537SWarner Losh                    (unsigned long long)info->windowSize);
3104*5ff13fbcSAllan Jude         DISPLAYOUT("Compressed Size: %.*f%s (%llu B)\n",
3105*5ff13fbcSAllan Jude                     compressed_hrs.precision, compressed_hrs.value, compressed_hrs.suffix,
31060c16b537SWarner Losh                     (unsigned long long)info->compressedSize);
31070c16b537SWarner Losh         if (!info->decompUnavailable) {
3108*5ff13fbcSAllan Jude             DISPLAYOUT("Decompressed Size: %.*f%s (%llu B)\n",
3109*5ff13fbcSAllan Jude                     decompressed_hrs.precision, decompressed_hrs.value, decompressed_hrs.suffix,
31100c16b537SWarner Losh                     (unsigned long long)info->decompressedSize);
31110c16b537SWarner Losh             DISPLAYOUT("Ratio: %.4f\n", ratio);
31120c16b537SWarner Losh         }
31130c16b537SWarner Losh         DISPLAYOUT("Check: %s\n", checkString);
31140c16b537SWarner Losh         DISPLAYOUT("\n");
31150c16b537SWarner Losh     }
31160c16b537SWarner Losh }
31170c16b537SWarner Losh 
FIO_addFInfo(fileInfo_t fi1,fileInfo_t fi2)31180c16b537SWarner Losh static fileInfo_t FIO_addFInfo(fileInfo_t fi1, fileInfo_t fi2)
31190c16b537SWarner Losh {
31200c16b537SWarner Losh     fileInfo_t total;
312119fcbaf1SConrad Meyer     memset(&total, 0, sizeof(total));
31220c16b537SWarner Losh     total.numActualFrames = fi1.numActualFrames + fi2.numActualFrames;
31230c16b537SWarner Losh     total.numSkippableFrames = fi1.numSkippableFrames + fi2.numSkippableFrames;
31240c16b537SWarner Losh     total.compressedSize = fi1.compressedSize + fi2.compressedSize;
31250c16b537SWarner Losh     total.decompressedSize = fi1.decompressedSize + fi2.decompressedSize;
31260c16b537SWarner Losh     total.decompUnavailable = fi1.decompUnavailable | fi2.decompUnavailable;
31270c16b537SWarner Losh     total.usesCheck = fi1.usesCheck & fi2.usesCheck;
31280c16b537SWarner Losh     total.nbFiles = fi1.nbFiles + fi2.nbFiles;
31290c16b537SWarner Losh     return total;
31300c16b537SWarner Losh }
31310c16b537SWarner Losh 
31320f743729SConrad Meyer static int
FIO_listFile(fileInfo_t * total,const char * inFileName,int displayLevel)31330f743729SConrad Meyer FIO_listFile(fileInfo_t* total, const char* inFileName, int displayLevel)
31340f743729SConrad Meyer {
31350c16b537SWarner Losh     fileInfo_t info;
31360c16b537SWarner Losh     memset(&info, 0, sizeof(info));
31370f743729SConrad Meyer     {   InfoError const error = getFileInfo(&info, inFileName);
31382b9c00cbSConrad Meyer         switch (error) {
31392b9c00cbSConrad Meyer             case info_frame_error:
31400c16b537SWarner Losh                 /* display error, but provide output */
31412b9c00cbSConrad Meyer                 DISPLAYLEVEL(1, "Error while parsing \"%s\" \n", inFileName);
31422b9c00cbSConrad Meyer                 break;
31432b9c00cbSConrad Meyer             case info_not_zstd:
31442b9c00cbSConrad Meyer                 DISPLAYOUT("File \"%s\" not compressed by zstd \n", inFileName);
31450c16b537SWarner Losh                 if (displayLevel > 2) DISPLAYOUT("\n");
31460c16b537SWarner Losh                 return 1;
31472b9c00cbSConrad Meyer             case info_file_error:
31480c16b537SWarner Losh                 /* error occurred while opening the file */
31490c16b537SWarner Losh                 if (displayLevel > 2) DISPLAYOUT("\n");
31500c16b537SWarner Losh                 return 1;
31512b9c00cbSConrad Meyer             case info_truncated_input:
31522b9c00cbSConrad Meyer                 DISPLAYOUT("File \"%s\" is truncated \n", inFileName);
31532b9c00cbSConrad Meyer                 if (displayLevel > 2) DISPLAYOUT("\n");
31542b9c00cbSConrad Meyer                 return 1;
31552b9c00cbSConrad Meyer             case info_success:
31562b9c00cbSConrad Meyer             default:
31572b9c00cbSConrad Meyer                 break;
31580c16b537SWarner Losh         }
31592b9c00cbSConrad Meyer 
31600c16b537SWarner Losh         displayInfo(inFileName, &info, displayLevel);
31610c16b537SWarner Losh         *total = FIO_addFInfo(*total, info);
3162a0483764SConrad Meyer         assert(error == info_success || error == info_frame_error);
316337f1f268SConrad Meyer         return (int)error;
31640c16b537SWarner Losh     }
31650c16b537SWarner Losh }
31660c16b537SWarner Losh 
FIO_listMultipleFiles(unsigned numFiles,const char ** filenameTable,int displayLevel)31670f743729SConrad Meyer int FIO_listMultipleFiles(unsigned numFiles, const char** filenameTable, int displayLevel)
31680f743729SConrad Meyer {
31690f743729SConrad Meyer     /* ensure no specified input is stdin (needs fseek() capability) */
31700f743729SConrad Meyer     {   unsigned u;
31710f743729SConrad Meyer         for (u=0; u<numFiles;u++) {
31720f743729SConrad Meyer             ERROR_IF(!strcmp (filenameTable[u], stdinmark),
31730f743729SConrad Meyer                     1, "zstd: --list does not support reading from standard input");
31740f743729SConrad Meyer     }   }
31750f743729SConrad Meyer 
31760c16b537SWarner Losh     if (numFiles == 0) {
31770f743729SConrad Meyer         if (!IS_CONSOLE(stdin)) {
31780f743729SConrad Meyer             DISPLAYLEVEL(1, "zstd: --list does not support reading from standard input \n");
31790c16b537SWarner Losh         }
31800f743729SConrad Meyer         DISPLAYLEVEL(1, "No files given \n");
31810f743729SConrad Meyer         return 1;
31820f743729SConrad Meyer     }
31830f743729SConrad Meyer 
31840c16b537SWarner Losh     if (displayLevel <= 2) {
31850c16b537SWarner Losh         DISPLAYOUT("Frames  Skips  Compressed  Uncompressed  Ratio  Check  Filename\n");
31860c16b537SWarner Losh     }
31870c16b537SWarner Losh     {   int error = 0;
31880c16b537SWarner Losh         fileInfo_t total;
31890c16b537SWarner Losh         memset(&total, 0, sizeof(total));
31900c16b537SWarner Losh         total.usesCheck = 1;
31910f743729SConrad Meyer         /* --list each file, and check for any error */
31920f743729SConrad Meyer         {   unsigned u;
31930c16b537SWarner Losh             for (u=0; u<numFiles;u++) {
31940c16b537SWarner Losh                 error |= FIO_listFile(&total, filenameTable[u], displayLevel);
31950f743729SConrad Meyer         }   }
3196052d3c12SConrad Meyer         if (numFiles > 1 && displayLevel <= 2) {   /* display total */
3197*5ff13fbcSAllan Jude             UTIL_HumanReadableSize_t const compressed_hrs = UTIL_makeHumanReadableSize(total.compressedSize);
3198*5ff13fbcSAllan Jude             UTIL_HumanReadableSize_t const decompressed_hrs = UTIL_makeHumanReadableSize(total.decompressedSize);
3199*5ff13fbcSAllan Jude             double const ratio = (total.compressedSize == 0) ? 0 : ((double)total.decompressedSize)/(double)total.compressedSize;
32000c16b537SWarner Losh             const char* const checkString = (total.usesCheck ? "XXH64" : "");
32010c16b537SWarner Losh             DISPLAYOUT("----------------------------------------------------------------- \n");
32020c16b537SWarner Losh             if (total.decompUnavailable) {
3203*5ff13fbcSAllan Jude                 DISPLAYOUT("%6d  %5d  %6.*f%4s                       %5s  %u files\n",
32040c16b537SWarner Losh                         total.numSkippableFrames + total.numActualFrames,
32050c16b537SWarner Losh                         total.numSkippableFrames,
3206*5ff13fbcSAllan Jude                         compressed_hrs.precision, compressed_hrs.value, compressed_hrs.suffix,
3207a0483764SConrad Meyer                         checkString, (unsigned)total.nbFiles);
32080c16b537SWarner Losh             } else {
3209*5ff13fbcSAllan Jude                 DISPLAYOUT("%6d  %5d  %6.*f%4s  %8.*f%4s  %5.3f  %5s  %u files\n",
32100c16b537SWarner Losh                         total.numSkippableFrames + total.numActualFrames,
32110c16b537SWarner Losh                         total.numSkippableFrames,
3212*5ff13fbcSAllan Jude                         compressed_hrs.precision, compressed_hrs.value, compressed_hrs.suffix,
3213*5ff13fbcSAllan Jude                         decompressed_hrs.precision, decompressed_hrs.value, decompressed_hrs.suffix,
3214a0483764SConrad Meyer                         ratio, checkString, (unsigned)total.nbFiles);
3215052d3c12SConrad Meyer         }   }
32160c16b537SWarner Losh         return error;
32170c16b537SWarner Losh     }
32180c16b537SWarner Losh }
32190c16b537SWarner Losh 
32200c16b537SWarner Losh 
32210c16b537SWarner Losh #endif /* #ifndef ZSTD_NODECOMPRESS */
3222