From 49b073905035c80fc285c2a8380202755e305457 Mon Sep 17 00:00:00 2001 From: emmanuel-marty Date: Fri, 3 May 2019 19:17:14 +0200 Subject: [PATCH] Isolate frame implementation details --- Makefile | 1 + VS2017/lzsa.vcxproj | 2 + VS2017/lzsa.vcxproj.filters | 6 ++ src/frame.c | 177 ++++++++++++++++++++++++++++++++++++ src/frame.h | 104 +++++++++++++++++++++ src/main.c | 123 ++++++++++++------------- 6 files changed, 348 insertions(+), 65 deletions(-) create mode 100644 src/frame.c create mode 100644 src/frame.h diff --git a/Makefile b/Makefile index 658df84..c04753b 100755 --- a/Makefile +++ b/Makefile @@ -11,6 +11,7 @@ $(OBJDIR)/%.o: src/../%.c APP := lzsa OBJS := $(OBJDIR)/src/main.o +OBJS += $(OBJDIR)/src/frame.o OBJS += $(OBJDIR)/src/shrink.o OBJS += $(OBJDIR)/src/expand.o OBJS += $(OBJDIR)/src/libdivsufsort/lib/divsufsort.o diff --git a/VS2017/lzsa.vcxproj b/VS2017/lzsa.vcxproj index eb13c6d..8461495 100755 --- a/VS2017/lzsa.vcxproj +++ b/VS2017/lzsa.vcxproj @@ -179,6 +179,7 @@ + @@ -187,6 +188,7 @@ + diff --git a/VS2017/lzsa.vcxproj.filters b/VS2017/lzsa.vcxproj.filters index 47ea518..6767357 100755 --- a/VS2017/lzsa.vcxproj.filters +++ b/VS2017/lzsa.vcxproj.filters @@ -45,6 +45,9 @@ Fichiers sources\libdivsufsort\include + + Fichiers sources + @@ -68,5 +71,8 @@ Fichiers sources\libdivsufsort\lib + + Fichiers sources + \ No newline at end of file diff --git a/src/frame.c b/src/frame.c new file mode 100644 index 0000000..ba99904 --- /dev/null +++ b/src/frame.c @@ -0,0 +1,177 @@ +/* + * frame.c - frame implementation + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include +#include "frame.h" +#include "shrink.h" + +#define LZSA_ID_0 0x7b +#define LZSA_ID_1 0x9e + +/** + * Get compressed file header size + * + * @return file header size + */ +int lzsa_get_header_size(void) { + return 3; +} + +/** + * Get compressed frame header size + * + * @return frame header size + */ +int lzsa_get_frame_size(void) { + return 3; +} + +/** + * Encode file header + * + * @param pFrameData encoding buffer + * @param nMaxFrameDataSize max encoding buffer size, in bytes + * + * @return number of encoded bytes, or -1 for failure + */ +int lzsa_encode_header(unsigned char *pFrameData, const int nMaxFrameDataSize) { + if (nMaxFrameDataSize >= 3) { + pFrameData[0] = LZSA_ID_0; /* Magic number */ + pFrameData[1] = LZSA_ID_1; + pFrameData[2] = 0; /* Format version 1 */ + + return 3; + } + else { + return -1; + } +} + +/** + * Encode compressed block frame header + * + * @param pFrameData encoding buffer + * @param nMaxFrameDataSize max encoding buffer size, in bytes + * @param nBlockDataSize compressed block's data size, in bytes + * + * @return number of encoded bytes, or -1 for failure + */ +int lzsa_encode_compressed_block_frame(unsigned char *pFrameData, const int nMaxFrameDataSize, const int nBlockDataSize) { + if (nMaxFrameDataSize >= 3 && nBlockDataSize <= 0x7fffff) { + pFrameData[0] = nBlockDataSize & 0xff; + pFrameData[1] = (nBlockDataSize >> 8) & 0xff; + pFrameData[2] = (nBlockDataSize >> 16) & 0x7f; + + return 3; + } + else { + return -1; + } +} + +/** + * Encode uncompressed block frame header + * + * @param pFrameData encoding buffer + * @param nMaxFrameDataSize max encoding buffer size, in bytes + * @param nBlockDataSize uncompressed block's data size, in bytes + * + * @return number of encoded bytes, or -1 for failure + */ +int lzsa_encode_uncompressed_block_frame(unsigned char *pFrameData, const int nMaxFrameDataSize, const int nBlockDataSize) { + if (nMaxFrameDataSize >= 3 && nBlockDataSize <= 0x7fffff) { + pFrameData[0] = nBlockDataSize & 0xff; + pFrameData[1] = (nBlockDataSize >> 8) & 0xff; + pFrameData[2] = ((nBlockDataSize >> 16) & 0x7f) | 0x80; /* Uncompressed block */ + + return 3; + } + else { + return -1; + } +} + +/** + * Encode terminal frame header + * + * @param pFrameData encoding buffer + * @param nMaxFrameDataSize max encoding buffer size, in bytes + * + * @return number of encoded bytes, or -1 for failure + */ +int lzsa_encode_footer_frame(unsigned char *pFrameData, const int nMaxFrameDataSize) { + if (nMaxFrameDataSize >= 3) { + pFrameData[0] = 0x00; /* EOD frame */ + pFrameData[1] = 0x00; + pFrameData[2] = 0x00; + + return 3; + } + else { + return -1; + } +} + +/** + * Decode file header + * + * @param pFrameData data bytes + * @param nFrameDataSize number of bytes to decode + * + * @return 0 for success, or -1 for failure + */ +int lzsa_decode_header(const unsigned char *pFrameData, const int nFrameDataSize) { + if (nFrameDataSize != 3 || + pFrameData[0] != LZSA_ID_0 || + pFrameData[1] != LZSA_ID_1 || + pFrameData[2] != 0) { + return -1; + } + else { + return 0; + } +} + +/** + * Decode frame header + * + * @param pFrameData data bytes + * @param nFrameDataSize number of bytes to decode + * @param nBlockSize pointer to block size, updated if this function succeeds (set to 0 if this is the terminal frame) + * @param nIsUncompressed pointer to compressed block flag, updated if this function succeeds + * + * @return 0 for success, or -1 for failure + */ +int lzsa_decode_frame(const unsigned char *pFrameData, const int nFrameDataSize, unsigned int *nBlockSize, int *nIsUncompressed) { + if (nFrameDataSize == 3) { + *nBlockSize = ((unsigned int)pFrameData[0]) | + (((unsigned int)pFrameData[1]) << 8) | + (((unsigned int)pFrameData[2]) << 16); + + *nIsUncompressed = ((*nBlockSize & 0x800000) != 0) ? 1 : 0; + *nBlockSize &= 0x7fffff; + return 0; + } + else { + return -1; + } +} diff --git a/src/frame.h b/src/frame.h new file mode 100644 index 0000000..e858110 --- /dev/null +++ b/src/frame.h @@ -0,0 +1,104 @@ +/* + * frame.h - frame definitions + * + * Copyright (C) 2019 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#ifndef _FRAME_H +#define _FRAME_H + +/** + * Get compressed file header size + * + * @return file header size + */ +int lzsa_get_header_size(void); + +/** + * Get compressed frame header size + * + * @return frame header size + */ +int lzsa_get_frame_size(void); + +/** + * Encode file header + * + * @param pFrameData encoding buffer + * @param nMaxFrameDataSize max encoding buffer size, in bytes + * + * @return number of encoded bytes, or -1 for failure + */ +int lzsa_encode_header(unsigned char *pFrameData, const int nMaxFrameDataSize); + +/** + * Encode compressed block frame header + * + * @param pFrameData encoding buffer + * @param nMaxFrameDataSize max encoding buffer size, in bytes + * @param nBlockDataSize compressed block's data size, in bytes + * + * @return number of encoded bytes, or -1 for failure + */ +int lzsa_encode_compressed_block_frame(unsigned char *pFrameData, const int nMaxFrameDataSize, const int nBlockDataSize); + +/** + * Encode uncompressed block frame header + * + * @param pFrameData encoding buffer + * @param nMaxFrameDataSize max encoding buffer size, in bytes + * @param nBlockDataSize uncompressed block's data size, in bytes + * + * @return number of encoded bytes, or -1 for failure + */ +int lzsa_encode_uncompressed_block_frame(unsigned char *pFrameData, const int nMaxFrameDataSize, const int nBlockDataSize); + +/** + * Encode terminal frame header + * + * @param pFrameData encoding buffer + * @param nMaxFrameDataSize max encoding buffer size, in bytes + * + * @return number of encoded bytes, or -1 for failure + */ +int lzsa_encode_footer_frame(unsigned char *pFrameData, const int nMaxFrameDataSize); + +/** + * Decode file header + * + * @param pFrameData data bytes + * @param nFrameDataSize number of bytes to decode + * + * @return 0 for success, or -1 for failure + */ +int lzsa_decode_header(const unsigned char *pFrameData, const int nFrameDataSize); + +/** + * Decode frame header + * + * @param pFrameData data bytes + * @param nFrameDataSize number of bytes to decode + * @param nBlockSize pointer to block size, updated if this function succeeds (set to 0 if this is the terminal frame) + * @param nIsUncompressed pointer to compressed block flag, updated if this function succeeds + * + * @return 0 for success, or -1 for failure + */ +int lzsa_decode_frame(const unsigned char *pFrameData, const int nFrameDataSize, unsigned int *nBlockSize, int *nIsUncompressed); + +#endif /* _FRAME_H */ diff --git a/src/main.c b/src/main.c index 0bbbdd1..e52dba8 100755 --- a/src/main.c +++ b/src/main.c @@ -30,6 +30,7 @@ #include #endif #include "format.h" +#include "frame.h" #include "shrink.h" #include "expand.h" @@ -67,6 +68,7 @@ static int lzsa_compress(const char *pszInFilename, const char *pszOutFilename, long long nOriginalSize = 0LL, nCompressedSize = 0LL; int nFlags; int nResult; + unsigned char cFrameData[16]; bool bError = false; f_in = fopen(pszInFilename, "rb"); @@ -163,14 +165,13 @@ static int lzsa_compress(const char *pszInFilename, const char *pszOutFilename, } if ((nOptions & OPT_RAW) == 0) { - unsigned char cHeader[3]; - - cHeader[0] = 0x7b; /* Magic number: 0x9e7b */ - cHeader[1] = 0x9e; - cHeader[2] = 0; /* Format version 1 */ - - bError = fwrite(cHeader, 1, 3, f_out) != 3; - nCompressedSize += 3LL; + int nHeaderSize = lzsa_encode_header(cFrameData, 16); + if (nHeaderSize < 0) + bError = true; + else { + bError = fwrite(cFrameData, 1, nHeaderSize, f_out) != nHeaderSize; + nCompressedSize += (long long)nHeaderSize; + } } if (nOptions & OPT_VERBOSE) { @@ -205,15 +206,14 @@ static int lzsa_compress(const char *pszInFilename, const char *pszOutFilename, /* Write compressed block */ if ((nOptions & OPT_RAW) == 0) { - unsigned char cBlockSize[3]; - - cBlockSize[0] = nOutDataSize & 0xff; - cBlockSize[1] = (nOutDataSize >> 8) & 0xff; - cBlockSize[2] = (nOutDataSize >> 16) & 0xff; - nCompressedSize += 3LL; - - if (fwrite(cBlockSize, 1, 3, f_out) != (size_t)3) { + int nBlockheaderSize = lzsa_encode_compressed_block_frame(cFrameData, 16, nOutDataSize); + if (nBlockheaderSize < 0) bError = true; + else { + nCompressedSize += (long long)nBlockheaderSize; + if (fwrite(cFrameData, 1, nBlockheaderSize, f_out) != (size_t)nBlockheaderSize) { + bError = true; + } } } @@ -236,22 +236,21 @@ static int lzsa_compress(const char *pszInFilename, const char *pszOutFilename, break; } - unsigned char cBlockSize[3]; - - cBlockSize[0] = nInDataSize & 0xff; - cBlockSize[1] = (nInDataSize >> 8) & 0xff; - cBlockSize[2] = ((nInDataSize >> 16) & 0x7f) | 0x80; /* Uncompressed block */ - - if (fwrite(cBlockSize, 1, 3, f_out) != (size_t)3) { + int nBlockheaderSize = lzsa_encode_uncompressed_block_frame(cFrameData, 16, nInDataSize); + if (nBlockheaderSize < 0) bError = true; - } else { - if (fwrite(pInData + BLOCK_SIZE, 1, (size_t)nInDataSize, f_out) != (size_t)nInDataSize) { + if (fwrite(cFrameData, 1, nBlockheaderSize, f_out) != (size_t)nBlockheaderSize) { bError = true; } else { - nOriginalSize += (long long)nInDataSize; - nCompressedSize += 3LL + (long long)nInDataSize; + if (fwrite(pInData + BLOCK_SIZE, 1, (size_t)nInDataSize, f_out) != (size_t)nInDataSize) { + bError = true; + } + else { + nOriginalSize += (long long)nInDataSize; + nCompressedSize += (long long)nBlockheaderSize + (long long)nInDataSize; + } } } } @@ -265,21 +264,19 @@ static int lzsa_compress(const char *pszInFilename, const char *pszOutFilename, } } - unsigned char cFooter[4]; int nFooterSize; if ((nOptions & OPT_RAW) != 0) { nFooterSize = 0; } else { - cFooter[0] = 0x00; /* EOD frame */ - cFooter[1] = 0x00; - cFooter[2] = 0x00; - nFooterSize = 3; + nFooterSize = lzsa_encode_footer_frame(cFrameData, 16); + if (nFooterSize < 0) + bError = true; } if (!bError) - bError = fwrite(cFooter, 1, nFooterSize, f_out) != nFooterSize; + bError = fwrite(cFrameData, 1, nFooterSize, f_out) != nFooterSize; nCompressedSize += (long long)nFooterSize; if (!bError && (nOptions & OPT_VERBOSE)) { @@ -322,6 +319,7 @@ static int lzsa_decompress(const char *pszInFilename, const char *pszOutFilename long long nStartTime = 0LL, nEndTime = 0LL; long long nOriginalSize = 0LL; unsigned int nFileSize = 0; + unsigned char cFrameData[16]; FILE *pInFile = fopen(pszInFilename, "rb"); if (!pInFile) { @@ -330,20 +328,17 @@ static int lzsa_decompress(const char *pszInFilename, const char *pszOutFilename } if ((nOptions & OPT_RAW) == 0) { - unsigned char cHeader[3]; + const int nHeaderSize = lzsa_get_header_size(); - memset(cHeader, 0, 3); - - if (fread(cHeader, 1, 3, pInFile) != 3) { + memset(cFrameData, 0, 16); + if (fread(cFrameData, 1, nHeaderSize, pInFile) != nHeaderSize) { fclose(pInFile); pInFile = NULL; fprintf(stderr, "error reading header in input file\n"); return 100; } - if (cHeader[0] != 0x7b || - cHeader[1] != 0x9e || - cHeader[2] != 0) { + if (lzsa_decode_header(cFrameData, nHeaderSize) < 0) { fclose(pInFile); pInFile = NULL; fprintf(stderr, "invalid magic number or format version in input file\n"); @@ -440,19 +435,21 @@ static int lzsa_decompress(const char *pszInFilename, const char *pszOutFilename while (!feof(pInFile) && !nDecompressionError) { unsigned int nBlockSize = 0; + int nIsUncompressed = 0; if (nPrevDecompressedSize != 0) { memcpy(pOutData + BLOCK_SIZE - nPrevDecompressedSize, pOutData + BLOCK_SIZE, nPrevDecompressedSize); } if ((nOptions & OPT_RAW) == 0) { - unsigned char cBlockSize[3]; + const int nFrameSize = lzsa_get_frame_size(); - memset(cBlockSize, 0, 3); - if (fread(cBlockSize, 1, 3, pInFile) == 3) { - nBlockSize = ((unsigned int)cBlockSize[0]) | - (((unsigned int)cBlockSize[1]) << 8) | - (((unsigned int)cBlockSize[2]) << 16); + memset(cFrameData, 0, 16); + if (fread(cFrameData, 1, nFrameSize, pInFile) == nFrameSize) { + if (lzsa_decode_frame(cFrameData, nFrameSize, &nBlockSize, &nIsUncompressed) < 0) { + nDecompressionError = 1; + nBlockSize = 0; + } } else { nBlockSize = 0; @@ -465,16 +462,14 @@ static int lzsa_decompress(const char *pszInFilename, const char *pszOutFilename } if (nBlockSize != 0) { - bool bIsUncompressed = (nBlockSize & 0x800000) != 0; int nDecompressedSize = 0; - nBlockSize &= 0x7fffff; if ((int)nBlockSize > BLOCK_SIZE) { fprintf(stderr, "block size %d > max size %d\n", nBlockSize, BLOCK_SIZE); break; } if (fread(pInBlock, 1, nBlockSize, pInFile) == nBlockSize) { - if (bIsUncompressed) { + if (nIsUncompressed) { memcpy(pOutData + BLOCK_SIZE, pInBlock, nBlockSize); nDecompressedSize = nBlockSize; } @@ -539,6 +534,7 @@ static int lzsa_compare(const char *pszInFilename, const char *pszOutFilename, c long long nOriginalSize = 0LL; long long nKnownGoodSize = 0LL; unsigned int nFileSize = 0; + unsigned char cFrameData[16]; FILE *pInFile = fopen(pszInFilename, "rb"); if (!pInFile) { @@ -547,20 +543,17 @@ static int lzsa_compare(const char *pszInFilename, const char *pszOutFilename, c } if ((nOptions & OPT_RAW) == 0) { - unsigned char cHeader[3]; + const int nHeaderSize = lzsa_get_header_size(); - memset(cHeader, 0, 3); - - if (fread(cHeader, 1, 3, pInFile) != 3) { + memset(cFrameData, 0, 16); + if (fread(cFrameData, 1, nHeaderSize, pInFile) != nHeaderSize) { fclose(pInFile); pInFile = NULL; fprintf(stderr, "error reading header in compressed input file\n"); return 100; } - if (cHeader[0] != 0x7b || - cHeader[1] != 0x9e || - cHeader[2] != 0) { + if (lzsa_decode_header(cFrameData, nHeaderSize) < 0) { fclose(pInFile); pInFile = NULL; fprintf(stderr, "invalid magic number or format version in input file\n"); @@ -679,6 +672,7 @@ static int lzsa_compare(const char *pszInFilename, const char *pszOutFilename, c while (!feof(pInFile) && !nDecompressionError && !bComparisonError) { unsigned int nBlockSize = 0; + int nIsUncompressed = 0; if (nPrevDecompressedSize != 0) { memcpy(pOutData + BLOCK_SIZE - nPrevDecompressedSize, pOutData + BLOCK_SIZE, nPrevDecompressedSize); @@ -687,13 +681,14 @@ static int lzsa_compare(const char *pszInFilename, const char *pszOutFilename, c int nBytesToCompare = (int)fread(pCompareData, 1, BLOCK_SIZE, pOutFile); if ((nOptions & OPT_RAW) == 0) { - unsigned char cBlockSize[3]; + const int nFrameSize = lzsa_get_frame_size(); - memset(cBlockSize, 0, 3); - if (fread(cBlockSize, 1, 3, pInFile) == 3) { - nBlockSize = ((unsigned int)cBlockSize[0]) | - (((unsigned int)cBlockSize[1]) << 8) | - (((unsigned int)cBlockSize[2]) << 16); + memset(cFrameData, 0, 16); + if (fread(cFrameData, 1, nFrameSize, pInFile) == nFrameSize) { + if (lzsa_decode_frame(cFrameData, nFrameSize, &nBlockSize, &nIsUncompressed) < 0) { + nDecompressionError = 1; + nBlockSize = 0; + } } else { nBlockSize = 0; @@ -706,16 +701,14 @@ static int lzsa_compare(const char *pszInFilename, const char *pszOutFilename, c } if (nBlockSize != 0) { - bool bIsUncompressed = (nBlockSize & 0x800000) != 0; int nDecompressedSize = 0; - nBlockSize &= 0x7fffff; if ((int)nBlockSize > BLOCK_SIZE) { fprintf(stderr, "block size %d > max size %d\n", nBlockSize, BLOCK_SIZE); break; } if (fread(pInBlock, 1, nBlockSize, pInFile) == nBlockSize) { - if (bIsUncompressed) { + if (nIsUncompressed) { memcpy(pOutData + BLOCK_SIZE, pInBlock, nBlockSize); nDecompressedSize = nBlockSize; }