xz/src/liblzma/common/filter_flags_encoder.c

358 lines
8.4 KiB
C
Raw Normal View History

2007-12-08 22:42:33 +00:00
///////////////////////////////////////////////////////////////////////////////
//
/// \file filter_flags_encoder.c
/// \brief Decodes a Filter Flags field
//
// Copyright (C) 2007 Lasse Collin
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
///////////////////////////////////////////////////////////////////////////////
#include "common.h"
#include "lzma_encoder.h"
#include "fastpos.h"
2007-12-08 22:42:33 +00:00
/// \brief Calculates the size of the Filter Properties field
///
/// This currently can return only LZMA_OK or LZMA_HEADER_ERROR, but
/// with some new filters it may return also LZMA_PROG_ERROR.
static lzma_ret
get_properties_size(uint32_t *size, const lzma_options_filter *options)
{
lzma_ret ret = LZMA_OK;
switch (options->id) {
#ifdef HAVE_FILTER_COPY
case LZMA_FILTER_COPY:
*size = 0;
break;
#endif
#ifdef HAVE_FILTER_SUBBLOCK
case LZMA_FILTER_SUBBLOCK:
*size = 0;
break;
#endif
#ifdef HAVE_FILTER_SIMPLE
# ifdef HAVE_FILTER_X86
case LZMA_FILTER_X86:
# endif
# ifdef HAVE_FILTER_POWERPC
case LZMA_FILTER_POWERPC:
# endif
# ifdef HAVE_FILTER_IA64
case LZMA_FILTER_IA64:
# endif
# ifdef HAVE_FILTER_ARM
case LZMA_FILTER_ARM:
# endif
# ifdef HAVE_FILTER_ARMTHUMB
case LZMA_FILTER_ARMTHUMB:
# endif
# ifdef HAVE_FILTER_SPARC
case LZMA_FILTER_SPARC:
# endif
if (options->options == NULL || ((const lzma_options_simple *)(
options->options))->start_offset == 0)
*size = 0;
else
*size = 4;
break;
#endif
#ifdef HAVE_FILTER_DELTA
case LZMA_FILTER_DELTA:
*size = 1;
break;
#endif
#ifdef HAVE_FILTER_LZMA
case LZMA_FILTER_LZMA:
*size = 2;
break;
#endif
default:
// Unknown filter - if the Filter ID is a proper VLI,
// return LZMA_HEADER_ERROR instead of LZMA_PROG_ERROR,
// because it's possible that we just don't have support
// compiled in for the requested filter.
ret = options->id <= LZMA_VLI_VALUE_MAX
? LZMA_HEADER_ERROR : LZMA_PROG_ERROR;
break;
}
return ret;
}
extern LZMA_API lzma_ret
lzma_filter_flags_size(uint32_t *size, const lzma_options_filter *options)
{
// Get size of Filter Properties.
uint32_t prop_size;
const lzma_ret ret = get_properties_size(&prop_size, options);
if (ret != LZMA_OK)
return ret;
// Size of Filter ID field if it exists.
size_t id_size;
size_t prop_size_size;
if (options->id < 0xE0
&& (lzma_vli)(prop_size) == options->id / 0x20) {
// ID and Size of Filter Properties fit into Misc.
id_size = 0;
prop_size_size = 0;
} else {
// At least Filter ID is stored using the External ID field.
id_size = lzma_vli_size(options->id);
if (id_size == 0)
return LZMA_PROG_ERROR;
if (prop_size <= 30) {
// Size of Filter Properties fits into Misc still.
prop_size_size = 0;
} else {
// The Size of Filter Properties field is used too.
prop_size_size = lzma_vli_size(prop_size);
if (prop_size_size == 0)
return LZMA_PROG_ERROR;
}
}
// 1 is for the Misc field.
*size = 1 + id_size + prop_size_size + prop_size;
return LZMA_OK;
}
#ifdef HAVE_FILTER_SIMPLE
/// Encodes Filter Properties of the so called simple filters
static lzma_ret
properties_simple(uint8_t *out, size_t *out_pos, size_t out_size,
const lzma_options_simple *options)
{
if (options == NULL || options->start_offset == 0)
return LZMA_OK;
if (out_size - *out_pos < 4)
return LZMA_BUF_ERROR;
for (size_t i = 0; i < 4; ++i)
out[(*out_pos)++] = options->start_offset >> (i * 8);
return LZMA_OK;
}
#endif
#ifdef HAVE_FILTER_DELTA
/// Encodes Filter Properties of the Delta filter
static lzma_ret
properties_delta(uint8_t *out, size_t *out_pos, size_t out_size,
const lzma_options_delta *options)
{
if (options == NULL)
return LZMA_PROG_ERROR;
// It's possible that newer liblzma versions will support larger
// distance values.
if (options->distance < LZMA_DELTA_DISTANCE_MIN
|| options->distance > LZMA_DELTA_DISTANCE_MAX)
return LZMA_HEADER_ERROR;
if (out_size - *out_pos < 1)
return LZMA_BUF_ERROR;
out[*out_pos] = options->distance - LZMA_DELTA_DISTANCE_MIN;
++*out_pos;
return LZMA_OK;
}
#endif
#ifdef HAVE_FILTER_LZMA
/// Encodes LZMA Properties and Dictionary Flags (two bytes)
static lzma_ret
properties_lzma(uint8_t *out, size_t *out_pos, size_t out_size,
const lzma_options_lzma *options)
{
if (options == NULL)
return LZMA_PROG_ERROR;
if (out_size - *out_pos < 2)
return LZMA_BUF_ERROR;
// LZMA Properties
if (lzma_lzma_encode_properties(options, out + *out_pos))
return LZMA_HEADER_ERROR;
++*out_pos;
// Dictionary flags
//
// Dictionary size is encoded using similar encoding that is used
// internally by LZMA.
2007-12-08 22:42:33 +00:00
//
// This won't work if dictionary size can be zero:
2007-12-08 22:42:33 +00:00
# if LZMA_DICTIONARY_SIZE_MIN < 1
# error LZMA_DICTIONARY_SIZE_MIN cannot be zero.
# endif
uint32_t d = options->dictionary_size;
2007-12-08 22:42:33 +00:00
// Validate it:
if (d < LZMA_DICTIONARY_SIZE_MIN || d > LZMA_DICTIONARY_SIZE_MAX)
2007-12-08 22:42:33 +00:00
return LZMA_HEADER_ERROR;
// Round up to to the next 2^n or 2^n + 2^(n - 1) depending on which
// one is the next:
--d;
d |= d >> 2;
d |= d >> 3;
d |= d >> 4;
d |= d >> 8;
d |= d >> 16;
++d;
// Get the highest two bits using the proper encoding:
out[*out_pos] = get_pos_slot(d) - 1;
2007-12-08 22:42:33 +00:00
++*out_pos;
return LZMA_OK;
}
#endif
extern LZMA_API lzma_ret
lzma_filter_flags_encode(uint8_t *out, size_t *out_pos, size_t out_size,
const lzma_options_filter *options)
{
// Minimum output is one byte (everything fits into Misc).
// The caller should have checked that there is enough output space,
// so we return LZMA_PROG_ERROR instead of LZMA_BUF_ERROR.
if (*out_pos >= out_size)
return LZMA_PROG_ERROR;
// Get size of Filter Properties.
uint32_t prop_size;
lzma_ret ret = get_properties_size(&prop_size, options);
if (ret != LZMA_OK)
return ret;
// Misc, External ID, and Size of Properties
if (options->id < 0xE0
&& (lzma_vli)(prop_size) == options->id / 0x20) {
// ID and Size of Filter Properties fit into Misc.
out[*out_pos] = options->id;
++*out_pos;
} else if (prop_size <= 30) {
// Size of Filter Properties fits into Misc.
out[*out_pos] = prop_size + 0xE0;
++*out_pos;
// External ID is used to encode the Filter ID. If encoding
// the VLI fails, it's because the caller has given as too
// little output space, which it should have checked already.
// So return LZMA_PROG_ERROR, not LZMA_BUF_ERROR.
size_t dummy = 0;
if (lzma_vli_encode(options->id, &dummy, 1,
out, out_pos, out_size) != LZMA_STREAM_END)
return LZMA_PROG_ERROR;
} else {
// Nothing fits into Misc.
out[*out_pos] = 0xFF;
++*out_pos;
// External ID is used to encode the Filter ID.
size_t dummy = 0;
if (lzma_vli_encode(options->id, &dummy, 1,
out, out_pos, out_size) != LZMA_STREAM_END)
return LZMA_PROG_ERROR;
// External Size of Filter Properties
dummy = 0;
if (lzma_vli_encode(prop_size, &dummy, 1,
out, out_pos, out_size) != LZMA_STREAM_END)
return LZMA_PROG_ERROR;
}
// Filter Properties
switch (options->id) {
#ifdef HAVE_FILTER_COPY
case LZMA_FILTER_COPY:
assert(prop_size == 0);
ret = options->options == NULL ? LZMA_OK : LZMA_HEADER_ERROR;
break;
#endif
#ifdef HAVE_FILTER_SUBBLOCK
case LZMA_FILTER_SUBBLOCK:
assert(prop_size == 0);
ret = LZMA_OK;
break;
#endif
#ifdef HAVE_FILTER_SIMPLE
# ifdef HAVE_FILTER_X86
case LZMA_FILTER_X86:
# endif
# ifdef HAVE_FILTER_POWERPC
case LZMA_FILTER_POWERPC:
# endif
# ifdef HAVE_FILTER_IA64
case LZMA_FILTER_IA64:
# endif
# ifdef HAVE_FILTER_ARM
case LZMA_FILTER_ARM:
# endif
# ifdef HAVE_FILTER_ARMTHUMB
case LZMA_FILTER_ARMTHUMB:
# endif
# ifdef HAVE_FILTER_SPARC
case LZMA_FILTER_SPARC:
# endif
ret = properties_simple(out, out_pos, out_size,
options->options);
break;
#endif
#ifdef HAVE_FILTER_DELTA
case LZMA_FILTER_DELTA:
ret = properties_delta(out, out_pos, out_size,
options->options);
break;
#endif
#ifdef HAVE_FILTER_LZMA
case LZMA_FILTER_LZMA:
ret = properties_lzma(out, out_pos, out_size,
options->options);
break;
#endif
default:
assert(0);
ret = LZMA_PROG_ERROR;
break;
}
return ret;
}