aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/zstd/programs/fileio.c
diff options
context:
space:
mode:
authorthegeorg <thegeorg@yandex-team.com>2023-02-27 12:38:58 +0300
committerthegeorg <thegeorg@yandex-team.com>2023-02-27 12:38:58 +0300
commite82ffade6959fe8feaf41fe12a57d9f56873be40 (patch)
tree784bf61d9ca6cab4beabc995186d9b41e762ae60 /contrib/libs/zstd/programs/fileio.c
parente58cceed352de42c1526ab4eecdfeee158b1ede3 (diff)
downloadydb-e82ffade6959fe8feaf41fe12a57d9f56873be40.tar.gz
Update contrib/libs/zstd to 1.5.4
Diffstat (limited to 'contrib/libs/zstd/programs/fileio.c')
-rw-r--r--contrib/libs/zstd/programs/fileio.c1508
1 files changed, 730 insertions, 778 deletions
diff --git a/contrib/libs/zstd/programs/fileio.c b/contrib/libs/zstd/programs/fileio.c
index 0a0dfc42cb..3b885bc65f 100644
--- a/contrib/libs/zstd/programs/fileio.c
+++ b/contrib/libs/zstd/programs/fileio.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under both the BSD-style license (found in the
@@ -28,6 +28,7 @@
#include <stdio.h> /* fprintf, open, fdopen, fread, _fileno, stdin, stdout */
#include <stdlib.h> /* malloc, free */
#include <string.h> /* strcmp, strlen */
+#include <time.h> /* clock_t, to measure process time */
#include <fcntl.h> /* O_WRONLY */
#include <assert.h>
#include <errno.h> /* errno */
@@ -40,8 +41,12 @@
# include <io.h>
#endif
-#include "../lib/common/mem.h" /* U32, U64 */
#include "fileio.h"
+#include "fileio_asyncio.h"
+#include "fileio_common.h"
+
+FIO_display_prefs_t g_display_prefs = {2, FIO_ps_auto};
+UTIL_time_t g_displayClock = UTIL_TIME_INITIALIZER;
#define ZSTD_STATIC_LINKING_ONLY /* ZSTD_magicNumber, ZSTD_frameHeaderSize_max */
#include "../lib/zstd.h"
@@ -65,6 +70,40 @@
# error #include <lz4.h>
#endif
+char const* FIO_zlibVersion(void)
+{
+#if defined(ZSTD_GZCOMPRESS) || defined(ZSTD_GZDECOMPRESS)
+ return zlibVersion();
+#else
+ return "Unsupported";
+#endif
+}
+
+char const* FIO_lz4Version(void)
+{
+#if defined(ZSTD_LZ4COMPRESS) || defined(ZSTD_LZ4DECOMPRESS)
+ /* LZ4_versionString() added in v1.7.3 */
+# if LZ4_VERSION_NUMBER >= 10703
+ return LZ4_versionString();
+# else
+# define ZSTD_LZ4_VERSION LZ4_VERSION_MAJOR.LZ4_VERSION_MINOR.LZ4_VERSION_RELEASE
+# define ZSTD_LZ4_VERSION_STRING ZSTD_EXPAND_AND_QUOTE(ZSTD_LZ4_VERSION)
+ return ZSTD_LZ4_VERSION_STRING;
+# endif
+#else
+ return "Unsupported";
+#endif
+}
+
+char const* FIO_lzmaVersion(void)
+{
+#if defined(ZSTD_LZMACOMPRESS) || defined(ZSTD_LZMADECOMPRESS)
+ return lzma_version_string();
+#else
+ return "Unsupported";
+#endif
+}
+
/*-*************************************
* Constants
@@ -75,69 +114,17 @@
#define FNSPACE 30
/* Default file permissions 0666 (modulated by umask) */
+/* Temporary restricted file permissions are used when we're going to
+ * chmod/chown at the end of the operation. */
#if !defined(_WIN32)
/* These macros aren't defined on windows. */
#define DEFAULT_FILE_PERMISSIONS (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
+#define TEMPORARY_FILE_PERMISSIONS (S_IRUSR|S_IWUSR)
#else
#define DEFAULT_FILE_PERMISSIONS (0666)
+#define TEMPORARY_FILE_PERMISSIONS (0600)
#endif
-/*-*************************************
-* Macros
-***************************************/
-#define KB *(1 <<10)
-#define MB *(1 <<20)
-#define GB *(1U<<30)
-#undef MAX
-#define MAX(a,b) ((a)>(b) ? (a) : (b))
-
-struct FIO_display_prefs_s {
- int displayLevel; /* 0 : no display; 1: errors; 2: + result + interaction + warnings; 3: + progression; 4: + information */
- FIO_progressSetting_e progressSetting;
-};
-
-static FIO_display_prefs_t g_display_prefs = {2, FIO_ps_auto};
-
-#define DISPLAY(...) fprintf(stderr, __VA_ARGS__)
-#define DISPLAYOUT(...) fprintf(stdout, __VA_ARGS__)
-#define DISPLAYLEVEL(l, ...) { if (g_display_prefs.displayLevel>=l) { DISPLAY(__VA_ARGS__); } }
-
-static const U64 g_refreshRate = SEC_TO_MICRO / 6;
-static UTIL_time_t g_displayClock = UTIL_TIME_INITIALIZER;
-
-#define READY_FOR_UPDATE() ((g_display_prefs.progressSetting != FIO_ps_never) && UTIL_clockSpanMicro(g_displayClock) > g_refreshRate)
-#define DELAY_NEXT_UPDATE() { g_displayClock = UTIL_getTime(); }
-#define DISPLAYUPDATE(l, ...) { \
- if (g_display_prefs.displayLevel>=l && (g_display_prefs.progressSetting != FIO_ps_never)) { \
- if (READY_FOR_UPDATE() || (g_display_prefs.displayLevel>=4)) { \
- DELAY_NEXT_UPDATE(); \
- DISPLAY(__VA_ARGS__); \
- if (g_display_prefs.displayLevel>=4) fflush(stderr); \
- } } }
-
-#undef MIN /* in case it would be already defined */
-#define MIN(a,b) ((a) < (b) ? (a) : (b))
-
-
-#define EXM_THROW(error, ...) \
-{ \
- DISPLAYLEVEL(1, "zstd: "); \
- DISPLAYLEVEL(5, "Error defined at %s, line %i : \n", __FILE__, __LINE__); \
- DISPLAYLEVEL(1, "error %i : ", error); \
- DISPLAYLEVEL(1, __VA_ARGS__); \
- DISPLAYLEVEL(1, " \n"); \
- exit(error); \
-}
-
-#define CHECK_V(v, f) \
- v = f; \
- if (ZSTD_isError(v)) { \
- DISPLAYLEVEL(5, "%s \n", #f); \
- EXM_THROW(11, "%s", ZSTD_getErrorName(v)); \
- }
-#define CHECK(f) { size_t err; CHECK_V(err, f); }
-
-
/*-************************************
* Signal (Ctrl-C trapping)
**************************************/
@@ -237,7 +224,7 @@ static void ABRThandler(int sig) {
}
#endif
-void FIO_addAbortHandler()
+void FIO_addAbortHandler(void)
{
#if BACKTRACE_ENABLE
signal(SIGABRT, ABRThandler);
@@ -248,94 +235,6 @@ void FIO_addAbortHandler()
#endif
}
-
-/*-************************************************************
-* Avoid fseek()'s 2GiB barrier with MSVC, macOS, *BSD, MinGW
-***************************************************************/
-#if defined(_MSC_VER) && _MSC_VER >= 1400
-# define LONG_SEEK _fseeki64
-# define LONG_TELL _ftelli64
-#elif !defined(__64BIT__) && (PLATFORM_POSIX_VERSION >= 200112L) /* No point defining Large file for 64 bit */
-# define LONG_SEEK fseeko
-# define LONG_TELL ftello
-#elif defined(__MINGW32__) && !defined(__STRICT_ANSI__) && !defined(__NO_MINGW_LFS) && defined(__MSVCRT__)
-# define LONG_SEEK fseeko64
-# define LONG_TELL ftello64
-#elif defined(_WIN32) && !defined(__DJGPP__)
-# include <windows.h>
- static int LONG_SEEK(FILE* file, __int64 offset, int origin) {
- LARGE_INTEGER off;
- DWORD method;
- off.QuadPart = offset;
- if (origin == SEEK_END)
- method = FILE_END;
- else if (origin == SEEK_CUR)
- method = FILE_CURRENT;
- else
- method = FILE_BEGIN;
-
- if (SetFilePointerEx((HANDLE) _get_osfhandle(_fileno(file)), off, NULL, method))
- return 0;
- else
- return -1;
- }
- static __int64 LONG_TELL(FILE* file) {
- LARGE_INTEGER off, newOff;
- off.QuadPart = 0;
- newOff.QuadPart = 0;
- SetFilePointerEx((HANDLE) _get_osfhandle(_fileno(file)), off, &newOff, FILE_CURRENT);
- return newOff.QuadPart;
- }
-#else
-# define LONG_SEEK fseek
-# define LONG_TELL ftell
-#endif
-
-
-/*-*************************************
-* Parameters: FIO_prefs_t
-***************************************/
-
-/* typedef'd to FIO_prefs_t within fileio.h */
-struct FIO_prefs_s {
-
- /* Algorithm preferences */
- FIO_compressionType_t compressionType;
- U32 sparseFileSupport; /* 0: no sparse allowed; 1: auto (file yes, stdout no); 2: force sparse */
- int dictIDFlag;
- int checksumFlag;
- int blockSize;
- int overlapLog;
- U32 adaptiveMode;
- U32 useRowMatchFinder;
- int rsyncable;
- int minAdaptLevel;
- int maxAdaptLevel;
- int ldmFlag;
- int ldmHashLog;
- int ldmMinMatch;
- int ldmBucketSizeLog;
- int ldmHashRateLog;
- size_t streamSrcSize;
- size_t targetCBlockSize;
- int srcSizeHint;
- int testMode;
- ZSTD_paramSwitch_e literalCompressionMode;
-
- /* IO preferences */
- U32 removeSrcFile;
- U32 overwrite;
-
- /* Computation resources preferences */
- unsigned memLimit;
- int nbWorkers;
-
- int excludeCompressedFiles;
- int patchFromMode;
- int contentSize;
- int allowBlockDevices;
-};
-
/*-*************************************
* Parameters: FIO_ctx_t
***************************************/
@@ -355,6 +254,18 @@ struct FIO_ctx_s {
size_t totalBytesOutput;
};
+static int FIO_shouldDisplayFileSummary(FIO_ctx_t const* fCtx)
+{
+ return fCtx->nbFilesTotal <= 1 || g_display_prefs.displayLevel >= 3;
+}
+
+static int FIO_shouldDisplayMultipleFileSummary(FIO_ctx_t const* fCtx)
+{
+ int const shouldDisplay = (fCtx->nbFilesProcessed >= 1 && fCtx->nbFilesTotal > 1);
+ assert(shouldDisplay || FIO_shouldDisplayFileSummary(fCtx) || fCtx->nbFilesProcessed == 0);
+ return shouldDisplay;
+}
+
/*-*************************************
* Parameters: Initialization
@@ -395,6 +306,8 @@ FIO_prefs_t* FIO_createPreferences(void)
ret->literalCompressionMode = ZSTD_ps_auto;
ret->excludeCompressedFiles = 0;
ret->allowBlockDevices = 0;
+ ret->asyncIO = AIO_supported();
+ ret->passThrough = -1;
return ret;
}
@@ -443,13 +356,13 @@ void FIO_setCompressionType(FIO_prefs_t* const prefs, FIO_compressionType_t comp
void FIO_overwriteMode(FIO_prefs_t* const prefs) { prefs->overwrite = 1; }
-void FIO_setSparseWrite(FIO_prefs_t* const prefs, unsigned sparse) { prefs->sparseFileSupport = sparse; }
+void FIO_setSparseWrite(FIO_prefs_t* const prefs, int sparse) { prefs->sparseFileSupport = sparse; }
void FIO_setDictIDFlag(FIO_prefs_t* const prefs, int dictIDFlag) { prefs->dictIDFlag = dictIDFlag; }
void FIO_setChecksumFlag(FIO_prefs_t* const prefs, int checksumFlag) { prefs->checksumFlag = checksumFlag; }
-void FIO_setRemoveSrcFile(FIO_prefs_t* const prefs, unsigned flag) { prefs->removeSrcFile = (flag>0); }
+void FIO_setRemoveSrcFile(FIO_prefs_t* const prefs, int flag) { prefs->removeSrcFile = (flag!=0); }
void FIO_setMemLimit(FIO_prefs_t* const prefs, unsigned memLimit) { prefs->memLimit = memLimit; }
@@ -476,7 +389,7 @@ void FIO_setOverlapLog(FIO_prefs_t* const prefs, int overlapLog){
prefs->overlapLog = overlapLog;
}
-void FIO_setAdaptiveMode(FIO_prefs_t* const prefs, unsigned adapt) {
+void FIO_setAdaptiveMode(FIO_prefs_t* const prefs, int adapt) {
if ((adapt>0) && (prefs->nbWorkers==0))
EXM_THROW(1, "Adaptive mode is not compatible with single thread mode \n");
prefs->adaptiveMode = adapt;
@@ -558,6 +471,20 @@ void FIO_setContentSize(FIO_prefs_t* const prefs, int value)
prefs->contentSize = value != 0;
}
+void FIO_setAsyncIOFlag(FIO_prefs_t* const prefs, int value) {
+#ifdef ZSTD_MULTITHREAD
+ prefs->asyncIO = value;
+#else
+ (void) prefs;
+ (void) value;
+ DISPLAYLEVEL(2, "Note : asyncio is disabled (lack of multithreading support) \n");
+#endif
+}
+
+void FIO_setPassThroughFlag(FIO_prefs_t* const prefs, int value) {
+ prefs->passThrough = (value != 0);
+}
+
/* FIO_ctx_t functions */
void FIO_setHasStdoutOutput(FIO_ctx_t* const fCtx, int value) {
@@ -608,26 +535,26 @@ static int FIO_removeFile(const char* path)
/** FIO_openSrcFile() :
* condition : `srcFileName` must be non-NULL. `prefs` may be NULL.
* @result : FILE* to `srcFileName`, or NULL if it fails */
-static FILE* FIO_openSrcFile(const FIO_prefs_t* const prefs, const char* srcFileName)
+static FILE* FIO_openSrcFile(const FIO_prefs_t* const prefs, const char* srcFileName, stat_t* statbuf)
{
- stat_t statbuf;
int allowBlockDevices = prefs != NULL ? prefs->allowBlockDevices : 0;
assert(srcFileName != NULL);
+ assert(statbuf != NULL);
if (!strcmp (srcFileName, stdinmark)) {
DISPLAYLEVEL(4,"Using stdin for input \n");
SET_BINARY_MODE(stdin);
return stdin;
}
- if (!UTIL_stat(srcFileName, &statbuf)) {
+ if (!UTIL_stat(srcFileName, statbuf)) {
DISPLAYLEVEL(1, "zstd: can't stat %s : %s -- ignored \n",
srcFileName, strerror(errno));
return NULL;
}
- if (!UTIL_isRegularFileStat(&statbuf)
- && !UTIL_isFIFOStat(&statbuf)
- && !(allowBlockDevices && UTIL_isBlockDevStat(&statbuf))
+ if (!UTIL_isRegularFileStat(statbuf)
+ && !UTIL_isFIFOStat(statbuf)
+ && !(allowBlockDevices && UTIL_isBlockDevStat(statbuf))
) {
DISPLAYLEVEL(1, "zstd: %s is not a regular file -- ignored \n",
srcFileName);
@@ -685,7 +612,7 @@ FIO_openDstFile(FIO_ctx_t* fCtx, FIO_prefs_t* const prefs,
if (!prefs->overwrite) {
if (g_display_prefs.displayLevel <= 1) {
/* No interaction possible */
- DISPLAY("zstd: %s already exists; not overwritten \n",
+ DISPLAYLEVEL(1, "zstd: %s already exists; not overwritten \n",
dstFileName);
return NULL;
}
@@ -718,6 +645,17 @@ FIO_openDstFile(FIO_ctx_t* fCtx, FIO_prefs_t* const prefs,
if (f == NULL) {
DISPLAYLEVEL(1, "zstd: %s: %s\n", dstFileName, strerror(errno));
}
+ /* An increased buffer size can provide a significant performance boost on some platforms.
+ * Note that providing a NULL buf with a size that's not 0 is not defined in ANSI C, but is defined
+ * in an extension. There are three possibilities here -
+ * 1. Libc supports the extended version and everything is good.
+ * 2. Libc ignores the size when buf is NULL, in which case everything will continue as if we didn't
+ * call `setvbuf`.
+ * 3. We fail the call and execution continues but a warning message might be shown.
+ * In all cases due execution continues. For now, I believe that this is a more cost-effective
+ * solution than managing the buffers allocations ourselves (will require an API change). */
+ if(setvbuf(f, NULL, _IOFBF, 1 MB))
+ DISPLAYLEVEL(2, "Warning: setvbuf failed for %s\n", dstFileName);
return f;
}
}
@@ -728,23 +666,23 @@ FIO_openDstFile(FIO_ctx_t* fCtx, FIO_prefs_t* const prefs,
* @return : loaded size
* if fileName==NULL, returns 0 and a NULL pointer
*/
-static size_t FIO_createDictBuffer(void** bufferPtr, const char* fileName, FIO_prefs_t* const prefs)
+static size_t FIO_createDictBuffer(void** bufferPtr, const char* fileName, FIO_prefs_t* const prefs, stat_t* dictFileStat)
{
FILE* fileHandle;
U64 fileSize;
- stat_t statbuf;
assert(bufferPtr != NULL);
+ assert(dictFileStat != NULL);
*bufferPtr = NULL;
if (fileName == NULL) return 0;
DISPLAYLEVEL(4,"Loading %s as dictionary \n", fileName);
- if (!UTIL_stat(fileName, &statbuf)) {
+ if (!UTIL_stat(fileName, dictFileStat)) {
EXM_THROW(31, "Stat failed on dictionary file %s: %s", fileName, strerror(errno));
}
- if (!UTIL_isRegularFileStat(&statbuf)) {
+ if (!UTIL_isRegularFileStat(dictFileStat)) {
EXM_THROW(32, "Dictionary %s must be a regular file.", fileName);
}
@@ -754,7 +692,7 @@ static size_t FIO_createDictBuffer(void** bufferPtr, const char* fileName, FIO_p
EXM_THROW(33, "Couldn't open dictionary %s: %s", fileName, strerror(errno));
}
- fileSize = UTIL_getFileSizeStat(&statbuf);
+ fileSize = UTIL_getFileSizeStat(dictFileStat);
{
size_t const dictSizeMax = prefs->patchFromMode ? prefs->memLimit : DICTSIZE_MAX;
if (fileSize > dictSizeMax) {
@@ -785,7 +723,7 @@ int FIO_checkFilenameCollisions(const char** filenameTable, unsigned nbFiles) {
filenameTableSorted = (const char**) malloc(sizeof(char*) * nbFiles);
if (!filenameTableSorted) {
- DISPLAY("Unable to malloc new str array, not checking for name collisions\n");
+ DISPLAYLEVEL(1, "Allocation error during filename collision checking \n");
return 1;
}
@@ -802,7 +740,7 @@ int FIO_checkFilenameCollisions(const char** filenameTable, unsigned nbFiles) {
prevElem = filenameTableSorted[0];
for (u = 1; u < nbFiles; ++u) {
if (strcmp(prevElem, filenameTableSorted[u]) == 0) {
- DISPLAY("WARNING: Two files have same filename: %s\n", prevElem);
+ DISPLAYLEVEL(2, "WARNING: Two files have same filename: %s\n", prevElem);
}
prevElem = filenameTableSorted[u];
}
@@ -885,45 +823,89 @@ static void FIO_adjustMemLimitForPatchFromMode(FIO_prefs_t* const prefs,
FIO_setMemLimit(prefs, (unsigned)maxSize);
}
-/* FIO_removeMultiFilesWarning() :
+/* FIO_multiFilesConcatWarning() :
+ * This function handles logic when processing multiple files with -o or -c, displaying the appropriate warnings/prompts.
* Returns 1 if the console should abort, 0 if console should proceed.
- * This function handles logic when processing multiple files with -o, displaying the appropriate warnings/prompts.
*
- * If -f is specified, or there is just 1 file, zstd will always proceed as usual.
- * If --rm is specified, there will be a prompt asking for user confirmation.
- * If -f is specified with --rm, zstd will proceed as usual
- * If -q is specified with --rm, zstd will abort pre-emptively
- * If neither flag is specified, zstd will prompt the user for confirmation to proceed.
- * If --rm is not specified, then zstd will print a warning to the user (which can be silenced with -q).
- * However, if the output is stdout, we will always abort rather than displaying the warning prompt.
+ * If output is stdout or test mode is active, check that `--rm` disabled.
+ *
+ * If there is just 1 file to process, zstd will proceed as usual.
+ * If each file get processed into its own separate destination file, proceed as usual.
+ *
+ * When multiple files are processed into a single output,
+ * display a warning message, then disable --rm if it's set.
+ *
+ * If -f is specified or if output is stdout, just proceed.
+ * If output is set with -o, prompt for confirmation.
*/
-static int FIO_removeMultiFilesWarning(FIO_ctx_t* const fCtx, const FIO_prefs_t* const prefs, const char* outFileName, int displayLevelCutoff)
+static int FIO_multiFilesConcatWarning(const FIO_ctx_t* fCtx, FIO_prefs_t* prefs, const char* outFileName, int displayLevelCutoff)
{
- int error = 0;
- if (fCtx->nbFilesTotal > 1 && !prefs->overwrite) {
- if (g_display_prefs.displayLevel <= displayLevelCutoff) {
- if (prefs->removeSrcFile) {
- DISPLAYLEVEL(1, "zstd: Aborting... not deleting files and processing into dst: %s\n", outFileName);
- error = 1;
- }
- } else {
- if (!strcmp(outFileName, stdoutmark)) {
- DISPLAYLEVEL(2, "zstd: WARNING: all input files will be processed and concatenated into stdout. \n");
- } else {
- DISPLAYLEVEL(2, "zstd: WARNING: all input files will be processed and concatenated into a single output file: %s \n", outFileName);
- }
- DISPLAYLEVEL(2, "The concatenated output CANNOT regenerate the original directory tree. \n")
- if (prefs->removeSrcFile) {
- if (fCtx->hasStdoutOutput) {
- DISPLAYLEVEL(1, "Aborting. Use -f if you really want to delete the files and output to stdout\n");
- error = 1;
- } else {
- error = g_display_prefs.displayLevel > displayLevelCutoff && UTIL_requireUserConfirmation("This is a destructive operation. Proceed? (y/n): ", "Aborting...", "yY", fCtx->hasStdinInput);
- }
- }
- }
+ if (fCtx->hasStdoutOutput) {
+ if (prefs->removeSrcFile)
+ /* this should not happen ; hard fail, to protect user's data
+ * note: this should rather be an assert(), but we want to be certain that user's data will not be wiped out in case it nonetheless happen */
+ EXM_THROW(43, "It's not allowed to remove input files when processed output is piped to stdout. "
+ "This scenario is not supposed to be possible. "
+ "This is a programming error. File an issue for it to be fixed.");
+ }
+ if (prefs->testMode) {
+ if (prefs->removeSrcFile)
+ /* this should not happen ; hard fail, to protect user's data
+ * note: this should rather be an assert(), but we want to be certain that user's data will not be wiped out in case it nonetheless happen */
+ EXM_THROW(43, "Test mode shall not remove input files! "
+ "This scenario is not supposed to be possible. "
+ "This is a programming error. File an issue for it to be fixed.");
+ return 0;
}
- return error;
+
+ if (fCtx->nbFilesTotal == 1) return 0;
+ assert(fCtx->nbFilesTotal > 1);
+
+ if (!outFileName) return 0;
+
+ if (fCtx->hasStdoutOutput) {
+ DISPLAYLEVEL(2, "zstd: WARNING: all input files will be processed and concatenated into stdout. \n");
+ } else {
+ DISPLAYLEVEL(2, "zstd: WARNING: all input files will be processed and concatenated into a single output file: %s \n", outFileName);
+ }
+ DISPLAYLEVEL(2, "The concatenated output CANNOT regenerate original file names nor directory structure. \n")
+
+ /* multi-input into single output : --rm is not allowed */
+ if (prefs->removeSrcFile) {
+ DISPLAYLEVEL(2, "Since it's a destructive operation, input files will not be removed. \n");
+ prefs->removeSrcFile = 0;
+ }
+
+ if (fCtx->hasStdoutOutput) return 0;
+ if (prefs->overwrite) return 0;
+
+ /* multiple files concatenated into single destination file using -o without -f */
+ if (g_display_prefs.displayLevel <= displayLevelCutoff) {
+ /* quiet mode => no prompt => fail automatically */
+ DISPLAYLEVEL(1, "Concatenating multiple processed inputs into a single output loses file metadata. \n");
+ DISPLAYLEVEL(1, "Aborting. \n");
+ return 1;
+ }
+ /* normal mode => prompt */
+ return UTIL_requireUserConfirmation("Proceed? (y/n): ", "Aborting...", "yY", fCtx->hasStdinInput);
+}
+
+static ZSTD_inBuffer setInBuffer(const void* buf, size_t s, size_t pos)
+{
+ ZSTD_inBuffer i;
+ i.src = buf;
+ i.size = s;
+ i.pos = pos;
+ return i;
+}
+
+static ZSTD_outBuffer setOutBuffer(void* buf, size_t s, size_t pos)
+{
+ ZSTD_outBuffer o;
+ o.dst = buf;
+ o.size = s;
+ o.pos = pos;
+ return o;
}
#ifndef ZSTD_NOCOMPRESS
@@ -932,16 +914,13 @@ static int FIO_removeMultiFilesWarning(FIO_ctx_t* const fCtx, const FIO_prefs_t*
* Compression
************************************************************************/
typedef struct {
- FILE* srcFile;
- FILE* dstFile;
- void* srcBuffer;
- size_t srcBufferSize;
- void* dstBuffer;
- size_t dstBufferSize;
void* dictBuffer;
size_t dictBufferSize;
const char* dictFileName;
+ stat_t dictFileStat;
ZSTD_CStream* cctx;
+ WritePoolCtx_t *writeCtx;
+ ReadPoolCtx_t *readCtx;
} cRess_t;
/** ZSTD_cycleLog() :
@@ -973,8 +952,8 @@ static void FIO_adjustParamsForPatchFromMode(FIO_prefs_t* const prefs,
if (cParams.strategy >= ZSTD_btopt) {
DISPLAYLEVEL(1, "[Optimal parser notes] Consider the following to improve patch size at the cost of speed:\n");
DISPLAYLEVEL(1, "- Use --single-thread mode in the zstd cli\n");
- DISPLAYLEVEL(1, "- Set a larger targetLength (eg. --zstd=targetLength=4096)\n");
- DISPLAYLEVEL(1, "- Set a larger chainLog (eg. --zstd=chainLog=%u)\n", ZSTD_CHAINLOG_MAX);
+ DISPLAYLEVEL(1, "- Set a larger targetLength (e.g. --zstd=targetLength=4096)\n");
+ DISPLAYLEVEL(1, "- Set a larger chainLog (e.g. --zstd=chainLog=%u)\n", ZSTD_CHAINLOG_MAX);
DISPLAYLEVEL(1, "Also consider playing around with searchLog and hashLog\n");
}
}
@@ -990,9 +969,6 @@ static cRess_t FIO_createCResources(FIO_prefs_t* const prefs,
if (ress.cctx == NULL)
EXM_THROW(30, "allocation error (%s): can't create ZSTD_CCtx",
strerror(errno));
- ress.srcBufferSize = ZSTD_CStreamInSize();
- ress.srcBuffer = malloc(ress.srcBufferSize);
- ress.dstBufferSize = ZSTD_CStreamOutSize();
/* need to update memLimit before calling createDictBuffer
* because of memLimit check inside it */
@@ -1000,10 +976,10 @@ static cRess_t FIO_createCResources(FIO_prefs_t* const prefs,
unsigned long long const ssSize = (unsigned long long)prefs->streamSrcSize;
FIO_adjustParamsForPatchFromMode(prefs, &comprParams, UTIL_getFileSize(dictFileName), ssSize > 0 ? ssSize : maxSrcFileSize, cLevel);
}
- ress.dstBuffer = malloc(ress.dstBufferSize);
- ress.dictBufferSize = FIO_createDictBuffer(&ress.dictBuffer, dictFileName, prefs); /* works with dictFileName==NULL */
- if (!ress.srcBuffer || !ress.dstBuffer)
- EXM_THROW(31, "allocation error : not enough memory");
+ ress.dictBufferSize = FIO_createDictBuffer(&ress.dictBuffer, dictFileName, prefs, &ress.dictFileStat); /* works with dictFileName==NULL */
+
+ ress.writeCtx = AIO_WritePool_create(prefs, ZSTD_CStreamOutSize());
+ ress.readCtx = AIO_ReadPool_create(prefs, ZSTD_CStreamInSize());
/* Advanced parameters, including dictionary */
if (dictFileName && (ress.dictBuffer==NULL))
@@ -1066,9 +1042,9 @@ static cRess_t FIO_createCResources(FIO_prefs_t* const prefs,
static void FIO_freeCResources(const cRess_t* const ress)
{
- free(ress->srcBuffer);
- free(ress->dstBuffer);
free(ress->dictBuffer);
+ AIO_WritePool_free(ress->writeCtx);
+ AIO_ReadPool_free(ress->readCtx);
ZSTD_freeCStream(ress->cctx); /* never fails */
}
@@ -1081,6 +1057,7 @@ FIO_compressGzFrame(const cRess_t* ress, /* buffers & handlers are used, but no
{
unsigned long long inFileSize = 0, outFileSize = 0;
z_stream strm;
+ IOJob_t *writeJob = NULL;
if (compressionLevel > Z_BEST_COMPRESSION)
compressionLevel = Z_BEST_COMPRESSION;
@@ -1091,56 +1068,65 @@ FIO_compressGzFrame(const cRess_t* ress, /* buffers & handlers are used, but no
{ int const ret = deflateInit2(&strm, compressionLevel, Z_DEFLATED,
15 /* maxWindowLogSize */ + 16 /* gzip only */,
- 8, Z_DEFAULT_STRATEGY); /* see http://www.zlib.net/manual.html */
+ 8, Z_DEFAULT_STRATEGY); /* see https://www.zlib.net/manual.html */
if (ret != Z_OK) {
EXM_THROW(71, "zstd: %s: deflateInit2 error %d \n", srcFileName, ret);
} }
+ writeJob = AIO_WritePool_acquireJob(ress->writeCtx);
strm.next_in = 0;
strm.avail_in = 0;
- strm.next_out = (Bytef*)ress->dstBuffer;
- strm.avail_out = (uInt)ress->dstBufferSize;
+ strm.next_out = (Bytef*)writeJob->buffer;
+ strm.avail_out = (uInt)writeJob->bufferSize;
while (1) {
int ret;
if (strm.avail_in == 0) {
- size_t const inSize = fread(ress->srcBuffer, 1, ress->srcBufferSize, ress->srcFile);
- if (inSize == 0) break;
- inFileSize += inSize;
- strm.next_in = (z_const unsigned char*)ress->srcBuffer;
- strm.avail_in = (uInt)inSize;
+ AIO_ReadPool_fillBuffer(ress->readCtx, ZSTD_CStreamInSize());
+ if (ress->readCtx->srcBufferLoaded == 0) break;
+ inFileSize += ress->readCtx->srcBufferLoaded;
+ strm.next_in = (z_const unsigned char*)ress->readCtx->srcBuffer;
+ strm.avail_in = (uInt)ress->readCtx->srcBufferLoaded;
+ }
+
+ {
+ size_t const availBefore = strm.avail_in;
+ ret = deflate(&strm, Z_NO_FLUSH);
+ AIO_ReadPool_consumeBytes(ress->readCtx, availBefore - strm.avail_in);
}
- ret = deflate(&strm, Z_NO_FLUSH);
+
if (ret != Z_OK)
EXM_THROW(72, "zstd: %s: deflate error %d \n", srcFileName, ret);
- { size_t const cSize = ress->dstBufferSize - strm.avail_out;
+ { size_t const cSize = writeJob->bufferSize - strm.avail_out;
if (cSize) {
- if (fwrite(ress->dstBuffer, 1, cSize, ress->dstFile) != cSize)
- EXM_THROW(73, "Write error : cannot write to output file : %s ", strerror(errno));
+ writeJob->usedBufferSize = cSize;
+ AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
outFileSize += cSize;
- strm.next_out = (Bytef*)ress->dstBuffer;
- strm.avail_out = (uInt)ress->dstBufferSize;
- } }
+ strm.next_out = (Bytef*)writeJob->buffer;
+ strm.avail_out = (uInt)writeJob->bufferSize;
+ } }
if (srcFileSize == UTIL_FILESIZE_UNKNOWN) {
- DISPLAYUPDATE(2, "\rRead : %u MB ==> %.2f%% ",
- (unsigned)(inFileSize>>20),
- (double)outFileSize/inFileSize*100)
+ DISPLAYUPDATE_PROGRESS(
+ "\rRead : %u MB ==> %.2f%% ",
+ (unsigned)(inFileSize>>20),
+ (double)outFileSize/(double)inFileSize*100)
} else {
- DISPLAYUPDATE(2, "\rRead : %u / %u MB ==> %.2f%% ",
- (unsigned)(inFileSize>>20), (unsigned)(srcFileSize>>20),
- (double)outFileSize/inFileSize*100);
+ DISPLAYUPDATE_PROGRESS(
+ "\rRead : %u / %u MB ==> %.2f%% ",
+ (unsigned)(inFileSize>>20), (unsigned)(srcFileSize>>20),
+ (double)outFileSize/(double)inFileSize*100);
} }
while (1) {
int const ret = deflate(&strm, Z_FINISH);
- { size_t const cSize = ress->dstBufferSize - strm.avail_out;
+ { size_t const cSize = writeJob->bufferSize - strm.avail_out;
if (cSize) {
- if (fwrite(ress->dstBuffer, 1, cSize, ress->dstFile) != cSize)
- EXM_THROW(75, "Write error : %s ", strerror(errno));
+ writeJob->usedBufferSize = cSize;
+ AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
outFileSize += cSize;
- strm.next_out = (Bytef*)ress->dstBuffer;
- strm.avail_out = (uInt)ress->dstBufferSize;
- } }
+ strm.next_out = (Bytef*)writeJob->buffer;
+ strm.avail_out = (uInt)writeJob->bufferSize;
+ } }
if (ret == Z_STREAM_END) break;
if (ret != Z_BUF_ERROR)
EXM_THROW(77, "zstd: %s: deflate error %d \n", srcFileName, ret);
@@ -1151,6 +1137,8 @@ FIO_compressGzFrame(const cRess_t* ress, /* buffers & handlers are used, but no
EXM_THROW(79, "zstd: %s: deflateEnd error %d \n", srcFileName, ret);
} }
*readsize = inFileSize;
+ AIO_WritePool_releaseIoJob(writeJob);
+ AIO_WritePool_sparseWriteEnd(ress->writeCtx);
return outFileSize;
}
#endif
@@ -1166,6 +1154,7 @@ FIO_compressLzmaFrame(cRess_t* ress,
lzma_stream strm = LZMA_STREAM_INIT;
lzma_action action = LZMA_RUN;
lzma_ret ret;
+ IOJob_t *writeJob = NULL;
if (compressionLevel < 0) compressionLevel = 0;
if (compressionLevel > 9) compressionLevel = 9;
@@ -1183,46 +1172,55 @@ FIO_compressLzmaFrame(cRess_t* ress,
EXM_THROW(83, "zstd: %s: lzma_easy_encoder error %d", srcFileName, ret);
}
+ writeJob =AIO_WritePool_acquireJob(ress->writeCtx);
+ strm.next_out = (Bytef*)writeJob->buffer;
+ strm.avail_out = (uInt)writeJob->bufferSize;
strm.next_in = 0;
strm.avail_in = 0;
- strm.next_out = (BYTE*)ress->dstBuffer;
- strm.avail_out = ress->dstBufferSize;
while (1) {
if (strm.avail_in == 0) {
- size_t const inSize = fread(ress->srcBuffer, 1, ress->srcBufferSize, ress->srcFile);
- if (inSize == 0) action = LZMA_FINISH;
+ size_t const inSize = AIO_ReadPool_fillBuffer(ress->readCtx, ZSTD_CStreamInSize());
+ if (ress->readCtx->srcBufferLoaded == 0) action = LZMA_FINISH;
inFileSize += inSize;
- strm.next_in = (BYTE const*)ress->srcBuffer;
- strm.avail_in = inSize;
+ strm.next_in = (BYTE const*)ress->readCtx->srcBuffer;
+ strm.avail_in = ress->readCtx->srcBufferLoaded;
+ }
+
+ {
+ size_t const availBefore = strm.avail_in;
+ ret = lzma_code(&strm, action);
+ AIO_ReadPool_consumeBytes(ress->readCtx, availBefore - strm.avail_in);
}
- ret = lzma_code(&strm, action);
if (ret != LZMA_OK && ret != LZMA_STREAM_END)
EXM_THROW(84, "zstd: %s: lzma_code encoding error %d", srcFileName, ret);
- { size_t const compBytes = ress->dstBufferSize - strm.avail_out;
+ { size_t const compBytes = writeJob->bufferSize - strm.avail_out;
if (compBytes) {
- if (fwrite(ress->dstBuffer, 1, compBytes, ress->dstFile) != compBytes)
- EXM_THROW(85, "Write error : %s", strerror(errno));
+ writeJob->usedBufferSize = compBytes;
+ AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
outFileSize += compBytes;
- strm.next_out = (BYTE*)ress->dstBuffer;
- strm.avail_out = ress->dstBufferSize;
+ strm.next_out = (Bytef*)writeJob->buffer;
+ strm.avail_out = writeJob->bufferSize;
} }
if (srcFileSize == UTIL_FILESIZE_UNKNOWN)
- DISPLAYUPDATE(2, "\rRead : %u MB ==> %.2f%%",
+ DISPLAYUPDATE_PROGRESS("\rRead : %u MB ==> %.2f%%",
(unsigned)(inFileSize>>20),
- (double)outFileSize/inFileSize*100)
+ (double)outFileSize/(double)inFileSize*100)
else
- DISPLAYUPDATE(2, "\rRead : %u / %u MB ==> %.2f%%",
+ DISPLAYUPDATE_PROGRESS("\rRead : %u / %u MB ==> %.2f%%",
(unsigned)(inFileSize>>20), (unsigned)(srcFileSize>>20),
- (double)outFileSize/inFileSize*100);
+ (double)outFileSize/(double)inFileSize*100);
if (ret == LZMA_STREAM_END) break;
}
lzma_end(&strm);
*readsize = inFileSize;
+ AIO_WritePool_releaseIoJob(writeJob);
+ AIO_WritePool_sparseWriteEnd(ress->writeCtx);
+
return outFileSize;
}
#endif
@@ -1248,15 +1246,18 @@ FIO_compressLz4Frame(cRess_t* ress,
LZ4F_preferences_t prefs;
LZ4F_compressionContext_t ctx;
+ IOJob_t* writeJob = AIO_WritePool_acquireJob(ress->writeCtx);
+
LZ4F_errorCode_t const errorCode = LZ4F_createCompressionContext(&ctx, LZ4F_VERSION);
if (LZ4F_isError(errorCode))
EXM_THROW(31, "zstd: failed to create lz4 compression context");
memset(&prefs, 0, sizeof(prefs));
- assert(blockSize <= ress->srcBufferSize);
+ assert(blockSize <= ress->readCtx->base.jobBufferSize);
- prefs.autoFlush = 1;
+ /* autoflush off to mitigate a bug in lz4<=1.9.3 for compression level 12 */
+ prefs.autoFlush = 0;
prefs.compressionLevel = compressionLevel;
prefs.frameInfo.blockMode = LZ4F_blockLinked;
prefs.frameInfo.blockSizeID = LZ4F_max64KB;
@@ -1264,75 +1265,68 @@ FIO_compressLz4Frame(cRess_t* ress,
#if LZ4_VERSION_NUMBER >= 10600
prefs.frameInfo.contentSize = (srcFileSize==UTIL_FILESIZE_UNKNOWN) ? 0 : srcFileSize;
#endif
- assert(LZ4F_compressBound(blockSize, &prefs) <= ress->dstBufferSize);
+ assert(LZ4F_compressBound(blockSize, &prefs) <= writeJob->bufferSize);
{
- size_t readSize;
- size_t headerSize = LZ4F_compressBegin(ctx, ress->dstBuffer, ress->dstBufferSize, &prefs);
+ size_t headerSize = LZ4F_compressBegin(ctx, writeJob->buffer, writeJob->bufferSize, &prefs);
if (LZ4F_isError(headerSize))
EXM_THROW(33, "File header generation failed : %s",
LZ4F_getErrorName(headerSize));
- if (fwrite(ress->dstBuffer, 1, headerSize, ress->dstFile) != headerSize)
- EXM_THROW(34, "Write error : %s (cannot write header)", strerror(errno));
+ writeJob->usedBufferSize = headerSize;
+ AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
outFileSize += headerSize;
/* Read first block */
- readSize = fread(ress->srcBuffer, (size_t)1, (size_t)blockSize, ress->srcFile);
- inFileSize += readSize;
+ inFileSize += AIO_ReadPool_fillBuffer(ress->readCtx, blockSize);
/* Main Loop */
- while (readSize>0) {
- size_t const outSize = LZ4F_compressUpdate(ctx,
- ress->dstBuffer, ress->dstBufferSize,
- ress->srcBuffer, readSize, NULL);
+ while (ress->readCtx->srcBufferLoaded) {
+ size_t inSize = MIN(blockSize, ress->readCtx->srcBufferLoaded);
+ size_t const outSize = LZ4F_compressUpdate(ctx, writeJob->buffer, writeJob->bufferSize,
+ ress->readCtx->srcBuffer, inSize, NULL);
if (LZ4F_isError(outSize))
EXM_THROW(35, "zstd: %s: lz4 compression failed : %s",
srcFileName, LZ4F_getErrorName(outSize));
outFileSize += outSize;
if (srcFileSize == UTIL_FILESIZE_UNKNOWN) {
- DISPLAYUPDATE(2, "\rRead : %u MB ==> %.2f%%",
+ DISPLAYUPDATE_PROGRESS("\rRead : %u MB ==> %.2f%%",
(unsigned)(inFileSize>>20),
- (double)outFileSize/inFileSize*100)
+ (double)outFileSize/(double)inFileSize*100)
} else {
- DISPLAYUPDATE(2, "\rRead : %u / %u MB ==> %.2f%%",
+ DISPLAYUPDATE_PROGRESS("\rRead : %u / %u MB ==> %.2f%%",
(unsigned)(inFileSize>>20), (unsigned)(srcFileSize>>20),
- (double)outFileSize/inFileSize*100);
+ (double)outFileSize/(double)inFileSize*100);
}
/* Write Block */
- { size_t const sizeCheck = fwrite(ress->dstBuffer, 1, outSize, ress->dstFile);
- if (sizeCheck != outSize)
- EXM_THROW(36, "Write error : %s", strerror(errno));
- }
+ writeJob->usedBufferSize = outSize;
+ AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
/* Read next block */
- readSize = fread(ress->srcBuffer, (size_t)1, (size_t)blockSize, ress->srcFile);
- inFileSize += readSize;
+ AIO_ReadPool_consumeBytes(ress->readCtx, inSize);
+ inFileSize += AIO_ReadPool_fillBuffer(ress->readCtx, blockSize);
}
- if (ferror(ress->srcFile)) EXM_THROW(37, "Error reading %s ", srcFileName);
/* End of Stream mark */
- headerSize = LZ4F_compressEnd(ctx, ress->dstBuffer, ress->dstBufferSize, NULL);
+ headerSize = LZ4F_compressEnd(ctx, writeJob->buffer, writeJob->bufferSize, NULL);
if (LZ4F_isError(headerSize))
EXM_THROW(38, "zstd: %s: lz4 end of file generation failed : %s",
srcFileName, LZ4F_getErrorName(headerSize));
- { size_t const sizeCheck = fwrite(ress->dstBuffer, 1, headerSize, ress->dstFile);
- if (sizeCheck != headerSize)
- EXM_THROW(39, "Write error : %s (cannot write end of stream)",
- strerror(errno));
- }
+ writeJob->usedBufferSize = headerSize;
+ AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
outFileSize += headerSize;
}
*readsize = inFileSize;
LZ4F_freeCompressionContext(ctx);
+ AIO_WritePool_releaseIoJob(writeJob);
+ AIO_WritePool_sparseWriteEnd(ress->writeCtx);
return outFileSize;
}
#endif
-
static unsigned long long
FIO_compressZstdFrame(FIO_ctx_t* const fCtx,
FIO_prefs_t* const prefs,
@@ -1341,8 +1335,8 @@ FIO_compressZstdFrame(FIO_ctx_t* const fCtx,
int compressionLevel, U64* readsize)
{
cRess_t const ress = *ressPtr;
- FILE* const srcFile = ress.srcFile;
- FILE* const dstFile = ress.dstFile;
+ IOJob_t *writeJob = AIO_WritePool_acquireJob(ressPtr->writeCtx);
+
U64 compressedfilesize = 0;
ZSTD_EndDirective directive = ZSTD_e_continue;
U64 pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN;
@@ -1356,6 +1350,9 @@ FIO_compressZstdFrame(FIO_ctx_t* const fCtx,
unsigned inputPresented = 0;
unsigned inputBlocked = 0;
unsigned lastJobID = 0;
+ UTIL_time_t lastAdaptTime = UTIL_getTime();
+ U64 const adaptEveryMicro = REFRESH_RATE;
+
UTIL_HumanReadableSize_t const file_hrs = UTIL_makeHumanReadableSize(fileSize);
DISPLAYLEVEL(6, "compression using zstd format \n");
@@ -1375,8 +1372,13 @@ FIO_compressZstdFrame(FIO_ctx_t* const fCtx,
UTIL_HumanReadableSize_t windowSize;
CHECK(ZSTD_CCtx_getParameter(ress.cctx, ZSTD_c_windowLog, &windowLog));
if (windowLog == 0) {
- const ZSTD_compressionParameters cParams = ZSTD_getCParams(compressionLevel, fileSize, 0);
- windowLog = cParams.windowLog;
+ if (prefs->ldmFlag) {
+ /* If long mode is set without a window size libzstd will set this size internally */
+ windowLog = ZSTD_WINDOWLOG_LIMIT_DEFAULT;
+ } else {
+ const ZSTD_compressionParameters cParams = ZSTD_getCParams(compressionLevel, fileSize, 0);
+ windowLog = (int)cParams.windowLog;
+ }
}
windowSize = UTIL_makeHumanReadableSize(MAX(1ULL, MIN(1ULL << windowLog, pledgedSrcSize)));
DISPLAYLEVEL(4, "Decompression will require %.*f%s of memory\n", windowSize.precision, windowSize.value, windowSize.suffix);
@@ -1387,12 +1389,12 @@ FIO_compressZstdFrame(FIO_ctx_t* const fCtx,
do {
size_t stillToFlush;
/* Fill input Buffer */
- size_t const inSize = fread(ress.srcBuffer, (size_t)1, ress.srcBufferSize, srcFile);
- ZSTD_inBuffer inBuff = { ress.srcBuffer, inSize, 0 };
+ size_t const inSize = AIO_ReadPool_fillBuffer(ress.readCtx, ZSTD_CStreamInSize());
+ ZSTD_inBuffer inBuff = setInBuffer( ress.readCtx->srcBuffer, ress.readCtx->srcBufferLoaded, 0 );
DISPLAYLEVEL(6, "fread %u bytes from source \n", (unsigned)inSize);
*readsize += inSize;
- if ((inSize == 0) || (*readsize == fileSize))
+ if ((ress.readCtx->srcBufferLoaded == 0) || (*readsize == fileSize))
directive = ZSTD_e_end;
stillToFlush = 1;
@@ -1400,9 +1402,10 @@ FIO_compressZstdFrame(FIO_ctx_t* const fCtx,
|| (directive == ZSTD_e_end && stillToFlush != 0) ) {
size_t const oldIPos = inBuff.pos;
- ZSTD_outBuffer outBuff = { ress.dstBuffer, ress.dstBufferSize, 0 };
+ ZSTD_outBuffer outBuff = setOutBuffer( writeJob->buffer, writeJob->bufferSize, 0 );
size_t const toFlushNow = ZSTD_toFlushNow(ress.cctx);
CHECK_V(stillToFlush, ZSTD_compressStream2(ress.cctx, &outBuff, &inBuff, directive));
+ AIO_ReadPool_consumeBytes(ress.readCtx, inBuff.pos - oldIPos);
/* count stats */
inputPresented++;
@@ -1411,151 +1414,155 @@ FIO_compressZstdFrame(FIO_ctx_t* const fCtx,
/* Write compressed stream */
DISPLAYLEVEL(6, "ZSTD_compress_generic(end:%u) => input pos(%u)<=(%u)size ; output generated %u bytes \n",
- (unsigned)directive, (unsigned)inBuff.pos, (unsigned)inBuff.size, (unsigned)outBuff.pos);
+ (unsigned)directive, (unsigned)inBuff.pos, (unsigned)inBuff.size, (unsigned)outBuff.pos);
if (outBuff.pos) {
- size_t const sizeCheck = fwrite(ress.dstBuffer, 1, outBuff.pos, dstFile);
- if (sizeCheck != outBuff.pos)
- EXM_THROW(25, "Write error : %s (cannot write compressed block)",
- strerror(errno));
+ writeJob->usedBufferSize = outBuff.pos;
+ AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
compressedfilesize += outBuff.pos;
}
- /* display notification; and adapt compression level */
- if (READY_FOR_UPDATE()) {
+ /* adaptive mode : statistics measurement and speed correction */
+ if (prefs->adaptiveMode && UTIL_clockSpanMicro(lastAdaptTime) > adaptEveryMicro) {
+ ZSTD_frameProgression const zfp = ZSTD_getFrameProgression(ress.cctx);
+
+ lastAdaptTime = UTIL_getTime();
+
+ /* check output speed */
+ if (zfp.currentJobID > 1) { /* only possible if nbWorkers >= 1 */
+
+ unsigned long long newlyProduced = zfp.produced - previous_zfp_update.produced;
+ unsigned long long newlyFlushed = zfp.flushed - previous_zfp_update.flushed;
+ assert(zfp.produced >= previous_zfp_update.produced);
+ assert(prefs->nbWorkers >= 1);
+
+ /* test if compression is blocked
+ * either because output is slow and all buffers are full
+ * or because input is slow and no job can start while waiting for at least one buffer to be filled.
+ * note : exclude starting part, since currentJobID > 1 */
+ 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)*/
+ && (zfp.nbActiveWorkers == 0) /* confirmed : no compression ongoing */
+ ) {
+ DISPLAYLEVEL(6, "all buffers full : compression stopped => slow down \n")
+ speedChange = slower;
+ }
+
+ previous_zfp_update = zfp;
+
+ 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) */
+ && (flushWaiting == 0) /* flush speed was never slowed by lack of production, so it's operating at max capacity */
+ ) {
+ DISPLAYLEVEL(6, "compression faster than flush (%llu > %llu), and flushed was never slowed down by lack of production => slow down \n", newlyProduced, newlyFlushed);
+ speedChange = slower;
+ }
+ flushWaiting = 0;
+ }
+
+ /* course correct only if there is at least one new job completed */
+ if (zfp.currentJobID > lastJobID) {
+ DISPLAYLEVEL(6, "compression level adaptation check \n")
+
+ /* check input speed */
+ if (zfp.currentJobID > (unsigned)(prefs->nbWorkers+1)) { /* warm up period, to fill all workers */
+ if (inputBlocked <= 0) {
+ DISPLAYLEVEL(6, "input is never blocked => input is slower than ingestion \n");
+ speedChange = slower;
+ } else if (speedChange == noChange) {
+ unsigned long long newlyIngested = zfp.ingested - previous_zfp_correction.ingested;
+ unsigned long long newlyConsumed = zfp.consumed - previous_zfp_correction.consumed;
+ unsigned long long newlyProduced = zfp.produced - previous_zfp_correction.produced;
+ unsigned long long newlyFlushed = zfp.flushed - previous_zfp_correction.flushed;
+ previous_zfp_correction = zfp;
+ assert(inputPresented > 0);
+ DISPLAYLEVEL(6, "input blocked %u/%u(%.2f) - ingested:%u vs %u:consumed - flushed:%u vs %u:produced \n",
+ inputBlocked, inputPresented, (double)inputBlocked/inputPresented*100,
+ (unsigned)newlyIngested, (unsigned)newlyConsumed,
+ (unsigned)newlyFlushed, (unsigned)newlyProduced);
+ if ( (inputBlocked > inputPresented / 8) /* input is waiting often, because input buffers is full : compression or output too slow */
+ && (newlyFlushed * 33 / 32 > newlyProduced) /* flush everything that is produced */
+ && (newlyIngested * 33 / 32 > newlyConsumed) /* input speed as fast or faster than compression speed */
+ ) {
+ DISPLAYLEVEL(6, "recommend faster as in(%llu) >= (%llu)comp(%llu) <= out(%llu) \n",
+ newlyIngested, newlyConsumed, newlyProduced, newlyFlushed);
+ speedChange = faster;
+ }
+ }
+ inputBlocked = 0;
+ inputPresented = 0;
+ }
+
+ if (speedChange == slower) {
+ DISPLAYLEVEL(6, "slower speed , higher compression \n")
+ compressionLevel ++;
+ if (compressionLevel > ZSTD_maxCLevel()) compressionLevel = ZSTD_maxCLevel();
+ if (compressionLevel > prefs->maxAdaptLevel) compressionLevel = prefs->maxAdaptLevel;
+ compressionLevel += (compressionLevel == 0); /* skip 0 */
+ ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_compressionLevel, compressionLevel);
+ }
+ if (speedChange == faster) {
+ DISPLAYLEVEL(6, "faster speed , lighter compression \n")
+ compressionLevel --;
+ if (compressionLevel < prefs->minAdaptLevel) compressionLevel = prefs->minAdaptLevel;
+ compressionLevel -= (compressionLevel == 0); /* skip 0 */
+ ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_compressionLevel, compressionLevel);
+ }
+ speedChange = noChange;
+
+ lastJobID = zfp.currentJobID;
+ } /* if (zfp.currentJobID > lastJobID) */
+ } /* if (prefs->adaptiveMode && UTIL_clockSpanMicro(lastAdaptTime) > adaptEveryMicro) */
+
+ /* display notification */
+ if (SHOULD_DISPLAY_PROGRESS() && READY_FOR_UPDATE()) {
ZSTD_frameProgression const zfp = ZSTD_getFrameProgression(ress.cctx);
double const cShare = (double)zfp.produced / (double)(zfp.consumed + !zfp.consumed/*avoid div0*/) * 100;
UTIL_HumanReadableSize_t const buffered_hrs = UTIL_makeHumanReadableSize(zfp.ingested - zfp.consumed);
UTIL_HumanReadableSize_t const consumed_hrs = UTIL_makeHumanReadableSize(zfp.consumed);
UTIL_HumanReadableSize_t const produced_hrs = UTIL_makeHumanReadableSize(zfp.produced);
+ DELAY_NEXT_UPDATE();
+
/* display progress notifications */
+ DISPLAY_PROGRESS("\r%79s\r", ""); /* Clear out the current displayed line */
if (g_display_prefs.displayLevel >= 3) {
- DISPLAYUPDATE(3, "\r(L%i) Buffered :%6.*f%4s - Consumed :%6.*f%4s - Compressed :%6.*f%4s => %.2f%% ",
- compressionLevel,
- buffered_hrs.precision, buffered_hrs.value, buffered_hrs.suffix,
- consumed_hrs.precision, consumed_hrs.value, consumed_hrs.suffix,
- produced_hrs.precision, produced_hrs.value, produced_hrs.suffix,
- cShare );
- } else if (g_display_prefs.displayLevel >= 2 || g_display_prefs.progressSetting == FIO_ps_always) {
+ /* Verbose progress update */
+ DISPLAY_PROGRESS(
+ "(L%i) Buffered:%5.*f%s - Consumed:%5.*f%s - Compressed:%5.*f%s => %.2f%% ",
+ compressionLevel,
+ buffered_hrs.precision, buffered_hrs.value, buffered_hrs.suffix,
+ consumed_hrs.precision, consumed_hrs.value, consumed_hrs.suffix,
+ produced_hrs.precision, produced_hrs.value, produced_hrs.suffix,
+ cShare );
+ } else {
/* Require level 2 or forcibly displayed progress counter for summarized updates */
- DISPLAYLEVEL(1, "\r%79s\r", ""); /* Clear out the current displayed line */
if (fCtx->nbFilesTotal > 1) {
size_t srcFileNameSize = strlen(srcFileName);
/* Ensure that the string we print is roughly the same size each time */
if (srcFileNameSize > 18) {
const char* truncatedSrcFileName = srcFileName + srcFileNameSize - 15;
- DISPLAYLEVEL(1, "Compress: %u/%u files. Current: ...%s ",
+ DISPLAY_PROGRESS("Compress: %u/%u files. Current: ...%s ",
fCtx->currFileIdx+1, fCtx->nbFilesTotal, truncatedSrcFileName);
} else {
- DISPLAYLEVEL(1, "Compress: %u/%u files. Current: %*s ",
+ DISPLAY_PROGRESS("Compress: %u/%u files. Current: %*s ",
fCtx->currFileIdx+1, fCtx->nbFilesTotal, (int)(18-srcFileNameSize), srcFileName);
}
}
- DISPLAYLEVEL(1, "Read:%6.*f%4s ", consumed_hrs.precision, consumed_hrs.value, consumed_hrs.suffix);
+ DISPLAY_PROGRESS("Read:%6.*f%4s ", consumed_hrs.precision, consumed_hrs.value, consumed_hrs.suffix);
if (fileSize != UTIL_FILESIZE_UNKNOWN)
- DISPLAYLEVEL(2, "/%6.*f%4s", file_hrs.precision, file_hrs.value, file_hrs.suffix);
- DISPLAYLEVEL(1, " ==> %2.f%%", cShare);
- DELAY_NEXT_UPDATE();
+ DISPLAY_PROGRESS("/%6.*f%4s", file_hrs.precision, file_hrs.value, file_hrs.suffix);
+ DISPLAY_PROGRESS(" ==> %2.f%%", cShare);
}
-
- /* adaptive mode : statistics measurement and speed correction */
- if (prefs->adaptiveMode) {
-
- /* check output speed */
- if (zfp.currentJobID > 1) { /* only possible if nbWorkers >= 1 */
-
- unsigned long long newlyProduced = zfp.produced - previous_zfp_update.produced;
- unsigned long long newlyFlushed = zfp.flushed - previous_zfp_update.flushed;
- assert(zfp.produced >= previous_zfp_update.produced);
- assert(prefs->nbWorkers >= 1);
-
- /* test if compression is blocked
- * either because output is slow and all buffers are full
- * or because input is slow and no job can start while waiting for at least one buffer to be filled.
- * note : exclude starting part, since currentJobID > 1 */
- 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)*/
- && (zfp.nbActiveWorkers == 0) /* confirmed : no compression ongoing */
- ) {
- DISPLAYLEVEL(6, "all buffers full : compression stopped => slow down \n")
- speedChange = slower;
- }
-
- previous_zfp_update = zfp;
-
- 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) */
- && (flushWaiting == 0) /* flush speed was never slowed by lack of production, so it's operating at max capacity */
- ) {
- DISPLAYLEVEL(6, "compression faster than flush (%llu > %llu), and flushed was never slowed down by lack of production => slow down \n", newlyProduced, newlyFlushed);
- speedChange = slower;
- }
- flushWaiting = 0;
- }
-
- /* course correct only if there is at least one new job completed */
- if (zfp.currentJobID > lastJobID) {
- DISPLAYLEVEL(6, "compression level adaptation check \n")
-
- /* check input speed */
- if (zfp.currentJobID > (unsigned)(prefs->nbWorkers+1)) { /* warm up period, to fill all workers */
- if (inputBlocked <= 0) {
- DISPLAYLEVEL(6, "input is never blocked => input is slower than ingestion \n");
- speedChange = slower;
- } else if (speedChange == noChange) {
- unsigned long long newlyIngested = zfp.ingested - previous_zfp_correction.ingested;
- unsigned long long newlyConsumed = zfp.consumed - previous_zfp_correction.consumed;
- unsigned long long newlyProduced = zfp.produced - previous_zfp_correction.produced;
- unsigned long long newlyFlushed = zfp.flushed - previous_zfp_correction.flushed;
- previous_zfp_correction = zfp;
- assert(inputPresented > 0);
- DISPLAYLEVEL(6, "input blocked %u/%u(%.2f) - ingested:%u vs %u:consumed - flushed:%u vs %u:produced \n",
- inputBlocked, inputPresented, (double)inputBlocked/inputPresented*100,
- (unsigned)newlyIngested, (unsigned)newlyConsumed,
- (unsigned)newlyFlushed, (unsigned)newlyProduced);
- if ( (inputBlocked > inputPresented / 8) /* input is waiting often, because input buffers is full : compression or output too slow */
- && (newlyFlushed * 33 / 32 > newlyProduced) /* flush everything that is produced */
- && (newlyIngested * 33 / 32 > newlyConsumed) /* input speed as fast or faster than compression speed */
- ) {
- DISPLAYLEVEL(6, "recommend faster as in(%llu) >= (%llu)comp(%llu) <= out(%llu) \n",
- newlyIngested, newlyConsumed, newlyProduced, newlyFlushed);
- speedChange = faster;
- }
- }
- inputBlocked = 0;
- inputPresented = 0;
- }
-
- if (speedChange == slower) {
- DISPLAYLEVEL(6, "slower speed , higher compression \n")
- compressionLevel ++;
- if (compressionLevel > ZSTD_maxCLevel()) compressionLevel = ZSTD_maxCLevel();
- if (compressionLevel > prefs->maxAdaptLevel) compressionLevel = prefs->maxAdaptLevel;
- compressionLevel += (compressionLevel == 0); /* skip 0 */
- ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_compressionLevel, compressionLevel);
- }
- if (speedChange == faster) {
- DISPLAYLEVEL(6, "faster speed , lighter compression \n")
- compressionLevel --;
- if (compressionLevel < prefs->minAdaptLevel) compressionLevel = prefs->minAdaptLevel;
- compressionLevel -= (compressionLevel == 0); /* skip 0 */
- ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_compressionLevel, compressionLevel);
- }
- speedChange = noChange;
-
- lastJobID = zfp.currentJobID;
- } /* if (zfp.currentJobID > lastJobID) */
- } /* if (g_adaptiveMode) */
- } /* if (READY_FOR_UPDATE()) */
+ } /* if (SHOULD_DISPLAY_PROGRESS() && READY_FOR_UPDATE()) */
} /* while ((inBuff.pos != inBuff.size) */
} while (directive != ZSTD_e_end);
- if (ferror(srcFile)) {
- EXM_THROW(26, "Read error : I/O error");
- }
if (fileSize != UTIL_FILESIZE_UNKNOWN && *readsize != fileSize) {
EXM_THROW(27, "Read error : Incomplete read : %llu / %llu B",
(unsigned long long)*readsize, (unsigned long long)fileSize);
}
+ AIO_WritePool_releaseIoJob(writeJob);
+ AIO_WritePool_sparseWriteEnd(ressPtr->writeCtx);
+
return compressedfilesize;
}
@@ -1620,20 +1627,18 @@ FIO_compressFilename_internal(FIO_ctx_t* const fCtx,
/* Status */
fCtx->totalBytesInput += (size_t)readsize;
fCtx->totalBytesOutput += (size_t)compressedfilesize;
- DISPLAYLEVEL(2, "\r%79s\r", "");
- if (g_display_prefs.displayLevel >= 2 &&
- !fCtx->hasStdoutOutput &&
- (g_display_prefs.displayLevel >= 3 || fCtx->nbFilesTotal <= 1)) {
+ DISPLAY_PROGRESS("\r%79s\r", "");
+ if (FIO_shouldDisplayFileSummary(fCtx)) {
UTIL_HumanReadableSize_t hr_isize = UTIL_makeHumanReadableSize((U64) readsize);
UTIL_HumanReadableSize_t hr_osize = UTIL_makeHumanReadableSize((U64) compressedfilesize);
if (readsize == 0) {
- DISPLAYLEVEL(2,"%-20s : (%6.*f%4s => %6.*f%4s, %s) \n",
+ DISPLAY_SUMMARY("%-20s : (%6.*f%s => %6.*f%s, %s) \n",
srcFileName,
hr_isize.precision, hr_isize.value, hr_isize.suffix,
hr_osize.precision, hr_osize.value, hr_osize.suffix,
dstFileName);
} else {
- DISPLAYLEVEL(2,"%-20s :%6.2f%% (%6.*f%4s => %6.*f%4s, %s) \n",
+ DISPLAY_SUMMARY("%-20s :%6.2f%% (%6.*f%s => %6.*f%s, %s) \n",
srcFileName,
(double)compressedfilesize / (double)readsize * 100,
hr_isize.precision, hr_isize.value, hr_isize.suffix,
@@ -1656,7 +1661,7 @@ FIO_compressFilename_internal(FIO_ctx_t* const fCtx,
/*! FIO_compressFilename_dstFile() :
- * open dstFileName, or pass-through if ress.dstFile != NULL,
+ * open dstFileName, or pass-through if ress.file != NULL,
* then start compression with FIO_compressFilename_internal().
* Manages source removal (--rm) and file permissions transfer.
* note : ress.srcFile must be != NULL,
@@ -1669,27 +1674,29 @@ static int FIO_compressFilename_dstFile(FIO_ctx_t* const fCtx,
cRess_t ress,
const char* dstFileName,
const char* srcFileName,
+ const stat_t* srcFileStat,
int compressionLevel)
{
int closeDstFile = 0;
int result;
- stat_t statbuf;
- int transferMTime = 0;
- assert(ress.srcFile != NULL);
- if (ress.dstFile == NULL) {
- int dstFilePermissions = DEFAULT_FILE_PERMISSIONS;
+ int transferStat = 0;
+ FILE *dstFile;
+
+ assert(AIO_ReadPool_getFile(ress.readCtx) != NULL);
+ if (AIO_WritePool_getFile(ress.writeCtx) == NULL) {
+ int dstFileInitialPermissions = DEFAULT_FILE_PERMISSIONS;
if ( strcmp (srcFileName, stdinmark)
&& strcmp (dstFileName, stdoutmark)
- && UTIL_stat(srcFileName, &statbuf)
- && UTIL_isRegularFileStat(&statbuf) ) {
- dstFilePermissions = statbuf.st_mode;
- transferMTime = 1;
+ && UTIL_isRegularFileStat(srcFileStat) ) {
+ transferStat = 1;
+ dstFileInitialPermissions = TEMPORARY_FILE_PERMISSIONS;
}
closeDstFile = 1;
DISPLAYLEVEL(6, "FIO_compressFilename_dstFile: opening dst: %s \n", dstFileName);
- ress.dstFile = FIO_openDstFile(fCtx, prefs, srcFileName, dstFileName, dstFilePermissions);
- if (ress.dstFile==NULL) return 1; /* could not open dstFileName */
+ dstFile = FIO_openDstFile(fCtx, prefs, srcFileName, dstFileName, dstFileInitialPermissions);
+ if (dstFile==NULL) return 1; /* could not open dstFileName */
+ AIO_WritePool_setFile(ress.writeCtx, dstFile);
/* Must only be added after FIO_openDstFile() succeeds.
* Otherwise we may delete the destination file if it already exists,
* and the user presses Ctrl-C when asked if they wish to overwrite.
@@ -1700,18 +1707,15 @@ static int FIO_compressFilename_dstFile(FIO_ctx_t* const fCtx,
result = FIO_compressFilename_internal(fCtx, prefs, ress, dstFileName, srcFileName, compressionLevel);
if (closeDstFile) {
- FILE* const dstFile = ress.dstFile;
- ress.dstFile = NULL;
-
clearHandler();
DISPLAYLEVEL(6, "FIO_compressFilename_dstFile: closing dst: %s \n", dstFileName);
- if (fclose(dstFile)) { /* error closing dstFile */
+ if (AIO_WritePool_closeFile(ress.writeCtx)) { /* error closing file */
DISPLAYLEVEL(1, "zstd: %s: %s \n", dstFileName, strerror(errno));
result=1;
}
- if (transferMTime) {
- UTIL_utime(dstFileName, &statbuf);
+ if (transferStat) {
+ UTIL_setFileStat(dstFileName, srcFileStat);
}
if ( (result != 0) /* operation failure */
&& strcmp(dstFileName, stdoutmark) /* special case : don't remove() stdout */
@@ -1752,18 +1756,27 @@ FIO_compressFilename_srcFile(FIO_ctx_t* const fCtx,
int compressionLevel)
{
int result;
+ FILE* srcFile;
+ stat_t srcFileStat;
+ U64 fileSize = UTIL_FILESIZE_UNKNOWN;
DISPLAYLEVEL(6, "FIO_compressFilename_srcFile: %s \n", srcFileName);
- /* ensure src is not a directory */
- if (UTIL_isDirectory(srcFileName)) {
- DISPLAYLEVEL(1, "zstd: %s is a directory -- ignored \n", srcFileName);
- return 1;
- }
+ if (strcmp(srcFileName, stdinmark)) {
+ if (UTIL_stat(srcFileName, &srcFileStat)) {
+ /* failure to stat at all is handled during opening */
- /* ensure src is not the same as dict (if present) */
- if (ress.dictFileName != NULL && UTIL_isSameFile(srcFileName, ress.dictFileName)) {
- DISPLAYLEVEL(1, "zstd: cannot use %s as an input file and dictionary \n", srcFileName);
- return 1;
+ /* ensure src is not a directory */
+ if (UTIL_isDirectoryStat(&srcFileStat)) {
+ DISPLAYLEVEL(1, "zstd: %s is a directory -- ignored \n", srcFileName);
+ return 1;
+ }
+
+ /* ensure src is not the same as dict (if present) */
+ if (ress.dictFileName != NULL && UTIL_isSameFileStat(srcFileName, ress.dictFileName, &srcFileStat, &ress.dictFileStat)) {
+ DISPLAYLEVEL(1, "zstd: cannot use %s as an input file and dictionary \n", srcFileName);
+ return 1;
+ }
+ }
}
/* Check if "srcFile" is compressed. Only done if --exclude-compressed flag is used
@@ -1775,16 +1788,30 @@ FIO_compressFilename_srcFile(FIO_ctx_t* const fCtx,
return 0;
}
- ress.srcFile = FIO_openSrcFile(prefs, srcFileName);
- if (ress.srcFile == NULL) return 1; /* srcFile could not be opened */
+ srcFile = FIO_openSrcFile(prefs, srcFileName, &srcFileStat);
+ if (srcFile == NULL) return 1; /* srcFile could not be opened */
+
+ /* Don't use AsyncIO for small files */
+ if (strcmp(srcFileName, stdinmark)) /* Stdin doesn't have stats */
+ fileSize = UTIL_getFileSizeStat(&srcFileStat);
+ if(fileSize != UTIL_FILESIZE_UNKNOWN && fileSize < ZSTD_BLOCKSIZE_MAX * 3) {
+ AIO_ReadPool_setAsync(ress.readCtx, 0);
+ AIO_WritePool_setAsync(ress.writeCtx, 0);
+ } else {
+ AIO_ReadPool_setAsync(ress.readCtx, 1);
+ AIO_WritePool_setAsync(ress.writeCtx, 1);
+ }
- result = FIO_compressFilename_dstFile(fCtx, prefs, ress, dstFileName, srcFileName, compressionLevel);
+ AIO_ReadPool_setFile(ress.readCtx, srcFile);
+ result = FIO_compressFilename_dstFile(
+ fCtx, prefs, ress,
+ dstFileName, srcFileName,
+ &srcFileStat, compressionLevel);
+ AIO_ReadPool_closeFile(ress.readCtx);
- fclose(ress.srcFile);
- ress.srcFile = NULL;
- if ( prefs->removeSrcFile /* --rm */
- && result == 0 /* success */
- && strcmp(srcFileName, stdinmark) /* exception : don't erase stdin */
+ if ( prefs->removeSrcFile /* --rm */
+ && result == 0 /* success */
+ && strcmp(srcFileName, stdinmark) /* exception : don't erase stdin */
) {
/* We must clear the handler, since after this point calling it would
* delete both the source and destination files.
@@ -1796,16 +1823,18 @@ FIO_compressFilename_srcFile(FIO_ctx_t* const fCtx,
return result;
}
-static const char* checked_index(const char* options[], size_t length, size_t index) {
+static const char*
+checked_index(const char* options[], size_t length, size_t index) {
assert(index < length);
- // Necessary to avoid warnings since -O3 will omit the above `assert`
+ /* Necessary to avoid warnings since -O3 will omit the above `assert` */
(void) length;
return options[index];
}
-#define INDEX(options, index) checked_index((options), sizeof(options) / sizeof(char*), (index))
+#define INDEX(options, index) checked_index((options), sizeof(options) / sizeof(char*), (size_t)(index))
-void FIO_displayCompressionParameters(const FIO_prefs_t* prefs) {
+void FIO_displayCompressionParameters(const FIO_prefs_t* prefs)
+{
static const char* formatOptions[5] = {ZSTD_EXTENSION, GZ_EXTENSION, XZ_EXTENSION,
LZMA_EXTENSION, LZ4_EXTENSION};
static const char* sparseOptions[3] = {" --no-sparse", "", " --sparse"};
@@ -1866,6 +1895,11 @@ FIO_determineCompressedName(const char* srcFileName, const char* outDirName, con
char* outDirFilename = NULL;
size_t sfnSize = strlen(srcFileName);
size_t const srcSuffixLen = strlen(suffix);
+
+ if(!strcmp(srcFileName, stdinmark)) {
+ return stdoutmark;
+ }
+
if (outDirName) {
outDirFilename = FIO_createFilename_fromOutDir(srcFileName, outDirName, srcSuffixLen);
sfnSize = strlen(outDirFilename);
@@ -1928,23 +1962,24 @@ int FIO_compressMultipleFilenames(FIO_ctx_t* const fCtx,
/* init */
assert(outFileName != NULL || suffix != NULL);
if (outFileName != NULL) { /* output into a single destination (stdout typically) */
- if (FIO_removeMultiFilesWarning(fCtx, prefs, outFileName, 1 /* displayLevelCutoff */)) {
+ FILE *dstFile;
+ if (FIO_multiFilesConcatWarning(fCtx, prefs, outFileName, 1 /* displayLevelCutoff */)) {
FIO_freeCResources(&ress);
return 1;
}
- ress.dstFile = FIO_openDstFile(fCtx, prefs, NULL, outFileName, DEFAULT_FILE_PERMISSIONS);
- if (ress.dstFile == NULL) { /* could not open outFileName */
+ dstFile = FIO_openDstFile(fCtx, prefs, NULL, outFileName, DEFAULT_FILE_PERMISSIONS);
+ if (dstFile == NULL) { /* could not open outFileName */
error = 1;
} else {
+ AIO_WritePool_setFile(ress.writeCtx, dstFile);
for (; fCtx->currFileIdx < fCtx->nbFilesTotal; ++fCtx->currFileIdx) {
status = FIO_compressFilename_srcFile(fCtx, prefs, ress, outFileName, inFileNamesTable[fCtx->currFileIdx], compressionLevel);
if (!status) fCtx->nbFilesProcessed++;
error |= status;
}
- if (fclose(ress.dstFile))
+ if (AIO_WritePool_closeFile(ress.writeCtx))
EXM_THROW(29, "Write error (%s) : cannot properly close %s",
strerror(errno), outFileName);
- ress.dstFile = NULL;
}
} else {
if (outMirroredRootDirName)
@@ -1975,16 +2010,23 @@ int FIO_compressMultipleFilenames(FIO_ctx_t* const fCtx,
FIO_checkFilenameCollisions(inFileNamesTable , (unsigned)fCtx->nbFilesTotal);
}
- if (fCtx->nbFilesProcessed >= 1 && fCtx->nbFilesTotal > 1 && fCtx->totalBytesInput != 0) {
+ if (FIO_shouldDisplayMultipleFileSummary(fCtx)) {
UTIL_HumanReadableSize_t hr_isize = UTIL_makeHumanReadableSize((U64) fCtx->totalBytesInput);
UTIL_HumanReadableSize_t hr_osize = UTIL_makeHumanReadableSize((U64) fCtx->totalBytesOutput);
- DISPLAYLEVEL(2, "\r%79s\r", "");
- DISPLAYLEVEL(2, "%3d files compressed :%.2f%% (%6.*f%4s => %6.*f%4s)\n",
- fCtx->nbFilesProcessed,
- (double)fCtx->totalBytesOutput/((double)fCtx->totalBytesInput)*100,
- hr_isize.precision, hr_isize.value, hr_isize.suffix,
- hr_osize.precision, hr_osize.value, hr_osize.suffix);
+ DISPLAY_PROGRESS("\r%79s\r", "");
+ if (fCtx->totalBytesInput == 0) {
+ DISPLAY_SUMMARY("%3d files compressed : (%6.*f%4s => %6.*f%4s)\n",
+ fCtx->nbFilesProcessed,
+ hr_isize.precision, hr_isize.value, hr_isize.suffix,
+ hr_osize.precision, hr_osize.value, hr_osize.suffix);
+ } else {
+ DISPLAY_SUMMARY("%3d files compressed : %.2f%% (%6.*f%4s => %6.*f%4s)\n",
+ fCtx->nbFilesProcessed,
+ (double)fCtx->totalBytesOutput/((double)fCtx->totalBytesInput)*100,
+ hr_isize.precision, hr_isize.value, hr_isize.suffix,
+ hr_osize.precision, hr_osize.value, hr_osize.suffix);
+ }
}
FIO_freeCResources(&ress);
@@ -2001,13 +2043,9 @@ int FIO_compressMultipleFilenames(FIO_ctx_t* const fCtx,
* Decompression
***************************************************************************/
typedef struct {
- void* srcBuffer;
- size_t srcBufferSize;
- size_t srcBufferLoaded;
- void* dstBuffer;
- size_t dstBufferSize;
ZSTD_DStream* dctx;
- FILE* dstFile;
+ WritePoolCtx_t *writeCtx;
+ ReadPoolCtx_t *readCtx;
} dRess_t;
static dRess_t FIO_createDResources(FIO_prefs_t* const prefs, const char* dictFileName)
@@ -2025,159 +2063,49 @@ static dRess_t FIO_createDResources(FIO_prefs_t* const prefs, const char* dictFi
CHECK( ZSTD_DCtx_setMaxWindowSize(ress.dctx, prefs->memLimit) );
CHECK( ZSTD_DCtx_setParameter(ress.dctx, ZSTD_d_forceIgnoreChecksum, !prefs->checksumFlag));
- ress.srcBufferSize = ZSTD_DStreamInSize();
- ress.srcBuffer = malloc(ress.srcBufferSize);
- ress.dstBufferSize = ZSTD_DStreamOutSize();
- ress.dstBuffer = malloc(ress.dstBufferSize);
- if (!ress.srcBuffer || !ress.dstBuffer)
- EXM_THROW(61, "Allocation error : not enough memory");
-
/* dictionary */
{ void* dictBuffer;
- size_t const dictBufferSize = FIO_createDictBuffer(&dictBuffer, dictFileName, prefs);
- CHECK( ZSTD_initDStream_usingDict(ress.dctx, dictBuffer, dictBufferSize) );
+ stat_t statbuf;
+ size_t const dictBufferSize = FIO_createDictBuffer(&dictBuffer, dictFileName, prefs, &statbuf);
+ CHECK( ZSTD_DCtx_reset(ress.dctx, ZSTD_reset_session_only) );
+ CHECK( ZSTD_DCtx_loadDictionary(ress.dctx, dictBuffer, dictBufferSize) );
free(dictBuffer);
}
+ ress.writeCtx = AIO_WritePool_create(prefs, ZSTD_DStreamOutSize());
+ ress.readCtx = AIO_ReadPool_create(prefs, ZSTD_DStreamInSize());
+
return ress;
}
static void FIO_freeDResources(dRess_t ress)
{
CHECK( ZSTD_freeDStream(ress.dctx) );
- free(ress.srcBuffer);
- free(ress.dstBuffer);
+ AIO_WritePool_free(ress.writeCtx);
+ AIO_ReadPool_free(ress.readCtx);
}
-
-/** FIO_fwriteSparse() :
-* @return : storedSkips,
-* argument for next call to FIO_fwriteSparse() or FIO_fwriteSparseEnd() */
-static unsigned
-FIO_fwriteSparse(FILE* file,
- const void* buffer, size_t bufferSize,
- const FIO_prefs_t* const prefs,
- unsigned storedSkips)
-{
- const size_t* const bufferT = (const size_t*)buffer; /* Buffer is supposed malloc'ed, hence aligned on size_t */
- size_t bufferSizeT = bufferSize / sizeof(size_t);
- const size_t* const bufferTEnd = bufferT + bufferSizeT;
- const size_t* ptrT = bufferT;
- static const size_t segmentSizeT = (32 KB) / sizeof(size_t); /* check every 32 KB */
-
- if (prefs->testMode) return 0; /* do not output anything in test mode */
-
- if (!prefs->sparseFileSupport) { /* normal write */
- size_t const sizeCheck = fwrite(buffer, 1, bufferSize, file);
- if (sizeCheck != bufferSize)
- EXM_THROW(70, "Write error : cannot write decoded block : %s",
- strerror(errno));
- return 0;
- }
-
- /* avoid int overflow */
- if (storedSkips > 1 GB) {
- if (LONG_SEEK(file, 1 GB, SEEK_CUR) != 0)
- EXM_THROW(91, "1 GB skip error (sparse file support)");
- storedSkips -= 1 GB;
- }
-
- while (ptrT < bufferTEnd) {
- size_t nb0T;
-
- /* adjust last segment if < 32 KB */
- size_t seg0SizeT = segmentSizeT;
- if (seg0SizeT > bufferSizeT) seg0SizeT = bufferSizeT;
- bufferSizeT -= seg0SizeT;
-
- /* count leading zeroes */
- for (nb0T=0; (nb0T < seg0SizeT) && (ptrT[nb0T] == 0); nb0T++) ;
- storedSkips += (unsigned)(nb0T * sizeof(size_t));
-
- if (nb0T != seg0SizeT) { /* not all 0s */
- size_t const nbNon0ST = seg0SizeT - nb0T;
- /* skip leading zeros */
- if (LONG_SEEK(file, storedSkips, SEEK_CUR) != 0)
- EXM_THROW(92, "Sparse skip error ; try --no-sparse");
- storedSkips = 0;
- /* write the rest */
- if (fwrite(ptrT + nb0T, sizeof(size_t), nbNon0ST, file) != nbNon0ST)
- EXM_THROW(93, "Write error : cannot write decoded block : %s",
- strerror(errno));
- }
- ptrT += seg0SizeT;
- }
-
- { static size_t const maskT = sizeof(size_t)-1;
- if (bufferSize & maskT) {
- /* size not multiple of sizeof(size_t) : implies end of block */
- const char* const restStart = (const char*)bufferTEnd;
- const char* restPtr = restStart;
- const char* const restEnd = (const char*)buffer + bufferSize;
- assert(restEnd > restStart && restEnd < restStart + sizeof(size_t));
- for ( ; (restPtr < restEnd) && (*restPtr == 0); restPtr++) ;
- storedSkips += (unsigned) (restPtr - restStart);
- if (restPtr != restEnd) {
- /* not all remaining bytes are 0 */
- size_t const restSize = (size_t)(restEnd - restPtr);
- if (LONG_SEEK(file, storedSkips, SEEK_CUR) != 0)
- EXM_THROW(92, "Sparse skip error ; try --no-sparse");
- if (fwrite(restPtr, 1, restSize, file) != restSize)
- EXM_THROW(95, "Write error : cannot write end of decoded block : %s",
- strerror(errno));
- storedSkips = 0;
- } } }
-
- return storedSkips;
-}
-
-static void
-FIO_fwriteSparseEnd(const FIO_prefs_t* const prefs, FILE* file, unsigned storedSkips)
-{
- if (prefs->testMode) assert(storedSkips == 0);
- if (storedSkips>0) {
- assert(prefs->sparseFileSupport > 0); /* storedSkips>0 implies sparse support is enabled */
- (void)prefs; /* assert can be disabled, in which case prefs becomes unused */
- if (LONG_SEEK(file, storedSkips-1, SEEK_CUR) != 0)
- EXM_THROW(69, "Final skip error (sparse file support)");
- /* last zero must be explicitly written,
- * so that skipped ones get implicitly translated as zero by FS */
- { const char lastZeroByte[1] = { 0 };
- if (fwrite(lastZeroByte, 1, 1, file) != 1)
- EXM_THROW(69, "Write error : cannot write last zero : %s", strerror(errno));
- } }
-}
-
-
/** FIO_passThrough() : just copy input into output, for compatibility with gzip -df mode
@return : 0 (no error) */
-static int FIO_passThrough(const FIO_prefs_t* const prefs,
- FILE* foutput, FILE* finput,
- void* buffer, size_t bufferSize,
- size_t alreadyLoaded)
-{
- size_t const blockSize = MIN(64 KB, bufferSize);
- size_t readFromInput;
- unsigned storedSkips = 0;
-
- /* assumption : ress->srcBufferLoaded bytes already loaded and stored within buffer */
- { size_t const sizeCheck = fwrite(buffer, 1, alreadyLoaded, foutput);
- if (sizeCheck != alreadyLoaded) {
- DISPLAYLEVEL(1, "Pass-through write error : %s\n", strerror(errno));
- return 1;
- } }
-
- do {
- readFromInput = fread(buffer, 1, blockSize, finput);
- storedSkips = FIO_fwriteSparse(foutput, buffer, readFromInput, prefs, storedSkips);
- } while (readFromInput == blockSize);
- if (ferror(finput)) {
- DISPLAYLEVEL(1, "Pass-through read error : %s\n", strerror(errno));
- return 1;
- }
- assert(feof(finput));
-
- FIO_fwriteSparseEnd(prefs, foutput, storedSkips);
+static int FIO_passThrough(dRess_t *ress)
+{
+ size_t const blockSize = MIN(MIN(64 KB, ZSTD_DStreamInSize()), ZSTD_DStreamOutSize());
+ IOJob_t *writeJob = AIO_WritePool_acquireJob(ress->writeCtx);
+ AIO_ReadPool_fillBuffer(ress->readCtx, blockSize);
+
+ while(ress->readCtx->srcBufferLoaded) {
+ size_t writeSize;
+ writeSize = MIN(blockSize, ress->readCtx->srcBufferLoaded);
+ assert(writeSize <= writeJob->bufferSize);
+ memcpy(writeJob->buffer, ress->readCtx->srcBuffer, writeSize);
+ writeJob->usedBufferSize = writeSize;
+ AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
+ AIO_ReadPool_consumeBytes(ress->readCtx, writeSize);
+ AIO_ReadPool_fillBuffer(ress->readCtx, blockSize);
+ }
+ assert(ress->readCtx->reachedEof);
+ AIO_WritePool_releaseIoJob(writeJob);
+ AIO_WritePool_sparseWriteEnd(ress->writeCtx);
return 0;
}
@@ -2195,7 +2123,7 @@ FIO_zstdErrorHelp(const FIO_prefs_t* const prefs,
return;
/* Try to decode the frame header */
- err = ZSTD_getFrameHeader(&header, ress->srcBuffer, ress->srcBufferLoaded);
+ err = ZSTD_getFrameHeader(&header, ress->readCtx->srcBuffer, ress->readCtx->srcBufferLoaded);
if (err == 0) {
unsigned long long const windowSize = header.windowSize;
unsigned const windowLog = FIO_highbit64(windowSize) + ((windowSize & (windowSize - 1)) != 0);
@@ -2218,13 +2146,13 @@ FIO_zstdErrorHelp(const FIO_prefs_t* const prefs,
*/
#define FIO_ERROR_FRAME_DECODING ((unsigned long long)(-2))
static unsigned long long
-FIO_decompressZstdFrame(FIO_ctx_t* const fCtx, dRess_t* ress, FILE* finput,
+FIO_decompressZstdFrame(FIO_ctx_t* const fCtx, dRess_t* ress,
const FIO_prefs_t* const prefs,
const char* srcFileName,
U64 alreadyDecoded) /* for multi-frames streams */
{
U64 frameSize = 0;
- U32 storedSkips = 0;
+ IOJob_t *writeJob = AIO_WritePool_acquireJob(ress->writeCtx);
/* display last 20 characters only */
{ size_t const srcFileLength = strlen(srcFileName);
@@ -2234,67 +2162,60 @@ FIO_decompressZstdFrame(FIO_ctx_t* const fCtx, dRess_t* ress, FILE* finput,
ZSTD_DCtx_reset(ress->dctx, ZSTD_reset_session_only);
/* Header loading : ensures ZSTD_getFrameHeader() will succeed */
- { size_t const toDecode = ZSTD_FRAMEHEADERSIZE_MAX;
- if (ress->srcBufferLoaded < toDecode) {
- size_t const toRead = toDecode - ress->srcBufferLoaded;
- void* const startPosition = (char*)ress->srcBuffer + ress->srcBufferLoaded;
- ress->srcBufferLoaded += fread(startPosition, 1, toRead, finput);
- } }
+ AIO_ReadPool_fillBuffer(ress->readCtx, ZSTD_FRAMEHEADERSIZE_MAX);
/* Main decompression Loop */
while (1) {
- ZSTD_inBuffer inBuff = { ress->srcBuffer, ress->srcBufferLoaded, 0 };
- ZSTD_outBuffer outBuff= { ress->dstBuffer, ress->dstBufferSize, 0 };
+ ZSTD_inBuffer inBuff = setInBuffer( ress->readCtx->srcBuffer, ress->readCtx->srcBufferLoaded, 0 );
+ ZSTD_outBuffer outBuff= setOutBuffer( writeJob->buffer, writeJob->bufferSize, 0 );
size_t const readSizeHint = ZSTD_decompressStream(ress->dctx, &outBuff, &inBuff);
- const int displayLevel = (g_display_prefs.progressSetting == FIO_ps_always) ? 1 : 2;
UTIL_HumanReadableSize_t const hrs = UTIL_makeHumanReadableSize(alreadyDecoded+frameSize);
if (ZSTD_isError(readSizeHint)) {
DISPLAYLEVEL(1, "%s : Decoding error (36) : %s \n",
srcFileName, ZSTD_getErrorName(readSizeHint));
FIO_zstdErrorHelp(prefs, ress, readSizeHint, srcFileName);
+ AIO_WritePool_releaseIoJob(writeJob);
return FIO_ERROR_FRAME_DECODING;
}
/* Write block */
- storedSkips = FIO_fwriteSparse(ress->dstFile, ress->dstBuffer, outBuff.pos, prefs, storedSkips);
+ writeJob->usedBufferSize = outBuff.pos;
+ AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
frameSize += outBuff.pos;
if (fCtx->nbFilesTotal > 1) {
size_t srcFileNameSize = strlen(srcFileName);
if (srcFileNameSize > 18) {
const char* truncatedSrcFileName = srcFileName + srcFileNameSize - 15;
- DISPLAYUPDATE(displayLevel, "\rDecompress: %2u/%2u files. Current: ...%s : %.*f%s... ",
- fCtx->currFileIdx+1, fCtx->nbFilesTotal, truncatedSrcFileName, hrs.precision, hrs.value, hrs.suffix);
+ DISPLAYUPDATE_PROGRESS(
+ "\rDecompress: %2u/%2u files. Current: ...%s : %.*f%s... ",
+ fCtx->currFileIdx+1, fCtx->nbFilesTotal, truncatedSrcFileName, hrs.precision, hrs.value, hrs.suffix);
} else {
- DISPLAYUPDATE(displayLevel, "\rDecompress: %2u/%2u files. Current: %s : %.*f%s... ",
+ DISPLAYUPDATE_PROGRESS("\rDecompress: %2u/%2u files. Current: %s : %.*f%s... ",
fCtx->currFileIdx+1, fCtx->nbFilesTotal, srcFileName, hrs.precision, hrs.value, hrs.suffix);
}
} else {
- DISPLAYUPDATE(displayLevel, "\r%-20.20s : %.*f%s... ",
+ DISPLAYUPDATE_PROGRESS("\r%-20.20s : %.*f%s... ",
srcFileName, hrs.precision, hrs.value, hrs.suffix);
}
- if (inBuff.pos > 0) {
- memmove(ress->srcBuffer, (char*)ress->srcBuffer + inBuff.pos, inBuff.size - inBuff.pos);
- ress->srcBufferLoaded -= inBuff.pos;
- }
+ AIO_ReadPool_consumeBytes(ress->readCtx, inBuff.pos);
if (readSizeHint == 0) break; /* end of frame */
/* Fill input buffer */
- { size_t const toDecode = MIN(readSizeHint, ress->srcBufferSize); /* support large skippable frames */
- if (ress->srcBufferLoaded < toDecode) {
- size_t const toRead = toDecode - ress->srcBufferLoaded; /* > 0 */
- void* const startPosition = (char*)ress->srcBuffer + ress->srcBufferLoaded;
- size_t const readSize = fread(startPosition, 1, toRead, finput);
+ { size_t const toDecode = MIN(readSizeHint, ZSTD_DStreamInSize()); /* support large skippable frames */
+ if (ress->readCtx->srcBufferLoaded < toDecode) {
+ size_t const readSize = AIO_ReadPool_fillBuffer(ress->readCtx, toDecode);
if (readSize==0) {
DISPLAYLEVEL(1, "%s : Read error (39) : premature end \n",
- srcFileName);
+ srcFileName);
+ AIO_WritePool_releaseIoJob(writeJob);
return FIO_ERROR_FRAME_DECODING;
}
- ress->srcBufferLoaded += readSize;
- } } }
+ } } }
- FIO_fwriteSparseEnd(prefs, ress->dstFile, storedSkips);
+ AIO_WritePool_releaseIoJob(writeJob);
+ AIO_WritePool_sparseWriteEnd(ress->writeCtx);
return frameSize;
}
@@ -2302,37 +2223,36 @@ FIO_decompressZstdFrame(FIO_ctx_t* const fCtx, dRess_t* ress, FILE* finput,
#ifdef ZSTD_GZDECOMPRESS
static unsigned long long
-FIO_decompressGzFrame(dRess_t* ress, FILE* srcFile,
- const FIO_prefs_t* const prefs,
- const char* srcFileName)
+FIO_decompressGzFrame(dRess_t* ress, const char* srcFileName)
{
unsigned long long outFileSize = 0;
z_stream strm;
int flush = Z_NO_FLUSH;
int decodingError = 0;
- unsigned storedSkips = 0;
+ IOJob_t *writeJob = NULL;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.next_in = 0;
strm.avail_in = 0;
- /* see http://www.zlib.net/manual.html */
+ /* see https://www.zlib.net/manual.html */
if (inflateInit2(&strm, 15 /* maxWindowLogSize */ + 16 /* gzip only */) != Z_OK)
return FIO_ERROR_FRAME_DECODING;
- strm.next_out = (Bytef*)ress->dstBuffer;
- strm.avail_out = (uInt)ress->dstBufferSize;
- strm.avail_in = (uInt)ress->srcBufferLoaded;
- strm.next_in = (z_const unsigned char*)ress->srcBuffer;
+ writeJob = AIO_WritePool_acquireJob(ress->writeCtx);
+ strm.next_out = (Bytef*)writeJob->buffer;
+ strm.avail_out = (uInt)writeJob->bufferSize;
+ strm.avail_in = (uInt)ress->readCtx->srcBufferLoaded;
+ strm.next_in = (z_const unsigned char*)ress->readCtx->srcBuffer;
for ( ; ; ) {
int ret;
if (strm.avail_in == 0) {
- ress->srcBufferLoaded = fread(ress->srcBuffer, 1, ress->srcBufferSize, srcFile);
- if (ress->srcBufferLoaded == 0) flush = Z_FINISH;
- strm.next_in = (z_const unsigned char*)ress->srcBuffer;
- strm.avail_in = (uInt)ress->srcBufferLoaded;
+ AIO_ReadPool_consumeAndRefill(ress->readCtx);
+ if (ress->readCtx->srcBufferLoaded == 0) flush = Z_FINISH;
+ strm.next_in = (z_const unsigned char*)ress->readCtx->srcBuffer;
+ strm.avail_in = (uInt)ress->readCtx->srcBufferLoaded;
}
ret = inflate(&strm, flush);
if (ret == Z_BUF_ERROR) {
@@ -2343,35 +2263,34 @@ FIO_decompressGzFrame(dRess_t* ress, FILE* srcFile,
DISPLAYLEVEL(1, "zstd: %s: inflate error %d \n", srcFileName, ret);
decodingError = 1; break;
}
- { size_t const decompBytes = ress->dstBufferSize - strm.avail_out;
+ { size_t const decompBytes = writeJob->bufferSize - strm.avail_out;
if (decompBytes) {
- storedSkips = FIO_fwriteSparse(ress->dstFile, ress->dstBuffer, decompBytes, prefs, storedSkips);
+ writeJob->usedBufferSize = decompBytes;
+ AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
outFileSize += decompBytes;
- strm.next_out = (Bytef*)ress->dstBuffer;
- strm.avail_out = (uInt)ress->dstBufferSize;
+ strm.next_out = (Bytef*)writeJob->buffer;
+ strm.avail_out = (uInt)writeJob->bufferSize;
}
}
if (ret == Z_STREAM_END) break;
}
- if (strm.avail_in > 0)
- memmove(ress->srcBuffer, strm.next_in, strm.avail_in);
- ress->srcBufferLoaded = strm.avail_in;
+ AIO_ReadPool_consumeBytes(ress->readCtx, ress->readCtx->srcBufferLoaded - strm.avail_in);
+
if ( (inflateEnd(&strm) != Z_OK) /* release resources ; error detected */
&& (decodingError==0) ) {
DISPLAYLEVEL(1, "zstd: %s: inflateEnd error \n", srcFileName);
decodingError = 1;
}
- FIO_fwriteSparseEnd(prefs, ress->dstFile, storedSkips);
+ AIO_WritePool_releaseIoJob(writeJob);
+ AIO_WritePool_sparseWriteEnd(ress->writeCtx);
return decodingError ? FIO_ERROR_FRAME_DECODING : outFileSize;
}
#endif
-
#ifdef ZSTD_LZMADECOMPRESS
static unsigned long long
-FIO_decompressLzmaFrame(dRess_t* ress, FILE* srcFile,
- const FIO_prefs_t* const prefs,
+FIO_decompressLzmaFrame(dRess_t* ress,
const char* srcFileName, int plain_lzma)
{
unsigned long long outFileSize = 0;
@@ -2379,7 +2298,7 @@ FIO_decompressLzmaFrame(dRess_t* ress, FILE* srcFile,
lzma_action action = LZMA_RUN;
lzma_ret initRet;
int decodingError = 0;
- unsigned storedSkips = 0;
+ IOJob_t *writeJob = NULL;
strm.next_in = 0;
strm.avail_in = 0;
@@ -2396,18 +2315,19 @@ FIO_decompressLzmaFrame(dRess_t* ress, FILE* srcFile,
return FIO_ERROR_FRAME_DECODING;
}
- strm.next_out = (BYTE*)ress->dstBuffer;
- strm.avail_out = ress->dstBufferSize;
- strm.next_in = (BYTE const*)ress->srcBuffer;
- strm.avail_in = ress->srcBufferLoaded;
+ writeJob = AIO_WritePool_acquireJob(ress->writeCtx);
+ strm.next_out = (Bytef*)writeJob->buffer;
+ strm.avail_out = (uInt)writeJob->bufferSize;
+ strm.next_in = (BYTE const*)ress->readCtx->srcBuffer;
+ strm.avail_in = ress->readCtx->srcBufferLoaded;
for ( ; ; ) {
lzma_ret ret;
if (strm.avail_in == 0) {
- ress->srcBufferLoaded = fread(ress->srcBuffer, 1, ress->srcBufferSize, srcFile);
- if (ress->srcBufferLoaded == 0) action = LZMA_FINISH;
- strm.next_in = (BYTE const*)ress->srcBuffer;
- strm.avail_in = ress->srcBufferLoaded;
+ AIO_ReadPool_consumeAndRefill(ress->readCtx);
+ if (ress->readCtx->srcBufferLoaded == 0) action = LZMA_FINISH;
+ strm.next_in = (BYTE const*)ress->readCtx->srcBuffer;
+ strm.avail_in = ress->readCtx->srcBufferLoaded;
}
ret = lzma_code(&strm, action);
@@ -2420,104 +2340,90 @@ FIO_decompressLzmaFrame(dRess_t* ress, FILE* srcFile,
srcFileName, ret);
decodingError = 1; break;
}
- { size_t const decompBytes = ress->dstBufferSize - strm.avail_out;
+ { size_t const decompBytes = writeJob->bufferSize - strm.avail_out;
if (decompBytes) {
- storedSkips = FIO_fwriteSparse(ress->dstFile, ress->dstBuffer, decompBytes, prefs, storedSkips);
+ writeJob->usedBufferSize = decompBytes;
+ AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
outFileSize += decompBytes;
- strm.next_out = (BYTE*)ress->dstBuffer;
- strm.avail_out = ress->dstBufferSize;
+ strm.next_out = (Bytef*)writeJob->buffer;
+ strm.avail_out = writeJob->bufferSize;
} }
if (ret == LZMA_STREAM_END) break;
}
- if (strm.avail_in > 0)
- memmove(ress->srcBuffer, strm.next_in, strm.avail_in);
- ress->srcBufferLoaded = strm.avail_in;
+ AIO_ReadPool_consumeBytes(ress->readCtx, ress->readCtx->srcBufferLoaded - strm.avail_in);
lzma_end(&strm);
- FIO_fwriteSparseEnd(prefs, ress->dstFile, storedSkips);
+ AIO_WritePool_releaseIoJob(writeJob);
+ AIO_WritePool_sparseWriteEnd(ress->writeCtx);
return decodingError ? FIO_ERROR_FRAME_DECODING : outFileSize;
}
#endif
#ifdef ZSTD_LZ4DECOMPRESS
static unsigned long long
-FIO_decompressLz4Frame(dRess_t* ress, FILE* srcFile,
- const FIO_prefs_t* const prefs,
- const char* srcFileName)
+FIO_decompressLz4Frame(dRess_t* ress, const char* srcFileName)
{
unsigned long long filesize = 0;
- LZ4F_errorCode_t nextToLoad;
+ LZ4F_errorCode_t nextToLoad = 4;
LZ4F_decompressionContext_t dCtx;
LZ4F_errorCode_t const errorCode = LZ4F_createDecompressionContext(&dCtx, LZ4F_VERSION);
int decodingError = 0;
- unsigned storedSkips = 0;
+ IOJob_t *writeJob = NULL;
if (LZ4F_isError(errorCode)) {
DISPLAYLEVEL(1, "zstd: failed to create lz4 decompression context \n");
return FIO_ERROR_FRAME_DECODING;
}
- /* Init feed with magic number (already consumed from FILE* sFile) */
- { size_t inSize = 4;
- size_t outSize= 0;
- MEM_writeLE32(ress->srcBuffer, LZ4_MAGICNUMBER);
- nextToLoad = LZ4F_decompress(dCtx, ress->dstBuffer, &outSize, ress->srcBuffer, &inSize, NULL);
- if (LZ4F_isError(nextToLoad)) {
- DISPLAYLEVEL(1, "zstd: %s: lz4 header error : %s \n",
- srcFileName, LZ4F_getErrorName(nextToLoad));
- LZ4F_freeDecompressionContext(dCtx);
- return FIO_ERROR_FRAME_DECODING;
- } }
+ writeJob = AIO_WritePool_acquireJob(ress->writeCtx);
/* Main Loop */
for (;nextToLoad;) {
- size_t readSize;
size_t pos = 0;
- size_t decodedBytes = ress->dstBufferSize;
+ size_t decodedBytes = writeJob->bufferSize;
+ int fullBufferDecoded = 0;
/* Read input */
- if (nextToLoad > ress->srcBufferSize) nextToLoad = ress->srcBufferSize;
- readSize = fread(ress->srcBuffer, 1, nextToLoad, srcFile);
- if (!readSize) break; /* reached end of file or stream */
+ AIO_ReadPool_fillBuffer(ress->readCtx, nextToLoad);
+ if(!ress->readCtx->srcBufferLoaded) break; /* reached end of file */
- while ((pos < readSize) || (decodedBytes == ress->dstBufferSize)) { /* still to read, or still to flush */
+ while ((pos < ress->readCtx->srcBufferLoaded) || fullBufferDecoded) { /* still to read, or still to flush */
/* Decode Input (at least partially) */
- size_t remaining = readSize - pos;
- decodedBytes = ress->dstBufferSize;
- nextToLoad = LZ4F_decompress(dCtx, ress->dstBuffer, &decodedBytes, (char*)(ress->srcBuffer)+pos, &remaining, NULL);
+ size_t remaining = ress->readCtx->srcBufferLoaded - pos;
+ decodedBytes = writeJob->bufferSize;
+ nextToLoad = LZ4F_decompress(dCtx, writeJob->buffer, &decodedBytes, (char*)(ress->readCtx->srcBuffer)+pos,
+ &remaining, NULL);
if (LZ4F_isError(nextToLoad)) {
DISPLAYLEVEL(1, "zstd: %s: lz4 decompression error : %s \n",
srcFileName, LZ4F_getErrorName(nextToLoad));
decodingError = 1; nextToLoad = 0; break;
}
pos += remaining;
+ assert(pos <= ress->readCtx->srcBufferLoaded);
+ fullBufferDecoded = decodedBytes == writeJob->bufferSize;
/* Write Block */
if (decodedBytes) {
UTIL_HumanReadableSize_t hrs;
- storedSkips = FIO_fwriteSparse(ress->dstFile, ress->dstBuffer, decodedBytes, prefs, storedSkips);
+ writeJob->usedBufferSize = decodedBytes;
+ AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
filesize += decodedBytes;
hrs = UTIL_makeHumanReadableSize(filesize);
- DISPLAYUPDATE(2, "\rDecompressed : %.*f%s ", hrs.precision, hrs.value, hrs.suffix);
+ DISPLAYUPDATE_PROGRESS("\rDecompressed : %.*f%s ", hrs.precision, hrs.value, hrs.suffix);
}
if (!nextToLoad) break;
}
+ AIO_ReadPool_consumeBytes(ress->readCtx, pos);
}
- /* can be out because readSize == 0, which could be an fread() error */
- if (ferror(srcFile)) {
- DISPLAYLEVEL(1, "zstd: %s: read error \n", srcFileName);
- decodingError=1;
- }
-
if (nextToLoad!=0) {
DISPLAYLEVEL(1, "zstd: %s: unfinished lz4 stream \n", srcFileName);
decodingError=1;
}
LZ4F_freeDecompressionContext(dCtx);
- ress->srcBufferLoaded = 0; /* LZ4F will reach exact frame boundary */
- FIO_fwriteSparseEnd(prefs, ress->dstFile, storedSkips);
+ AIO_WritePool_releaseIoJob(writeJob);
+ AIO_WritePool_sparseWriteEnd(ress->writeCtx);
return decodingError ? FIO_ERROR_FRAME_DECODING : filesize;
}
@@ -2532,23 +2438,30 @@ FIO_decompressLz4Frame(dRess_t* ress, FILE* srcFile,
* 1 : error
*/
static int FIO_decompressFrames(FIO_ctx_t* const fCtx,
- dRess_t ress, FILE* srcFile,
- const FIO_prefs_t* const prefs,
- const char* dstFileName, const char* srcFileName)
+ dRess_t ress, const FIO_prefs_t* const prefs,
+ const char* dstFileName, const char* srcFileName)
{
unsigned readSomething = 0;
unsigned long long filesize = 0;
- assert(srcFile != NULL);
+ int passThrough = prefs->passThrough;
+
+ if (passThrough == -1) {
+ /* If pass-through mode is not explicitly enabled or disabled,
+ * default to the legacy behavior of enabling it if we are writing
+ * to stdout with the overwrite flag enabled.
+ */
+ passThrough = prefs->overwrite && !strcmp(dstFileName, stdoutmark);
+ }
+ assert(passThrough == 0 || passThrough == 1);
/* for each frame */
for ( ; ; ) {
/* check magic number -> version */
size_t const toRead = 4;
- const BYTE* const buf = (const BYTE*)ress.srcBuffer;
- if (ress.srcBufferLoaded < toRead) /* load up to 4 bytes for header */
- ress.srcBufferLoaded += fread((char*)ress.srcBuffer + ress.srcBufferLoaded,
- (size_t)1, toRead - ress.srcBufferLoaded, srcFile);
- if (ress.srcBufferLoaded==0) {
+ const BYTE* buf;
+ AIO_ReadPool_fillBuffer(ress.readCtx, toRead);
+ buf = (const BYTE*)ress.readCtx->srcBuffer;
+ if (ress.readCtx->srcBufferLoaded==0) {
if (readSomething==0) { /* srcFile is empty (which is invalid) */
DISPLAYLEVEL(1, "zstd: %s: unexpected end of file \n", srcFileName);
return 1;
@@ -2556,17 +2469,20 @@ static int FIO_decompressFrames(FIO_ctx_t* const fCtx,
break; /* no more input */
}
readSomething = 1; /* there is at least 1 byte in srcFile */
- if (ress.srcBufferLoaded < toRead) {
+ if (ress.readCtx->srcBufferLoaded < toRead) { /* not enough input to check magic number */
+ if (passThrough) {
+ return FIO_passThrough(&ress);
+ }
DISPLAYLEVEL(1, "zstd: %s: unknown header \n", srcFileName);
return 1;
}
- if (ZSTD_isFrame(buf, ress.srcBufferLoaded)) {
- unsigned long long const frameSize = FIO_decompressZstdFrame(fCtx, &ress, srcFile, prefs, srcFileName, filesize);
+ if (ZSTD_isFrame(buf, ress.readCtx->srcBufferLoaded)) {
+ unsigned long long const frameSize = FIO_decompressZstdFrame(fCtx, &ress, prefs, srcFileName, filesize);
if (frameSize == FIO_ERROR_FRAME_DECODING) return 1;
filesize += frameSize;
} else if (buf[0] == 31 && buf[1] == 139) { /* gz magic number */
#ifdef ZSTD_GZDECOMPRESS
- unsigned long long const frameSize = FIO_decompressGzFrame(&ress, srcFile, prefs, srcFileName);
+ unsigned long long const frameSize = FIO_decompressGzFrame(&ress, srcFileName);
if (frameSize == FIO_ERROR_FRAME_DECODING) return 1;
filesize += frameSize;
#else
@@ -2576,7 +2492,7 @@ static int FIO_decompressFrames(FIO_ctx_t* const fCtx,
} else if ((buf[0] == 0xFD && buf[1] == 0x37) /* xz magic number */
|| (buf[0] == 0x5D && buf[1] == 0x00)) { /* lzma header (no magic number) */
#ifdef ZSTD_LZMADECOMPRESS
- unsigned long long const frameSize = FIO_decompressLzmaFrame(&ress, srcFile, prefs, srcFileName, buf[0] != 0xFD);
+ unsigned long long const frameSize = FIO_decompressLzmaFrame(&ress, srcFileName, buf[0] != 0xFD);
if (frameSize == FIO_ERROR_FRAME_DECODING) return 1;
filesize += frameSize;
#else
@@ -2585,18 +2501,15 @@ static int FIO_decompressFrames(FIO_ctx_t* const fCtx,
#endif
} else if (MEM_readLE32(buf) == LZ4_MAGICNUMBER) {
#ifdef ZSTD_LZ4DECOMPRESS
- unsigned long long const frameSize = FIO_decompressLz4Frame(&ress, srcFile, prefs, srcFileName);
+ unsigned long long const frameSize = FIO_decompressLz4Frame(&ress, srcFileName);
if (frameSize == FIO_ERROR_FRAME_DECODING) return 1;
filesize += frameSize;
#else
DISPLAYLEVEL(1, "zstd: %s: lz4 file cannot be uncompressed (zstd compiled without HAVE_LZ4) -- ignored \n", srcFileName);
return 1;
#endif
- } else if ((prefs->overwrite) && !strcmp (dstFileName, stdoutmark)) { /* pass-through mode */
- return FIO_passThrough(prefs,
- ress.dstFile, srcFile,
- ress.srcBuffer, ress.srcBufferSize,
- ress.srcBufferLoaded);
+ } else if (passThrough) {
+ return FIO_passThrough(&ress);
} else {
DISPLAYLEVEL(1, "zstd: %s: unsupported format \n", srcFileName);
return 1;
@@ -2604,48 +2517,45 @@ static int FIO_decompressFrames(FIO_ctx_t* const fCtx,
/* Final Status */
fCtx->totalBytesOutput += (size_t)filesize;
- DISPLAYLEVEL(2, "\r%79s\r", "");
- /* No status message in pipe mode (stdin - stdout) or multi-files mode */
- if ((g_display_prefs.displayLevel >= 2 && fCtx->nbFilesTotal <= 1) ||
- g_display_prefs.displayLevel >= 3 ||
- g_display_prefs.progressSetting == FIO_ps_always) {
- DISPLAYLEVEL(1, "\r%-20s: %llu bytes \n", srcFileName, filesize);
- }
+ DISPLAY_PROGRESS("\r%79s\r", "");
+ if (FIO_shouldDisplayFileSummary(fCtx))
+ DISPLAY_SUMMARY("%-20s: %llu bytes \n", srcFileName, filesize);
return 0;
}
/** FIO_decompressDstFile() :
- open `dstFileName`,
- or path-through if ress.dstFile is already != 0,
+ open `dstFileName`, or pass-through if writeCtx's file is already != 0,
then start decompression process (FIO_decompressFrames()).
@return : 0 : OK
1 : operation aborted
*/
static int FIO_decompressDstFile(FIO_ctx_t* const fCtx,
FIO_prefs_t* const prefs,
- dRess_t ress, FILE* srcFile,
- const char* dstFileName, const char* srcFileName)
+ dRess_t ress,
+ const char* dstFileName,
+ const char* srcFileName,
+ const stat_t* srcFileStat)
{
int result;
- stat_t statbuf;
int releaseDstFile = 0;
- int transferMTime = 0;
+ int transferStat = 0;
- if ((ress.dstFile == NULL) && (prefs->testMode==0)) {
+ if ((AIO_WritePool_getFile(ress.writeCtx) == NULL) && (prefs->testMode == 0)) {
+ FILE *dstFile;
int dstFilePermissions = DEFAULT_FILE_PERMISSIONS;
if ( strcmp(srcFileName, stdinmark) /* special case : don't transfer permissions from stdin */
&& strcmp(dstFileName, stdoutmark)
- && UTIL_stat(srcFileName, &statbuf)
- && UTIL_isRegularFileStat(&statbuf) ) {
- dstFilePermissions = statbuf.st_mode;
- transferMTime = 1;
+ && UTIL_isRegularFileStat(srcFileStat) ) {
+ transferStat = 1;
+ dstFilePermissions = TEMPORARY_FILE_PERMISSIONS;
}
releaseDstFile = 1;
- ress.dstFile = FIO_openDstFile(fCtx, prefs, srcFileName, dstFileName, dstFilePermissions);
- if (ress.dstFile==NULL) return 1;
+ dstFile = FIO_openDstFile(fCtx, prefs, srcFileName, dstFileName, dstFilePermissions);
+ if (dstFile==NULL) return 1;
+ AIO_WritePool_setFile(ress.writeCtx, dstFile);
/* Must only be added after FIO_openDstFile() succeeds.
* Otherwise we may delete the destination file if it already exists,
@@ -2654,19 +2564,17 @@ static int FIO_decompressDstFile(FIO_ctx_t* const fCtx,
addHandler(dstFileName);
}
- result = FIO_decompressFrames(fCtx, ress, srcFile, prefs, dstFileName, srcFileName);
+ result = FIO_decompressFrames(fCtx, ress, prefs, dstFileName, srcFileName);
if (releaseDstFile) {
- FILE* const dstFile = ress.dstFile;
clearHandler();
- ress.dstFile = NULL;
- if (fclose(dstFile)) {
+ if (AIO_WritePool_closeFile(ress.writeCtx)) {
DISPLAYLEVEL(1, "zstd: %s: %s \n", dstFileName, strerror(errno));
result = 1;
}
- if (transferMTime) {
- UTIL_utime(dstFileName, &statbuf);
+ if (transferStat) {
+ UTIL_setFileStat(dstFileName, srcFileStat);
}
if ( (result != 0) /* operation failure */
@@ -2688,18 +2596,34 @@ static int FIO_decompressDstFile(FIO_ctx_t* const fCtx,
static int FIO_decompressSrcFile(FIO_ctx_t* const fCtx, FIO_prefs_t* const prefs, dRess_t ress, const char* dstFileName, const char* srcFileName)
{
FILE* srcFile;
+ stat_t srcFileStat;
int result;
+ U64 fileSize = UTIL_FILESIZE_UNKNOWN;
if (UTIL_isDirectory(srcFileName)) {
DISPLAYLEVEL(1, "zstd: %s is a directory -- ignored \n", srcFileName);
return 1;
}
- srcFile = FIO_openSrcFile(prefs, srcFileName);
+ srcFile = FIO_openSrcFile(prefs, srcFileName, &srcFileStat);
if (srcFile==NULL) return 1;
- ress.srcBufferLoaded = 0;
- result = FIO_decompressDstFile(fCtx, prefs, ress, srcFile, dstFileName, srcFileName);
+ /* Don't use AsyncIO for small files */
+ if (strcmp(srcFileName, stdinmark)) /* Stdin doesn't have stats */
+ fileSize = UTIL_getFileSizeStat(&srcFileStat);
+ if(fileSize != UTIL_FILESIZE_UNKNOWN && fileSize < ZSTD_BLOCKSIZE_MAX * 3) {
+ AIO_ReadPool_setAsync(ress.readCtx, 0);
+ AIO_WritePool_setAsync(ress.writeCtx, 0);
+ } else {
+ AIO_ReadPool_setAsync(ress.readCtx, 1);
+ AIO_WritePool_setAsync(ress.writeCtx, 1);
+ }
+
+ AIO_ReadPool_setFile(ress.readCtx, srcFile);
+
+ result = FIO_decompressDstFile(fCtx, prefs, ress, dstFileName, srcFileName, &srcFileStat);
+
+ AIO_ReadPool_setFile(ress.readCtx, NULL);
/* Close file */
if (fclose(srcFile)) {
@@ -2788,6 +2712,11 @@ FIO_determineDstName(const char* srcFileName, const char* outDirName)
size_t srcSuffixLen;
const char* const srcSuffix = strrchr(srcFileName, '.');
+
+ if(!strcmp(srcFileName, stdinmark)) {
+ return stdoutmark;
+ }
+
if (srcSuffix == NULL) {
DISPLAYLEVEL(1,
"zstd: %s: unknown suffix (%s expected). "
@@ -2869,20 +2798,21 @@ FIO_decompressMultipleFilenames(FIO_ctx_t* const fCtx,
dRess_t ress = FIO_createDResources(prefs, dictFileName);
if (outFileName) {
- if (FIO_removeMultiFilesWarning(fCtx, prefs, outFileName, 1 /* displayLevelCutoff */)) {
+ if (FIO_multiFilesConcatWarning(fCtx, prefs, outFileName, 1 /* displayLevelCutoff */)) {
FIO_freeDResources(ress);
return 1;
}
if (!prefs->testMode) {
- ress.dstFile = FIO_openDstFile(fCtx, prefs, NULL, outFileName, DEFAULT_FILE_PERMISSIONS);
- if (ress.dstFile == 0) EXM_THROW(19, "cannot open %s", outFileName);
+ FILE* dstFile = FIO_openDstFile(fCtx, prefs, NULL, outFileName, DEFAULT_FILE_PERMISSIONS);
+ if (dstFile == 0) EXM_THROW(19, "cannot open %s", outFileName);
+ AIO_WritePool_setFile(ress.writeCtx, dstFile);
}
for (; fCtx->currFileIdx < fCtx->nbFilesTotal; fCtx->currFileIdx++) {
status = FIO_decompressSrcFile(fCtx, prefs, ress, outFileName, srcNamesTable[fCtx->currFileIdx]);
if (!status) fCtx->nbFilesProcessed++;
error |= status;
}
- if ((!prefs->testMode) && (fclose(ress.dstFile)))
+ if ((!prefs->testMode) && (AIO_WritePool_closeFile(ress.writeCtx)))
EXM_THROW(72, "Write error : %s : cannot properly close output file",
strerror(errno));
} else {
@@ -2912,8 +2842,11 @@ FIO_decompressMultipleFilenames(FIO_ctx_t* const fCtx,
FIO_checkFilenameCollisions(srcNamesTable , (unsigned)fCtx->nbFilesTotal);
}
- if (fCtx->nbFilesProcessed >= 1 && fCtx->nbFilesTotal > 1 && fCtx->totalBytesOutput != 0)
- DISPLAYLEVEL(2, "%d files decompressed : %6zu bytes total \n", fCtx->nbFilesProcessed, fCtx->totalBytesOutput);
+ if (FIO_shouldDisplayMultipleFileSummary(fCtx)) {
+ DISPLAY_PROGRESS("\r%79s\r", "");
+ DISPLAY_SUMMARY("%d files decompressed : %6llu bytes total \n",
+ fCtx->nbFilesProcessed, (unsigned long long)fCtx->totalBytesOutput);
+ }
FIO_freeDResources(ress);
return error;
@@ -2931,7 +2864,9 @@ typedef struct {
int numSkippableFrames;
int decompUnavailable;
int usesCheck;
+ BYTE checksum[4];
U32 nbFiles;
+ unsigned dictID;
} fileInfo_t;
typedef enum {
@@ -2939,7 +2874,7 @@ typedef enum {
info_frame_error=1,
info_not_zstd=2,
info_file_error=3,
- info_truncated_input=4,
+ info_truncated_input=4
} InfoError;
#define ERROR_IF(c,n,...) { \
@@ -2986,6 +2921,12 @@ FIO_analyzeFrames(fileInfo_t* info, FILE* const srcFile)
}
ERROR_IF(ZSTD_getFrameHeader(&header, headerBuffer, numBytesRead) != 0,
info_frame_error, "Error: could not decode frame header");
+ if (info->dictID != 0 && info->dictID != header.dictID) {
+ DISPLAY("WARNING: File contains multiple frames with different dictionary IDs. Showing dictID 0 instead");
+ info->dictID = 0;
+ } else {
+ info->dictID = header.dictID;
+ }
info->windowSize = header.windowSize;
/* move to the end of the frame header */
{ size_t const headerSize = ZSTD_frameHeaderSize(headerBuffer, numBytesRead);
@@ -3018,8 +2959,8 @@ FIO_analyzeFrames(fileInfo_t* info, FILE* const srcFile)
int const contentChecksumFlag = (frameHeaderDescriptor & (1 << 2)) >> 2;
if (contentChecksumFlag) {
info->usesCheck = 1;
- ERROR_IF(fseek(srcFile, 4, SEEK_CUR) != 0,
- info_frame_error, "Error: could not skip past checksum");
+ ERROR_IF(fread(info->checksum, 1, 4, srcFile) != 4,
+ info_frame_error, "Error: could not read checksum");
} }
info->numActualFrames++;
}
@@ -3045,10 +2986,11 @@ static InfoError
getFileInfo_fileConfirmed(fileInfo_t* info, const char* inFileName)
{
InfoError status;
- FILE* const srcFile = FIO_openSrcFile(NULL, inFileName);
+ stat_t srcFileStat;
+ FILE* const srcFile = FIO_openSrcFile(NULL, inFileName, &srcFileStat);
ERROR_IF(srcFile == NULL, info_file_error, "Error: could not open source file %s", inFileName);
- info->compressedSize = UTIL_getFileSize(inFileName);
+ info->compressedSize = UTIL_getFileSizeStat(&srcFileStat);
status = FIO_analyzeFrames(info, srcFile);
fclose(srcFile);
@@ -3098,6 +3040,7 @@ displayInfo(const char* inFileName, const fileInfo_t* info, int displayLevel)
DISPLAYOUT("# Zstandard Frames: %d\n", info->numActualFrames);
if (info->numSkippableFrames)
DISPLAYOUT("# Skippable Frames: %d\n", info->numSkippableFrames);
+ DISPLAYOUT("DictID: %u\n", info->dictID);
DISPLAYOUT("Window Size: %.*f%s (%llu B)\n",
window_hrs.precision, window_hrs.value, window_hrs.suffix,
(unsigned long long)info->windowSize);
@@ -3110,7 +3053,16 @@ displayInfo(const char* inFileName, const fileInfo_t* info, int displayLevel)
(unsigned long long)info->decompressedSize);
DISPLAYOUT("Ratio: %.4f\n", ratio);
}
- DISPLAYOUT("Check: %s\n", checkString);
+
+ if (info->usesCheck && info->numActualFrames == 1) {
+ DISPLAYOUT("Check: %s %02x%02x%02x%02x\n", checkString,
+ info->checksum[3], info->checksum[2],
+ info->checksum[1], info->checksum[0]
+ );
+ } else {
+ DISPLAYOUT("Check: %s\n", checkString);
+ }
+
DISPLAYOUT("\n");
}
}
@@ -3174,7 +3126,7 @@ int FIO_listMultipleFiles(unsigned numFiles, const char** filenameTable, int dis
} }
if (numFiles == 0) {
- if (!IS_CONSOLE(stdin)) {
+ if (!UTIL_isConsole(stdin)) {
DISPLAYLEVEL(1, "zstd: --list does not support reading from standard input \n");
}
DISPLAYLEVEL(1, "No files given \n");