1/*
2 * Copyright (c) Yann Collet, Facebook, Inc.
3 * All rights reserved.
4 *
5 * This source code is licensed under both the BSD-style license (found in the
6 * LICENSE file in the root directory of this source tree) and the GPLv2 (found
7 * in the COPYING file in the root directory of this source tree).
8 * You may select, at your option, one of the above-listed licenses.
9 */
10
11
12/* The objective of this example is to show of to compress multiple successive files
13*  while preserving memory management.
14*  All structures and buffers will be created only once,
15*  and shared across all compression operations */
16
17#include <stdio.h>     // printf
18#include <stdlib.h>    // free
19#include <string.h>    // memset, strcat
20#include <zstd.h>      // presumes zstd library is installed
21#include "common.h"    // Helper functions, CHECK(), and CHECK_ZSTD()
22
23typedef struct {
24    void* buffIn;
25    void* buffOut;
26    size_t buffInSize;
27    size_t buffOutSize;
28    ZSTD_CCtx* cctx;
29} resources;
30
31static resources createResources_orDie(int cLevel)
32{
33    resources ress;
34    ress.buffInSize = ZSTD_CStreamInSize();   /* can always read one full block */
35    ress.buffOutSize= ZSTD_CStreamOutSize();  /* can always flush a full block */
36    ress.buffIn = malloc_orDie(ress.buffInSize);
37    ress.buffOut= malloc_orDie(ress.buffOutSize);
38    ress.cctx = ZSTD_createCCtx();
39    CHECK(ress.cctx != NULL, "ZSTD_createCCtx() failed!");
40
41    /* Set any compression parameters you want here.
42     * They will persist for every compression operation.
43     * Here we set the compression level, and enable the checksum.
44     */
45    CHECK_ZSTD( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_compressionLevel, cLevel) );
46    CHECK_ZSTD( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_checksumFlag, 1) );
47    return ress;
48}
49
50static void freeResources(resources ress)
51{
52    ZSTD_freeCCtx(ress.cctx);
53    free(ress.buffIn);
54    free(ress.buffOut);
55}
56
57static void compressFile_orDie(resources ress, const char* fname, const char* outName)
58{
59    // Open the input and output files.
60    FILE* const fin  = fopen_orDie(fname, "rb");
61    FILE* const fout = fopen_orDie(outName, "wb");
62
63    /* Reset the context to a clean state to start a new compression operation.
64     * The parameters are sticky, so we keep the compression level and extra
65     * parameters that we set in createResources_orDie().
66     */
67    CHECK_ZSTD( ZSTD_CCtx_reset(ress.cctx, ZSTD_reset_session_only) );
68
69    size_t const toRead = ress.buffInSize;
70    size_t read;
71    while ( (read = fread_orDie(ress.buffIn, toRead, fin)) ) {
72        /* This loop is the same as streaming_compression.c.
73         * See that file for detailed comments.
74         */
75        int const lastChunk = (read < toRead);
76        ZSTD_EndDirective const mode = lastChunk ? ZSTD_e_end : ZSTD_e_continue;
77
78        ZSTD_inBuffer input = { ress.buffIn, read, 0 };
79        int finished;
80        do {
81            ZSTD_outBuffer output = { ress.buffOut, ress.buffOutSize, 0 };
82            size_t const remaining = ZSTD_compressStream2(ress.cctx, &output, &input, mode);
83            CHECK_ZSTD(remaining);
84            fwrite_orDie(ress.buffOut, output.pos, fout);
85            finished = lastChunk ? (remaining == 0) : (input.pos == input.size);
86        } while (!finished);
87        CHECK(input.pos == input.size,
88              "Impossible: zstd only returns 0 when the input is completely consumed!");
89    }
90
91    fclose_orDie(fout);
92    fclose_orDie(fin);
93}
94
95int main(int argc, const char** argv)
96{
97    const char* const exeName = argv[0];
98
99    if (argc<2) {
100        printf("wrong arguments\n");
101        printf("usage:\n");
102        printf("%s FILE(s)\n", exeName);
103        return 1;
104    }
105
106    int const cLevel = 7;
107    resources const ress = createResources_orDie(cLevel);
108    void* ofnBuffer = NULL;
109    size_t ofnbSize = 0;
110
111    int argNb;
112    for (argNb = 1; argNb < argc; argNb++) {
113        const char* const ifn = argv[argNb];
114        size_t const ifnSize = strlen(ifn);
115        size_t const ofnSize = ifnSize + 5;
116        if (ofnbSize <= ofnSize) {
117            ofnbSize = ofnSize + 16;
118            free(ofnBuffer);
119            ofnBuffer = malloc_orDie(ofnbSize);
120        }
121        memset(ofnBuffer, 0, ofnSize);
122        strcat(ofnBuffer, ifn);
123        strcat(ofnBuffer, ".zst");
124        compressFile_orDie(ress, ifn, ofnBuffer);
125    }
126
127    freeResources(ress);
128    free(ofnBuffer);
129
130    printf("compressed %i files \n", argc-1);
131
132    return 0;
133}
134