Home | History | Annotate | Download | only in examples
      1 // LZ4frame API example : compress a file
      2 // Based on sample code from Zbigniew Jdrzejewski-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 
     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 
    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 
    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 
    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 
    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