mirror of https://git.tukaani.org/xz.git
383 lines
8.9 KiB
C
383 lines
8.9 KiB
C
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
/// \file filter_flags_decoder.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_decoder.h"
|
|
|
|
|
|
struct lzma_coder_s {
|
|
lzma_options_filter *options;
|
|
|
|
enum {
|
|
SEQ_MISC,
|
|
SEQ_ID,
|
|
SEQ_SIZE,
|
|
SEQ_PROPERTIES,
|
|
} sequence;
|
|
|
|
/// \brief Position in variable-length integers
|
|
size_t pos;
|
|
|
|
/// \brief Size of Filter Properties
|
|
lzma_vli properties_size;
|
|
};
|
|
|
|
|
|
#ifdef HAVE_FILTER_SUBBLOCK
|
|
static lzma_ret
|
|
properties_subblock(lzma_coder *coder, lzma_allocator *allocator,
|
|
const uint8_t *in lzma_attribute((unused)),
|
|
size_t *in_pos lzma_attribute((unused)),
|
|
size_t in_size lzma_attribute((unused)))
|
|
{
|
|
if (coder->properties_size != 0)
|
|
return LZMA_HEADER_ERROR;
|
|
|
|
coder->options->options = lzma_alloc(
|
|
sizeof(lzma_options_subblock), allocator);
|
|
if (coder->options->options == NULL)
|
|
return LZMA_MEM_ERROR;
|
|
|
|
((lzma_options_subblock *)(coder->options->options))
|
|
->allow_subfilters = true;
|
|
return LZMA_STREAM_END;
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef HAVE_FILTER_SIMPLE
|
|
static lzma_ret
|
|
properties_simple(lzma_coder *coder, lzma_allocator *allocator,
|
|
const uint8_t *in, size_t *in_pos, size_t in_size)
|
|
{
|
|
if (coder->properties_size == 0)
|
|
return LZMA_STREAM_END;
|
|
|
|
if (coder->properties_size != 4)
|
|
return LZMA_HEADER_ERROR;
|
|
|
|
lzma_options_simple *options = coder->options->options;
|
|
|
|
if (options == NULL) {
|
|
options = lzma_alloc(sizeof(lzma_options_simple), allocator);
|
|
if (options == NULL)
|
|
return LZMA_MEM_ERROR;
|
|
|
|
options->start_offset = 0;
|
|
coder->options->options = options;
|
|
}
|
|
|
|
while (coder->pos < 4) {
|
|
if (*in_pos == in_size)
|
|
return LZMA_OK;
|
|
|
|
options->start_offset
|
|
|= (uint32_t)(in[*in_pos]) << (8 * coder->pos);
|
|
++*in_pos;
|
|
++coder->pos;
|
|
}
|
|
|
|
// Don't leave an options structure allocated if start_offset is zero.
|
|
if (options->start_offset == 0) {
|
|
lzma_free(options, allocator);
|
|
coder->options->options = NULL;
|
|
}
|
|
|
|
return LZMA_STREAM_END;
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef HAVE_FILTER_DELTA
|
|
static lzma_ret
|
|
properties_delta(lzma_coder *coder, lzma_allocator *allocator,
|
|
const uint8_t *in, size_t *in_pos, size_t in_size)
|
|
{
|
|
if (coder->properties_size != 1)
|
|
return LZMA_HEADER_ERROR;
|
|
|
|
if (*in_pos == in_size)
|
|
return LZMA_OK;
|
|
|
|
lzma_options_delta *options = lzma_alloc(
|
|
sizeof(lzma_options_delta), allocator);
|
|
if (options == NULL)
|
|
return LZMA_MEM_ERROR;
|
|
|
|
coder->options->options = options;
|
|
|
|
options->distance = (uint32_t)(in[*in_pos]) + 1;
|
|
++*in_pos;
|
|
|
|
return LZMA_STREAM_END;
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef HAVE_FILTER_LZMA
|
|
static lzma_ret
|
|
properties_lzma(lzma_coder *coder, lzma_allocator *allocator,
|
|
const uint8_t *in, size_t *in_pos, size_t in_size)
|
|
{
|
|
// LZMA properties are always two bytes (at least for now).
|
|
if (coder->properties_size != 2)
|
|
return LZMA_HEADER_ERROR;
|
|
|
|
assert(coder->pos < 2);
|
|
|
|
while (*in_pos < in_size) {
|
|
switch (coder->pos) {
|
|
case 0:
|
|
// Allocate the options structure.
|
|
coder->options->options = lzma_alloc(
|
|
sizeof(lzma_options_lzma), allocator);
|
|
if (coder->options->options == NULL)
|
|
return LZMA_MEM_ERROR;
|
|
|
|
// Decode lc, lp, and pb.
|
|
if (lzma_lzma_decode_properties(
|
|
coder->options->options, in[*in_pos]))
|
|
return LZMA_HEADER_ERROR;
|
|
|
|
++*in_pos;
|
|
++coder->pos;
|
|
break;
|
|
|
|
case 1: {
|
|
lzma_options_lzma *options = coder->options->options;
|
|
|
|
// Check that reserved bits are unset.
|
|
if (in[*in_pos] & 0xC0)
|
|
return LZMA_HEADER_ERROR;
|
|
|
|
// Decode the dictionary size. See the file format
|
|
// specification section 4.3.4.2 to understand this.
|
|
if (in[*in_pos] == 0) {
|
|
options->dictionary_size = 1;
|
|
|
|
} else if (in[*in_pos] > 59) {
|
|
// Dictionary size is over 1 GiB.
|
|
// It's not supported at the moment.
|
|
return LZMA_HEADER_ERROR;
|
|
# if LZMA_DICTIONARY_SIZE_MAX != UINT32_C(1) << 30
|
|
# error Update the if()-condition a few lines
|
|
# error above to match LZMA_DICTIONARY_SIZE_MAX.
|
|
# endif
|
|
|
|
} else {
|
|
options->dictionary_size
|
|
= 2 | ((in[*in_pos] + 1) & 1);
|
|
options->dictionary_size
|
|
<<= (in[*in_pos] - 1) / 2;
|
|
}
|
|
|
|
++*in_pos;
|
|
return LZMA_STREAM_END;
|
|
}
|
|
}
|
|
}
|
|
|
|
assert(coder->pos < 2);
|
|
return LZMA_OK;
|
|
}
|
|
#endif
|
|
|
|
|
|
static lzma_ret
|
|
filter_flags_decode(lzma_coder *coder, lzma_allocator *allocator,
|
|
const uint8_t *restrict in, size_t *restrict in_pos,
|
|
size_t in_size, uint8_t *restrict out lzma_attribute((unused)),
|
|
size_t *restrict out_pos lzma_attribute((unused)),
|
|
size_t out_size lzma_attribute((unused)),
|
|
lzma_action action lzma_attribute((unused)))
|
|
{
|
|
while (*in_pos < in_size || coder->sequence == SEQ_PROPERTIES)
|
|
switch (coder->sequence) {
|
|
case SEQ_MISC:
|
|
// Determine the Filter ID and Size of Filter Properties.
|
|
if (in[*in_pos] >= 0xE0) {
|
|
// Using External ID. Prepare the ID
|
|
// for variable-length integer parsing.
|
|
coder->options->id = 0;
|
|
|
|
if (in[*in_pos] == 0xFF) {
|
|
// Mark that Size of Filter Properties is
|
|
// unknown, so we know later that there is
|
|
// external Size of Filter Properties present.
|
|
coder->properties_size
|
|
= LZMA_VLI_VALUE_UNKNOWN;
|
|
} else {
|
|
// Take Size of Filter Properties from Misc.
|
|
coder->properties_size = in[*in_pos] - 0xE0;
|
|
}
|
|
|
|
coder->sequence = SEQ_ID;
|
|
|
|
} else {
|
|
// The Filter ID is the same as Misc.
|
|
coder->options->id = in[*in_pos];
|
|
|
|
// The Size of Filter Properties can be calculated
|
|
// from Misc too.
|
|
coder->properties_size = in[*in_pos] / 0x20;
|
|
|
|
coder->sequence = SEQ_PROPERTIES;
|
|
}
|
|
|
|
++*in_pos;
|
|
break;
|
|
|
|
case SEQ_ID: {
|
|
const lzma_ret ret = lzma_vli_decode(&coder->options->id,
|
|
&coder->pos, in, in_pos, in_size);
|
|
if (ret != LZMA_STREAM_END)
|
|
return ret;
|
|
|
|
if (coder->properties_size == LZMA_VLI_VALUE_UNKNOWN) {
|
|
// We have also external Size of Filter
|
|
// Properties. Prepare the size for
|
|
// variable-length integer parsing.
|
|
coder->properties_size = 0;
|
|
coder->sequence = SEQ_SIZE;
|
|
} else {
|
|
coder->sequence = SEQ_PROPERTIES;
|
|
}
|
|
|
|
// Reset pos for its next job.
|
|
coder->pos = 0;
|
|
break;
|
|
}
|
|
|
|
case SEQ_SIZE: {
|
|
const lzma_ret ret = lzma_vli_decode(&coder->properties_size,
|
|
&coder->pos, in, in_pos, in_size);
|
|
if (ret != LZMA_STREAM_END)
|
|
return ret;
|
|
|
|
coder->pos = 0;
|
|
coder->sequence = SEQ_PROPERTIES;
|
|
break;
|
|
}
|
|
|
|
case SEQ_PROPERTIES: {
|
|
lzma_ret (*get_properties)(lzma_coder *coder,
|
|
lzma_allocator *allocator, const uint8_t *in,
|
|
size_t *in_pos, size_t in_size);
|
|
|
|
switch (coder->options->id) {
|
|
#ifdef HAVE_FILTER_COPY
|
|
case LZMA_FILTER_COPY:
|
|
return coder->properties_size > 0
|
|
? LZMA_HEADER_ERROR : LZMA_STREAM_END;
|
|
#endif
|
|
#ifdef HAVE_FILTER_SUBBLOCK
|
|
case LZMA_FILTER_SUBBLOCK:
|
|
get_properties = &properties_subblock;
|
|
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
|
|
get_properties = &properties_simple;
|
|
break;
|
|
#endif
|
|
#ifdef HAVE_FILTER_DELTA
|
|
case LZMA_FILTER_DELTA:
|
|
get_properties = &properties_delta;
|
|
break;
|
|
#endif
|
|
#ifdef HAVE_FILTER_LZMA
|
|
case LZMA_FILTER_LZMA:
|
|
get_properties = &properties_lzma;
|
|
break;
|
|
#endif
|
|
default:
|
|
return LZMA_HEADER_ERROR;
|
|
}
|
|
|
|
return get_properties(coder, allocator, in, in_pos, in_size);
|
|
}
|
|
|
|
default:
|
|
return LZMA_PROG_ERROR;
|
|
}
|
|
|
|
return LZMA_OK;
|
|
}
|
|
|
|
|
|
static void
|
|
filter_flags_decoder_end(lzma_coder *coder, lzma_allocator *allocator)
|
|
{
|
|
lzma_free(coder, allocator);
|
|
return;
|
|
}
|
|
|
|
|
|
extern lzma_ret
|
|
lzma_filter_flags_decoder_init(lzma_next_coder *next,
|
|
lzma_allocator *allocator, lzma_options_filter *options)
|
|
{
|
|
if (next->coder == NULL) {
|
|
next->coder = lzma_alloc(sizeof(lzma_coder), allocator);
|
|
if (next->coder == NULL)
|
|
return LZMA_MEM_ERROR;
|
|
|
|
next->code = &filter_flags_decode;
|
|
next->end = &filter_flags_decoder_end;
|
|
}
|
|
|
|
options->id = 0;
|
|
options->options = NULL;
|
|
|
|
next->coder->options = options;
|
|
next->coder->sequence = SEQ_MISC;
|
|
next->coder->pos = 0;
|
|
next->coder->properties_size = 0;
|
|
|
|
return LZMA_OK;
|
|
}
|
|
|
|
|
|
extern LZMA_API lzma_ret
|
|
lzma_filter_flags_decoder(lzma_stream *strm, lzma_options_filter *options)
|
|
{
|
|
lzma_next_strm_init(strm, lzma_filter_flags_decoder_init, options);
|
|
|
|
strm->internal->supported_actions[LZMA_RUN] = true;
|
|
|
|
return LZMA_OK;
|
|
}
|