/* * Copyright (c) Meta Platforms, Inc. and affiliates. * 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. */ #include // fprintf #include // free #include // presumes zstd library is installed #include "common.h" // Helper functions, CHECK(), and CHECK_ZSTD() static void decompressFile_orDie(const char* fname) { FILE* const fin = fopen_orDie(fname, "rb"); size_t const buffInSize = ZSTD_DStreamInSize(); void* const buffIn = malloc_orDie(buffInSize); FILE* const fout = stdout; size_t const buffOutSize = ZSTD_DStreamOutSize(); /* Guarantee to successfully flush at least one complete compressed block in all circumstances. */ void* const buffOut = malloc_orDie(buffOutSize); ZSTD_DCtx* const dctx = ZSTD_createDCtx(); CHECK(dctx != NULL, "ZSTD_createDCtx() failed!"); /* This loop assumes that the input file is one or more concatenated zstd * streams. This example won't work if there is trailing non-zstd data at * the end, but streaming decompression in general handles this case. * ZSTD_decompressStream() returns 0 exactly when the frame is completed, * and doesn't consume input after the frame. */ size_t const toRead = buffInSize; size_t read; size_t lastRet = 0; int isEmpty = 1; while ( (read = fread_orDie(buffIn, toRead, fin)) ) { isEmpty = 0; ZSTD_inBuffer input = { buffIn, read, 0 }; /* Given a valid frame, zstd won't consume the last byte of the frame * until it has flushed all of the decompressed data of the frame. * Therefore, instead of checking if the return code is 0, we can * decompress just check if input.pos < input.size. */ while (input.pos < input.size) { ZSTD_outBuffer output = { buffOut, buffOutSize, 0 }; /* The return code is zero if the frame is complete, but there may * be multiple frames concatenated together. Zstd will automatically * reset the context when a frame is complete. Still, calling * ZSTD_DCtx_reset() can be useful to reset the context to a clean * state, for instance if the last decompression call returned an * error. */ size_t const ret = ZSTD_decompressStream(dctx, &output , &input); CHECK_ZSTD(ret); fwrite_orDie(buffOut, output.pos, fout); lastRet = ret; } } if (isEmpty) { fprintf(stderr, "input is empty\n"); exit(1); } if (lastRet != 0) { /* The last return value from ZSTD_decompressStream did not end on a * frame, but we reached the end of the file! We assume this is an * error, and the input was truncated. */ fprintf(stderr, "EOF before end of stream: %zu\n", lastRet); exit(1); } ZSTD_freeDCtx(dctx); fclose_orDie(fin); fclose_orDie(fout); free(buffIn); free(buffOut); } int main(int argc, const char** argv) { const char* const exeName = argv[0]; if (argc!=2) { fprintf(stderr, "wrong arguments\n"); fprintf(stderr, "usage:\n"); fprintf(stderr, "%s FILE\n", exeName); return 1; } const char* const inFilename = argv[1]; decompressFile_orDie(inFilename); return 0; }