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