aboutsummaryrefslogtreecommitdiff
path: root/src/liblzma/common/stream_decoder.c
blob: 1bf7f1f83fda9dc7f368ca510438c67aa15ac83f (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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
///////////////////////////////////////////////////////////////////////////////
//
/// \file       stream_decoder.c
/// \brief      Decodes .lzma Streams
//
//  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 "stream_common.h"
#include "stream_decoder.h"
#include "check.h"
#include "stream_flags_decoder.h"
#include "block_decoder.h"


struct lzma_coder_s {
	enum {
		SEQ_STREAM_HEADER,
		SEQ_BLOCK_HEADER,
		SEQ_BLOCK,
		SEQ_INDEX,
		SEQ_STREAM_FOOTER,
	} sequence;

	/// Block or Metadata decoder. This takes little memory and the same
	/// data structure can be used to decode every Block Header, so it's
	/// a good idea to have a separate lzma_next_coder structure for it.
	lzma_next_coder block_decoder;

	/// Block options decoded by the Block Header decoder and used by
	/// the Block decoder.
	lzma_options_block block_options;

	/// Stream Flags from Stream Header
	lzma_stream_flags stream_flags;

	/// Index is hashed so that it can be compared to the sizes of Blocks
	/// with O(1) memory usage.
	lzma_index_hash *index_hash;

	/// Write position in buffer[]
	size_t buffer_pos;

	/// Buffer to hold Stream Header, Block Header, and Stream Footer.
	/// Block Header has biggest maximum size.
	uint8_t buffer[LZMA_BLOCK_HEADER_SIZE_MAX];
};


static lzma_ret
stream_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,
		size_t *restrict out_pos, size_t out_size, lzma_action action)
{
	// When decoding the actual Block, it may be able to produce more
	// output even if we don't give it any new input.
	while (*out_pos < out_size && (*in_pos < in_size
			|| coder->sequence == SEQ_BLOCK))
	switch (coder->sequence) {
	case SEQ_STREAM_HEADER: {
		// Copy the Stream Header to the internal buffer.
		bufcpy(in, in_pos, in_size, coder->buffer, &coder->buffer_pos,
				LZMA_STREAM_HEADER_SIZE);

		// Return if we didn't get the whole Stream Header yet.
		if (coder->buffer_pos < LZMA_STREAM_HEADER_SIZE)
			return LZMA_OK;

		coder->buffer_pos = 0;

		// Decode the Stream Header.
		return_if_error(lzma_stream_header_decode(
				&coder->stream_flags, coder->buffer));

		// Copy the type of the Check so that Block Header and Block
		// decoders see it.
		coder->block_options.check = coder->stream_flags.check;

		// Even if we return LZMA_UNSUPPORTED_CHECK below, we want
		// to continue from Block Header decoding.
		coder->sequence = SEQ_BLOCK_HEADER;

		// Detect if the Check type is supported and give appropriate
		// warning if it isn't. We don't warn every time a new Block
		// is started.
		if (!lzma_available_checks[coder->block_options.check])
			return LZMA_UNSUPPORTED_CHECK;

		break;
	}

	case SEQ_BLOCK_HEADER: {
		if (coder->buffer_pos == 0) {
			// Detect if it's Index.
			if (in[*in_pos] == 0x00) {
				coder->sequence = SEQ_INDEX;
				break;
			}

			// Calculate the size of the Block Header. Note that
			// Block Header decoder wants to see this byte too
			// so don't advance *in_pos.
			coder->block_options.header_size
					= lzma_block_header_size_decode(
						in[*in_pos]);
		}

		// Copy the Block Header to the internal buffer.
		bufcpy(in, in_pos, in_size, coder->buffer, &coder->buffer_pos,
				coder->block_options.header_size);

		// Return if we didn't get the whole Block Header yet.
		if (coder->buffer_pos < coder->block_options.header_size)
			return LZMA_OK;

		coder->buffer_pos = 0;

		// Set up a buffer to hold the filter chain. Block Header
		// decoder will initialize all members of this array so
		// we don't need to do it here.
		lzma_options_filter filters[LZMA_BLOCK_FILTERS_MAX + 1];
		coder->block_options.filters = filters;

		// Decode the Block Header.
		return_if_error(lzma_block_header_decode(&coder->block_options,
				allocator, coder->buffer));

		// Initialize the Block decoder.
		const lzma_ret ret = lzma_block_decoder_init(
				&coder->block_decoder,
				allocator, &coder->block_options);

		// Free the allocated filter options since they are needed
		// only to initialize the Block decoder.
		for (size_t i = 0; i < LZMA_BLOCK_FILTERS_MAX; ++i)
			lzma_free(filters[i].options, allocator);

		coder->block_options.filters = NULL;

		// Check if Block enocoder initialization succeeded. Don't
		// warn about unsupported check anymore since we did it
		// earlier if it was needed.
		if (ret != LZMA_OK && ret != LZMA_UNSUPPORTED_CHECK)
			return ret;

		coder->sequence = SEQ_BLOCK;
		break;
	}

	case SEQ_BLOCK: {
		lzma_ret ret = coder->block_decoder.code(
				coder->block_decoder.coder, allocator,
				in, in_pos, in_size, out, out_pos, out_size,
				action);

		if (ret != LZMA_STREAM_END)
			return ret;

		// Block decoded successfully. Add the new size pair to
		// the Index hash.
		return_if_error(lzma_index_hash_append(coder->index_hash,
				lzma_block_total_size_get(
					&coder->block_options),
				coder->block_options.uncompressed_size));

		coder->sequence = SEQ_BLOCK_HEADER;
		break;
	}

	case SEQ_INDEX: {
		// Decode the Index and compare it to the hash calculated
		// from the sizes of the Blocks (if any).
		const lzma_ret ret = lzma_index_hash_decode(coder->index_hash,
				in, in_pos, in_size);
		if (ret != LZMA_STREAM_END)
			return ret;

		coder->sequence = SEQ_STREAM_FOOTER;
		break;
	}

	case SEQ_STREAM_FOOTER:
		// Copy the Stream Footer to the internal buffer.
		bufcpy(in, in_pos, in_size, coder->buffer, &coder->buffer_pos,
				LZMA_STREAM_HEADER_SIZE);

		// Return if we didn't get the whole Stream Footer yet.
		if (coder->buffer_pos < LZMA_STREAM_HEADER_SIZE)
			return LZMA_OK;

		// Decode the Stream Footer.
		lzma_stream_flags footer_flags;
		return_if_error(lzma_stream_footer_decode(
				&footer_flags, coder->buffer));

		// Check that Index Size stored in the Stream Footer matches
		// the real size of the Index field.
		if (lzma_index_hash_size(coder->index_hash)
				!= footer_flags.backward_size)
			return LZMA_DATA_ERROR;

		// Compare that the Stream Flags fields are identical in
		// both Stream Header and Stream Footer.
		if (!lzma_stream_flags_equal(&coder->stream_flags,
				&footer_flags))
			return LZMA_DATA_ERROR;

		return LZMA_STREAM_END;

	default:
		assert(0);
		return LZMA_PROG_ERROR;
	}

	return LZMA_OK;
}


static void
stream_decoder_end(lzma_coder *coder, lzma_allocator *allocator)
{
	lzma_next_coder_end(&coder->block_decoder, allocator);
	lzma_index_hash_end(coder->index_hash, allocator);
	lzma_free(coder, allocator);
	return;
}


static lzma_ret
stream_decoder_init(lzma_next_coder *next, lzma_allocator *allocator)
{
	if (next->coder == NULL) {
		next->coder = lzma_alloc(sizeof(lzma_coder), allocator);
		if (next->coder == NULL)
			return LZMA_MEM_ERROR;

		next->code = &stream_decode;
		next->end = &stream_decoder_end;

		next->coder->block_decoder = LZMA_NEXT_CODER_INIT;
		next->coder->index_hash = NULL;
	}

	// Initialize the Index hash used to verify the Index.
	next->coder->index_hash = lzma_index_hash_init(
			next->coder->index_hash, allocator);
	if (next->coder->index_hash == NULL)
		return LZMA_MEM_ERROR;

	// Reset the rest of the variables.
	next->coder->sequence = SEQ_STREAM_HEADER;
	next->coder->block_options.filters = NULL;
	next->coder->buffer_pos = 0;

	return LZMA_OK;
}


extern lzma_ret
lzma_stream_decoder_init(lzma_next_coder *next, lzma_allocator *allocator)
{
	lzma_next_coder_init0(stream_decoder_init, next, allocator);
}


extern LZMA_API lzma_ret
lzma_stream_decoder(lzma_stream *strm)
{
	lzma_next_strm_init0(strm, stream_decoder_init);

	strm->internal->supported_actions[LZMA_RUN] = true;
	strm->internal->supported_actions[LZMA_SYNC_FLUSH] = true;

	return LZMA_OK;
}