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