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