OLD | NEW |
(Empty) | |
| 1 /////////////////////////////////////////////////////////////////////////////// |
| 2 // |
| 3 /// \file filter_common.c |
| 4 /// \brief Filter-specific stuff common for both encoder and decoder |
| 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 "filter_common.h" |
| 14 |
| 15 |
| 16 static const struct { |
| 17 /// Filter ID |
| 18 lzma_vli id; |
| 19 |
| 20 /// Size of the filter-specific options structure |
| 21 size_t options_size; |
| 22 |
| 23 /// True if it is OK to use this filter as non-last filter in |
| 24 /// the chain. |
| 25 bool non_last_ok; |
| 26 |
| 27 /// True if it is OK to use this filter as the last filter in |
| 28 /// the chain. |
| 29 bool last_ok; |
| 30 |
| 31 /// True if the filter may change the size of the data (that is, the |
| 32 /// amount of encoded output can be different than the amount of |
| 33 /// uncompressed input). |
| 34 bool changes_size; |
| 35 |
| 36 } features[] = { |
| 37 #if defined (HAVE_ENCODER_LZMA1) || defined(HAVE_DECODER_LZMA1) |
| 38 { |
| 39 .id = LZMA_FILTER_LZMA1, |
| 40 .options_size = sizeof(lzma_options_lzma), |
| 41 .non_last_ok = false, |
| 42 .last_ok = true, |
| 43 .changes_size = true, |
| 44 }, |
| 45 #endif |
| 46 #ifdef HAVE_DECODER_LZMA2 |
| 47 { |
| 48 .id = LZMA_FILTER_LZMA2, |
| 49 .options_size = sizeof(lzma_options_lzma), |
| 50 .non_last_ok = false, |
| 51 .last_ok = true, |
| 52 .changes_size = true, |
| 53 }, |
| 54 #endif |
| 55 #ifdef HAVE_DECODER_X86 |
| 56 { |
| 57 .id = LZMA_FILTER_X86, |
| 58 .options_size = sizeof(lzma_options_bcj), |
| 59 .non_last_ok = true, |
| 60 .last_ok = false, |
| 61 .changes_size = false, |
| 62 }, |
| 63 #endif |
| 64 #if defined(HAVE_ENCODER_POWERPC) || defined(HAVE_DECODER_POWERPC) |
| 65 { |
| 66 .id = LZMA_FILTER_POWERPC, |
| 67 .options_size = sizeof(lzma_options_bcj), |
| 68 .non_last_ok = true, |
| 69 .last_ok = false, |
| 70 .changes_size = false, |
| 71 }, |
| 72 #endif |
| 73 #ifdef HAVE_DECODER_IA64 |
| 74 { |
| 75 .id = LZMA_FILTER_IA64, |
| 76 .options_size = sizeof(lzma_options_bcj), |
| 77 .non_last_ok = true, |
| 78 .last_ok = false, |
| 79 .changes_size = false, |
| 80 }, |
| 81 #endif |
| 82 #if defined(HAVE_ENCODER_ARM) || defined(HAVE_DECODER_ARM) |
| 83 { |
| 84 .id = LZMA_FILTER_ARM, |
| 85 .options_size = sizeof(lzma_options_bcj), |
| 86 .non_last_ok = true, |
| 87 .last_ok = false, |
| 88 .changes_size = false, |
| 89 }, |
| 90 #endif |
| 91 #if defined(HAVE_ENCODER_ARMTHUMB) || defined(HAVE_DECODER_ARMTHUMB) |
| 92 { |
| 93 .id = LZMA_FILTER_ARMTHUMB, |
| 94 .options_size = sizeof(lzma_options_bcj), |
| 95 .non_last_ok = true, |
| 96 .last_ok = false, |
| 97 .changes_size = false, |
| 98 }, |
| 99 #endif |
| 100 #if defined(HAVE_ENCODER_SPARC) || defined(HAVE_DECODER_SPARC) |
| 101 { |
| 102 .id = LZMA_FILTER_SPARC, |
| 103 .options_size = sizeof(lzma_options_bcj), |
| 104 .non_last_ok = true, |
| 105 .last_ok = false, |
| 106 .changes_size = false, |
| 107 }, |
| 108 #endif |
| 109 #if defined(HAVE_ENCODER_DELTA) || defined(HAVE_DECODER_DELTA) |
| 110 { |
| 111 .id = LZMA_FILTER_DELTA, |
| 112 .options_size = sizeof(lzma_options_delta), |
| 113 .non_last_ok = true, |
| 114 .last_ok = false, |
| 115 .changes_size = false, |
| 116 }, |
| 117 #endif |
| 118 { |
| 119 .id = LZMA_VLI_UNKNOWN |
| 120 } |
| 121 }; |
| 122 |
| 123 |
| 124 extern LZMA_API(lzma_ret) |
| 125 lzma_filters_copy(const lzma_filter *src, lzma_filter *dest, |
| 126 lzma_allocator *allocator) |
| 127 { |
| 128 if (src == NULL || dest == NULL) |
| 129 return LZMA_PROG_ERROR; |
| 130 |
| 131 lzma_ret ret; |
| 132 size_t i; |
| 133 for (i = 0; src[i].id != LZMA_VLI_UNKNOWN; ++i) { |
| 134 // There must be a maximum of four filters plus |
| 135 // the array terminator. |
| 136 if (i == LZMA_FILTERS_MAX) { |
| 137 ret = LZMA_OPTIONS_ERROR; |
| 138 goto error; |
| 139 } |
| 140 |
| 141 dest[i].id = src[i].id; |
| 142 |
| 143 if (src[i].options == NULL) { |
| 144 dest[i].options = NULL; |
| 145 } else { |
| 146 // See if the filter is supported only when the |
| 147 // options is not NULL. This might be convenient |
| 148 // sometimes if the app is actually copying only |
| 149 // a partial filter chain with a place holder ID. |
| 150 // |
| 151 // When options is not NULL, the Filter ID must be |
| 152 // supported by us, because otherwise we don't know |
| 153 // how big the options are. |
| 154 size_t j; |
| 155 for (j = 0; src[i].id != features[j].id; ++j) { |
| 156 if (features[j].id == LZMA_VLI_UNKNOWN) { |
| 157 ret = LZMA_OPTIONS_ERROR; |
| 158 goto error; |
| 159 } |
| 160 } |
| 161 |
| 162 // Allocate and copy the options. |
| 163 dest[i].options = lzma_alloc(features[j].options_size, |
| 164 allocator); |
| 165 if (dest[i].options == NULL) { |
| 166 ret = LZMA_MEM_ERROR; |
| 167 goto error; |
| 168 } |
| 169 |
| 170 memcpy(dest[i].options, src[i].options, |
| 171 features[j].options_size); |
| 172 } |
| 173 } |
| 174 |
| 175 // Terminate the filter array. |
| 176 assert(i <= LZMA_FILTERS_MAX + 1); |
| 177 dest[i].id = LZMA_VLI_UNKNOWN; |
| 178 dest[i].options = NULL; |
| 179 |
| 180 return LZMA_OK; |
| 181 |
| 182 error: |
| 183 // Free the options which we have already allocated. |
| 184 while (i-- > 0) { |
| 185 lzma_free(dest[i].options, allocator); |
| 186 dest[i].options = NULL; |
| 187 } |
| 188 |
| 189 return ret; |
| 190 } |
| 191 |
| 192 |
| 193 static lzma_ret |
| 194 validate_chain(const lzma_filter *filters, size_t *count) |
| 195 { |
| 196 // There must be at least one filter. |
| 197 if (filters == NULL || filters[0].id == LZMA_VLI_UNKNOWN) |
| 198 return LZMA_PROG_ERROR; |
| 199 |
| 200 // Number of non-last filters that may change the size of the data |
| 201 // significantly (that is, more than 1-2 % or so). |
| 202 size_t changes_size_count = 0; |
| 203 |
| 204 // True if it is OK to add a new filter after the current filter. |
| 205 bool non_last_ok = true; |
| 206 |
| 207 // True if the last filter in the given chain is actually usable as |
| 208 // the last filter. Only filters that support embedding End of Payload |
| 209 // Marker can be used as the last filter in the chain. |
| 210 bool last_ok = false; |
| 211 |
| 212 size_t i = 0; |
| 213 do { |
| 214 size_t j; |
| 215 for (j = 0; filters[i].id != features[j].id; ++j) |
| 216 if (features[j].id == LZMA_VLI_UNKNOWN) |
| 217 return LZMA_OPTIONS_ERROR; |
| 218 |
| 219 // If the previous filter in the chain cannot be a non-last |
| 220 // filter, the chain is invalid. |
| 221 if (!non_last_ok) |
| 222 return LZMA_OPTIONS_ERROR; |
| 223 |
| 224 non_last_ok = features[j].non_last_ok; |
| 225 last_ok = features[j].last_ok; |
| 226 changes_size_count += features[j].changes_size; |
| 227 |
| 228 } while (filters[++i].id != LZMA_VLI_UNKNOWN); |
| 229 |
| 230 // There must be 1-4 filters. The last filter must be usable as |
| 231 // the last filter in the chain. A maximum of three filters are |
| 232 // allowed to change the size of the data. |
| 233 if (i > LZMA_FILTERS_MAX || !last_ok || changes_size_count > 3) |
| 234 return LZMA_OPTIONS_ERROR; |
| 235 |
| 236 *count = i; |
| 237 return LZMA_OK; |
| 238 } |
| 239 |
| 240 |
| 241 extern lzma_ret |
| 242 lzma_raw_coder_init(lzma_next_coder *next, lzma_allocator *allocator, |
| 243 const lzma_filter *options, |
| 244 lzma_filter_find coder_find, bool is_encoder) |
| 245 { |
| 246 // Do some basic validation and get the number of filters. |
| 247 size_t count; |
| 248 return_if_error(validate_chain(options, &count)); |
| 249 |
| 250 // Set the filter functions and copy the options pointer. |
| 251 lzma_filter_info filters[LZMA_FILTERS_MAX + 1]; |
| 252 if (is_encoder) { |
| 253 for (size_t i = 0; i < count; ++i) { |
| 254 // The order of the filters is reversed in the |
| 255 // encoder. It allows more efficient handling |
| 256 // of the uncompressed data. |
| 257 const size_t j = count - i - 1; |
| 258 |
| 259 const lzma_filter_coder *const fc |
| 260 = coder_find(options[i].id); |
| 261 if (fc == NULL || fc->init == NULL) |
| 262 return LZMA_OPTIONS_ERROR; |
| 263 |
| 264 filters[j].id = options[i].id; |
| 265 filters[j].init = fc->init; |
| 266 filters[j].options = options[i].options; |
| 267 } |
| 268 } else { |
| 269 for (size_t i = 0; i < count; ++i) { |
| 270 const lzma_filter_coder *const fc |
| 271 = coder_find(options[i].id); |
| 272 if (fc == NULL || fc->init == NULL) |
| 273 return LZMA_OPTIONS_ERROR; |
| 274 |
| 275 filters[i].id = options[i].id; |
| 276 filters[i].init = fc->init; |
| 277 filters[i].options = options[i].options; |
| 278 } |
| 279 } |
| 280 |
| 281 // Terminate the array. |
| 282 filters[count].id = LZMA_VLI_UNKNOWN; |
| 283 filters[count].init = NULL; |
| 284 |
| 285 // Initialize the filters. |
| 286 const lzma_ret ret = lzma_next_filter_init(next, allocator, filters); |
| 287 if (ret != LZMA_OK) |
| 288 lzma_next_end(next, allocator); |
| 289 |
| 290 return ret; |
| 291 } |
| 292 |
| 293 |
| 294 extern uint64_t |
| 295 lzma_raw_coder_memusage(lzma_filter_find coder_find, |
| 296 const lzma_filter *filters) |
| 297 { |
| 298 // The chain has to have at least one filter. |
| 299 { |
| 300 size_t tmp; |
| 301 if (validate_chain(filters, &tmp) != LZMA_OK) |
| 302 return UINT64_MAX; |
| 303 } |
| 304 |
| 305 uint64_t total = 0; |
| 306 size_t i = 0; |
| 307 |
| 308 do { |
| 309 const lzma_filter_coder *const fc |
| 310 = coder_find(filters[i].id); |
| 311 if (fc == NULL) |
| 312 return UINT64_MAX; // Unsupported Filter ID |
| 313 |
| 314 if (fc->memusage == NULL) { |
| 315 // This filter doesn't have a function to calculate |
| 316 // the memory usage and validate the options. Such |
| 317 // filters need only little memory, so we use 1 KiB |
| 318 // as a good estimate. They also accept all possible |
| 319 // options, so there's no need to worry about lack |
| 320 // of validation. |
| 321 total += 1024; |
| 322 } else { |
| 323 // Call the filter-specific memory usage calculation |
| 324 // function. |
| 325 const uint64_t usage |
| 326 = fc->memusage(filters[i].options); |
| 327 if (usage == UINT64_MAX) |
| 328 return UINT64_MAX; // Invalid options |
| 329 |
| 330 total += usage; |
| 331 } |
| 332 } while (filters[++i].id != LZMA_VLI_UNKNOWN); |
| 333 |
| 334 // Add some fixed amount of extra. It's to compensate memory usage |
| 335 // of Stream, Block etc. coders, malloc() overhead, stack etc. |
| 336 return total + LZMA_MEMUSAGE_BASE; |
| 337 } |
OLD | NEW |