| /* |
| * LZ4 file library |
| * Copyright (C) 2022, Xiaomi Inc. |
| * |
| * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * - Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * - Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| * You can contact the author at : |
| * - LZ4 homepage : http://www.lz4.org |
| * - LZ4 source repository : https://github.com/lz4/lz4 |
| */ |
| #include <stdlib.h> /* malloc, free */ |
| #include <string.h> |
| #include <assert.h> |
| #include "lz4.h" |
| #include "lz4file.h" |
| |
| static LZ4F_errorCode_t returnErrorCode(LZ4F_errorCodes code) |
| { |
| return (LZ4F_errorCode_t)-(ptrdiff_t)code; |
| } |
| #undef RETURN_ERROR |
| #define RETURN_ERROR(e) return returnErrorCode(LZ4F_ERROR_ ## e) |
| |
| /* ===== read API ===== */ |
| |
| struct LZ4_readFile_s { |
| LZ4F_dctx* dctxPtr; |
| FILE* fp; |
| LZ4_byte* srcBuf; |
| size_t srcBufNext; |
| size_t srcBufSize; |
| size_t srcBufMaxSize; |
| }; |
| |
| static void LZ4F_freeReadFile(LZ4_readFile_t* lz4fRead) |
| { |
| if (lz4fRead==NULL) return; |
| LZ4F_freeDecompressionContext(lz4fRead->dctxPtr); |
| free(lz4fRead->srcBuf); |
| free(lz4fRead); |
| } |
| |
| static void LZ4F_freeAndNullReadFile(LZ4_readFile_t** statePtr) |
| { |
| assert(statePtr != NULL); |
| LZ4F_freeReadFile(*statePtr); |
| *statePtr = NULL; |
| } |
| |
| LZ4F_errorCode_t LZ4F_readOpen(LZ4_readFile_t** lz4fRead, FILE* fp) |
| { |
| char buf[LZ4F_HEADER_SIZE_MAX]; |
| size_t consumedSize; |
| LZ4F_errorCode_t ret; |
| |
| if (fp == NULL || lz4fRead == NULL) { |
| RETURN_ERROR(parameter_null); |
| } |
| |
| *lz4fRead = (LZ4_readFile_t*)calloc(1, sizeof(LZ4_readFile_t)); |
| if (*lz4fRead == NULL) { |
| RETURN_ERROR(allocation_failed); |
| } |
| |
| ret = LZ4F_createDecompressionContext(&(*lz4fRead)->dctxPtr, LZ4F_VERSION); |
| if (LZ4F_isError(ret)) { |
| LZ4F_freeAndNullReadFile(lz4fRead); |
| return ret; |
| } |
| |
| (*lz4fRead)->fp = fp; |
| consumedSize = fread(buf, 1, sizeof(buf), (*lz4fRead)->fp); |
| if (consumedSize < LZ4F_HEADER_SIZE_MIN + LZ4F_ENDMARK_SIZE) { |
| LZ4F_freeAndNullReadFile(lz4fRead); |
| RETURN_ERROR(io_read); |
| } |
| |
| { LZ4F_frameInfo_t info; |
| LZ4F_errorCode_t const r = LZ4F_getFrameInfo((*lz4fRead)->dctxPtr, &info, buf, &consumedSize); |
| if (LZ4F_isError(r)) { |
| LZ4F_freeAndNullReadFile(lz4fRead); |
| return r; |
| } |
| |
| switch (info.blockSizeID) { |
| case LZ4F_default : |
| case LZ4F_max64KB : |
| (*lz4fRead)->srcBufMaxSize = 64 * 1024; |
| break; |
| case LZ4F_max256KB: |
| (*lz4fRead)->srcBufMaxSize = 256 * 1024; |
| break; |
| case LZ4F_max1MB: |
| (*lz4fRead)->srcBufMaxSize = 1 * 1024 * 1024; |
| break; |
| case LZ4F_max4MB: |
| (*lz4fRead)->srcBufMaxSize = 4 * 1024 * 1024; |
| break; |
| default: |
| LZ4F_freeAndNullReadFile(lz4fRead); |
| RETURN_ERROR(maxBlockSize_invalid); |
| } |
| } |
| |
| (*lz4fRead)->srcBuf = (LZ4_byte*)malloc((*lz4fRead)->srcBufMaxSize); |
| if ((*lz4fRead)->srcBuf == NULL) { |
| LZ4F_freeAndNullReadFile(lz4fRead); |
| RETURN_ERROR(allocation_failed); |
| } |
| |
| (*lz4fRead)->srcBufSize = sizeof(buf) - consumedSize; |
| memcpy((*lz4fRead)->srcBuf, buf + consumedSize, (*lz4fRead)->srcBufSize); |
| |
| return ret; |
| } |
| |
| size_t LZ4F_read(LZ4_readFile_t* lz4fRead, void* buf, size_t size) |
| { |
| LZ4_byte* p = (LZ4_byte*)buf; |
| size_t next = 0; |
| |
| if (lz4fRead == NULL || buf == NULL) |
| RETURN_ERROR(parameter_null); |
| |
| while (next < size) { |
| size_t srcsize = lz4fRead->srcBufSize - lz4fRead->srcBufNext; |
| size_t dstsize = size - next; |
| size_t ret; |
| |
| if (srcsize == 0) { |
| ret = fread(lz4fRead->srcBuf, 1, lz4fRead->srcBufMaxSize, lz4fRead->fp); |
| if (ret > 0) { |
| lz4fRead->srcBufSize = ret; |
| srcsize = lz4fRead->srcBufSize; |
| lz4fRead->srcBufNext = 0; |
| } else if (ret == 0) { |
| break; |
| } else { |
| RETURN_ERROR(io_read); |
| } |
| } |
| |
| ret = LZ4F_decompress(lz4fRead->dctxPtr, |
| p, &dstsize, |
| lz4fRead->srcBuf + lz4fRead->srcBufNext, |
| &srcsize, |
| NULL); |
| if (LZ4F_isError(ret)) { |
| return ret; |
| } |
| |
| lz4fRead->srcBufNext += srcsize; |
| next += dstsize; |
| p += dstsize; |
| } |
| |
| return next; |
| } |
| |
| LZ4F_errorCode_t LZ4F_readClose(LZ4_readFile_t* lz4fRead) |
| { |
| if (lz4fRead == NULL) |
| RETURN_ERROR(parameter_null); |
| LZ4F_freeReadFile(lz4fRead); |
| return LZ4F_OK_NoError; |
| } |
| |
| /* ===== write API ===== */ |
| |
| struct LZ4_writeFile_s { |
| LZ4F_cctx* cctxPtr; |
| FILE* fp; |
| LZ4_byte* dstBuf; |
| size_t maxWriteSize; |
| size_t dstBufMaxSize; |
| LZ4F_errorCode_t errCode; |
| }; |
| |
| static void LZ4F_freeWriteFile(LZ4_writeFile_t* state) |
| { |
| if (state == NULL) return; |
| LZ4F_freeCompressionContext(state->cctxPtr); |
| free(state->dstBuf); |
| free(state); |
| } |
| |
| static void LZ4F_freeAndNullWriteFile(LZ4_writeFile_t** statePtr) |
| { |
| assert(statePtr != NULL); |
| LZ4F_freeWriteFile(*statePtr); |
| *statePtr = NULL; |
| } |
| |
| LZ4F_errorCode_t LZ4F_writeOpen(LZ4_writeFile_t** lz4fWrite, FILE* fp, const LZ4F_preferences_t* prefsPtr) |
| { |
| LZ4_byte buf[LZ4F_HEADER_SIZE_MAX]; |
| size_t ret; |
| |
| if (fp == NULL || lz4fWrite == NULL) |
| RETURN_ERROR(parameter_null); |
| |
| *lz4fWrite = (LZ4_writeFile_t*)calloc(1, sizeof(LZ4_writeFile_t)); |
| if (*lz4fWrite == NULL) { |
| RETURN_ERROR(allocation_failed); |
| } |
| if (prefsPtr != NULL) { |
| switch (prefsPtr->frameInfo.blockSizeID) { |
| case LZ4F_default : |
| case LZ4F_max64KB : |
| (*lz4fWrite)->maxWriteSize = 64 * 1024; |
| break; |
| case LZ4F_max256KB: |
| (*lz4fWrite)->maxWriteSize = 256 * 1024; |
| break; |
| case LZ4F_max1MB: |
| (*lz4fWrite)->maxWriteSize = 1 * 1024 * 1024; |
| break; |
| case LZ4F_max4MB: |
| (*lz4fWrite)->maxWriteSize = 4 * 1024 * 1024; |
| break; |
| default: |
| LZ4F_freeAndNullWriteFile(lz4fWrite); |
| RETURN_ERROR(maxBlockSize_invalid); |
| } |
| } else { |
| (*lz4fWrite)->maxWriteSize = 64 * 1024; |
| } |
| |
| (*lz4fWrite)->dstBufMaxSize = LZ4F_compressBound((*lz4fWrite)->maxWriteSize, prefsPtr); |
| (*lz4fWrite)->dstBuf = (LZ4_byte*)malloc((*lz4fWrite)->dstBufMaxSize); |
| if ((*lz4fWrite)->dstBuf == NULL) { |
| LZ4F_freeAndNullWriteFile(lz4fWrite); |
| RETURN_ERROR(allocation_failed); |
| } |
| |
| ret = LZ4F_createCompressionContext(&(*lz4fWrite)->cctxPtr, LZ4F_VERSION); |
| if (LZ4F_isError(ret)) { |
| LZ4F_freeAndNullWriteFile(lz4fWrite); |
| return ret; |
| } |
| |
| ret = LZ4F_compressBegin((*lz4fWrite)->cctxPtr, buf, LZ4F_HEADER_SIZE_MAX, prefsPtr); |
| if (LZ4F_isError(ret)) { |
| LZ4F_freeAndNullWriteFile(lz4fWrite); |
| return ret; |
| } |
| |
| if (ret != fwrite(buf, 1, ret, fp)) { |
| LZ4F_freeAndNullWriteFile(lz4fWrite); |
| RETURN_ERROR(io_write); |
| } |
| |
| (*lz4fWrite)->fp = fp; |
| (*lz4fWrite)->errCode = LZ4F_OK_NoError; |
| return LZ4F_OK_NoError; |
| } |
| |
| size_t LZ4F_write(LZ4_writeFile_t* lz4fWrite, const void* buf, size_t size) |
| { |
| const LZ4_byte* p = (const LZ4_byte*)buf; |
| size_t remain = size; |
| size_t chunk; |
| size_t ret; |
| |
| if (lz4fWrite == NULL || buf == NULL) |
| RETURN_ERROR(parameter_null); |
| while (remain) { |
| if (remain > lz4fWrite->maxWriteSize) |
| chunk = lz4fWrite->maxWriteSize; |
| else |
| chunk = remain; |
| |
| ret = LZ4F_compressUpdate(lz4fWrite->cctxPtr, |
| lz4fWrite->dstBuf, lz4fWrite->dstBufMaxSize, |
| p, chunk, |
| NULL); |
| if (LZ4F_isError(ret)) { |
| lz4fWrite->errCode = ret; |
| return ret; |
| } |
| |
| if (ret != fwrite(lz4fWrite->dstBuf, 1, ret, lz4fWrite->fp)) { |
| lz4fWrite->errCode = returnErrorCode(LZ4F_ERROR_io_write); |
| RETURN_ERROR(io_write); |
| } |
| |
| p += chunk; |
| remain -= chunk; |
| } |
| |
| return size; |
| } |
| |
| LZ4F_errorCode_t LZ4F_writeClose(LZ4_writeFile_t* lz4fWrite) |
| { |
| LZ4F_errorCode_t ret = LZ4F_OK_NoError; |
| |
| if (lz4fWrite == NULL) { |
| RETURN_ERROR(parameter_null); |
| } |
| |
| if (lz4fWrite->errCode == LZ4F_OK_NoError) { |
| ret = LZ4F_compressEnd(lz4fWrite->cctxPtr, |
| lz4fWrite->dstBuf, lz4fWrite->dstBufMaxSize, |
| NULL); |
| if (LZ4F_isError(ret)) { |
| goto out; |
| } |
| |
| if (ret != fwrite(lz4fWrite->dstBuf, 1, ret, lz4fWrite->fp)) { |
| ret = returnErrorCode(LZ4F_ERROR_io_write); |
| } |
| } |
| |
| out: |
| LZ4F_freeWriteFile(lz4fWrite); |
| return ret; |
| } |