aboutsummaryrefslogtreecommitdiff
path: root/src/liblzma/common/raw_common.c
blob: 35252fc2b7fae0309f8ed1e5eaae15fc689f4046 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
///////////////////////////////////////////////////////////////////////////////
//
/// \file       raw_common.c
/// \brief      Stuff shared between raw encoder and raw decoder
//
//  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 "raw_common.h"


static lzma_ret
validate_options(const lzma_options_filter *options, size_t *count)
{
	if (options == NULL)
		return LZMA_PROG_ERROR;

	// Number of non-last filters that may change the size of the data
	// significantly (that is, more than 1-2 % or so).
	size_t change = 0;

	// True if the last filter in the given chain is actually usable as
	// the last filter. Only filters that support embedding End of Payload
	// Marker can be used as the last filter in the chain.
	bool last_ok = false;

	size_t i;
	for (i = 0; options[i].id != LZMA_VLI_VALUE_UNKNOWN; ++i) {
		switch (options[i].id) {
		// Not #ifdeffing these for simplicity.
		case LZMA_FILTER_X86:
		case LZMA_FILTER_POWERPC:
		case LZMA_FILTER_IA64:
		case LZMA_FILTER_ARM:
		case LZMA_FILTER_ARMTHUMB:
		case LZMA_FILTER_SPARC:
		case LZMA_FILTER_DELTA:
			// These don't change the size of the data and cannot
			// be used as the last filter in the chain.
			last_ok = false;
			break;

#ifdef HAVE_FILTER_SUBBLOCK
		case LZMA_FILTER_SUBBLOCK:
			last_ok = true;
			++change;
			break;
#endif

#ifdef HAVE_FILTER_LZMA
		case LZMA_FILTER_LZMA:
			last_ok = true;
			break;
#endif

		default:
			return LZMA_HEADER_ERROR;
		}
	}

	// There must be 1-4 filters and the last filter must be usable as
	// the last filter in the chain.
	if (i == 0 || i > 4 || !last_ok)
		return LZMA_HEADER_ERROR;

	// At maximum of two non-last filters are allowed to change the
	// size of the data.
	if (change > 2)
		return LZMA_HEADER_ERROR;

	*count = i;
	return LZMA_OK;
}


extern lzma_ret
lzma_raw_coder_init(lzma_next_coder *next, lzma_allocator *allocator,
		const lzma_options_filter *options,
		lzma_init_function (*get_function)(lzma_vli id),
		bool is_encoder)
{
	// Do some basic validation and get the number of filters.
	size_t count;
	return_if_error(validate_options(options, &count));

	// Set the filter functions and copy the options pointer.
	lzma_filter_info filters[count + 1];
	if (is_encoder) {
		for (size_t i = 0; i < count; ++i) {
			// The order of the filters is reversed in the
			// encoder. It allows more efficient handling
			// of the uncompressed data.
			const size_t j = count - i - 1;

			filters[j].init = get_function(options[i].id);
			if (filters[j].init == NULL)
				return LZMA_HEADER_ERROR;

			filters[j].options = options[i].options;
		}
	} else {
		for (size_t i = 0; i < count; ++i) {
			filters[i].init = get_function(options[i].id);
			if (filters[i].init == NULL)
				return LZMA_HEADER_ERROR;

			filters[i].options = options[i].options;
		}
	}

	// Terminate the array.
	filters[count].init = NULL;

	// Initialize the filters.
	return lzma_next_filter_init(next, allocator, filters);
}