]> icculus.org git repositories - icculus/xz.git/blob - src/liblzma/common/filter_flags_encoder.c
Imported to git.
[icculus/xz.git] / src / liblzma / common / filter_flags_encoder.c
1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 /// \file       filter_flags_encoder.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_encoder.h"
22
23
24 /// \brief      Calculates the size of the Filter Properties field
25 ///
26 /// This currently can return only LZMA_OK or LZMA_HEADER_ERROR, but
27 /// with some new filters it may return also LZMA_PROG_ERROR.
28 static lzma_ret
29 get_properties_size(uint32_t *size, const lzma_options_filter *options)
30 {
31         lzma_ret ret = LZMA_OK;
32
33         switch (options->id) {
34 #ifdef HAVE_FILTER_COPY
35         case LZMA_FILTER_COPY:
36                 *size = 0;
37                 break;
38 #endif
39
40 #ifdef HAVE_FILTER_SUBBLOCK
41         case LZMA_FILTER_SUBBLOCK:
42                 *size = 0;
43                 break;
44 #endif
45
46 #ifdef HAVE_FILTER_SIMPLE
47 #       ifdef HAVE_FILTER_X86
48         case LZMA_FILTER_X86:
49 #       endif
50 #       ifdef HAVE_FILTER_POWERPC
51         case LZMA_FILTER_POWERPC:
52 #       endif
53 #       ifdef HAVE_FILTER_IA64
54         case LZMA_FILTER_IA64:
55 #       endif
56 #       ifdef HAVE_FILTER_ARM
57         case LZMA_FILTER_ARM:
58 #       endif
59 #       ifdef HAVE_FILTER_ARMTHUMB
60         case LZMA_FILTER_ARMTHUMB:
61 #       endif
62 #       ifdef HAVE_FILTER_SPARC
63         case LZMA_FILTER_SPARC:
64 #       endif
65                 if (options->options == NULL || ((const lzma_options_simple *)(
66                                 options->options))->start_offset == 0)
67                         *size = 0;
68                 else
69                         *size = 4;
70                 break;
71 #endif
72
73 #ifdef HAVE_FILTER_DELTA
74         case LZMA_FILTER_DELTA:
75                 *size = 1;
76                 break;
77 #endif
78
79 #ifdef HAVE_FILTER_LZMA
80         case LZMA_FILTER_LZMA:
81                 *size = 2;
82                 break;
83 #endif
84
85         default:
86                 // Unknown filter - if the Filter ID is a proper VLI,
87                 // return LZMA_HEADER_ERROR instead of LZMA_PROG_ERROR,
88                 // because it's possible that we just don't have support
89                 // compiled in for the requested filter.
90                 ret = options->id <= LZMA_VLI_VALUE_MAX
91                                 ? LZMA_HEADER_ERROR : LZMA_PROG_ERROR;
92                 break;
93         }
94
95         return ret;
96 }
97
98
99 extern LZMA_API lzma_ret
100 lzma_filter_flags_size(uint32_t *size, const lzma_options_filter *options)
101 {
102         // Get size of Filter Properties.
103         uint32_t prop_size;
104         const lzma_ret ret = get_properties_size(&prop_size, options);
105         if (ret != LZMA_OK)
106                 return ret;
107
108         // Size of Filter ID field if it exists.
109         size_t id_size;
110         size_t prop_size_size;
111         if (options->id < 0xE0
112                         && (lzma_vli)(prop_size) == options->id / 0x20) {
113                 // ID and Size of Filter Properties fit into Misc.
114                 id_size = 0;
115                 prop_size_size = 0;
116
117         } else {
118                 // At least Filter ID is stored using the External ID field.
119                 id_size = lzma_vli_size(options->id);
120                 if (id_size == 0)
121                         return LZMA_PROG_ERROR;
122
123                 if (prop_size <= 30) {
124                         // Size of Filter Properties fits into Misc still.
125                         prop_size_size = 0;
126                 } else {
127                         // The Size of Filter Properties field is used too.
128                         prop_size_size = lzma_vli_size(prop_size);
129                         if (prop_size_size == 0)
130                                 return LZMA_PROG_ERROR;
131                 }
132         }
133
134         // 1 is for the Misc field.
135         *size = 1 + id_size + prop_size_size + prop_size;
136
137         return LZMA_OK;
138 }
139
140
141 #ifdef HAVE_FILTER_SIMPLE
142 /// Encodes Filter Properties of the so called simple filters
143 static lzma_ret
144 properties_simple(uint8_t *out, size_t *out_pos, size_t out_size,
145                 const lzma_options_simple *options)
146 {
147         if (options == NULL || options->start_offset == 0)
148                 return LZMA_OK;
149
150         if (out_size - *out_pos < 4)
151                 return LZMA_BUF_ERROR;
152
153         for (size_t i = 0; i < 4; ++i)
154                 out[(*out_pos)++] = options->start_offset >> (i * 8);
155
156         return LZMA_OK;
157 }
158 #endif
159
160
161 #ifdef HAVE_FILTER_DELTA
162 /// Encodes Filter Properties of the Delta filter
163 static lzma_ret
164 properties_delta(uint8_t *out, size_t *out_pos, size_t out_size,
165                 const lzma_options_delta *options)
166 {
167         if (options == NULL)
168                 return LZMA_PROG_ERROR;
169
170         // It's possible that newer liblzma versions will support larger
171         // distance values.
172         if (options->distance < LZMA_DELTA_DISTANCE_MIN
173                         || options->distance > LZMA_DELTA_DISTANCE_MAX)
174                 return LZMA_HEADER_ERROR;
175
176         if (out_size - *out_pos < 1)
177                 return LZMA_BUF_ERROR;
178
179         out[*out_pos] = options->distance - LZMA_DELTA_DISTANCE_MIN;
180         ++*out_pos;
181
182         return LZMA_OK;
183 }
184 #endif
185
186
187 #ifdef HAVE_FILTER_LZMA
188 /// Encodes LZMA Properties and Dictionary Flags (two bytes)
189 static lzma_ret
190 properties_lzma(uint8_t *out, size_t *out_pos, size_t out_size,
191                 const lzma_options_lzma *options)
192 {
193         if (options == NULL)
194                 return LZMA_PROG_ERROR;
195
196         if (out_size - *out_pos < 2)
197                 return LZMA_BUF_ERROR;
198
199         // LZMA Properties
200         if (lzma_lzma_encode_properties(options, out + *out_pos))
201                 return LZMA_HEADER_ERROR;
202
203         ++*out_pos;
204
205         // Dictionary flags
206         //
207         // Dictionary size is encoded using six bits of
208         // which one is mantissa and five are exponent.
209         //
210         // There are some limits that must hold to keep
211         // this coding working.
212 #       if LZMA_DICTIONARY_SIZE_MAX > UINT32_MAX / 2
213 #               error LZMA_DICTIONARY_SIZE_MAX is too big.
214 #       endif
215 #       if LZMA_DICTIONARY_SIZE_MIN < 1
216 #               error LZMA_DICTIONARY_SIZE_MIN cannot be zero.
217 #       endif
218
219         // Validate it:
220         if (options->dictionary_size < LZMA_DICTIONARY_SIZE_MIN
221                         || options->dictionary_size > LZMA_DICTIONARY_SIZE_MAX)
222                 return LZMA_HEADER_ERROR;
223
224         if (options->dictionary_size == 1) {
225                 // Special case
226                 out[*out_pos] = 0x00;
227         } else {
228                 // TODO This could be more elegant.
229                 uint32_t i = 1;
230                 while (((2 | ((i + 1) & 1)) << ((i - 1) / 2))
231                                 < options->dictionary_size)
232                         ++i;
233                 out[*out_pos] = i;
234         }
235
236         ++*out_pos;
237
238         return LZMA_OK;
239 }
240 #endif
241
242
243 extern LZMA_API lzma_ret
244 lzma_filter_flags_encode(uint8_t *out, size_t *out_pos, size_t out_size,
245                 const lzma_options_filter *options)
246 {
247         // Minimum output is one byte (everything fits into Misc).
248         // The caller should have checked that there is enough output space,
249         // so we return LZMA_PROG_ERROR instead of LZMA_BUF_ERROR.
250         if (*out_pos >= out_size)
251                 return LZMA_PROG_ERROR;
252
253         // Get size of Filter Properties.
254         uint32_t prop_size;
255         lzma_ret ret = get_properties_size(&prop_size, options);
256         if (ret != LZMA_OK)
257                 return ret;
258
259         // Misc, External ID, and Size of Properties
260         if (options->id < 0xE0
261                         && (lzma_vli)(prop_size) == options->id / 0x20) {
262                 // ID and Size of Filter Properties fit into Misc.
263                 out[*out_pos] = options->id;
264                 ++*out_pos;
265
266         } else if (prop_size <= 30) {
267                 // Size of Filter Properties fits into Misc.
268                 out[*out_pos] = prop_size + 0xE0;
269                 ++*out_pos;
270
271                 // External ID is used to encode the Filter ID. If encoding
272                 // the VLI fails, it's because the caller has given as too
273                 // little output space, which it should have checked already.
274                 // So return LZMA_PROG_ERROR, not LZMA_BUF_ERROR.
275                 size_t dummy = 0;
276                 if (lzma_vli_encode(options->id, &dummy, 1,
277                                 out, out_pos, out_size) != LZMA_STREAM_END)
278                         return LZMA_PROG_ERROR;
279
280         } else {
281                 // Nothing fits into Misc.
282                 out[*out_pos] = 0xFF;
283                 ++*out_pos;
284
285                 // External ID is used to encode the Filter ID.
286                 size_t dummy = 0;
287                 if (lzma_vli_encode(options->id, &dummy, 1,
288                                 out, out_pos, out_size) != LZMA_STREAM_END)
289                         return LZMA_PROG_ERROR;
290
291                 // External Size of Filter Properties
292                 dummy = 0;
293                 if (lzma_vli_encode(prop_size, &dummy, 1,
294                                 out, out_pos, out_size) != LZMA_STREAM_END)
295                         return LZMA_PROG_ERROR;
296         }
297
298         // Filter Properties
299         switch (options->id) {
300 #ifdef HAVE_FILTER_COPY
301         case LZMA_FILTER_COPY:
302                 assert(prop_size == 0);
303                 ret = options->options == NULL ? LZMA_OK : LZMA_HEADER_ERROR;
304                 break;
305 #endif
306
307 #ifdef HAVE_FILTER_SUBBLOCK
308         case LZMA_FILTER_SUBBLOCK:
309                 assert(prop_size == 0);
310                 ret = LZMA_OK;
311                 break;
312 #endif
313
314 #ifdef HAVE_FILTER_SIMPLE
315 #       ifdef HAVE_FILTER_X86
316         case LZMA_FILTER_X86:
317 #       endif
318 #       ifdef HAVE_FILTER_POWERPC
319         case LZMA_FILTER_POWERPC:
320 #       endif
321 #       ifdef HAVE_FILTER_IA64
322         case LZMA_FILTER_IA64:
323 #       endif
324 #       ifdef HAVE_FILTER_ARM
325         case LZMA_FILTER_ARM:
326 #       endif
327 #       ifdef HAVE_FILTER_ARMTHUMB
328         case LZMA_FILTER_ARMTHUMB:
329 #       endif
330 #       ifdef HAVE_FILTER_SPARC
331         case LZMA_FILTER_SPARC:
332 #       endif
333                 ret = properties_simple(out, out_pos, out_size,
334                                 options->options);
335                 break;
336 #endif
337
338 #ifdef HAVE_FILTER_DELTA
339         case LZMA_FILTER_DELTA:
340                 ret = properties_delta(out, out_pos, out_size,
341                                 options->options);
342                 break;
343 #endif
344
345 #ifdef HAVE_FILTER_LZMA
346         case LZMA_FILTER_LZMA:
347                 ret = properties_lzma(out, out_pos, out_size,
348                                 options->options);
349                 break;
350 #endif
351
352         default:
353                 assert(0);
354                 ret = LZMA_PROG_ERROR;
355                 break;
356         }
357
358         return ret;
359 }