auto_decoder.c revision 292588
1///////////////////////////////////////////////////////////////////////////////
2//
3/// \file       auto_decoder.c
4/// \brief      Autodetect between .xz Stream and .lzma (LZMA_Alone) formats
5//
6//  Author:     Lasse Collin
7//
8//  This file has been put into the public domain.
9//  You can do whatever you want with this file.
10//
11///////////////////////////////////////////////////////////////////////////////
12
13#include "stream_decoder.h"
14#include "alone_decoder.h"
15
16
17struct lzma_coder_s {
18	/// Stream decoder or LZMA_Alone decoder
19	lzma_next_coder next;
20
21	uint64_t memlimit;
22	uint32_t flags;
23
24	enum {
25		SEQ_INIT,
26		SEQ_CODE,
27		SEQ_FINISH,
28	} sequence;
29};
30
31
32static lzma_ret
33auto_decode(lzma_coder *coder, const lzma_allocator *allocator,
34		const uint8_t *restrict in, size_t *restrict in_pos,
35		size_t in_size, uint8_t *restrict out,
36		size_t *restrict out_pos, size_t out_size, lzma_action action)
37{
38	switch (coder->sequence) {
39	case SEQ_INIT:
40		if (*in_pos >= in_size)
41			return LZMA_OK;
42
43		// Update the sequence now, because we want to continue from
44		// SEQ_CODE even if we return some LZMA_*_CHECK.
45		coder->sequence = SEQ_CODE;
46
47		// Detect the file format. For now this is simple, since if
48		// it doesn't start with 0xFD (the first magic byte of the
49		// new format), it has to be LZMA_Alone, or something that
50		// we don't support at all.
51		if (in[*in_pos] == 0xFD) {
52			return_if_error(lzma_stream_decoder_init(
53					&coder->next, allocator,
54					coder->memlimit, coder->flags));
55		} else {
56			return_if_error(lzma_alone_decoder_init(&coder->next,
57					allocator, coder->memlimit, true));
58
59			// If the application wants to know about missing
60			// integrity check or about the check in general, we
61			// need to handle it here, because LZMA_Alone decoder
62			// doesn't accept any flags.
63			if (coder->flags & LZMA_TELL_NO_CHECK)
64				return LZMA_NO_CHECK;
65
66			if (coder->flags & LZMA_TELL_ANY_CHECK)
67				return LZMA_GET_CHECK;
68		}
69
70	// Fall through
71
72	case SEQ_CODE: {
73		const lzma_ret ret = coder->next.code(
74				coder->next.coder, allocator,
75				in, in_pos, in_size,
76				out, out_pos, out_size, action);
77		if (ret != LZMA_STREAM_END
78				|| (coder->flags & LZMA_CONCATENATED) == 0)
79			return ret;
80
81		coder->sequence = SEQ_FINISH;
82	}
83
84	// Fall through
85
86	case SEQ_FINISH:
87		// When LZMA_DECODE_CONCATENATED was used and we were decoding
88		// LZMA_Alone file, we need to check check that there is no
89		// trailing garbage and wait for LZMA_FINISH.
90		if (*in_pos < in_size)
91			return LZMA_DATA_ERROR;
92
93		return action == LZMA_FINISH ? LZMA_STREAM_END : LZMA_OK;
94
95	default:
96		assert(0);
97		return LZMA_PROG_ERROR;
98	}
99}
100
101
102static void
103auto_decoder_end(lzma_coder *coder, const lzma_allocator *allocator)
104{
105	lzma_next_end(&coder->next, allocator);
106	lzma_free(coder, allocator);
107	return;
108}
109
110
111static lzma_check
112auto_decoder_get_check(const lzma_coder *coder)
113{
114	// It is LZMA_Alone if get_check is NULL.
115	return coder->next.get_check == NULL ? LZMA_CHECK_NONE
116			: coder->next.get_check(coder->next.coder);
117}
118
119
120static lzma_ret
121auto_decoder_memconfig(lzma_coder *coder, uint64_t *memusage,
122		uint64_t *old_memlimit, uint64_t new_memlimit)
123{
124	lzma_ret ret;
125
126	if (coder->next.memconfig != NULL) {
127		ret = coder->next.memconfig(coder->next.coder,
128				memusage, old_memlimit, new_memlimit);
129		assert(*old_memlimit == coder->memlimit);
130	} else {
131		// No coder is configured yet. Use the base value as
132		// the current memory usage.
133		*memusage = LZMA_MEMUSAGE_BASE;
134		*old_memlimit = coder->memlimit;
135		ret = LZMA_OK;
136	}
137
138	if (ret == LZMA_OK && new_memlimit != 0)
139		coder->memlimit = new_memlimit;
140
141	return ret;
142}
143
144
145static lzma_ret
146auto_decoder_init(lzma_next_coder *next, const lzma_allocator *allocator,
147		uint64_t memlimit, uint32_t flags)
148{
149	lzma_next_coder_init(&auto_decoder_init, next, allocator);
150
151	if (memlimit == 0)
152		return LZMA_PROG_ERROR;
153
154	if (flags & ~LZMA_SUPPORTED_FLAGS)
155		return LZMA_OPTIONS_ERROR;
156
157	if (next->coder == NULL) {
158		next->coder = lzma_alloc(sizeof(lzma_coder), allocator);
159		if (next->coder == NULL)
160			return LZMA_MEM_ERROR;
161
162		next->code = &auto_decode;
163		next->end = &auto_decoder_end;
164		next->get_check = &auto_decoder_get_check;
165		next->memconfig = &auto_decoder_memconfig;
166		next->coder->next = LZMA_NEXT_CODER_INIT;
167	}
168
169	next->coder->memlimit = memlimit;
170	next->coder->flags = flags;
171	next->coder->sequence = SEQ_INIT;
172
173	return LZMA_OK;
174}
175
176
177extern LZMA_API(lzma_ret)
178lzma_auto_decoder(lzma_stream *strm, uint64_t memlimit, uint32_t flags)
179{
180	lzma_next_strm_init(auto_decoder_init, strm, memlimit, flags);
181
182	strm->internal->supported_actions[LZMA_RUN] = true;
183	strm->internal->supported_actions[LZMA_FINISH] = true;
184
185	return LZMA_OK;
186}
187