1 // LZ4frame API example : compress a file
2 // Based on sample code from Zbigniew Jędrzejewski-Szmek
3
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <errno.h>
8
9 #include <lz4frame.h>
10
11 #define BUF_SIZE 16*1024
12 #define LZ4_HEADER_SIZE 19
13 #define LZ4_FOOTER_SIZE 4
14
15 static const LZ4F_preferences_t lz4_preferences = {
16 { LZ4F_max256KB, LZ4F_blockLinked, LZ4F_noContentChecksum, LZ4F_frame, 0, { 0, 0 } },
17 0, /* compression level */
18 0, /* autoflush */
19 { 0, 0, 0, 0 }, /* reserved, must be set to 0 */
20 };
21
compress_file(FILE * in,FILE * out,size_t * size_in,size_t * size_out)22 static size_t compress_file(FILE *in, FILE *out, size_t *size_in, size_t *size_out) {
23 LZ4F_errorCode_t r;
24 LZ4F_compressionContext_t ctx;
25 char *src, *buf = NULL;
26 size_t size, n, k, count_in = 0, count_out, offset = 0, frame_size;
27
28 r = LZ4F_createCompressionContext(&ctx, LZ4F_VERSION);
29 if (LZ4F_isError(r)) {
30 printf("Failed to create context: error %zu\n", r);
31 return 1;
32 }
33 r = 1;
34
35 src = malloc(BUF_SIZE);
36 if (!src) {
37 printf("Not enough memory\n");
38 goto cleanup;
39 }
40
41 frame_size = LZ4F_compressBound(BUF_SIZE, &lz4_preferences);
42 size = frame_size + LZ4_HEADER_SIZE + LZ4_FOOTER_SIZE;
43 buf = malloc(size);
44 if (!buf) {
45 printf("Not enough memory\n");
46 goto cleanup;
47 }
48
49 n = offset = count_out = LZ4F_compressBegin(ctx, buf, size, &lz4_preferences);
50 if (LZ4F_isError(n)) {
51 printf("Failed to start compression: error %zu\n", n);
52 goto cleanup;
53 }
54
55 printf("Buffer size is %zu bytes, header size %zu bytes\n", size, n);
56
57 for (;;) {
58 k = fread(src, 1, BUF_SIZE, in);
59 if (k == 0)
60 break;
61 count_in += k;
62
63 n = LZ4F_compressUpdate(ctx, buf + offset, size - offset, src, k, NULL);
64 if (LZ4F_isError(n)) {
65 printf("Compression failed: error %zu\n", n);
66 goto cleanup;
67 }
68
69 offset += n;
70 count_out += n;
71 if (size - offset < frame_size + LZ4_FOOTER_SIZE) {
72 printf("Writing %zu bytes\n", offset);
73
74 k = fwrite(buf, 1, offset, out);
75 if (k < offset) {
76 if (ferror(out))
77 printf("Write failed\n");
78 else
79 printf("Short write\n");
80 goto cleanup;
81 }
82
83 offset = 0;
84 }
85 }
86
87 n = LZ4F_compressEnd(ctx, buf + offset, size - offset, NULL);
88 if (LZ4F_isError(n)) {
89 printf("Failed to end compression: error %zu\n", n);
90 goto cleanup;
91 }
92
93 offset += n;
94 count_out += n;
95 printf("Writing %zu bytes\n", offset);
96
97 k = fwrite(buf, 1, offset, out);
98 if (k < offset) {
99 if (ferror(out))
100 printf("Write failed\n");
101 else
102 printf("Short write\n");
103 goto cleanup;
104 }
105
106 *size_in = count_in;
107 *size_out = count_out;
108 r = 0;
109 cleanup:
110 if (ctx)
111 LZ4F_freeCompressionContext(ctx);
112 free(src);
113 free(buf);
114 return r;
115 }
116
get_block_size(const LZ4F_frameInfo_t * info)117 static size_t get_block_size(const LZ4F_frameInfo_t* info) {
118 switch (info->blockSizeID) {
119 case LZ4F_default:
120 case LZ4F_max64KB: return 1 << 16;
121 case LZ4F_max256KB: return 1 << 18;
122 case LZ4F_max1MB: return 1 << 20;
123 case LZ4F_max4MB: return 1 << 22;
124 default:
125 printf("Impossible unless more block sizes are allowed\n");
126 exit(1);
127 }
128 }
129
decompress_file(FILE * in,FILE * out)130 static size_t decompress_file(FILE *in, FILE *out) {
131 void* const src = malloc(BUF_SIZE);
132 void* dst = NULL;
133 size_t dstCapacity = 0;
134 LZ4F_dctx *dctx = NULL;
135 size_t ret;
136
137 /* Initialization */
138 if (!src) { perror("decompress_file(src)"); goto cleanup; }
139 ret = LZ4F_createDecompressionContext(&dctx, 100);
140 if (LZ4F_isError(ret)) {
141 printf("LZ4F_dctx creation error: %s\n", LZ4F_getErrorName(ret));
142 goto cleanup;
143 }
144
145 /* Decompression */
146 ret = 1;
147 while (ret != 0) {
148 /* Load more input */
149 size_t srcSize = fread(src, 1, BUF_SIZE, in);
150 void* srcPtr = src;
151 void* srcEnd = srcPtr + srcSize;
152 if (srcSize == 0 || ferror(in)) {
153 printf("Decompress: not enough input or error reading file\n");
154 goto cleanup;
155 }
156 /* Allocate destination buffer if it isn't already */
157 if (!dst) {
158 LZ4F_frameInfo_t info;
159 ret = LZ4F_getFrameInfo(dctx, &info, src, &srcSize);
160 if (LZ4F_isError(ret)) {
161 printf("LZ4F_getFrameInfo error: %s\n", LZ4F_getErrorName(ret));
162 goto cleanup;
163 }
164 /* Allocating enough space for an entire block isn't necessary for
165 * correctness, but it allows some memcpy's to be elided.
166 */
167 dstCapacity = get_block_size(&info);
168 dst = malloc(dstCapacity);
169 if (!dst) { perror("decompress_file(dst)"); goto cleanup; }
170 srcPtr += srcSize;
171 srcSize = srcEnd - srcPtr;
172 }
173 /* Decompress:
174 * Continue while there is more input to read and the frame isn't over.
175 * If srcPtr == srcEnd then we know that there is no more output left in the
176 * internal buffer left to flush.
177 */
178 while (srcPtr != srcEnd && ret != 0) {
179 /* INVARIANT: Any data left in dst has already been written */
180 size_t dstSize = dstCapacity;
181 ret = LZ4F_decompress(dctx, dst, &dstSize, srcPtr, &srcSize, /* LZ4F_decompressOptions_t */ NULL);
182 if (LZ4F_isError(ret)) {
183 printf("Decompression error: %s\n", LZ4F_getErrorName(ret));
184 goto cleanup;
185 }
186 /* Flush output */
187 if (dstSize != 0){
188 size_t written = fwrite(dst, 1, dstSize, out);
189 printf("Writing %zu bytes\n", dstSize);
190 if (written != dstSize) {
191 printf("Decompress: Failed to write to file\n");
192 goto cleanup;
193 }
194 }
195 /* Update input */
196 srcPtr += srcSize;
197 srcSize = srcEnd - srcPtr;
198 }
199 }
200 /* Check that there isn't trailing input data after the frame.
201 * It is valid to have multiple frames in the same file, but this example
202 * doesn't support it.
203 */
204 ret = fread(src, 1, 1, in);
205 if (ret != 0 || !feof(in)) {
206 printf("Decompress: Trailing data left in file after frame\n");
207 goto cleanup;
208 }
209
210 cleanup:
211 free(src);
212 free(dst);
213 return LZ4F_freeDecompressionContext(dctx); /* note : free works on NULL */
214 }
215
compare(FILE * fp0,FILE * fp1)216 int compare(FILE* fp0, FILE* fp1)
217 {
218 int result = 0;
219
220 while(0 == result) {
221 char b0[1024];
222 char b1[1024];
223 const size_t r0 = fread(b0, 1, sizeof(b0), fp0);
224 const size_t r1 = fread(b1, 1, sizeof(b1), fp1);
225
226 result = (int) r0 - (int) r1;
227
228 if (0 == r0 || 0 == r1) {
229 break;
230 }
231 if (0 == result) {
232 result = memcmp(b0, b1, r0);
233 }
234 }
235
236 return result;
237 }
238
main(int argc,const char ** argv)239 int main(int argc, const char **argv) {
240 char inpFilename[256] = { 0 };
241 char lz4Filename[256] = { 0 };
242 char decFilename[256] = { 0 };
243
244 if(argc < 2) {
245 printf("Please specify input filename\n");
246 return 0;
247 }
248
249 snprintf(inpFilename, 256, "%s", argv[1]);
250 snprintf(lz4Filename, 256, "%s.lz4", argv[1]);
251 snprintf(decFilename, 256, "%s.lz4.dec", argv[1]);
252
253 printf("inp = [%s]\n", inpFilename);
254 printf("lz4 = [%s]\n", lz4Filename);
255 printf("dec = [%s]\n", decFilename);
256
257 /* compress */
258 { FILE* const inpFp = fopen(inpFilename, "rb");
259 FILE* const outFp = fopen(lz4Filename, "wb");
260 size_t sizeIn = 0;
261 size_t sizeOut = 0;
262 size_t ret;
263
264 printf("compress : %s -> %s\n", inpFilename, lz4Filename);
265 ret = compress_file(inpFp, outFp, &sizeIn, &sizeOut);
266 if (ret) {
267 printf("compress : failed with code %zu\n", ret);
268 return ret;
269 }
270 printf("%s: %zu → %zu bytes, %.1f%%\n",
271 inpFilename, sizeIn, sizeOut,
272 (double)sizeOut / sizeIn * 100);
273 printf("compress : done\n");
274
275 fclose(outFp);
276 fclose(inpFp);
277 }
278
279 /* decompress */
280 { FILE* const inpFp = fopen(lz4Filename, "rb");
281 FILE* const outFp = fopen(decFilename, "wb");
282 size_t ret;
283
284 printf("decompress : %s -> %s\n", lz4Filename, decFilename);
285 ret = decompress_file(inpFp, outFp);
286 if (ret) {
287 printf("decompress : failed with code %zu\n", ret);
288 return ret;
289 }
290 printf("decompress : done\n");
291
292 fclose(outFp);
293 fclose(inpFp);
294 }
295
296 /* verify */
297 { FILE* const inpFp = fopen(inpFilename, "rb");
298 FILE* const decFp = fopen(decFilename, "rb");
299
300 printf("verify : %s <-> %s\n", inpFilename, decFilename);
301 const int cmp = compare(inpFp, decFp);
302 if(0 == cmp) {
303 printf("verify : OK\n");
304 } else {
305 printf("verify : NG\n");
306 }
307
308 fclose(decFp);
309 fclose(inpFp);
310 }
311 }
312