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