/* Copyright (c) 2015-2016, Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder(s) nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // LZFSE command line tool #if !defined(_POSIX_C_SOURCE) || (_POSIX_C_SOURCE < 200112L) # undef _POSIX_C_SOURCE # define _POSIX_C_SOURCE 200112L #endif #if defined(_MSC_VER) # if !defined(_CRT_NONSTDC_NO_DEPRECATE) # define _CRT_NONSTDC_NO_DEPRECATE # endif # if !defined(_CRT_SECURE_NO_WARNINGS) # define _CRT_SECURE_NO_WARNINGS # endif # if !defined(__clang__) # define inline __inline # endif #endif #include "lzfse.h" #include #include #include #include #include #include #if defined(_MSC_VER) # include # include #else # include # include #endif // Same as realloc(x,s), except x is freed when realloc fails static inline void *lzfse_reallocf(void *x, size_t s) { void *y = realloc(x, s); if (y == 0) { free(x); return 0; } return y; } static double get_time() { #if defined(_MSC_VER) LARGE_INTEGER count, freq; if (QueryPerformanceFrequency(&freq) && QueryPerformanceCounter(&count)) { return (double)count.QuadPart / (double)freq.QuadPart; } return 1.0e-3 * (double)GetTickCount(); #else struct timeval tv; if (gettimeofday(&tv, 0) != 0) { perror("gettimeofday"); exit(1); } return (double)tv.tv_sec + 1.0e-6 * (double)tv.tv_usec; #endif } //-------------------- enum { LZFSE_ENCODE = 0, LZFSE_DECODE }; void usage(int argc, char **argv) { fprintf( stderr, "Usage: %s -encode|-decode [-i input_file] [-o output_file] [-h] [-v]\n", argv[0]); } #define USAGE(argc, argv) \ do { \ usage(argc, argv); \ exit(0); \ } while (0) #define USAGE_MSG(argc, argv, ...) \ do { \ usage(argc, argv); \ fprintf(stderr, __VA_ARGS__); \ exit(1); \ } while (0) int main(int argc, char **argv) { const char *in_file = 0; // stdin const char *out_file = 0; // stdout int op = -1; // invalid op int verbosity = 0; // quiet // Parse options for (int i = 1; i < argc;) { // no args const char *a = argv[i++]; if (strcmp(a, "-h") == 0) USAGE(argc, argv); if (strcmp(a, "-v") == 0) { verbosity++; continue; } if (strcmp(a, "-encode") == 0) { op = LZFSE_ENCODE; continue; } if (strcmp(a, "-decode") == 0) { op = LZFSE_DECODE; continue; } // one arg const char **arg_var = 0; if (strcmp(a, "-i") == 0 && in_file == 0) arg_var = &in_file; else if (strcmp(a, "-o") == 0 && out_file == 0) arg_var = &out_file; if (arg_var != 0) { // Flag is recognized. Check if there is an argument. if (i == argc) USAGE_MSG(argc, argv, "Error: Missing arg after %s\n", a); *arg_var = argv[i++]; continue; } USAGE_MSG(argc, argv, "Error: invalid flag %s\n", a); } if (op < 0) USAGE_MSG(argc, argv, "Error: -encode|-decode required\n"); // Info if (verbosity > 0) { if (op == LZFSE_ENCODE) fprintf(stderr, "LZFSE encode\n"); if (op == LZFSE_DECODE) fprintf(stderr, "LZFSE decode\n"); fprintf(stderr, "Input: %s\n", in_file ? in_file : "stdin"); fprintf(stderr, "Output: %s\n", out_file ? out_file : "stdout"); } // Load input size_t in_allocated = 0; // allocated in IN size_t in_size = 0; // used in IN uint8_t *in = 0; // input buffer int in_fd = -1; // input file desc if (in_file != 0) { // If we have a file name, open it, and allocate the exact input size struct stat st; #if defined(_WIN32) in_fd = open(in_file, O_RDONLY | O_BINARY); #else in_fd = open(in_file, O_RDONLY); #endif if (in_fd < 0) { perror(in_file); exit(1); } if (fstat(in_fd, &st) != 0) { perror(in_file); exit(1); } if (st.st_size > SIZE_MAX) { fprintf(stderr, "File is too large\n"); exit(1); } in_allocated = (size_t)st.st_size; } else { // Otherwise, read from stdin, and allocate to 1 MB, grow as needed in_allocated = 1 << 20; in_fd = 0; #if defined(_WIN32) if (setmode(in_fd, O_BINARY) == -1) { perror("setmode"); exit(1); } #endif } in = (uint8_t *)malloc(in_allocated); if (in == 0) { perror("malloc"); exit(1); } while (1) { // re-alloc if needed if (in_size == in_allocated) { if (in_allocated < (100 << 20)) in_allocated <<= 1; // double it else in_allocated += (100 << 20); // or add 100 MB if already large in = lzfse_reallocf(in, in_allocated); if (in == 0) { perror("malloc"); exit(1); } } ptrdiff_t r = read(in_fd, in + in_size, in_allocated - in_size); if (r < 0) { perror("read"); exit(1); } if (r == 0) break; // end of file in_size += (size_t)r; } if (in_file != 0) { close(in_fd); in_fd = -1; } // Size info if (verbosity > 0) { fprintf(stderr, "Input size: %zu B\n", in_size); } // Encode/decode // Compute size for result buffer; we assume here that encode shrinks size, // and that decode grows by no more than 4x. These are reasonable common- // case guidelines, but are not formally guaranteed to be satisfied. size_t out_allocated = (op == LZFSE_ENCODE) ? in_size : (4 * in_size); size_t out_size = 0; size_t aux_allocated = (op == LZFSE_ENCODE) ? lzfse_encode_scratch_size() : lzfse_decode_scratch_size(); void *aux = aux_allocated ? malloc(aux_allocated) : 0; if (aux_allocated != 0 && aux == 0) { perror("malloc"); exit(1); } uint8_t *out = (uint8_t *)malloc(out_allocated); if (out == 0) { perror("malloc"); exit(1); } double c0 = get_time(); while (1) { if (op == LZFSE_ENCODE) out_size = lzfse_encode_buffer(out, out_allocated, in, in_size, aux); else out_size = lzfse_decode_buffer(out, out_allocated, in, in_size, aux); // If output buffer was too small, grow and retry. if (out_size == 0 || (op == LZFSE_DECODE && out_size == out_allocated)) { if (verbosity > 0) fprintf(stderr, "Output buffer was too small, increasing size...\n"); out_allocated <<= 1; out = (uint8_t *)lzfse_reallocf(out, out_allocated); if (out == 0) { perror("malloc"); exit(1); } continue; } break; } double c1 = get_time(); if (verbosity > 0) { fprintf(stderr, "Output size: %zu B\n", out_size); size_t raw_size = (op == LZFSE_ENCODE) ? in_size : out_size; size_t compressed_size = (op == LZFSE_ENCODE) ? out_size : in_size; fprintf(stderr, "Compression ratio: %.3f\n", (double)raw_size / (double)compressed_size); double ns_per_byte = 1.0e9 * (c1 - c0) / (double)raw_size; double mb_per_s = (double)raw_size / 1024.0 / 1024.0 / (c1 - c0); fprintf(stderr, "Speed: %.2f ns/B, %.2f MB/s\n",ns_per_byte,mb_per_s); } // Write output int out_fd = -1; if (out_file) { #if defined(_WIN32) out_fd = open(out_file, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IWRITE); #else out_fd = open(out_file, O_WRONLY | O_CREAT | O_TRUNC, 0644); #endif if (out_fd < 0) { perror(out_file); exit(1); } } else { out_fd = 1; // stdout #if defined(_WIN32) if (setmode(out_fd, O_BINARY) == -1) { perror("setmode"); exit(1); } #endif } for (size_t out_pos = 0; out_pos < out_size;) { ptrdiff_t w = write(out_fd, out + out_pos, out_size - out_pos); if (w < 0) { perror("write"); exit(1); } if (w == 0) { fprintf(stderr, "Failed to write to output file\n"); exit(1); } out_pos += (size_t)w; } if (out_file != 0) { close(out_fd); out_fd = -1; } free(in); free(out); free(aux); return 0; // OK }