Exemplo n.º 1
0
/**
 * @brief Get a list of options applicable to the codec
 *
 * @param codec name of the codec
 * @return a list of options, terminated by an option with a NULL name
 */
const SquashOptionInfo*
squash_get_option_info (const char* codec) {
  SquashCodec* codec_real = squash_get_codec (codec);

  if (codec_real != NULL)
    return squash_codec_get_option_info (codec_real);
  else
    return NULL;
}
Exemplo n.º 2
0
/**
 * @brief Get the maximum buffer size necessary to store compressed data.
 *
 * Typically the return value will be some percentage larger than the
 * uncompressed length, plus a few bytes.  For example, for bzip2 it
 * is the uncompressed length plus 1%, plus an additional 600 bytes.
 *
 * @warning The result of this function is not guaranteed to be
 * correct for use with the @ref SquashStream API—it should only be
 * used with the single-squashl buffer-to-buffer functions such as
 * ::squash_codec_compress and ::squash_codec_compress_with_options.
 *
 * @param codec The name of the codec
 * @param uncompressed_length Size of the uncompressed data in bytes
 * @return The maximum size required to store a compressed buffer
 *   representing @a uncompressed_length of uncompressed data.
 * @see squash_codec_get_max_compressed_size
 */
size_t
squash_get_max_compressed_size (const char* codec, size_t uncompressed_length) {
  SquashCodec* codec_real = squash_get_codec (codec);

  if (codec_real == NULL) {
    return 0;
  }

  return squash_codec_get_max_compressed_size (codec_real, uncompressed_length);
}
Exemplo n.º 3
0
Arquivo: file.c Projeto: jibsen/squash
/**
 * @brief Open an existing stdio file with the specified options
 *
 * @param fp the stdio file to use
 * @param codec codec to use
 * @param options options
 * @return The opened file, or *NULL* on error
 * @see squash_file_steal_codec
 * @see squash_file_steal_with_options
 * @see squash_file_steal_codec_with_options
 */
SquashFile*
squash_file_steal_with_options (const char* codec, FILE* fp, SquashOptions* options) {
  assert (fp != NULL);
  assert (codec != NULL);

  SquashCodec* codec_i = squash_get_codec (codec);
  if (codec_i == NULL)
    return NULL;

  return squash_file_steal_codec_with_options (codec_i, fp, options);
}
Exemplo n.º 4
0
/**
 * @brief Get a bitmask of information about the codec
 *
 * @param codec The codec
 * @return the codec info, or @ref SQUASH_CODEC_INFO_INVALID if there
 *   is no such codec
 */
SquashCodecInfo
squash_get_info (const char* codec) {
  SquashCodec* codec_real = squash_get_codec (codec);

  if (codec_real != NULL) {
    return squash_codec_get_info (codec_real);
  } else {
    squash_error (SQUASH_NOT_FOUND);
    return SQUASH_CODEC_INFO_INVALID;
  }
}
Exemplo n.º 5
0
Arquivo: file.c Projeto: jibsen/squash
/**
 * @brief Open a file with the specified options
 *
 * @param filename name of the file to open
 * @param mode file mode
 * @param codec codec to use
 * @param options options
 * @return The opened file, or *NULL* on error
 * @see squash_file_open
 */
SquashFile*
squash_file_open_with_options (const char* codec, const char* filename, const char* mode, SquashOptions* options) {
  assert (filename != NULL);
  assert (mode != NULL);
  assert (codec != NULL);

  SquashCodec* codec_i = squash_get_codec (codec);
  if (codec_i == NULL)
    return NULL;

  return squash_file_open_codec_with_options (codec_i, filename, mode, options);
}
Exemplo n.º 6
0
	//----------
	Codec::Codec(string codecName) {
		ofxSquash::initialize();

		this->name = codecName;
		this->squashCodec = squash_get_codec(codecName.c_str());
		if (this->squashCodec) {
			this->valid = true;
		}
		else {
			OFXSQUASH_WARNING << "Codec [" << this->name << "] cannot be initialized";
			this->valid = false;
		}
	}
Exemplo n.º 7
0
size_t
squash_get_uncompressed_size (const char* codec,
                              size_t compressed_length,
                              const uint8_t compressed[SQUASH_ARRAY_PARAM(compressed_length)]) {
  assert (codec != NULL);
  assert (compressed_length > 0);
  assert (compressed != NULL);

  SquashCodec* codec_real = squash_get_codec (codec);
  if (codec_real == NULL)
    return 0;

  return squash_codec_get_uncompressed_size (codec_real, compressed_length, compressed);
}
Exemplo n.º 8
0
/**
 * @brief Decompress a buffer
 *
 * @param codec The name of the codec to use
 * @param[out] decompressed Location to store the decompressed data
 * @param[in,out] decompressed_length Location storing the size of the
 *   @a decompressed buffer on input, replaced with the actual size of
 *   the decompressed data
 * @param compressed The compressed data
 * @param compressed_length Length of the compressed data (in bytes)
 * @param options Decompression options, or *NULL* to use the defaults
 * @return A status code
 */
SquashStatus squash_decompress_with_options (const char* codec,
                                             size_t* decompressed_length,
                                             uint8_t decompressed[SQUASH_ARRAY_PARAM(*decompressed_length)],
                                             size_t compressed_length,
                                             const uint8_t compressed[SQUASH_ARRAY_PARAM(compressed_length)],
                                             SquashOptions* options) {
  SquashCodec* codec_real = squash_get_codec (codec);

  if (codec_real == NULL)
    return squash_error (SQUASH_NOT_FOUND);

  return squash_codec_decompress_with_options (codec_real,
                                               decompressed_length, decompressed,
                                               compressed_length, compressed,
                                               options);
}
Exemplo n.º 9
0
Arquivo: file.c Projeto: jibsen/squash
/**
 * @brief Open an existing stdio file
 *
 * Note that Squash expects to have exclusive access to @a fp.  When
 * possible, Squash will acquire @a fp's lock (using flockfile) in
 * this function and will not release it until the @ref SquashFile
 * instance is destroyed.
 *
 * @param fp the stdio file to use
 * @param codec codec to use
 * @param ... options
 * @return The opened file, or *NULL* on error
 * @see squash_file_steal_codec
 * @see squash_file_steal_with_options
 * @see squash_file_steal_codec_with_options
 */
SquashFile*
squash_file_steal (const char* codec, FILE* fp, ...) {
  va_list ap;
  SquashOptions* options;

  assert (fp != NULL);
  assert (codec != NULL);

  SquashCodec* codec_i = squash_get_codec (codec);
  if (codec_i == NULL)
    return NULL;
  va_start (ap, fp);
  options = squash_options_newv (codec_i, ap);
  va_end (ap);

  return squash_file_steal_codec_with_options (codec_i, fp, options);
}
Exemplo n.º 10
0
static SquashCodec*
benchmark_parse_codec (const char* str, SquashOptions** options) {
  SquashCodec* codec;
  char* s = strdup (str);
  char* sp_outer = NULL;
  char* sp_inner = NULL;
  char* cur_opt = NULL;
  char* cur_key = NULL;
  char* cur_val = NULL;
  SquashStatus res;

  char* name = strtok_r (s, "/", &sp_outer);
  codec = squash_get_codec (name);
  if (codec == NULL)
    return NULL;

  while ((cur_opt = strtok_r (NULL, ",", &sp_outer)) != NULL) {
    if (*options == NULL) {
      assert (codec != NULL);
      *options = squash_options_new (codec, NULL);
      squash_object_ref_sink (*options);
    }

    cur_key = strtok_r (cur_opt, "=", &sp_inner);
    cur_val = strtok_r (NULL,    "=", &sp_inner);

    if (cur_val == NULL) {
      cur_val = cur_key;
      cur_key = "level";
    }

    assert (*options != NULL);
    res = squash_options_parse_option (*options, cur_key, cur_val);
    if (res != SQUASH_OK) {
      fprintf (stderr, "Unable to parse options: %s (%d)\n", squash_status_to_string (res), res);
      exit (EXIT_FAILURE);
    }
  }

  free (s);

  return codec;
}
Exemplo n.º 11
0
Arquivo: file.c Projeto: jibsen/squash
/**
 * @brief Open a file
 *
 * The @a mode parameter will be passed through to @a fopen, so the
 * value must valid.  Note that Squash may attempt to use @a mmap
 * regardless of whether the *m* flag is passed.
 *
 * The file is always assumed to be compressed—calling @ref
 * squash_file_write will always compress, and calling @ref
 * squash_file_read will always decompress.  Note, however, that you
 * cannot mix reading and writing to the same file as you can with a
 * standard *FILE*.
 *
 * @note Error handling for this function is somewhat limited, and it
 * may be difficult to determine the exact nature of problems such as
 * an invalid codec, where errno is not set.  If this is unacceptable
 * you should call @ref squash_get_codec and @ref squash_options_parse
 * yourself and pass the results to @ref
 * squash_file_open_codec_with_options (which will only fail due to
 * the underlying *fopen* failing).
 *
 * @param filename name of the file to open
 * @param mode file mode
 * @param codec codec to use
 * @param ... options
 * @return The opened file, or *NULL* on error
 * @see squash_file_open_codec
 * @see squash_file_open_with_options
 * @see squash_file_open_codec_with_options
 */
SquashFile*
squash_file_open (const char* codec, const char* filename, const char* mode, ...) {
  va_list ap;
  SquashOptions* options;
  SquashCodec* codec_i;

  assert (filename != NULL);
  assert (mode != NULL);
  assert (codec != NULL);

  codec_i = squash_get_codec (codec);
  if (codec_i == NULL)
    return NULL;

  va_start (ap, mode);
  options = squash_options_newv (codec_i, ap);
  va_end (ap);

  return squash_file_open_codec_with_options (codec_i, filename, mode, options);
}
Exemplo n.º 12
0
/**
 * @brief Decompress a buffer with an existing @ref SquashOptions
 *
 * @param codec The name of the codec to use
 * @param[out] decompressed Location to store the decompressed data
 * @param[in,out] decompressed_length Location storing the size of the
 *   @a decompressed buffer on input, replaced with the actual size of
 *   the decompressed data
 * @param compressed The compressed data
 * @param compressed_length Length of the compressed data (in bytes)
 * @param ... A variadic list of key/value option pairs, followed by
 *   *NULL*
 * @return A status code
 */
SquashStatus
squash_decompress (const char* codec,
                   size_t* decompressed_length,
                   uint8_t decompressed[SQUASH_ARRAY_PARAM(*decompressed_length)],
                   size_t compressed_length,
                   const uint8_t compressed[SQUASH_ARRAY_PARAM(compressed_length)],
                   ...) {
  SquashOptions* options;
  va_list ap;
  SquashCodec* codec_real = squash_get_codec (codec);

  if (codec_real == NULL)
    return squash_error (SQUASH_NOT_FOUND);

  va_start (ap, compressed);
  options = squash_options_newv (codec_real, ap);
  va_end (ap);

  return squash_codec_decompress_with_options (codec_real,
                                            decompressed_length, decompressed,
                                            compressed_length, compressed,
                                            options);
}
Exemplo n.º 13
0
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;
}
Exemplo n.º 14
0
int main(int argc, char *argv[])
{
  int ret;
  off_t size = 0, size_c = 0;
  size_t len, len_c;
  FILE *in;
  void *in_buf, *out_buf;
  uint64_t full_len, out_len;
  struct stat st;
  void *in_map = NULL;
  void *out_map = NULL;
  int out_fd;
  double time = 0.0;
#ifdef USE_MMAP
  uint64_t out_mapped;
#endif
  
  struct timespec start, stop, start_full, stop_full;
  
  if (argc < 4 || argc > 6)
    help();
  if (argc == 5 || argc == 6)
    assert(*argv[1] == 's');
  if (strlen(argv[1]) != 1)
    help();
  
  //file handling
  in = fopen(argv[2], "r");
  assert(in);

  out_fd = open(argv[3], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
  assert(out_fd != -1);
  
  fstat(fileno(in), &st);
  full_len = st.st_size;
  
#ifdef USE_MMAP
  switch(argv[1][0]) {
    case 'e' : out_mapped = full_len*2;   break;
    case 'd' : out_mapped = CHUNK_SIZE*8; break;
    case 'm' : out_mapped = full_len; break;
  }
#endif
  
  //initialisation
#ifndef USE_MMAP_READ
  in_buf = malloc(CHUNK_SIZE);
#else
  in_map = mmap(NULL, full_len, PROT_READ, MAP_SHARED, fileno(in), 0);
  if (in_map == MAP_FAILED) {
    printf("WARNING: mmap failed for %s, using regular write\n", argv[2]);
    in_map = NULL;
    in_buf = malloc(2*CHUNK_SIZE);
  }
  else
    in_buf = in_map;
#endif
#ifndef USE_MMAP_WRITE
  out_buf = malloc(2*CHUNK_SIZE);
#else
  ftruncate(out_fd, out_mapped);
  out_map = mmap(NULL, out_mapped, PROT_WRITE, MAP_SHARED, out_fd, 0);
  if (out_map == MAP_FAILED) {
    printf("WARNING: mmap failed for %s, using regular write\n", argv[3]);
    out_map = NULL;
    out_buf = malloc(2*CHUNK_SIZE);
  }
  else
    out_buf = out_map;
#endif
  
  switch(argv[1][0]) {
#ifdef BBP_USE_SIMDCOMP
    case 'e' :
      clock_gettime(CLOCK_MONOTONIC, &start_full);
#ifndef USE_MMAP_READ
      while ((len = fread(in_buf, 1, CHUNK_SIZE, in))) {
#else
      for(in_buf=in_map;in_buf-in_map<full_len;in_buf+=CHUNK_SIZE) {
        len = full_len - (in_buf-in_map);
        if (len > CHUNK_SIZE)
          len = CHUNK_SIZE;
        if (len % (128*4))
          break;
#endif
        int b;
	clock_gettime(CLOCK_MONOTONIC, &start);
        //compression
        for(b=0;b<BENCHMARK_ITERATIONS;b++)
          len_c = compress(in_buf, len/4, out_buf);
	clock_gettime(CLOCK_MONOTONIC, &stop);
        time += ms_delta(start, stop);
	size += len;
	size_c += len_c;
        
        if (!out_map) {
          len = write(out_fd, out_buf, len_c);
          assert(len == len_c);
        }
        else
          out_buf += len_c;
      }
      out_len = size_c;
      
      clock_gettime(CLOCK_MONOTONIC, &stop_full);
      printf("compressed at %.3f MB/s / %.3fMB/s ratio %.2f\n",(float)size*BENCHMARK_ITERATIONS/1024/1024*1000/time, (float)size/1024/1024*1000/ms_delta(start_full, stop_full), (float)size/size_c);
      printf("%.2f %.3f simdcomp\n",(float)size/size_c, (float)size*BENCHMARK_ITERATIONS/1024/1024*1000/time);
      break;
#endif
#ifdef BBP_USE_SQUASH
    case 's' :
      assert(argc == 5 || argc == 6);
      SquashCodec* codec = squash_get_codec(argv[4]);
      SquashOptions *options;
      assert(codec);
      
      if (argc == 6) {
        options = squash_options_new(codec, NULL);
        ret = squash_options_parse_option(options, "level", argv[5]);
        if (ret != SQUASH_OK) {
          if (ret == SQUASH_BAD_PARAM) printf("ERROR: SQUASH: bad param\n");
          if (ret == SQUASH_BAD_VALUE) printf("ERROR: SQUASH: bad value\n");
          break;
        }
      }
      else
        options = NULL;
      
      clock_gettime(CLOCK_MONOTONIC, &start_full);
#ifndef USE_MMAP_READ
      while ((len = fread(in_buf, 1, CHUNK_SIZE, in))) {
#else
      for(in_buf=in_map;in_buf-in_map<full_len;in_buf+=CHUNK_SIZE) {
        len = full_len - (in_buf-in_map);
        if (len > CHUNK_SIZE)
          len = CHUNK_SIZE;
#endif
	clock_gettime(CLOCK_MONOTONIC, &start);
        //compression
        len_c = 2*CHUNK_SIZE;
        if (options)
          squash_codec_compress_with_options(codec, &len_c, out_buf, len, in_buf, options);
        else
          ret = squash_compress(argv[4], &len_c, out_buf, len, in_buf, NULL);
        if (ret != SQUASH_OK)
          printf("ERROR: SQUASH (%s): %s\n",squash_status_to_string(ret), argv[4]);
	clock_gettime(CLOCK_MONOTONIC, &stop);
        time += ms_delta(start, stop);
	size += len;
	size_c += len_c;
        
        if (!out_map) {
          len = write(out_fd, out_buf, len_c);
          assert(len == len_c);
        }
        else
          out_buf += len_c;
      }
      out_len = size_c;
      
      clock_gettime(CLOCK_MONOTONIC, &stop_full);
      printf("compressed at %.3fMB/s / %.3fMB/s ratio %.2f\n",(float)size/1024/1024*1000/time, (float)size/1024/1024*1000/ms_delta(start_full, stop_full), (float)size/size_c);
      printf("%.2f %.3f %s\n",(float)size/size_c, (float)size/1024/1024*1000/time, argv[4]);
      break;
#endif
  }
  
  if (in_map)
    munmap(in_map, full_len);
  fclose(in);
  
  if (out_map)
    munmap(out_map, full_len*2);
  ftruncate(out_fd, out_len);
  close(out_fd);

  
  return EXIT_SUCCESS;
}
Exemplo n.º 15
0
int main (int argc, char** argv) {
  SquashCodec* codec;
  const char* uncompressed;
  size_t uncompressed_length;
  size_t compressed_length;
  uint8_t* compressed;
  size_t decompressed_length;
  char* decompressed;

  if (argc != 3) {
    fprintf (stderr, "USAGE: %s ALGORITHM STRING\n", argv[0]);
    return EXIT_FAILURE;
  }

  codec = squash_get_codec (argv[1]);
  if (codec == NULL) {
    fprintf (stderr, "Unable to find algorithm '%s'.\n", argv[1]);
    return EXIT_FAILURE;
  }

  uncompressed = argv[2];
  uncompressed_length = strlen (uncompressed);
  compressed_length = squash_codec_get_max_compressed_size (codec, uncompressed_length);
  compressed = (uint8_t*) malloc (compressed_length);
  decompressed_length = uncompressed_length + 1;
  decompressed = (char*) malloc (uncompressed_length + 1);

  SquashStatus res =
    squash_codec_compress (codec,
                           &compressed_length, compressed,
                           uncompressed_length, (const uint8_t*) uncompressed,
                           NULL);
  if (res != SQUASH_OK) {
    fprintf (stderr, "Unable to compress data [%d]: %s\n",
             res, squash_status_to_string (res));
    return EXIT_FAILURE;
  }

  fprintf (stdout, "Compressed a %u byte buffer to %u bytes.\n",
           (unsigned int) uncompressed_length, (unsigned int) compressed_length);

  res = squash_codec_decompress (codec,
                                 &decompressed_length, (uint8_t*) decompressed,
                                 compressed_length, compressed, NULL);

  if (res != SQUASH_OK) {
    fprintf (stderr, "Unable to decompress data [%d]: %s\n",
             res, squash_status_to_string (res));
    return EXIT_FAILURE;
  }

  /* Notice that we didn't compress the *NULL* byte at the end of the
     string.  We could have, it's just a waste to do so. */
  decompressed[decompressed_length] = '\0';

  if (strcmp (decompressed, uncompressed) != 0) {
    fprintf (stderr, "Bad decompressed data.\n");
    EXIT_FAILURE;
  }

  fprintf (stdout, "Successfully decompressed.\n");

  return EXIT_SUCCESS;
}