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"
|
2008-01-15 12:23:35 +00:00
|
|
|
#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
|
|
|
|
//
|
2008-01-15 12:23:35 +00:00
|
|
|
// Dictionary size is encoded using similar encoding that is used
|
|
|
|
// internally by LZMA.
|
2007-12-08 22:42:33 +00:00
|
|
|
//
|
2008-01-15 12:23:35 +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
|
|
|
|
|
2008-01-15 12:23:35 +00:00
|
|
|
uint32_t d = options->dictionary_size;
|
|
|
|
|
2007-12-08 22:42:33 +00:00
|
|
|
// Validate it:
|
2008-01-15 12:23:35 +00:00
|
|
|
if (d < LZMA_DICTIONARY_SIZE_MIN || d > LZMA_DICTIONARY_SIZE_MAX)
|
2007-12-08 22:42:33 +00:00
|
|
|
return LZMA_HEADER_ERROR;
|
|
|
|
|
2008-01-15 12:23:35 +00:00
|
|
|
// 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;
|
|
|
|
}
|