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 49static 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 63LZ4_stream_t LZ4_cState; 64LZ4_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 */ 74static 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 100static 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 120static 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 136static 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` */ 154static 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 174static 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 188int 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 201int 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