/* cbfs-compression-tool, CLI utility for dealing with CBFS compressed data */ /* SPDX-License-Identifier: GPL-2.0-only */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <strings.h> #include <time.h> #include "cbfs.h" #include "common.h" const char *usage_text = "cbfs-compression-tool benchmark\n" " runs benchmarks for all implemented algorithms\n" "cbfs-compression-tool compress inFile outFile algo\n" " compresses inFile with algo and stores in outFile\n" "\n" "'compress' file format:\n" " 4 bytes little endian: algorithm ID (as used in CBFS)\n" " 4 bytes little endian: uncompressed size\n" " ...: compressed data stream\n"; static void usage(void) { puts(usage_text); } static int benchmark(void) { const int bufsize = 10*1024*1024; char *data = malloc(bufsize); if (!data) { fprintf(stderr, "out of memory\n"); return 1; } char *compressed_data = malloc(bufsize); if (!compressed_data) { free(data); fprintf(stderr, "out of memory\n"); return 1; } int i, l = strlen(usage_text) + 1; for (i = 0; i + l < bufsize; i += l) { memcpy(data + i, usage_text, l); } memset(data + i, 0, bufsize - i); const struct typedesc_t *algo; for (algo = &types_cbfs_compression[0]; algo->name != NULL; algo++) { int outsize = bufsize; printf("measuring '%s'\n", algo->name); comp_func_ptr comp = compression_function(algo->type); if (comp == NULL) { printf("no handler associated with algorithm\n"); free(data); free(compressed_data); return 1; } struct timespec t_s, t_e; clock_gettime(CLOCK_MONOTONIC, &t_s); if (comp(data, bufsize, compressed_data, &outsize)) { printf("compression failed"); return 1; } clock_gettime(CLOCK_MONOTONIC, &t_e); printf("compressing %d bytes to %d took %ld seconds\n", bufsize, outsize, (long)(t_e.tv_sec - t_s.tv_sec)); } free(data); free(compressed_data); return 0; } static int compress(char *infile, char *outfile, char *algoname, int write_header) { int err = 1; FILE *fin = NULL; FILE *fout = NULL; void *indata = NULL; const struct typedesc_t *algo = &types_cbfs_compression[0]; while (algo->name != NULL) { if (strcasecmp(algo->name, algoname) == 0) break; algo++; } if (algo->name == NULL) { fprintf(stderr, "algo '%s' is not supported.\n", algoname); return 1; } comp_func_ptr comp = compression_function(algo->type); if (comp == NULL) { printf("no handler associated with algorithm\n"); return 1; } fin = fopen(infile, "rb"); if (!fin) { fprintf(stderr, "could not open '%s'\n", infile); return 1; } fout = fopen(outfile, "wb"); if (!fout) { fprintf(stderr, "could not open '%s' for writing\n", outfile); goto out; } if (fseek(fin, 0, SEEK_END) != 0) { fprintf(stderr, "could not seek in input\n"); goto out; } long insize = ftell(fin); if (insize < 0) { fprintf(stderr, "could not determine input size\n"); goto out; } rewind(fin); indata = malloc(insize); if (!indata) { fprintf(stderr, "out of memory\n"); goto out; } void *outdata = malloc(insize); if (!outdata) { fprintf(stderr, "out of memory\n"); goto out; } int outsize; int remsize = insize; while (remsize > 0) { int readsz = fread(indata, 1, remsize, fin); if (readsz < 0) { fprintf(stderr, "failed to read input with %d bytes left\n", remsize); goto out; } remsize -= readsz; } if (comp(indata, insize, outdata, &outsize) == -1) { outsize = insize; free(outdata); outdata = indata; algo = &types_cbfs_compression[0]; } if (write_header) { char header[8]; header[0] = algo->type & 0xff; header[1] = (algo->type >> 8) & 0xff; header[2] = (algo->type >> 16) & 0xff; header[3] = (algo->type >> 24) & 0xff; header[4] = insize & 0xff; header[5] = (insize >> 8) & 0xff; header[6] = (insize >> 16) & 0xff; header[7] = (insize >> 24) & 0xff; if (fwrite(header, 8, 1, fout) != 1) { fprintf(stderr, "failed writing header\n"); goto out; } } if (fwrite(outdata, outsize, 1, fout) != 1) { fprintf(stderr, "failed writing compressed data\n"); goto out; } err = 0; out: if (fin) fclose(fin); if (fout) fclose(fout); if (indata) free(indata); return err; } int main(int argc, char **argv) { if ((argc == 2) && (strcmp(argv[1], "benchmark") == 0)) return benchmark(); if ((argc == 5) && (strcmp(argv[1], "compress") == 0)) return compress(argv[2], argv[3], argv[4], 1); if ((argc == 5) && (strcmp(argv[1], "rawcompress") == 0)) return compress(argv[2], argv[3], argv[4], 0); usage(); return 1; }