blob: 8a4dc96c1121248b8307de77cd255979ca312ece [file] [log] [blame]
/*
* Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
* All rights reserved.
*
* 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).
* You may select, at your option, one of the above-listed licenses.
*/
/* 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 */
#include <stdio.h> // printf
#include <stdlib.h> // free
#include <string.h> // memset, strcat
#include <zstd.h> // presumes zstd library is installed
#include "common.h" // Helper functions, CHECK(), and CHECK_ZSTD()
typedef struct {
void* buffIn;
void* buffOut;
size_t buffInSize;
size_t buffOutSize;
ZSTD_CCtx* cctx;
} resources;
static resources createResources_orDie(int cLevel)
{
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);
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) );
return ress;
}
static void freeResources(resources ress)
{
ZSTD_freeCCtx(ress.cctx);
free(ress.buffIn);
free(ress.buffOut);
}
static void compressFile_orDie(resources ress, const char* fname, const char* outName)
{
// Open the input and output files.
FILE* const fin = fopen_orDie(fname, "rb");
FILE* const fout = fopen_orDie(outName, "wb");
/* 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;
ZSTD_inBuffer input = { ress.buffIn, read, 0 };
int finished;
do {
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);
} while (!finished);
CHECK(input.pos == input.size,
"Impossible: zstd only returns 0 when the input is completely consumed!");
}
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;
}
int const cLevel = 7;
resources const ress = createResources_orDie(cLevel);
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");
compressFile_orDie(ress, ifn, ofnBuffer);
}
freeResources(ress);
free(ofnBuffer);
printf("compressed %i files \n", argc-1);
return 0;
}