2017-08-29 16:24:11 +00:00
|
|
|
/*
|
2020-03-26 22:19:05 +00:00
|
|
|
* Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
|
2016-10-27 01:10:43 +00:00
|
|
|
* All rights reserved.
|
|
|
|
*
|
2017-08-29 16:24:11 +00:00
|
|
|
* This source code is licensed under both the BSD-style license (found in the
|
|
|
|
* LICENSE file in the root directory of this source tree) and the GPLv2 (found
|
|
|
|
* in the COPYING file in the root directory of this source tree).
|
2017-09-08 07:09:23 +00:00
|
|
|
* You may select, at your option, one of the above-listed licenses.
|
2016-10-27 01:10:43 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/* The objective of this example is to show of to compress multiple successive files
|
|
|
|
* while preserving memory management.
|
|
|
|
* All structures and buffers will be created only once,
|
|
|
|
* and shared across all compression operations */
|
|
|
|
|
2019-04-06 01:11:17 +00:00
|
|
|
#include <stdio.h> // printf
|
|
|
|
#include <stdlib.h> // free
|
|
|
|
#include <string.h> // memset, strcat
|
2016-10-27 01:10:43 +00:00
|
|
|
#include <zstd.h> // presumes zstd library is installed
|
2019-04-06 01:11:17 +00:00
|
|
|
#include "common.h" // Helper functions, CHECK(), and CHECK_ZSTD()
|
2016-10-27 01:10:43 +00:00
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
void* buffIn;
|
|
|
|
void* buffOut;
|
|
|
|
size_t buffInSize;
|
|
|
|
size_t buffOutSize;
|
2019-04-01 23:41:06 +00:00
|
|
|
ZSTD_CCtx* cctx;
|
|
|
|
} resources;
|
2016-10-27 01:10:43 +00:00
|
|
|
|
2019-04-01 23:41:06 +00:00
|
|
|
static resources createResources_orDie(int cLevel)
|
2016-10-27 01:10:43 +00:00
|
|
|
{
|
|
|
|
resources ress;
|
|
|
|
ress.buffInSize = ZSTD_CStreamInSize(); /* can always read one full block */
|
|
|
|
ress.buffOutSize= ZSTD_CStreamOutSize(); /* can always flush a full block */
|
|
|
|
ress.buffIn = malloc_orDie(ress.buffInSize);
|
|
|
|
ress.buffOut= malloc_orDie(ress.buffOutSize);
|
2019-04-01 23:41:06 +00:00
|
|
|
ress.cctx = ZSTD_createCCtx();
|
|
|
|
CHECK(ress.cctx != NULL, "ZSTD_createCCtx() failed!");
|
|
|
|
|
|
|
|
/* Set any compression parameters you want here.
|
|
|
|
* They will persist for every compression operation.
|
|
|
|
* Here we set the compression level, and enable the checksum.
|
|
|
|
*/
|
|
|
|
CHECK_ZSTD( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_compressionLevel, cLevel) );
|
|
|
|
CHECK_ZSTD( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_checksumFlag, 1) );
|
2016-10-27 01:10:43 +00:00
|
|
|
return ress;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void freeResources(resources ress)
|
|
|
|
{
|
2019-04-01 23:41:06 +00:00
|
|
|
ZSTD_freeCCtx(ress.cctx);
|
2016-10-27 01:10:43 +00:00
|
|
|
free(ress.buffIn);
|
|
|
|
free(ress.buffOut);
|
|
|
|
}
|
|
|
|
|
2019-04-01 23:41:06 +00:00
|
|
|
static void compressFile_orDie(resources ress, const char* fname, const char* outName)
|
2016-10-27 01:10:43 +00:00
|
|
|
{
|
2019-04-01 23:41:06 +00:00
|
|
|
// Open the input and output files.
|
2016-10-27 01:10:43 +00:00
|
|
|
FILE* const fin = fopen_orDie(fname, "rb");
|
|
|
|
FILE* const fout = fopen_orDie(outName, "wb");
|
|
|
|
|
2019-04-01 23:41:06 +00:00
|
|
|
/* Reset the context to a clean state to start a new compression operation.
|
|
|
|
* The parameters are sticky, so we keep the compression level and extra
|
|
|
|
* parameters that we set in createResources_orDie().
|
|
|
|
*/
|
|
|
|
CHECK_ZSTD( ZSTD_CCtx_reset(ress.cctx, ZSTD_reset_session_only) );
|
|
|
|
|
|
|
|
size_t const toRead = ress.buffInSize;
|
|
|
|
size_t read;
|
|
|
|
while ( (read = fread_orDie(ress.buffIn, toRead, fin)) ) {
|
|
|
|
/* This loop is the same as streaming_compression.c.
|
|
|
|
* See that file for detailed comments.
|
|
|
|
*/
|
|
|
|
int const lastChunk = (read < toRead);
|
|
|
|
ZSTD_EndDirective const mode = lastChunk ? ZSTD_e_end : ZSTD_e_continue;
|
2016-10-27 01:10:43 +00:00
|
|
|
|
|
|
|
ZSTD_inBuffer input = { ress.buffIn, read, 0 };
|
2019-04-01 23:41:06 +00:00
|
|
|
int finished;
|
|
|
|
do {
|
2019-04-02 00:33:49 +00:00
|
|
|
ZSTD_outBuffer output = { ress.buffOut, ress.buffOutSize, 0 };
|
|
|
|
size_t const remaining = ZSTD_compressStream2(ress.cctx, &output, &input, mode);
|
|
|
|
CHECK_ZSTD(remaining);
|
|
|
|
fwrite_orDie(ress.buffOut, output.pos, fout);
|
|
|
|
finished = lastChunk ? (remaining == 0) : (input.pos == input.size);
|
2019-04-01 23:41:06 +00:00
|
|
|
} while (!finished);
|
2019-04-06 01:11:17 +00:00
|
|
|
CHECK(input.pos == input.size,
|
|
|
|
"Impossible: zstd only returns 0 when the input is completely consumed!");
|
2016-10-27 01:10:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fclose_orDie(fout);
|
|
|
|
fclose_orDie(fin);
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, const char** argv)
|
|
|
|
{
|
|
|
|
const char* const exeName = argv[0];
|
|
|
|
|
|
|
|
if (argc<2) {
|
|
|
|
printf("wrong arguments\n");
|
|
|
|
printf("usage:\n");
|
|
|
|
printf("%s FILE(s)\n", exeName);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2019-04-01 23:41:06 +00:00
|
|
|
int const cLevel = 7;
|
|
|
|
resources const ress = createResources_orDie(cLevel);
|
2016-10-27 01:10:43 +00:00
|
|
|
void* ofnBuffer = NULL;
|
|
|
|
size_t ofnbSize = 0;
|
|
|
|
|
|
|
|
int argNb;
|
|
|
|
for (argNb = 1; argNb < argc; argNb++) {
|
|
|
|
const char* const ifn = argv[argNb];
|
|
|
|
size_t const ifnSize = strlen(ifn);
|
|
|
|
size_t const ofnSize = ifnSize + 5;
|
|
|
|
if (ofnbSize <= ofnSize) {
|
|
|
|
ofnbSize = ofnSize + 16;
|
|
|
|
free(ofnBuffer);
|
|
|
|
ofnBuffer = malloc_orDie(ofnbSize);
|
|
|
|
}
|
|
|
|
memset(ofnBuffer, 0, ofnSize);
|
|
|
|
strcat(ofnBuffer, ifn);
|
|
|
|
strcat(ofnBuffer, ".zst");
|
2019-04-01 23:41:06 +00:00
|
|
|
compressFile_orDie(ress, ifn, ofnBuffer);
|
2016-10-27 01:10:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
freeResources(ress);
|
2018-08-16 21:40:47 +00:00
|
|
|
free(ofnBuffer);
|
|
|
|
|
2016-10-27 01:37:29 +00:00
|
|
|
printf("compressed %i files \n", argc-1);
|
|
|
|
|
2016-10-27 01:10:43 +00:00
|
|
|
return 0;
|
|
|
|
}
|