1 // LZ4 HC streaming API example : ring buffer 2 // Based on previous work from Takayuki Matsuoka 3 4 5 /************************************** 6 * Compiler Options 7 **************************************/ 8 #ifdef _MSC_VER /* Visual Studio */ 9 # define _CRT_SECURE_NO_WARNINGS // for MSVC 10 # define snprintf sprintf_s 11 #endif 12 13 #define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) 14 #ifdef __GNUC__ 15 # pragma GCC diagnostic ignored "-Wmissing-braces" /* GCC bug 53119 : doesn't accept { 0 } as initializer (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53119) */ 16 #endif 17 18 19 /************************************** 20 * Includes 21 **************************************/ 22 #include "lz4hc.h" 23 #include "lz4.h" 24 25 #include <stdio.h> 26 #include <stdint.h> 27 #include <stdlib.h> 28 #include <string.h> 29 30 enum { 31 MESSAGE_MAX_BYTES = 1024, 32 RING_BUFFER_BYTES = 1024 * 8 + MESSAGE_MAX_BYTES, 33 DEC_BUFFER_BYTES = RING_BUFFER_BYTES + MESSAGE_MAX_BYTES // Intentionally larger to test unsynchronized ring buffers 34 }; 35 36 37 size_t write_int32(FILE* fp, int32_t i) { 38 return fwrite(&i, sizeof(i), 1, fp); 39 } 40 41 size_t write_bin(FILE* fp, const void* array, int arrayBytes) { 42 return fwrite(array, 1, arrayBytes, fp); 43 } 44 45 size_t read_int32(FILE* fp, int32_t* i) { 46 return fread(i, sizeof(*i), 1, fp); 47 } 48 49 size_t read_bin(FILE* fp, void* array, int arrayBytes) { 50 return fread(array, 1, arrayBytes, fp); 51 } 52 53 54 void test_compress(FILE* outFp, FILE* inpFp) 55 { 56 LZ4_streamHC_t lz4Stream_body = { 0 }; 57 LZ4_streamHC_t* lz4Stream = &lz4Stream_body; 58 59 static char inpBuf[RING_BUFFER_BYTES]; 60 int inpOffset = 0; 61 62 for(;;) 63 { 64 // Read random length ([1,MESSAGE_MAX_BYTES]) data to the ring buffer. 65 char* const inpPtr = &inpBuf[inpOffset]; 66 const int randomLength = (rand() % MESSAGE_MAX_BYTES) + 1; 67 const int inpBytes = (int) read_bin(inpFp, inpPtr, randomLength); 68 if (0 == inpBytes) break; 69 70 { 71 char cmpBuf[LZ4_COMPRESSBOUND(MESSAGE_MAX_BYTES)]; 72 const int cmpBytes = LZ4_compressHC_continue(lz4Stream, inpPtr, cmpBuf, inpBytes); 73 74 if(cmpBytes <= 0) break; 75 write_int32(outFp, cmpBytes); 76 write_bin(outFp, cmpBuf, cmpBytes); 77 78 inpOffset += inpBytes; 79 80 // Wraparound the ringbuffer offset 81 if(inpOffset >= RING_BUFFER_BYTES - MESSAGE_MAX_BYTES) 82 inpOffset = 0; 83 } 84 } 85 86 write_int32(outFp, 0); 87 } 88 89 90 void test_decompress(FILE* outFp, FILE* inpFp) 91 { 92 static char decBuf[DEC_BUFFER_BYTES]; 93 int decOffset = 0; 94 LZ4_streamDecode_t lz4StreamDecode_body = { 0 }; 95 LZ4_streamDecode_t* lz4StreamDecode = &lz4StreamDecode_body; 96 97 for(;;) 98 { 99 int cmpBytes = 0; 100 char cmpBuf[LZ4_COMPRESSBOUND(MESSAGE_MAX_BYTES)]; 101 102 { 103 const size_t r0 = read_int32(inpFp, &cmpBytes); 104 size_t r1; 105 if(r0 != 1 || cmpBytes <= 0) 106 break; 107 108 r1 = read_bin(inpFp, cmpBuf, cmpBytes); 109 if(r1 != (size_t) cmpBytes) 110 break; 111 } 112 113 { 114 char* const decPtr = &decBuf[decOffset]; 115 const int decBytes = LZ4_decompress_safe_continue( 116 lz4StreamDecode, cmpBuf, decPtr, cmpBytes, MESSAGE_MAX_BYTES); 117 if(decBytes <= 0) 118 break; 119 120 decOffset += decBytes; 121 write_bin(outFp, decPtr, decBytes); 122 123 // Wraparound the ringbuffer offset 124 if(decOffset >= DEC_BUFFER_BYTES - MESSAGE_MAX_BYTES) 125 decOffset = 0; 126 } 127 } 128 } 129 130 131 // Compare 2 files content 132 // return 0 if identical 133 // return ByteNb>0 if different 134 size_t compare(FILE* f0, FILE* f1) 135 { 136 size_t result = 1; 137 138 for (;;) 139 { 140 char b0[65536]; 141 char b1[65536]; 142 const size_t r0 = fread(b0, 1, sizeof(b0), f0); 143 const size_t r1 = fread(b1, 1, sizeof(b1), f1); 144 145 if ((r0==0) && (r1==0)) return 0; // success 146 147 if (r0 != r1) 148 { 149 size_t smallest = r0; 150 if (r1<r0) smallest = r1; 151 result += smallest; 152 break; 153 } 154 155 if (memcmp(b0, b1, r0)) 156 { 157 unsigned errorPos = 0; 158 while ((errorPos < r0) && (b0[errorPos]==b1[errorPos])) errorPos++; 159 result += errorPos; 160 break; 161 } 162 163 result += sizeof(b0); 164 } 165 166 return result; 167 } 168 169 170 int main(int argc, char** argv) 171 { 172 char inpFilename[256] = { 0 }; 173 char lz4Filename[256] = { 0 }; 174 char decFilename[256] = { 0 }; 175 unsigned fileID = 1; 176 unsigned pause = 0; 177 178 179 if(argc < 2) { 180 printf("Please specify input filename\n"); 181 return 0; 182 } 183 184 if (!strcmp(argv[1], "-p")) pause = 1, fileID = 2; 185 186 snprintf(inpFilename, 256, "%s", argv[fileID]); 187 snprintf(lz4Filename, 256, "%s.lz4s-%d", argv[fileID], 9); 188 snprintf(decFilename, 256, "%s.lz4s-%d.dec", argv[fileID], 9); 189 190 printf("input = [%s]\n", inpFilename); 191 printf("lz4 = [%s]\n", lz4Filename); 192 printf("decoded = [%s]\n", decFilename); 193 194 // compress 195 { 196 FILE* inpFp = fopen(inpFilename, "rb"); 197 FILE* outFp = fopen(lz4Filename, "wb"); 198 199 test_compress(outFp, inpFp); 200 201 fclose(outFp); 202 fclose(inpFp); 203 } 204 205 // decompress 206 { 207 FILE* inpFp = fopen(lz4Filename, "rb"); 208 FILE* outFp = fopen(decFilename, "wb"); 209 210 test_decompress(outFp, inpFp); 211 212 fclose(outFp); 213 fclose(inpFp); 214 } 215 216 // verify 217 { 218 FILE* inpFp = fopen(inpFilename, "rb"); 219 FILE* decFp = fopen(decFilename, "rb"); 220 221 const size_t cmp = compare(inpFp, decFp); 222 if(0 == cmp) { 223 printf("Verify : OK\n"); 224 } else { 225 printf("Verify : NG : error at pos %u\n", (unsigned)cmp-1); 226 } 227 228 fclose(decFp); 229 fclose(inpFp); 230 } 231 232 if (pause) 233 { 234 printf("Press enter to continue ...\n"); 235 getchar(); 236 } 237 238 return 0; 239 } 240