static bool benchmark_codec_with_options (struct BenchmarkContext* context, SquashCodec* codec, SquashOptions* opts) { SquashBenchmarkResult result = { 0, 0.0, 0.0, 0.0, 0.0 }; bool success = false; SquashStatus res = SQUASH_OK; const int level = squash_options_get_int (opts, codec, "level"); #if !defined(SQUASH_BENCHMARK_NO_FORK) char fifo_name[] = ".squash-benchmark-fifo-XXXXXX"; assert (mkfifo (mktemp (fifo_name), 0600) == 0); if (fork () == 0) { int out_descriptor = open (fifo_name, O_WRONLY); #else int descriptors[2]; assert (pipe (descriptors) == 0); int out_descriptor = descriptors[1]; #endif FILE* compressed = squash_tmpfile (); FILE* decompressed = squash_tmpfile (); SquashTimer* timer = squash_timer_new (); int iterations = 0; if (level < 0) { fputs (" compressing: ", stdout); } else { fprintf (stdout, " level %d: ", level); } if (fseek (context->input, 0, SEEK_SET) != 0) { perror ("Unable to seek to beginning of input file"); exit (-1); } for ( iterations = 0 ; squash_timer_get_elapsed_cpu (timer) < min_exec_time ; iterations++ ) { fseek (context->input, 0, SEEK_SET); fseek (compressed, 0, SEEK_SET); rewind (compressed); squash_timer_start (timer); res = squash_splice_with_options (codec, SQUASH_STREAM_COMPRESS, compressed, context->input, 0, opts); squash_timer_stop (timer); rewind (context->input); if (res != SQUASH_OK) { fprintf (stderr, "ERROR: %s (%d) squash_splice_with_options (%s, compress, %p, %p, 0, %p)\n", squash_status_to_string (res), res, squash_codec_get_name (codec), compressed, context->input, opts); break; } } if (res == SQUASH_OK) { result.compressed_size = ftell (compressed); result.compress_cpu = squash_timer_get_elapsed_cpu (timer) / iterations; result.compress_wall = squash_timer_get_elapsed_wall (timer) / iterations; squash_timer_reset (timer); if (result.compressed_size == 0) { fprintf (stdout, "failed (0 byte output, %s [%d]).\n", squash_status_to_string (res), res); } else { fprintf (stdout, "compressed (%.6fs CPU, %.6fs wall, %ld bytes)... ", result.compress_cpu, result.compress_wall, result.compressed_size); for ( iterations = 0 ; squash_timer_get_elapsed_cpu (timer) < min_exec_time ; iterations++ ) { fseek (compressed, 0, SEEK_SET); fseek (decompressed, 0, SEEK_SET); squash_timer_start (timer); res = squash_splice_with_options (codec, SQUASH_STREAM_DECOMPRESS, decompressed, compressed, 0, opts); squash_timer_stop (timer); rewind (compressed); if (res != SQUASH_OK) { break; } } if (res != SQUASH_OK) { fprintf (stderr, "Failed (%s [%d]).\n", squash_status_to_string (res), res); } else { result.decompress_cpu = squash_timer_get_elapsed_cpu (timer) / iterations; result.decompress_wall = squash_timer_get_elapsed_wall (timer) / iterations; squash_timer_reset (timer); if (ftell (decompressed) != context->input_size) { /* Should never happen. */ fprintf (stderr, "Failed (size mismatch; expected %ld, got %ld.\n", context->input_size, ftell (decompressed)); } else { fprintf (stdout, "decompressed (%.6fs CPU, %.6fs wall).\n", result.decompress_cpu, result.decompress_wall); write (out_descriptor, &result, sizeof (SquashBenchmarkResult)); } } } } squash_timer_free (timer); fclose (compressed); fclose (decompressed); close (out_descriptor); #if !defined(SQUASH_BENCHMARK_NO_FORK) exit (0); } else { int in_descriptor = open (fifo_name, O_RDONLY); #else int in_descriptor = descriptors[0]; #endif size_t bytes_read = read (in_descriptor, &result, sizeof (SquashBenchmarkResult)); wait (NULL); if (bytes_read == sizeof (SquashBenchmarkResult)) { if (context->csv != NULL) { if (level >= 0) { fprintf (context->csv, "%s,%s,%s,%d,%ld,%f,%f,%f,%f\r\n", context->input_name, squash_plugin_get_name (squash_codec_get_plugin (codec)), squash_codec_get_name (codec), level, result.compressed_size, result.compress_cpu, result.compress_wall, result.decompress_cpu, result.decompress_wall); } else { fprintf (context->csv, "%s,%s,%s,,%ld,%f,%f,%f,%f\r\n", context->input_name, squash_plugin_get_name (squash_codec_get_plugin (codec)), squash_codec_get_name (codec), result.compressed_size, result.compress_cpu, result.compress_wall, result.decompress_cpu, result.decompress_wall); } } success = true; } close (in_descriptor); #if !defined(SQUASH_BENCHMARK_NO_FORK) unlink (fifo_name); } #endif return success; }
int main (int argc, char** argv) { SquashStatus res; SquashCodec* codec = NULL; SquashOptions* options = NULL; SquashStreamType direction = SQUASH_STREAM_COMPRESS; FILE* input = NULL; FILE* output = NULL; char* input_name = NULL; char* output_name = NULL; bool list_codecs = false; bool list_plugins = false; char** option_keys = NULL; char** option_values = NULL; bool keep = false; bool force = false; int opt; int optc = 0; char* tmp_string; int retval = EXIT_SUCCESS; struct parg_state ps; int optend; const struct parg_option squash_options[] = { {"keep", PARG_NOARG, NULL, 'k'}, {"option", PARG_REQARG, NULL, 'o'}, {"codec", PARG_REQARG, NULL, 'c'}, {"list-codecs", PARG_NOARG, NULL, 'L'}, {"list-plugins", PARG_NOARG, NULL, 'P'}, {"force", PARG_NOARG, NULL, 'f'}, {"decompress", PARG_NOARG, NULL, 'd'}, {"version", PARG_NOARG, NULL, 'V'}, {"help", PARG_NOARG, NULL, 'h'}, {NULL, 0, NULL, 0} }; option_keys = (char**) malloc (sizeof (char*)); option_values = (char**) malloc (sizeof (char*)); *option_keys = NULL; *option_values = NULL; optend = parg_reorder (argc, argv, "c:ko:123456789LPfdhb:V", squash_options); parg_init(&ps); while ( (opt = parg_getopt_long (&ps, optend, argv, "c:ko:123456789LPfdhb:V", squash_options, NULL)) != -1 ) { switch ( opt ) { case 'c': codec = squash_get_codec (ps.optarg); if ( codec == NULL ) { fprintf (stderr, "Unable to find codec '%s'\n", ps.optarg); retval = exit_failure (); goto cleanup; } break; case 'k': keep = true; break; case 'o': parse_option (&option_keys, &option_values, ps.optarg); break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': tmp_string = malloc (8); snprintf (tmp_string, 8, "level=%c", (char) opt); parse_option (&option_keys, &option_values, tmp_string); free (tmp_string); break; case 'L': list_codecs = true; break; case 'P': list_plugins = true; break; case 'f': force = true; break; case 'h': print_help_and_exit (argc, argv, EXIT_SUCCESS); break; case 'd': direction = SQUASH_STREAM_DECOMPRESS; break; case 'V': print_version_and_exit (argc, argv, EXIT_SUCCESS); break; } optc++; } if (list_plugins) { if (list_codecs) squash_foreach_plugin (list_plugins_and_codecs_foreach_cb, NULL); else squash_foreach_plugin (list_plugins_foreach_cb, NULL); goto cleanup; } else if (list_codecs) { squash_foreach_codec (list_codecs_foreach_cb, NULL); goto cleanup; } if ( ps.optind < argc ) { input_name = argv[ps.optind++]; if ( (direction == SQUASH_STREAM_DECOMPRESS) && codec == NULL ) { char* extension; extension = strrchr (input_name, '.'); if (extension != NULL) extension++; if (extension != NULL) codec = squash_get_codec_from_extension (extension); } } else { fprintf (stderr, "You must provide an input file name.\n"); retval = exit_failure (); goto cleanup; } if ( ps.optind < argc ) { output_name = strdup (argv[ps.optind++]); if ( codec == NULL && direction == SQUASH_STREAM_COMPRESS ) { const char* extension = strrchr (output_name, '.'); if (extension != NULL) extension++; if (extension != NULL) codec = squash_get_codec_from_extension (extension); } } else { if ( codec != NULL ) { const char* extension = squash_codec_get_extension (codec); if (extension != NULL) { if (strcmp (input_name, "-") == 0) { output_name = strdup ("-"); } else { size_t extension_length = strlen (extension); size_t input_name_length = strlen (input_name); if ( (extension_length + 1) < input_name_length && input_name[input_name_length - (1 + extension_length)] == '.' && strcasecmp (extension, input_name + (input_name_length - (extension_length))) == 0 ) { output_name = squash_strndup (input_name, input_name_length - (1 + extension_length)); } } } } } if ( ps.optind < argc ) { fprintf (stderr, "Too many arguments.\n"); } if ( codec == NULL ) { fprintf (stderr, "Unable to determine codec. Please pass -c \"codec\", or -L to see a list of available codecs.\n"); retval = exit_failure (); goto cleanup; } if ( output_name == NULL ) { fprintf (stderr, "Unable to determine output file.\n"); retval = exit_failure (); goto cleanup; } if ( strcmp (input_name, "-") == 0 ) { input = stdin; } else { input = fopen (input_name, "rb"); if ( input == NULL ) { perror ("Unable to open input file"); retval = exit_failure (); goto cleanup; } } if ( strcmp (output_name, "-") == 0 ) { output = stdout; } else { int output_fd = open (output_name, #if !defined(_WIN32) O_RDWR | O_CREAT | (force ? O_TRUNC : O_EXCL), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH #else O_RDWR | O_CREAT | (force ? O_TRUNC : O_EXCL) | O_BINARY, S_IREAD | S_IWRITE #endif ); if ( output_fd < 0 ) { perror ("Unable to open output file"); retval = exit_failure (); goto cleanup; } output = fdopen (output_fd, "wb"); if ( output == NULL ) { perror ("Unable to open output"); retval = exit_failure (); goto cleanup; } } options = squash_options_newa (codec, (const char * const*) option_keys, (const char * const*) option_values); res = squash_splice_with_options (codec, direction, output, input, 0, options); if ( res != SQUASH_OK ) { fprintf (stderr, "Failed to %s: %s\n", (direction == SQUASH_STREAM_COMPRESS) ? "compress" : "decompress", squash_status_to_string (res)); retval = exit_failure (); goto cleanup; } if ( !keep && input != stdin ) { fclose (input); if ( unlink (input_name) != 0 ) { perror ("Unable to remove input file"); } input = NULL; } cleanup: if (input != stdin && input != NULL) fclose (stdin); if (output != stdout) fclose (stdout); if (option_keys != NULL) { for (opt = 0 ; option_keys[opt] != NULL ; opt++) { free(option_keys[opt]); } free (option_keys); } if (option_values != NULL) { for (opt = 0 ; option_values[opt] != NULL ; opt++) { free(option_values[opt]); } free (option_values); } free (output_name); return retval; }