// Copyright 2016 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include #include #include #include #include #include #include #include #include #include #define BLOCK_SIZE 65536 #define WR_NEWFILE O_WRONLY | O_CREAT | O_TRUNC #define PERM_644 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH static void usage(const char* arg0) { printf("usage: %s [-1|-9] [-d] \n", arg0); printf(" -1 fast compression (default)\n"); printf(" -9 high compression (slower)\n"); printf(" -d decompress\n"); } static int do_decompress(const char* infile, const char* outfile) { int infd, outfd; infd = open(infile, O_RDONLY); if (infd < 0) { fprintf(stderr, "could not open %s: %s\n", infile, strerror(errno)); return -1; } outfd = open(outfile, WR_NEWFILE, PERM_644); if (outfd < 0) { fprintf(stderr, "could not open %s: %s\n", outfile, strerror(errno)); close(infd); return -1; } LZ4F_decompressionContext_t dctx; LZ4F_errorCode_t errc = LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION); if (LZ4F_isError(errc)) { fprintf(stderr, "could not initialize decompression: %s\n", LZ4F_getErrorName(errc)); close(outfd); close(infd); return -1; } uint8_t inbuf[BLOCK_SIZE]; uint8_t outbuf[BLOCK_SIZE]; // Read first 4 bytes to let LZ4 tell us how much it expects in the first // pass. size_t src_sz = 4; size_t dst_sz = 0; ssize_t nr = read(infd, inbuf, src_sz); if (nr < (ssize_t)src_sz) { fprintf(stderr, "could not read from %s", infile); if (nr < 0) { fprintf(stderr, ": %s", strerror(errno)); } fprintf(stderr, "\n"); goto done; } size_t to_read = LZ4F_decompress(dctx, outbuf, &dst_sz, inbuf, &src_sz, NULL); if (LZ4F_isError(to_read)) { fprintf(stderr, "could not decompress %s: %s\n", infile, LZ4F_getErrorName(to_read)); goto done; } if (to_read > BLOCK_SIZE) { to_read = BLOCK_SIZE; } while ((nr = read(infd, inbuf, to_read)) > 0) { src_sz = nr; ssize_t pos = 0; size_t next = 0; while (pos < nr) { dst_sz = BLOCK_SIZE; next = LZ4F_decompress(dctx, outbuf, &dst_sz, inbuf + pos, &src_sz, NULL); if (LZ4F_isError(next)) { fprintf(stderr, "could not decompress %s: %s\n", infile, LZ4F_getErrorName(to_read)); goto done; } if (dst_sz) { ssize_t nw = write(outfd, outbuf, dst_sz); if (nw != (ssize_t)dst_sz) { fprintf(stderr, "could not write to %s", outfile); if (nw < 0) { fprintf(stderr, ": %s", strerror(errno)); } fprintf(stderr, "\n"); goto done; } } pos += src_sz; src_sz = nr - pos; } to_read = next; if (to_read > BLOCK_SIZE || to_read == 0) { to_read = BLOCK_SIZE; } } if (nr < 0) { fprintf(stderr, "error reading %s: %s\n", infile, strerror(errno)); goto done; } done: LZ4F_freeDecompressionContext(dctx); close(outfd); close(infd); return 0; } static int do_compress(const char* infile, const char* outfile, int clevel) { int infd, outfd; infd = open(infile, O_RDONLY); if (infd < 0) { fprintf(stderr, "could not open %s: %s\n", infile, strerror(errno)); return -1; } outfd = open(outfile, WR_NEWFILE, PERM_644); if (outfd < 0) { fprintf(stderr, "could not open %s: %s\n", outfile, strerror(errno)); close(infd); return -1; } LZ4F_preferences_t prefs; memset(&prefs, 0, sizeof(prefs)); prefs.compressionLevel = clevel; uint8_t inbuf[BLOCK_SIZE]; size_t outsize = LZ4F_compressFrameBound(BLOCK_SIZE, &prefs); uint8_t* outbuf = malloc(outsize); if (!outbuf) { fprintf(stderr, "out of memory\n"); close(outfd); close(infd); return ENOMEM; } // TODO: do the whole file in one frame, using the LZ4F begin/update/end // functions. ssize_t nr = 0; while ((nr = read(infd, inbuf, BLOCK_SIZE)) > 0) { ssize_t csz = LZ4F_compressFrame(outbuf, outsize, inbuf, nr, &prefs); if (LZ4F_isError(csz)) { fprintf(stderr, "error compressing %s: %s\n", infile, LZ4F_getErrorName(csz)); goto done; } ssize_t nw = write(outfd, outbuf, csz); if (nw != csz) { fprintf(stderr, "could not write to %s", outfile); if (nw < 0) { fprintf(stderr, ": %s", strerror(errno)); } fprintf(stderr, "\n"); goto done; } } if (nr < 0) { fprintf(stderr, "error reading %s: %s\n", infile, strerror(errno)); goto done; } done: free(outbuf); close(outfd); close(infd); return 0; } int main(int argc, char* argv[]) { int clevel = 1; bool decompress = false; const char* infile = NULL; const char* outfile = NULL; for (int i = 1; i < argc; i++) { if (!strcmp("-d", argv[i])) { decompress = true; continue; } if (!strcmp("-9", argv[i])) { clevel = 9; continue; } if (!strcmp("-h", argv[i])) { usage(argv[0]); return 0; } if (!infile) { infile = argv[i]; continue; } if (!outfile) { outfile = argv[i]; continue; } fprintf(stderr, "Unknown argument: %s\n", argv[i]); usage(argv[0]); return -1; } if (!infile || !outfile) { usage(argv[0]); return 0; } printf("%scompressing %s into %s", decompress ? "de" : "", infile, outfile); if (!decompress) { printf(" at level %d", clevel); } printf("\n"); if (decompress) { return do_decompress(infile, outfile); } else { return do_compress(infile, outfile, clevel); } }