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 
31 enum {
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 
write_int32(FILE * fp,int32_t i)38 size_t write_int32(FILE* fp, int32_t i) {
39     return fwrite(&i, sizeof(i), 1, fp);
40 }
41 
write_bin(FILE * fp,const void * array,int arrayBytes)42 size_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 
read_int32(FILE * fp,int32_t * i)47 size_t read_int32(FILE* fp, int32_t* i) {
48     return fread(i, sizeof(*i), 1, fp);
49 }
50 
read_bin(FILE * fp,void * array,int arrayBytes)51 size_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 
test_compress(FILE * outFp,FILE * inpFp)57 void 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 
test_decompress(FILE * outFp,FILE * inpFp)92 void 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
compare(FILE * f0,FILE * f1)133 size_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 
main(int argc,const char ** argv)166 int 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