| 1 | /* |
| 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. |
| 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 | |
| 23 | typedef struct { |
| 24 | void* buffIn; |
| 25 | void* buffOut; |
| 26 | size_t buffInSize; |
| 27 | size_t buffOutSize; |
| 28 | ZSTD_CCtx* cctx; |
| 29 | } resources; |
| 30 | |
| 31 | static 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 | |
| 50 | static void freeResources(resources ress) |
| 51 | { |
| 52 | ZSTD_freeCCtx(ress.cctx); |
| 53 | free(ress.buffIn); |
| 54 | free(ress.buffOut); |
| 55 | } |
| 56 | |
| 57 | static 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 | |
| 95 | int 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 | } |