summaryrefslogtreecommitdiffstats
path: root/depends/lzma/wrapper/decompress.c
blob: 65ff911957b5c0bbd5d599e06c076888143d4e80 (plain)
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
/*
 * Written in 2009 by Lloyd Hilaiel
 *
 * License
 *
 * All the cruft you find here is public domain.  You don't have to credit
 * anyone to use this code, but my personal request is that you mention
 * Igor Pavlov for his hard, high quality work.
 */

#include "include/decompress.h"
#include "pavlov/LzmaDec.h"
#include "pavlov/7zCrc.h"
#include "common_internal.h"
#include "lzma_header.h"
#include "lzip_header.h"

#include <string.h>
#include <assert.h>

#define ELZMA_DECOMPRESS_INPUT_BUFSIZE (1024 * 64)
#define ELZMA_DECOMPRESS_OUTPUT_BUFSIZE (1024 * 256)

/** an opaque handle to an lzma decompressor */
struct _elzma_decompress_handle
{
	char inbuf[ELZMA_DECOMPRESS_INPUT_BUFSIZE];
	char outbuf[ELZMA_DECOMPRESS_OUTPUT_BUFSIZE];
	struct elzma_alloc_struct allocStruct;
};

elzma_decompress_handle elzma_decompress_alloc()
{
	elzma_decompress_handle hand = malloc(sizeof(struct _elzma_decompress_handle));
	memset((void *)hand, 0, sizeof(struct _elzma_decompress_handle));
	init_alloc_struct(&(hand->allocStruct), NULL, NULL, NULL, NULL);
	return hand;
}

void elzma_decompress_set_allocation_callbacks(elzma_decompress_handle hand,
											   elzma_malloc mallocFunc, void *mallocFuncContext,
											   elzma_free freeFunc, void *freeFuncContext)
{
	if (hand)
	{
		init_alloc_struct(&(hand->allocStruct), mallocFunc, mallocFuncContext, freeFunc,
						  freeFuncContext);
	}
}

void elzma_decompress_free(elzma_decompress_handle *hand)
{
	if (*hand)
		free(*hand);
	*hand = NULL;
}

int elzma_decompress_run(elzma_decompress_handle hand, elzma_read_callback inputStream,
						 void *inputContext, elzma_write_callback outputStream,
						 void *outputContext, elzma_file_format format)
{
	unsigned long long int totalRead = 0; /* total amount read from stream */
	unsigned int crc32 = CRC_INIT_VAL;	/* running crc32 (lzip case) */
	CLzmaDec dec;
	unsigned int errorCode = ELZMA_E_OK;
	struct elzma_format_handler formatHandler;
	struct elzma_file_header h;
	struct elzma_file_footer f;

	/* switch between supported formats */
	if (format == ELZMA_lzma)
	{
		initializeLZMAFormatHandler(&formatHandler);
	}
	else if (format == ELZMA_lzip)
	{
		CrcGenerateTable();
		initializeLZIPFormatHandler(&formatHandler);
	}
	else
	{
		return ELZMA_E_BAD_PARAMS;
	}

	/* initialize footer */
	f.crc32 = 0;
	f.uncompressedSize = 0;

	/* initialize decoder memory */
	memset((void *)&dec, 0, sizeof(dec));
	LzmaDec_Init(&dec);

	/* decode the header. */
	{
		unsigned char *hdr =
			hand->allocStruct.Alloc(&(hand->allocStruct), formatHandler.header_size);

		size_t sz = formatHandler.header_size;

		formatHandler.init_header(&h);

		if (inputStream(inputContext, hdr, &sz) != 0 || sz != formatHandler.header_size)
		{
			hand->allocStruct.Free(&(hand->allocStruct), hdr);
			return ELZMA_E_INPUT_ERROR;
		}

		if (0 != formatHandler.parse_header(hdr, &h))
		{
			hand->allocStruct.Free(&(hand->allocStruct), hdr);
			return ELZMA_E_CORRUPT_HEADER;
		}

		/* the LzmaDec_Allocate call requires 5 bytes which have
		 * compression properties encoded in them.  In the case of
		 * lzip, the header format does not already contain what
		 * LzmaDec_Allocate expects, so we must craft it, silly */
		{
			unsigned char propsBuf[13];
			const unsigned char *propsPtr = hdr;

			if (format == ELZMA_lzip)
			{
				struct elzma_format_handler lzmaHand;
				initializeLZMAFormatHandler(&lzmaHand);
				lzmaHand.serialize_header(propsBuf, &h);
				propsPtr = propsBuf;
			}

			/* now we're ready to allocate the decoder */
			LzmaDec_Allocate(&dec, propsPtr, 5);
		}

		hand->allocStruct.Free(&(hand->allocStruct), hdr);
	}

	/* perform the decoding */
	for (;;)
	{
		size_t dstLen = ELZMA_DECOMPRESS_OUTPUT_BUFSIZE;
		size_t srcLen = ELZMA_DECOMPRESS_INPUT_BUFSIZE;
		size_t amt = 0;
		size_t bufOff = 0;
		ELzmaStatus stat;

		if (0 != inputStream(inputContext, hand->inbuf, &srcLen))
		{
			errorCode = ELZMA_E_INPUT_ERROR;
			goto decompressEnd;
		}

		/* handle the case where the input prematurely finishes */
		if (srcLen == 0)
		{
			errorCode = ELZMA_E_INSUFFICIENT_INPUT;
			goto decompressEnd;
		}

		amt = srcLen;

		/* handle the case where a single read buffer of compressed bytes
		 * will translate into multiple buffers of uncompressed bytes,
		 * with this inner loop */
		stat = LZMA_STATUS_NOT_SPECIFIED;

		while (bufOff < srcLen)
		{
			SRes r = LzmaDec_DecodeToBuf(&dec, (uint8_t *)hand->outbuf, &dstLen,
										 ((uint8_t *)hand->inbuf + bufOff), &amt,
										 LZMA_FINISH_ANY, &stat);

			/* XXX deal with result code more granularly*/
			if (r != SZ_OK)
			{
				errorCode = ELZMA_E_DECOMPRESS_ERROR;
				goto decompressEnd;
			}

			/* write what we've read */
			{
				size_t wt;

				/* if decoding lzip, update our crc32 value */
				if (format == ELZMA_lzip && dstLen > 0)
				{
					crc32 = CrcUpdate(crc32, hand->outbuf, dstLen);
				}
				totalRead += dstLen;

				wt = outputStream(outputContext, hand->outbuf, dstLen);
				if (wt != dstLen)
				{
					errorCode = ELZMA_E_OUTPUT_ERROR;
					goto decompressEnd;
				}
			}

			/* do we have more data on the input buffer? */
			bufOff += amt;
			assert(bufOff <= srcLen);
			if (bufOff >= srcLen)
				break;
			amt = srcLen - bufOff;

			/* with lzip, we will have the footer left on the buffer! */
			if (stat == LZMA_STATUS_FINISHED_WITH_MARK)
			{
				break;
			}
		}

		/* now check status */
		if (stat == LZMA_STATUS_FINISHED_WITH_MARK)
		{
			/* read a footer if one is expected and
			 * present */
			if (formatHandler.footer_size > 0 && amt >= formatHandler.footer_size &&
				formatHandler.parse_footer != NULL)
			{
				formatHandler.parse_footer((unsigned char *)hand->inbuf + bufOff, &f);
			}

			break;
		}
		/* for LZMA utils,  we don't always have a finished mark */
		if (!h.isStreamed && totalRead >= h.uncompressedSize)
		{
			break;
		}
	}

	/* finish the calculated crc32 */
	crc32 ^= 0xFFFFFFFF;

	/* if we have a footer, check that the calculated crc32 matches
	 * the encoded crc32, and that the sizes match */
	if (formatHandler.footer_size)
	{
		if (f.crc32 != crc32)
		{
			errorCode = ELZMA_E_CRC32_MISMATCH;
		}
		else if (f.uncompressedSize != totalRead)
		{
			errorCode = ELZMA_E_SIZE_MISMATCH;
		}
	}
	else if (!h.isStreamed)
	{
		/* if the format does not support a footer and has an uncompressed
		 * size in the header, let's compare that with how much we actually
		 * read */
		if (h.uncompressedSize != totalRead)
		{
			errorCode = ELZMA_E_SIZE_MISMATCH;
		}
	}

decompressEnd:
	LzmaDec_Free(&dec);

	return errorCode;
}