]> icculus.org git repositories - icculus/xz.git/blob - src/liblzma/common/filter_flags_decoder.c
Use fastpos.h when encoding LZMA dictionary size in
[icculus/xz.git] / src / liblzma / common / filter_flags_decoder.c
1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 /// \file       filter_flags_decoder.c
4 /// \brief      Decodes a Filter Flags field
5 //
6 //  Copyright (C) 2007 Lasse Collin
7 //
8 //  This library is free software; you can redistribute it and/or
9 //  modify it under the terms of the GNU Lesser General Public
10 //  License as published by the Free Software Foundation; either
11 //  version 2.1 of the License, or (at your option) any later version.
12 //
13 //  This library is distributed in the hope that it will be useful,
14 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 //  Lesser General Public License for more details.
17 //
18 ///////////////////////////////////////////////////////////////////////////////
19
20 #include "common.h"
21 #include "lzma_decoder.h"
22
23
24 struct lzma_coder_s {
25         lzma_options_filter *options;
26
27         enum {
28                 SEQ_MISC,
29                 SEQ_ID,
30                 SEQ_SIZE,
31                 SEQ_PROPERTIES,
32         } sequence;
33
34         /// \brief      Position in variable-length integers
35         size_t pos;
36
37         /// \brief      Size of Filter Properties
38         lzma_vli properties_size;
39 };
40
41
42 #ifdef HAVE_FILTER_SUBBLOCK
43 static lzma_ret
44 properties_subblock(lzma_coder *coder, lzma_allocator *allocator,
45                 const uint8_t *in lzma_attribute((unused)),
46                 size_t *in_pos lzma_attribute((unused)),
47                 size_t in_size lzma_attribute((unused)))
48 {
49         if (coder->properties_size != 0)
50                 return LZMA_HEADER_ERROR;
51
52         coder->options->options = lzma_alloc(
53                         sizeof(lzma_options_subblock), allocator);
54         if (coder->options->options == NULL)
55                 return LZMA_MEM_ERROR;
56
57         ((lzma_options_subblock *)(coder->options->options))
58                         ->allow_subfilters = true;
59         return LZMA_STREAM_END;
60 }
61 #endif
62
63
64 #ifdef HAVE_FILTER_SIMPLE
65 static lzma_ret
66 properties_simple(lzma_coder *coder, lzma_allocator *allocator,
67                 const uint8_t *in, size_t *in_pos, size_t in_size)
68 {
69         if (coder->properties_size == 0)
70                 return LZMA_STREAM_END;
71
72         if (coder->properties_size != 4)
73                 return LZMA_HEADER_ERROR;
74
75         lzma_options_simple *options = coder->options->options;
76
77         if (options == NULL) {
78                 options = lzma_alloc(sizeof(lzma_options_simple), allocator);
79                 if (options == NULL)
80                         return LZMA_MEM_ERROR;
81
82                 options->start_offset = 0;
83                 coder->options->options = options;
84         }
85
86         while (coder->pos < 4) {
87                 if (*in_pos == in_size)
88                         return LZMA_OK;
89
90                 options->start_offset
91                                 |= (uint32_t)(in[*in_pos]) << (8 * coder->pos);
92                 ++*in_pos;
93                 ++coder->pos;
94         }
95
96         // Don't leave an options structure allocated if start_offset is zero.
97         if (options->start_offset == 0) {
98                 lzma_free(options, allocator);
99                 coder->options->options = NULL;
100         }
101
102         return LZMA_STREAM_END;
103 }
104 #endif
105
106
107 #ifdef HAVE_FILTER_DELTA
108 static lzma_ret
109 properties_delta(lzma_coder *coder, lzma_allocator *allocator,
110                 const uint8_t *in, size_t *in_pos, size_t in_size)
111 {
112         if (coder->properties_size != 1)
113                 return LZMA_HEADER_ERROR;
114
115         if (*in_pos == in_size)
116                 return LZMA_OK;
117
118         lzma_options_delta *options = lzma_alloc(
119                         sizeof(lzma_options_delta), allocator);
120         if (options == NULL)
121                 return LZMA_MEM_ERROR;
122
123         coder->options->options = options;
124
125         options->distance = (uint32_t)(in[*in_pos]) + 1;
126         ++*in_pos;
127
128         return LZMA_STREAM_END;
129 }
130 #endif
131
132
133 #ifdef HAVE_FILTER_LZMA
134 static lzma_ret
135 properties_lzma(lzma_coder *coder, lzma_allocator *allocator,
136                 const uint8_t *in, size_t *in_pos, size_t in_size)
137 {
138         // LZMA properties are always two bytes (at least for now).
139         if (coder->properties_size != 2)
140                 return LZMA_HEADER_ERROR;
141
142         assert(coder->pos < 2);
143
144         while (*in_pos < in_size) {
145                 switch (coder->pos) {
146                 case 0:
147                         // Allocate the options structure.
148                         coder->options->options = lzma_alloc(
149                                         sizeof(lzma_options_lzma), allocator);
150                         if (coder->options->options == NULL)
151                                 return LZMA_MEM_ERROR;
152
153                         // Decode lc, lp, and pb.
154                         if (lzma_lzma_decode_properties(
155                                         coder->options->options, in[*in_pos]))
156                                 return LZMA_HEADER_ERROR;
157
158                         ++*in_pos;
159                         ++coder->pos;
160                         break;
161
162                 case 1: {
163                         lzma_options_lzma *options = coder->options->options;
164
165                         // Check that reserved bits are unset.
166                         if (in[*in_pos] & 0xC0)
167                                 return LZMA_HEADER_ERROR;
168
169                         // Decode the dictionary size. See the file format
170                         // specification section 4.3.4.2 to understand this.
171                         if (in[*in_pos] == 0) {
172                                 options->dictionary_size = 1;
173
174                         } else if (in[*in_pos] > 59) {
175                                 // Dictionary size is over 1 GiB.
176                                 // It's not supported at the moment.
177                                 return LZMA_HEADER_ERROR;
178 #                       if LZMA_DICTIONARY_SIZE_MAX != UINT32_C(1) << 30
179 #                               error Update the if()-condition a few lines
180 #                               error above to match LZMA_DICTIONARY_SIZE_MAX.
181 #                       endif
182
183                         } else {
184                                 options->dictionary_size
185                                         = 2 | ((in[*in_pos] + 1) & 1);
186                                 options->dictionary_size
187                                         <<= (in[*in_pos] - 1) / 2;
188                         }
189
190                         ++*in_pos;
191                         return LZMA_STREAM_END;
192                 }
193                 }
194         }
195
196         assert(coder->pos < 2);
197         return LZMA_OK;
198 }
199 #endif
200
201
202 static lzma_ret
203 filter_flags_decode(lzma_coder *coder, lzma_allocator *allocator,
204                 const uint8_t *restrict in, size_t *restrict in_pos,
205                 size_t in_size, uint8_t *restrict out lzma_attribute((unused)),
206                 size_t *restrict out_pos lzma_attribute((unused)),
207                 size_t out_size lzma_attribute((unused)),
208                 lzma_action action lzma_attribute((unused)))
209 {
210         while (*in_pos < in_size || coder->sequence == SEQ_PROPERTIES)
211         switch (coder->sequence) {
212         case SEQ_MISC:
213                 // Determine the Filter ID and Size of Filter Properties.
214                 if (in[*in_pos] >= 0xE0) {
215                         // Using External ID. Prepare the ID
216                         // for variable-length integer parsing.
217                         coder->options->id = 0;
218
219                         if (in[*in_pos] == 0xFF) {
220                                 // Mark that Size of Filter Properties is
221                                 // unknown, so we know later that there is
222                                 // external Size of Filter Properties present.
223                                 coder->properties_size
224                                                 = LZMA_VLI_VALUE_UNKNOWN;
225                         } else {
226                                 // Take Size of Filter Properties from Misc.
227                                 coder->properties_size = in[*in_pos] - 0xE0;
228                         }
229
230                         coder->sequence = SEQ_ID;
231
232                 } else {
233                         // The Filter ID is the same as Misc.
234                         coder->options->id = in[*in_pos];
235
236                         // The Size of Filter Properties can be calculated
237                         // from Misc too.
238                         coder->properties_size = in[*in_pos] / 0x20;
239
240                         coder->sequence = SEQ_PROPERTIES;
241                 }
242
243                 ++*in_pos;
244                 break;
245
246         case SEQ_ID: {
247                 const lzma_ret ret = lzma_vli_decode(&coder->options->id,
248                                 &coder->pos, in, in_pos, in_size);
249                 if (ret != LZMA_STREAM_END)
250                         return ret;
251
252                 if (coder->properties_size == LZMA_VLI_VALUE_UNKNOWN) {
253                         // We have also external Size of Filter
254                         // Properties. Prepare the size for
255                         // variable-length integer parsing.
256                         coder->properties_size = 0;
257                         coder->sequence = SEQ_SIZE;
258                 } else {
259                         coder->sequence = SEQ_PROPERTIES;
260                 }
261
262                 // Reset pos for its next job.
263                 coder->pos = 0;
264                 break;
265         }
266
267         case SEQ_SIZE: {
268                 const lzma_ret ret = lzma_vli_decode(&coder->properties_size,
269                                 &coder->pos, in, in_pos, in_size);
270                 if (ret != LZMA_STREAM_END)
271                         return ret;
272
273                 coder->pos = 0;
274                 coder->sequence = SEQ_PROPERTIES;
275                 break;
276         }
277
278         case SEQ_PROPERTIES: {
279                 lzma_ret (*get_properties)(lzma_coder *coder,
280                                 lzma_allocator *allocator, const uint8_t *in,
281                                 size_t *in_pos, size_t in_size);
282
283                 switch (coder->options->id) {
284 #ifdef HAVE_FILTER_COPY
285                 case LZMA_FILTER_COPY:
286                         return coder->properties_size > 0
287                                         ? LZMA_HEADER_ERROR : LZMA_STREAM_END;
288 #endif
289 #ifdef HAVE_FILTER_SUBBLOCK
290                 case LZMA_FILTER_SUBBLOCK:
291                         get_properties = &properties_subblock;
292                         break;
293 #endif
294 #ifdef HAVE_FILTER_SIMPLE
295 #       ifdef HAVE_FILTER_X86
296                 case LZMA_FILTER_X86:
297 #       endif
298 #       ifdef HAVE_FILTER_POWERPC
299                 case LZMA_FILTER_POWERPC:
300 #       endif
301 #       ifdef HAVE_FILTER_IA64
302                 case LZMA_FILTER_IA64:
303 #       endif
304 #       ifdef HAVE_FILTER_ARM
305                 case LZMA_FILTER_ARM:
306 #       endif
307 #       ifdef HAVE_FILTER_ARMTHUMB
308                 case LZMA_FILTER_ARMTHUMB:
309 #       endif
310 #       ifdef HAVE_FILTER_SPARC
311                 case LZMA_FILTER_SPARC:
312 #       endif
313                         get_properties = &properties_simple;
314                         break;
315 #endif
316 #ifdef HAVE_FILTER_DELTA
317                 case LZMA_FILTER_DELTA:
318                         get_properties = &properties_delta;
319                         break;
320 #endif
321 #ifdef HAVE_FILTER_LZMA
322                 case LZMA_FILTER_LZMA:
323                         get_properties = &properties_lzma;
324                         break;
325 #endif
326                 default:
327                         return LZMA_HEADER_ERROR;
328                 }
329
330                 return get_properties(coder, allocator, in, in_pos, in_size);
331         }
332
333         default:
334                 return LZMA_PROG_ERROR;
335         }
336
337         return LZMA_OK;
338 }
339
340
341 static void
342 filter_flags_decoder_end(lzma_coder *coder, lzma_allocator *allocator)
343 {
344         lzma_free(coder, allocator);
345         return;
346 }
347
348
349 extern lzma_ret
350 lzma_filter_flags_decoder_init(lzma_next_coder *next,
351                 lzma_allocator *allocator, lzma_options_filter *options)
352 {
353         if (next->coder == NULL) {
354                 next->coder = lzma_alloc(sizeof(lzma_coder), allocator);
355                 if (next->coder == NULL)
356                         return LZMA_MEM_ERROR;
357
358                 next->code = &filter_flags_decode;
359                 next->end = &filter_flags_decoder_end;
360         }
361
362         options->id = 0;
363         options->options = NULL;
364
365         next->coder->options = options;
366         next->coder->sequence = SEQ_MISC;
367         next->coder->pos = 0;
368         next->coder->properties_size = 0;
369
370         return LZMA_OK;
371 }
372
373
374 extern LZMA_API lzma_ret
375 lzma_filter_flags_decoder(lzma_stream *strm, lzma_options_filter *options)
376 {
377         lzma_next_strm_init(strm, lzma_filter_flags_decoder_init, options);
378
379         strm->internal->supported_actions[LZMA_RUN] = true;
380
381         return LZMA_OK;
382 }