• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
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  * meaning you may select, at your option, one of the above-listed licenses.
9  */
10 
11 /*
12  * abiTest :
13  * ensure ABI stability expectations are not broken by a new version
14 **/
15 
16 
17 /*===========================================
18 *   Dependencies
19 *==========================================*/
20 #include <stddef.h>     /* size_t */
21 #include <stdlib.h>     /* malloc, free, exit */
22 #include <stdio.h>      /* fprintf */
23 #include <string.h>     /* strcmp */
24 #include <assert.h>
25 #include <sys/types.h>  /* stat */
26 #include <sys/stat.h>   /* stat */
27 #include "xxhash.h"
28 
29 #include "lz4.h"
30 #include "lz4frame.h"
31 
32 
33 /*===========================================
34 *   Macros
35 *==========================================*/
36 #define MIN(a,b)  ( (a) < (b) ? (a) : (b) )
37 
38 #define MSG(...)    fprintf(stderr, __VA_ARGS__)
39 
40 #define CONTROL_MSG(c, ...) {   \
41     if ((c)) {                  \
42         MSG(__VA_ARGS__);       \
43         MSG(" \n");             \
44         abort();                \
45     }                           \
46 }
47 
48 
checkBuffers(const void * buff1,const void * buff2,size_t buffSize)49 static size_t checkBuffers(const void* buff1, const void* buff2, size_t buffSize)
50 {
51     const char* const ip1 = (const char*)buff1;
52     const char* const ip2 = (const char*)buff2;
53     size_t pos;
54 
55     for (pos=0; pos<buffSize; pos++)
56         if (ip1[pos]!=ip2[pos])
57             break;
58 
59     return pos;
60 }
61 
62 
63 LZ4_stream_t LZ4_cState;
64 LZ4_streamDecode_t LZ4_dState;
65 
66 /** roundTripTest() :
67  *  Compresses `srcBuff` into `compressedBuff`,
68  *  then decompresses `compressedBuff` into `resultBuff`.
69  *  If clevel==0, compression level is derived from srcBuff's content head bytes.
70  *  This function abort() if it detects any round-trip error.
71  *  Therefore, if it returns, round trip is considered successfully validated.
72  *  Note : `compressedBuffCapacity` should be `>= LZ4_compressBound(srcSize)`
73  *         for compression to be guaranteed to work */
roundTripTest(void * resultBuff,size_t resultBuffCapacity,void * compressedBuff,size_t compressedBuffCapacity,const void * srcBuff,size_t srcSize)74 static void roundTripTest(void* resultBuff, size_t resultBuffCapacity,
75                           void* compressedBuff, size_t compressedBuffCapacity,
76                     const void* srcBuff, size_t srcSize)
77 {
78     int const acceleration = 1;
79     // Note : can't use LZ4_initStream(), because it's only present since v1.9.0
80     memset(&LZ4_cState, 0, sizeof(LZ4_cState));
81     {   int const cSize = LZ4_compress_fast_continue(&LZ4_cState, (const char*)srcBuff, (char*)compressedBuff, (int)srcSize, (int)compressedBuffCapacity, acceleration);
82         CONTROL_MSG(cSize == 0, "Compression error !");
83         {   int const dInit = LZ4_setStreamDecode(&LZ4_dState, NULL, 0);
84             CONTROL_MSG(dInit == 0, "LZ4_setStreamDecode error !");
85         }
86         {   int const dSize = LZ4_decompress_safe_continue (&LZ4_dState, (const char*)compressedBuff, (char*)resultBuff, cSize, (int)resultBuffCapacity);
87             CONTROL_MSG(dSize < 0, "Decompression detected an error !");
88             CONTROL_MSG(dSize != (int)srcSize, "Decompression corruption error : wrong decompressed size !");
89     }   }
90 
91     /* check potential content corruption error */
92     assert(resultBuffCapacity >= srcSize);
93     {   size_t const errorPos = checkBuffers(srcBuff, resultBuff, srcSize);
94         CONTROL_MSG(errorPos != srcSize,
95                     "Silent decoding corruption, at pos %u !!!",
96                     (unsigned)errorPos);
97     }
98 }
99 
roundTripCheck(const void * srcBuff,size_t srcSize)100 static void roundTripCheck(const void* srcBuff, size_t srcSize)
101 {
102     size_t const cBuffSize = LZ4_COMPRESSBOUND(srcSize);
103     void* const cBuff = malloc(cBuffSize);
104     void* const rBuff = malloc(cBuffSize);
105 
106     if (!cBuff || !rBuff) {
107         fprintf(stderr, "not enough memory ! \n");
108         exit(1);
109     }
110 
111     roundTripTest(rBuff, cBuffSize,
112                   cBuff, cBuffSize,
113                   srcBuff, srcSize);
114 
115     free(rBuff);
116     free(cBuff);
117 }
118 
119 
getFileSize(const char * infilename)120 static size_t getFileSize(const char* infilename)
121 {
122     int r;
123 #if defined(_MSC_VER)
124     struct _stat64 statbuf;
125     r = _stat64(infilename, &statbuf);
126     if (r || !(statbuf.st_mode & S_IFREG)) return 0;   /* No good... */
127 #else
128     struct stat statbuf;
129     r = stat(infilename, &statbuf);
130     if (r || !S_ISREG(statbuf.st_mode)) return 0;   /* No good... */
131 #endif
132     return (size_t)statbuf.st_size;
133 }
134 
135 
isDirectory(const char * infilename)136 static int isDirectory(const char* infilename)
137 {
138     int r;
139 #if defined(_MSC_VER)
140     struct _stat64 statbuf;
141     r = _stat64(infilename, &statbuf);
142     if (!r && (statbuf.st_mode & _S_IFDIR)) return 1;
143 #else
144     struct stat statbuf;
145     r = stat(infilename, &statbuf);
146     if (!r && S_ISDIR(statbuf.st_mode)) return 1;
147 #endif
148     return 0;
149 }
150 
151 
152 /** loadFile() :
153  *  requirement : `buffer` size >= `fileSize` */
loadFile(void * buffer,const char * fileName,size_t fileSize)154 static void loadFile(void* buffer, const char* fileName, size_t fileSize)
155 {
156     FILE* const f = fopen(fileName, "rb");
157     if (isDirectory(fileName)) {
158         MSG("Ignoring %s directory \n", fileName);
159         exit(2);
160     }
161     if (f==NULL) {
162         MSG("Impossible to open %s \n", fileName);
163         exit(3);
164     }
165     {   size_t const readSize = fread(buffer, 1, fileSize, f);
166         if (readSize != fileSize) {
167             MSG("Error reading %s \n", fileName);
168             exit(5);
169     }   }
170     fclose(f);
171 }
172 
173 
fileCheck(const char * fileName)174 static void fileCheck(const char* fileName)
175 {
176     size_t const fileSize = getFileSize(fileName);
177     void* const buffer = malloc(fileSize + !fileSize /* avoid 0 */);
178     if (!buffer) {
179         MSG("not enough memory \n");
180         exit(4);
181     }
182     loadFile(buffer, fileName, fileSize);
183     roundTripCheck(buffer, fileSize);
184     free (buffer);
185 }
186 
187 
bad_usage(const char * exeName)188 int bad_usage(const char* exeName)
189 {
190     MSG(" \n");
191     MSG("bad usage: \n");
192     MSG(" \n");
193     MSG("%s [Options] fileName \n", exeName);
194     MSG(" \n");
195     MSG("Options: \n");
196     MSG("-#     : use #=[0-9] compression level (default:0 == random) \n");
197     return 1;
198 }
199 
200 
main(int argCount,const char ** argv)201 int main(int argCount, const char** argv)
202 {
203     const char* const exeName = argv[0];
204     int argNb = 1;
205     // Note : LZ4_VERSION_STRING requires >= v1.7.3+
206     MSG("abiTest, built binary based on API %s \n", LZ4_VERSION_STRING);
207     // Note : LZ4_versionString() requires >= v1.7.5+
208     MSG("currently linked to dll %s \n", LZ4_versionString());
209 
210     assert(argCount >= 1);
211     if (argCount < 2) return bad_usage(exeName);
212 
213     if (argNb >= argCount) return bad_usage(exeName);
214 
215     fileCheck(argv[argNb]);
216     MSG("no pb detected \n");
217     return 0;
218 }
219