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