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