• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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