|
| 1 | +#include <assert.h> |
| 2 | +#include <fcntl.h> |
| 3 | +#include <inttypes.h> |
| 4 | +#include <stdint.h> |
| 5 | +#include <stdio.h> |
| 6 | +#include <stdlib.h> |
| 7 | +#include <sys/stat.h> |
| 8 | +#include <unistd.h> |
| 9 | + |
| 10 | +#include "streamvbyte.h" |
| 11 | + |
| 12 | +#define MAX_INTEGERS 10000 |
| 13 | + |
| 14 | +size_t read_integers(const char* filename, uint32_t* buffer); |
| 15 | +void write_file_or_die(const char* filename, ssize_t length, const uint8_t* data); |
| 16 | + |
| 17 | +// This application reads decimal-encoded integers from an input file |
| 18 | +// and compresses them using StreamVByte. There's a fairly low limit |
| 19 | +// on how much data is allowed to be in the input file because this |
| 20 | +// application is, for the moment, intended to measure the compression |
| 21 | +// ratio. |
| 22 | +// If the second argument, the output filename, is absent, then this |
| 23 | +// application does not write the compressed data to the file system, |
| 24 | +// but it still prints out information about the compression ratio. |
| 25 | +int main(int argc, char *argv[]){ |
| 26 | + if(argc != 2 && argc != 3) { |
| 27 | + fprintf(stderr, "Expected arguments: IN_FILENAME, [OUT_FILENAME]\n"); |
| 28 | + exit(EXIT_FAILURE); |
| 29 | + } |
| 30 | + char* in_filename = argv[1]; |
| 31 | + char* out_filename = NULL; |
| 32 | + if(argc == 3) { |
| 33 | + out_filename = argv[2]; |
| 34 | + } |
| 35 | + |
| 36 | + uint32_t input[MAX_INTEGERS] = {0}; |
| 37 | + size_t count = read_integers(in_filename, input); |
| 38 | + uint8_t* compressed_buffer = malloc(streamvbyte_max_compressedbytes(count)); |
| 39 | + size_t compressed_size = streamvbyte_encode(input, count, compressed_buffer); |
| 40 | + size_t millibytes_per_element = (1000 * compressed_size) / count; |
| 41 | + fprintf( |
| 42 | + stdout, |
| 43 | + "Compressed %llu 32-bit integers to %llu bytes (%llu.%03llu bytes per integer)\n", |
| 44 | + (long long unsigned)count, |
| 45 | + (long long unsigned)compressed_size, |
| 46 | + (long long unsigned)(millibytes_per_element / 1000), |
| 47 | + (long long unsigned)(millibytes_per_element % 1000) |
| 48 | + ); |
| 49 | + if(out_filename != NULL) { |
| 50 | + fprintf(stdout, "Writing file to %s\n", out_filename); |
| 51 | + write_file_or_die(out_filename, compressed_size, compressed_buffer); |
| 52 | + } |
| 53 | + return 0; |
| 54 | +} |
| 55 | + |
| 56 | +size_t read_integers(const char* filename, uint32_t* buffer) { |
| 57 | + FILE* fp = fopen(filename, "r"); |
| 58 | + if (!fp) { |
| 59 | + fprintf(stderr, "Error: cannot open file '%s'\n", filename); |
| 60 | + exit(1); |
| 61 | + } |
| 62 | + |
| 63 | + size_t count = 0; |
| 64 | + uint32_t value; |
| 65 | + while (fscanf(fp, "%" SCNu32, &value) == 1) { |
| 66 | + if (count >= MAX_INTEGERS) { |
| 67 | + fprintf(stderr, "Fatal: more than %d integers in file\n", MAX_INTEGERS); |
| 68 | + fclose(fp); |
| 69 | + exit(1); |
| 70 | + } |
| 71 | + buffer[count++] = value; |
| 72 | + } |
| 73 | + |
| 74 | + fclose(fp); |
| 75 | + return count; |
| 76 | +} |
| 77 | + |
| 78 | +int open_trunc_or_die(const char* filename) { |
| 79 | + int out_fd = open(filename, O_TRUNC | O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH); |
| 80 | + if (out_fd == -1) { |
| 81 | + perror("Could not open output file"); |
| 82 | + exit(EXIT_FAILURE); |
| 83 | + } |
| 84 | + return out_fd; |
| 85 | +} |
| 86 | + |
| 87 | +void append_or_die(int fd, ssize_t length, const uint8_t* data) { |
| 88 | + if (write(fd, data, length) != length) { |
| 89 | + fprintf(stderr, "Some kind of problem writing the output file\n"); |
| 90 | + exit(EXIT_FAILURE); |
| 91 | + } |
| 92 | +} |
| 93 | + |
| 94 | +void write_file_or_die(const char* filename, ssize_t length, const uint8_t* data) { |
| 95 | + int out_fd = open_trunc_or_die(filename); |
| 96 | + append_or_die(out_fd, length, data); |
| 97 | + close(out_fd); |
| 98 | +} |
0 commit comments