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