Add lzma_filters_copy().
authorLasse Collin <lasse.collin@tukaani.org>
Fri, 16 Oct 2009 22:47:07 +0000 (01:47 +0300)
committerLasse Collin <lasse.collin@tukaani.org>
Fri, 16 Oct 2009 22:47:07 +0000 (01:47 +0300)
This will be needed internally by liblzma once I fix
a design mistake in the encoder API. This function may
be useful to applications too so it's good to export it.

src/liblzma/api/lzma/filter.h
src/liblzma/common/filter_common.c

index 8d0db96..c3a2099 100644 (file)
@@ -95,6 +95,37 @@ extern LZMA_API(lzma_bool) lzma_filter_decoder_is_supported(lzma_vli id)
 
 
 /**
+ * \brief       Copy the filters array
+ *
+ * Copy the Filter IDs and filter-specific options from src to dest.
+ * Up to LZMA_FILTERS_MAX filters are copied, plus the terminating
+ * .id == LZMA_VLI_UNKNOWN. Thus, dest should have at least
+ * LZMA_FILTERS_MAX + 1 elements space unless the caller knows that
+ * src is smaller than that.
+ *
+ * Unless the filter-specific options is NULL, the Filter ID has to be
+ * supported by liblzma, because liblzma needs to know the size of every
+ * filter-specific options structure. The filter-specific options are not
+ * validated. If options is NULL, any unsupported Filter IDs are copied
+ * without returning an error.
+ *
+ * Old filter-specific options in dest are not freed, so dest doesn't
+ * need to be initialized by the caller in any way.
+ *
+ * If an error occurs, memory possibly already allocated by this function
+ * is always freed.
+ *
+ * \return      - LZMA_OK
+ *              - LZMA_MEM_ERROR
+ *              - LZMA_OPTIONS_ERROR: Unsupported Filter ID and its options
+ *                is not NULL.
+ *              - LZMA_PROG_ERROR: src or dest is NULL.
+ */
+extern LZMA_API(lzma_ret) lzma_filters_dup(const lzma_filter *src,
+               lzma_filter *dest, lzma_allocator *allocator);
+
+
+/**
  * \brief       Calculate rough memory requirements for raw encoder
  *
  * Because the calculation is rough, this function can be used to calculate
index 4762460..c839e23 100644 (file)
@@ -17,6 +17,9 @@ static const struct {
        /// Filter ID
        lzma_vli id;
 
+       /// Size of the filter-specific options structure
+       size_t options_size;
+
        /// True if it is OK to use this filter as non-last filter in
        /// the chain.
        bool non_last_ok;
@@ -34,6 +37,7 @@ static const struct {
 #if defined (HAVE_ENCODER_LZMA1) || defined(HAVE_DECODER_LZMA1)
        {
                .id = LZMA_FILTER_LZMA1,
+               .options_size = sizeof(lzma_options_lzma),
                .non_last_ok = false,
                .last_ok = true,
                .changes_size = true,
@@ -42,6 +46,7 @@ static const struct {
 #ifdef HAVE_DECODER_LZMA2
        {
                .id = LZMA_FILTER_LZMA2,
+               .options_size = sizeof(lzma_options_lzma),
                .non_last_ok = false,
                .last_ok = true,
                .changes_size = true,
@@ -50,6 +55,7 @@ static const struct {
 #if defined(HAVE_ENCODER_SUBBLOCK) || defined(HAVE_DECODER_SUBBLOCK)
        {
                .id = LZMA_FILTER_SUBBLOCK,
+               .options_size = sizeof(lzma_options_subblock),
                .non_last_ok = true,
                .last_ok = true,
                .changes_size = true,
@@ -58,6 +64,7 @@ static const struct {
 #ifdef HAVE_DECODER_X86
        {
                .id = LZMA_FILTER_X86,
+               .options_size = sizeof(lzma_options_bcj),
                .non_last_ok = true,
                .last_ok = false,
                .changes_size = false,
@@ -66,6 +73,7 @@ static const struct {
 #if defined(HAVE_ENCODER_POWERPC) || defined(HAVE_DECODER_POWERPC)
        {
                .id = LZMA_FILTER_POWERPC,
+               .options_size = sizeof(lzma_options_bcj),
                .non_last_ok = true,
                .last_ok = false,
                .changes_size = false,
@@ -74,6 +82,7 @@ static const struct {
 #ifdef HAVE_DECODER_IA64
        {
                .id = LZMA_FILTER_IA64,
+               .options_size = sizeof(lzma_options_bcj),
                .non_last_ok = true,
                .last_ok = false,
                .changes_size = false,
@@ -82,6 +91,7 @@ static const struct {
 #if defined(HAVE_ENCODER_ARM) || defined(HAVE_DECODER_ARM)
        {
                .id = LZMA_FILTER_ARM,
+               .options_size = sizeof(lzma_options_bcj),
                .non_last_ok = true,
                .last_ok = false,
                .changes_size = false,
@@ -90,6 +100,7 @@ static const struct {
 #if defined(HAVE_ENCODER_ARMTHUMB) || defined(HAVE_DECODER_ARMTHUMB)
        {
                .id = LZMA_FILTER_ARMTHUMB,
+               .options_size = sizeof(lzma_options_bcj),
                .non_last_ok = true,
                .last_ok = false,
                .changes_size = false,
@@ -98,6 +109,7 @@ static const struct {
 #if defined(HAVE_ENCODER_SPARC) || defined(HAVE_DECODER_SPARC)
        {
                .id = LZMA_FILTER_SPARC,
+               .options_size = sizeof(lzma_options_bcj),
                .non_last_ok = true,
                .last_ok = false,
                .changes_size = false,
@@ -106,6 +118,7 @@ static const struct {
 #if defined(HAVE_ENCODER_DELTA) || defined(HAVE_DECODER_DELTA)
        {
                .id = LZMA_FILTER_DELTA,
+               .options_size = sizeof(lzma_options_delta),
                .non_last_ok = true,
                .last_ok = false,
                .changes_size = false,
@@ -117,6 +130,75 @@ static const struct {
 };
 
 
+extern LZMA_API(lzma_ret)
+lzma_filters_dup(const lzma_filter *src, lzma_filter *dest,
+               lzma_allocator *allocator)
+{
+       if (src == NULL || dest == NULL)
+               return LZMA_PROG_ERROR;
+
+       lzma_ret ret;
+       size_t i;
+       for (i = 0; src[i].id != LZMA_VLI_UNKNOWN; ++i) {
+               // There must be a maximum of four filters plus
+               // the array terminator.
+               if (i == LZMA_FILTERS_MAX) {
+                       ret = LZMA_OPTIONS_ERROR;
+                       goto error;
+               }
+
+               dest[i].id = src[i].id;
+
+               if (src[i].options == NULL) {
+                       dest[i].options = NULL;
+               } else {
+                       // See if the filter is supported only when the
+                       // options is not NULL. This might be convenient
+                       // sometimes if the app is actually copying only
+                       // a partial filter chain with a place holder ID.
+                       //
+                       // When options is not NULL, the Filter ID must be
+                       // supported by us, because otherwise we don't know
+                       // how big the options are.
+                       size_t j;
+                       for (j = 0; src[i].id != features[j].id; ++j) {
+                               if (features[j].id == LZMA_VLI_UNKNOWN) {
+                                       ret = LZMA_OPTIONS_ERROR;
+                                       goto error;
+                               }
+                       }
+
+                       // Allocate and copy the options.
+                       dest[i].options = lzma_alloc(features[j].options_size,
+                                       allocator);
+                       if (dest[i].options == NULL) {
+                               ret = LZMA_MEM_ERROR;
+                               goto error;
+                       }
+
+                       memcpy(dest[i].options, src[i].options,
+                                       features[j].options_size);
+               }
+       }
+
+       // Terminate the filter array.
+       assert(i <= LZMA_FILTERS_MAX + 1);
+       dest[i].id = LZMA_VLI_UNKNOWN;
+       dest[i].options = NULL;
+
+       return LZMA_OK;
+
+error:
+       // Free the options which we have already allocated.
+       while (i-- > 0) {
+               lzma_free(dest[i].options, allocator);
+               dest[i].options = NULL;
+       }
+
+       return ret;
+}
+
+
 static lzma_ret
 validate_chain(const lzma_filter *filters, size_t *count)
 {