1*3117ece4Schristos /* 2*3117ece4Schristos * Copyright (c) Martin Liska, SUSE, Meta Platforms, Inc. and affiliates. 3*3117ece4Schristos * All rights reserved. 4*3117ece4Schristos * 5*3117ece4Schristos * This source code is licensed under both the BSD-style license (found in the 6*3117ece4Schristos * LICENSE file in the root directory of this source tree) and the GPLv2 (found 7*3117ece4Schristos * in the COPYING file in the root directory of this source tree). 8*3117ece4Schristos * You may select, at your option, one of the above-listed licenses. 9*3117ece4Schristos */ 10*3117ece4Schristos 11*3117ece4Schristos 12*3117ece4Schristos #include <stdio.h> // printf 13*3117ece4Schristos #include <stdlib.h> // free 14*3117ece4Schristos #include <string.h> // memset, strcat, strlen 15*3117ece4Schristos #include <zstd.h> // presumes zstd library is installed 16*3117ece4Schristos #include "common.h" // Helper functions, CHECK(), and CHECK_ZSTD() 17*3117ece4Schristos #include <pthread.h> 18*3117ece4Schristos 19*3117ece4Schristos typedef struct compress_args 20*3117ece4Schristos { 21*3117ece4Schristos const char *fname; 22*3117ece4Schristos char *outName; 23*3117ece4Schristos int cLevel; 24*3117ece4Schristos #if defined(ZSTD_STATIC_LINKING_ONLY) 25*3117ece4Schristos ZSTD_threadPool *pool; 26*3117ece4Schristos #endif 27*3117ece4Schristos } compress_args_t; 28*3117ece4Schristos 29*3117ece4Schristos static void *compressFile_orDie(void *data) 30*3117ece4Schristos { 31*3117ece4Schristos const int nbThreads = 16; 32*3117ece4Schristos 33*3117ece4Schristos compress_args_t *args = (compress_args_t *)data; 34*3117ece4Schristos fprintf (stderr, "Starting compression of %s with level %d, using %d threads\n", args->fname, args->cLevel, nbThreads); 35*3117ece4Schristos /* Open the input and output files. */ 36*3117ece4Schristos FILE* const fin = fopen_orDie(args->fname, "rb"); 37*3117ece4Schristos FILE* const fout = fopen_orDie(args->outName, "wb"); 38*3117ece4Schristos /* Create the input and output buffers. 39*3117ece4Schristos * They may be any size, but we recommend using these functions to size them. 40*3117ece4Schristos * Performance will only suffer significantly for very tiny buffers. 41*3117ece4Schristos */ 42*3117ece4Schristos size_t const buffInSize = ZSTD_CStreamInSize(); 43*3117ece4Schristos void* const buffIn = malloc_orDie(buffInSize); 44*3117ece4Schristos size_t const buffOutSize = ZSTD_CStreamOutSize(); 45*3117ece4Schristos void* const buffOut = malloc_orDie(buffOutSize); 46*3117ece4Schristos 47*3117ece4Schristos /* Create the context. */ 48*3117ece4Schristos ZSTD_CCtx* const cctx = ZSTD_createCCtx(); 49*3117ece4Schristos CHECK(cctx != NULL, "ZSTD_createCCtx() failed!"); 50*3117ece4Schristos 51*3117ece4Schristos #if defined(ZSTD_STATIC_LINKING_ONLY) 52*3117ece4Schristos size_t r = ZSTD_CCtx_refThreadPool(cctx, args->pool); 53*3117ece4Schristos CHECK(r == 0, "ZSTD_CCtx_refThreadPool failed!"); 54*3117ece4Schristos #endif 55*3117ece4Schristos 56*3117ece4Schristos /* Set any parameters you want. 57*3117ece4Schristos * Here we set the compression level, and enable the checksum. 58*3117ece4Schristos */ 59*3117ece4Schristos CHECK_ZSTD( ZSTD_CCtx_setParameter(cctx, ZSTD_c_compressionLevel, args->cLevel) ); 60*3117ece4Schristos CHECK_ZSTD( ZSTD_CCtx_setParameter(cctx, ZSTD_c_checksumFlag, 1) ); 61*3117ece4Schristos ZSTD_CCtx_setParameter(cctx, ZSTD_c_nbWorkers, nbThreads); 62*3117ece4Schristos 63*3117ece4Schristos /* This loop reads from the input file, compresses that entire chunk, 64*3117ece4Schristos * and writes all output produced to the output file. 65*3117ece4Schristos */ 66*3117ece4Schristos size_t const toRead = buffInSize; 67*3117ece4Schristos for (;;) { 68*3117ece4Schristos size_t read = fread_orDie(buffIn, toRead, fin); 69*3117ece4Schristos /* Select the flush mode. 70*3117ece4Schristos * If the read may not be finished (read == toRead) we use 71*3117ece4Schristos * ZSTD_e_continue. If this is the last chunk, we use ZSTD_e_end. 72*3117ece4Schristos * Zstd optimizes the case where the first flush mode is ZSTD_e_end, 73*3117ece4Schristos * since it knows it is compressing the entire source in one pass. 74*3117ece4Schristos */ 75*3117ece4Schristos int const lastChunk = (read < toRead); 76*3117ece4Schristos ZSTD_EndDirective const mode = lastChunk ? ZSTD_e_end : ZSTD_e_continue; 77*3117ece4Schristos /* Set the input buffer to what we just read. 78*3117ece4Schristos * We compress until the input buffer is empty, each time flushing the 79*3117ece4Schristos * output. 80*3117ece4Schristos */ 81*3117ece4Schristos ZSTD_inBuffer input = { buffIn, read, 0 }; 82*3117ece4Schristos int finished; 83*3117ece4Schristos do { 84*3117ece4Schristos /* Compress into the output buffer and write all of the output to 85*3117ece4Schristos * the file so we can reuse the buffer next iteration. 86*3117ece4Schristos */ 87*3117ece4Schristos ZSTD_outBuffer output = { buffOut, buffOutSize, 0 }; 88*3117ece4Schristos size_t const remaining = ZSTD_compressStream2(cctx, &output , &input, mode); 89*3117ece4Schristos CHECK_ZSTD(remaining); 90*3117ece4Schristos fwrite_orDie(buffOut, output.pos, fout); 91*3117ece4Schristos /* If we're on the last chunk we're finished when zstd returns 0, 92*3117ece4Schristos * which means its consumed all the input AND finished the frame. 93*3117ece4Schristos * Otherwise, we're finished when we've consumed all the input. 94*3117ece4Schristos */ 95*3117ece4Schristos finished = lastChunk ? (remaining == 0) : (input.pos == input.size); 96*3117ece4Schristos } while (!finished); 97*3117ece4Schristos CHECK(input.pos == input.size, 98*3117ece4Schristos "Impossible: zstd only returns 0 when the input is completely consumed!"); 99*3117ece4Schristos 100*3117ece4Schristos if (lastChunk) { 101*3117ece4Schristos break; 102*3117ece4Schristos } 103*3117ece4Schristos } 104*3117ece4Schristos 105*3117ece4Schristos fprintf (stderr, "Finishing compression of %s\n", args->outName); 106*3117ece4Schristos 107*3117ece4Schristos ZSTD_freeCCtx(cctx); 108*3117ece4Schristos fclose_orDie(fout); 109*3117ece4Schristos fclose_orDie(fin); 110*3117ece4Schristos free(buffIn); 111*3117ece4Schristos free(buffOut); 112*3117ece4Schristos free(args->outName); 113*3117ece4Schristos 114*3117ece4Schristos return NULL; 115*3117ece4Schristos } 116*3117ece4Schristos 117*3117ece4Schristos 118*3117ece4Schristos static char* createOutFilename_orDie(const char* filename) 119*3117ece4Schristos { 120*3117ece4Schristos size_t const inL = strlen(filename); 121*3117ece4Schristos size_t const outL = inL + 5; 122*3117ece4Schristos void* const outSpace = malloc_orDie(outL); 123*3117ece4Schristos memset(outSpace, 0, outL); 124*3117ece4Schristos strcat(outSpace, filename); 125*3117ece4Schristos strcat(outSpace, ".zst"); 126*3117ece4Schristos return (char*)outSpace; 127*3117ece4Schristos } 128*3117ece4Schristos 129*3117ece4Schristos int main(int argc, const char** argv) 130*3117ece4Schristos { 131*3117ece4Schristos const char* const exeName = argv[0]; 132*3117ece4Schristos 133*3117ece4Schristos if (argc<=3) { 134*3117ece4Schristos printf("wrong arguments\n"); 135*3117ece4Schristos printf("usage:\n"); 136*3117ece4Schristos printf("%s POOL_SIZE LEVEL FILES\n", exeName); 137*3117ece4Schristos return 1; 138*3117ece4Schristos } 139*3117ece4Schristos 140*3117ece4Schristos int pool_size = atoi (argv[1]); 141*3117ece4Schristos CHECK(pool_size != 0, "can't parse POOL_SIZE!"); 142*3117ece4Schristos 143*3117ece4Schristos int level = atoi (argv[2]); 144*3117ece4Schristos CHECK(level != 0, "can't parse LEVEL!"); 145*3117ece4Schristos 146*3117ece4Schristos argc -= 3; 147*3117ece4Schristos argv += 3; 148*3117ece4Schristos 149*3117ece4Schristos #if defined(ZSTD_STATIC_LINKING_ONLY) 150*3117ece4Schristos ZSTD_threadPool *pool = ZSTD_createThreadPool (pool_size); 151*3117ece4Schristos CHECK(pool != NULL, "ZSTD_createThreadPool() failed!"); 152*3117ece4Schristos fprintf (stderr, "Using shared thread pool of size %d\n", pool_size); 153*3117ece4Schristos #else 154*3117ece4Schristos fprintf (stderr, "All threads use its own thread pool\n"); 155*3117ece4Schristos #endif 156*3117ece4Schristos 157*3117ece4Schristos pthread_t *threads = malloc_orDie(argc * sizeof(pthread_t)); 158*3117ece4Schristos compress_args_t *args = malloc_orDie(argc * sizeof(compress_args_t)); 159*3117ece4Schristos 160*3117ece4Schristos for (unsigned i = 0; i < argc; i++) 161*3117ece4Schristos { 162*3117ece4Schristos args[i].fname = argv[i]; 163*3117ece4Schristos args[i].outName = createOutFilename_orDie(args[i].fname); 164*3117ece4Schristos args[i].cLevel = level; 165*3117ece4Schristos #if defined(ZSTD_STATIC_LINKING_ONLY) 166*3117ece4Schristos args[i].pool = pool; 167*3117ece4Schristos #endif 168*3117ece4Schristos 169*3117ece4Schristos pthread_create (&threads[i], NULL, compressFile_orDie, &args[i]); 170*3117ece4Schristos } 171*3117ece4Schristos 172*3117ece4Schristos for (unsigned i = 0; i < argc; i++) 173*3117ece4Schristos pthread_join (threads[i], NULL); 174*3117ece4Schristos 175*3117ece4Schristos #if defined(ZSTD_STATIC_LINKING_ONLY) 176*3117ece4Schristos ZSTD_freeThreadPool (pool); 177*3117ece4Schristos #endif 178*3117ece4Schristos 179*3117ece4Schristos return 0; 180*3117ece4Schristos } 181