コード例 #1
0
ファイル: codec.c プロジェクト: eevans/squash-deb
/**
 * @brief Decompress a buffer with an existing @ref SquashOptions
 *
 * @param codec 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 Compression options
 * @return A status code
 */
SquashStatus
squash_codec_decompress_with_options (SquashCodec* 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) {
  SquashCodecImpl* impl = NULL;

  assert (codec != NULL);

  impl = squash_codec_get_impl (codec);
  if (impl == NULL)
    return squash_error (SQUASH_UNABLE_TO_LOAD);

  if (decompressed == compressed)
    return squash_error (SQUASH_INVALID_BUFFER);

  if (impl->decompress_buffer != NULL) {
    SquashStatus res;
    res = impl->decompress_buffer (codec,
                                   decompressed_length, decompressed,
                                   compressed_length, compressed,
                                   squash_object_ref (options));
    squash_object_unref (options);
    return res;
  } else {
    SquashStatus status;
    SquashStream* stream;

    stream = squash_codec_create_stream_with_options (codec, SQUASH_STREAM_DECOMPRESS, options);
    stream->next_in = compressed;
    stream->avail_in = compressed_length;
    stream->next_out = decompressed;
    stream->avail_out = *decompressed_length;

    do {
      status = squash_stream_process (stream);
    } while (status == SQUASH_PROCESSING);

    if (status == SQUASH_END_OF_STREAM) {
      status = SQUASH_OK;
      *decompressed_length = stream->total_out;
    } else if (status == SQUASH_OK) {
      do {
        status = squash_stream_finish (stream);
      } while (status == SQUASH_PROCESSING);

      if (status == SQUASH_OK) {
        *decompressed_length = stream->total_out;
      }
    }

    assert (stream->stream_type == SQUASH_STREAM_DECOMPRESS);
    squash_object_unref (stream);

    return status;
  }
}
コード例 #2
0
ファイル: codec.c プロジェクト: Bulat-Ziganshin/squash
/**
 * @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 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.
 */
size_t
squash_codec_get_max_compressed_size (SquashCodec* codec, size_t uncompressed_length) {
  SquashCodecImpl* impl = NULL;

  assert (codec != NULL);

  impl = squash_codec_get_impl (codec);
  if (impl != NULL && impl->get_max_compressed_size != NULL) {
    return impl->get_max_compressed_size (codec, uncompressed_length);
  } else {
    return 0;
  }
}
コード例 #3
0
ファイル: codec.c プロジェクト: Bulat-Ziganshin/squash
/**
 * @brief Get the uncompressed size of the compressed buffer
 *
 * This function is only useful for codecs with the @ref
 * SQUASH_CODEC_INFO_KNOWS_UNCOMPRESSED_SIZE flag set.  For situations
 * where the codec does not know the uncompressed size, *0* will be
 * returned.
 *
 * @param codec The codec
 * @param compressed The compressed data
 * @param compressed_length The length of the compressed data
 * @return The uncompressed size, or *0* if unknown
 */
size_t
squash_codec_get_uncompressed_size (SquashCodec* codec,
                                    size_t compressed_length,
                                    const uint8_t compressed[SQUASH_ARRAY_PARAM(compressed_length)]) {
  SquashCodecImpl* impl = NULL;

  assert (codec != NULL);

  impl = squash_codec_get_impl (codec);
  if (impl != NULL && impl->get_uncompressed_size != NULL) {
    return impl->get_uncompressed_size (codec, compressed_length, compressed);
  } else {
    return 0;
  }
}
コード例 #4
0
ファイル: codec.c プロジェクト: Bulat-Ziganshin/squash
/**
 * @brief Create a new stream with existing @ref SquashOptions
 *
 * @param codec The codec
 * @param stream_type The direction of the stream
 * @param options The options for the stream, or *NULL* to use the
 *     defaults
 * @return A new stream, or *NULL* on failure
 */
SquashStream*
squash_codec_create_stream_with_options (SquashCodec* codec, SquashStreamType stream_type, SquashOptions* options) {
  SquashCodecImpl* impl = NULL;

  assert (codec != NULL);
  assert (stream_type == SQUASH_STREAM_COMPRESS || stream_type == SQUASH_STREAM_DECOMPRESS);

  impl = squash_codec_get_impl (codec);
  if (impl == NULL) {
    return NULL;
  }

  if (impl->create_stream != NULL) {
    return impl->create_stream (codec, stream_type, options);
  } else {
    if (impl->process_stream == NULL) {
      return (SquashStream*) squash_buffer_stream_new (codec, stream_type, options);
    } else {
      return NULL;
    }
  }
}
コード例 #5
0
ファイル: stream.c プロジェクト: Dr-Emann/squash
static SquashStatus
squash_stream_process_internal (SquashStream* stream, SquashOperation operation) {
    SquashCodec* codec;
    SquashCodecImpl* impl = NULL;
    SquashStatus res = SQUASH_OK;
    SquashOperation current_operation = SQUASH_OPERATION_PROCESS;

    assert (stream != NULL);
    codec = stream->codec;
    assert (codec != NULL);
    impl = squash_codec_get_impl (codec);
    assert (impl != NULL);

    /* Flush is optional, so return an error if it doesn't exist but
       flushing was requested. */
    if (SQUASH_UNLIKELY(operation == SQUASH_OPERATION_FLUSH && ((impl->info & SQUASH_CODEC_INFO_CAN_FLUSH) == 0))) {
        return squash_error (SQUASH_INVALID_OPERATION);
    }

    /* In order to take some of the load off of the plugins, there is
       some extra logic here which may seem a bit disorienting at first
       glance.  Basically, instead of requiring that plugins handle
       flushing or finishing with arbitrarily large inputs, we first try
       to process as much input as we can.  So, when someone calls
       squash_stream_flush or squash_stream finish Squash may, depending
       on the stream state, first call the process function.  Note that
       Squash will not flush a stream before finishing it (unless there
       is logic to do so in the plugin) as it could cause an increase in
       the output size (it does with zlib).

       One interesting consequence of this is that the stream_state
       field may not be what you're expecting.  If an earlier operation
       returned SQUASH_PROCESSING, stream_type may never transition to
       the new value.  In this case, the stream_type does accurately
       represent the state of the stream, though it probably isn't wise
       to depend on that behavior. */

    if ((operation == SQUASH_OPERATION_PROCESS && stream->state > SQUASH_STREAM_STATE_RUNNING) ||
            (operation == SQUASH_OPERATION_FLUSH   && stream->state > SQUASH_STREAM_STATE_FLUSHING) ||
            (operation == SQUASH_OPERATION_FINISH  && stream->state > SQUASH_STREAM_STATE_FINISHING)) {
        return squash_error (SQUASH_STATE);
    }

    switch (stream->state) {
    case SQUASH_STREAM_STATE_IDLE:
    case SQUASH_STREAM_STATE_RUNNING:
        current_operation = SQUASH_OPERATION_PROCESS;
        break;
    case SQUASH_STREAM_STATE_FLUSHING:
        current_operation = SQUASH_OPERATION_FLUSH;
        break;
    case SQUASH_STREAM_STATE_FINISHING:
        current_operation = SQUASH_OPERATION_FINISH;
        break;
    case SQUASH_STREAM_STATE_FINISHED:
        current_operation = (SquashOperation) (SQUASH_OPERATION_FINISH + 1);
        break;
    }

    if (SQUASH_UNLIKELY(current_operation > operation)) {
        return squash_error (SQUASH_STATE);
    }

    const size_t avail_in = stream->avail_in;
    const size_t avail_out = stream->avail_out;

    /* Some libraries (like zlib) will realize that we're not providing
       it any room for output and are eager to tell us that we don't
       have any space instead of decoding the stream enough to know if we
       actually need that space.

       In cases where this might be problematic, we provide a
       single-byte buffer to the plugin instead.  If anything actually
       gets written to it then we'll return an error
       (SQUASH_BUFFER_FULL), which is non-recoverable.

       There are a few cases where this might reasonably be a problem:

        * Decompression streams which know the exact size of the
          decompressed output, when using codecs which contain extra
          data at the end, such as a footer or EOS marker.

        * Compression streams writing to a fixed buffer with a size of
          less than or equal to max_compressed_size bytes.  This is a
          pretty reasonable thing to do, since you might want to only
          bother using compression if you can achieve a certain ratio.

       For consumers which don't satisfy either of these conditions,
       this code should never be reached. */

    uint8_t* next_out = NULL;
    uint8_t output_sbb = 0;
    if (stream->avail_out == 0) {
        next_out = stream->next_out;
        stream->avail_out = 1;
        stream->next_out = &output_sbb;
    }

    while (current_operation <= operation) {
        if (current_operation == SQUASH_OPERATION_PROCESS) {
            if (stream->avail_in == 0 && stream->state == SQUASH_STREAM_STATE_IDLE) {
                res = SQUASH_OK;
            } else {
                stream->state = SQUASH_STREAM_STATE_RUNNING;

                if (impl->process_stream != NULL) {
                    res = impl->process_stream (stream, current_operation);
                } else if (impl->splice != NULL) {
                    res = squash_stream_send_to_thread (stream, current_operation);
                } else {
                    res = squash_buffer_stream_process ((SquashBufferStream*) stream);
                }
            }

            switch ((int) res) {
            case SQUASH_OK:
                stream->state = SQUASH_STREAM_STATE_IDLE;
                break;
            case SQUASH_PROCESSING:
                stream->state = SQUASH_STREAM_STATE_RUNNING;
                break;
            case SQUASH_END_OF_STREAM:
                stream->state = SQUASH_STREAM_STATE_FINISHED;
                break;
            default:
                return res;
            }
        } else if (current_operation == SQUASH_OPERATION_FLUSH) {
            stream->state = SQUASH_STREAM_STATE_FLUSHING;

            if (current_operation == operation) {
                if ((impl->info & SQUASH_CODEC_INFO_CAN_FLUSH) == SQUASH_CODEC_INFO_CAN_FLUSH) {
                    assert (impl->process_stream != NULL);

                    res = impl->process_stream (stream, current_operation);
                } else {
                    /* We aready checked to make sure the stream is flushable if
                       the user called flush directly, so if this code is
                       reached the user didn't call flush, they called finish
                       which attempts to flush internally.  Just pretend it
                       worked so we can proceed to finishing. */
                    res = SQUASH_OK;
                }
            }

            switch ((int) res) {
            case SQUASH_OK:
                stream->state = SQUASH_STREAM_STATE_IDLE;
                break;
            case SQUASH_PROCESSING:
                stream->state = SQUASH_STREAM_STATE_FLUSHING;
                break;
            case SQUASH_END_OF_STREAM:
                stream->state = SQUASH_STREAM_STATE_FINISHED;
                break;
            default:
                return res;
            }
        } else if (current_operation == SQUASH_OPERATION_FINISH) {
            stream->state = SQUASH_STREAM_STATE_FINISHING;

            if (impl->process_stream != NULL) {
                res = impl->process_stream (stream, current_operation);
            } else if (impl->splice) {
                res = squash_stream_send_to_thread (stream, current_operation);
            } else {
                res = squash_buffer_stream_finish ((SquashBufferStream*) stream);
            }

            /* Plugins *should* return SQUASH_OK, not SQUASH_END_OF_STREAM,
               from the finish function, but it's an easy mistake to make
               (and correct), so... */
            if (SQUASH_UNLIKELY(res == SQUASH_END_OF_STREAM)) {
                res = SQUASH_OK;
            }

            switch ((int) res) {
            case SQUASH_OK:
                stream->state = SQUASH_STREAM_STATE_FINISHED;
                break;
            case SQUASH_PROCESSING:
                stream->state = SQUASH_STREAM_STATE_FINISHING;
                break;
            default:
                return res;
            }
        }

        /* Check our internal single byte buffer */
        if (next_out != 0) {
            if (SQUASH_UNLIKELY(stream->avail_out == 0)) {
                res = squash_error (SQUASH_BUFFER_FULL);
            }
        }

        if (res == SQUASH_PROCESSING) {
            break;
        } else if (res == SQUASH_END_OF_STREAM || (current_operation == SQUASH_OPERATION_FINISH && res == SQUASH_OK)) {
            assert (stream->state == SQUASH_STREAM_STATE_FINISHED);
            current_operation++;
            break;
        } else if (res == SQUASH_OK) {
            current_operation++;
        } else {
            break;
        }
    }

    if (next_out != 0) {
        stream->avail_out = 0;
        stream->next_out = next_out;
    }

    stream->total_in += (avail_in - stream->avail_in);
    stream->total_out += (avail_out - stream->avail_out);

    return res;
}
コード例 #6
0
ファイル: codec.c プロジェクト: Bulat-Ziganshin/squash
/**
 * @brief Compress a buffer with an existing @ref SquashOptions
 *
 * @param codec The codec to use
 * @param[out] compressed Location to store the compressed data
 * @param[in,out] compressed_length Location storing the size of the
 *   @a compressed buffer on input, replaced with the actual size of
 *   the compressed data
 * @param uncompressed The uncompressed data
 * @param uncompressed_length Length of the uncompressed data (in bytes)
 * @param options Compression options
 * @return A status code
 */
SquashStatus
squash_codec_compress_with_options (SquashCodec* codec,
                                    size_t* compressed_length,
                                    uint8_t compressed[SQUASH_ARRAY_PARAM(*compressed_length)],
                                    size_t uncompressed_length,
                                    const uint8_t uncompressed[SQUASH_ARRAY_PARAM(uncompressed_length)],
                                    SquashOptions* options) {
  SquashCodecImpl* impl = NULL;

  assert (codec != NULL);

  assert (compressed != NULL);
  assert (uncompressed != NULL);

  impl = squash_codec_get_impl (codec);
  if (impl == NULL)
    return squash_error (SQUASH_UNABLE_TO_LOAD);

  if (compressed == uncompressed)
    return squash_error (SQUASH_INVALID_BUFFER);

  if (impl->compress_buffer ||
      impl->compress_buffer_unsafe) {
    size_t max_compressed_length = squash_codec_get_max_compressed_size (codec, uncompressed_length);

    if (*compressed_length >= max_compressed_length) {
      if (impl->compress_buffer_unsafe != NULL) {
        return impl->compress_buffer_unsafe (codec,
                                              compressed_length, compressed,
                                              uncompressed_length, uncompressed,
                                              options);
      } else {
        return impl->compress_buffer (codec,
                                       compressed_length, compressed,
                                       uncompressed_length, uncompressed,
                                       options);
      }
    } else if (impl->compress_buffer != NULL) {
      return impl->compress_buffer (codec,
                                     compressed_length, compressed,
                                     uncompressed_length, uncompressed,
                                     options);
    } else {
      SquashStatus status;
      uint8_t* tmp_buf = malloc (max_compressed_length);
      if (tmp_buf == NULL)
        return squash_error (SQUASH_MEMORY);

      status = impl->compress_buffer_unsafe (codec,
                                              &max_compressed_length, tmp_buf,
                                              uncompressed_length, uncompressed,
                                              options);
      if (status == SQUASH_OK) {
        if (*compressed_length < max_compressed_length) {
          *compressed_length = max_compressed_length;
          free (tmp_buf);
          return squash_error (SQUASH_BUFFER_FULL);
        } else {
          *compressed_length = max_compressed_length;
          memcpy (compressed, tmp_buf, max_compressed_length);
          free (tmp_buf);
          return SQUASH_OK;
        }
      } else {
        free (tmp_buf);
        return status;
      }
    }
  } else {
    SquashStatus status;
    SquashStream* stream;

    stream = squash_codec_create_stream_with_options (codec, SQUASH_STREAM_COMPRESS, options);
    if (stream == NULL)
      return squash_error (SQUASH_FAILED);

    stream->next_in = uncompressed;
    stream->avail_in = uncompressed_length;
    stream->next_out = compressed;
    stream->avail_out = *compressed_length;

    do {
      status = squash_stream_process (stream);
    } while (status == SQUASH_PROCESSING);

    if (status != SQUASH_OK) {
      squash_object_unref (stream);
      return status;
    }

    do {
      status = squash_stream_finish (stream);
    } while (status == SQUASH_PROCESSING);

    if (status != SQUASH_OK) {
      squash_object_unref (stream);
      return status;
    }

    *compressed_length = stream->total_out;
    squash_object_unref (stream);
    return status;
  }
}
コード例 #7
0
ファイル: codec.c プロジェクト: eevans/squash-deb
/**
 * @brief Get a list of options applicable to the codec
 *
 * @param codec The codec
 * @return a list of options, terminated by an option with a NULL name
 */
const SquashOptionInfo*
squash_codec_get_option_info (SquashCodec* codec) {
  SquashCodecImpl* impl = squash_codec_get_impl (codec);
  return SQUASH_LIKELY(impl != NULL) ? impl->options : NULL;
}