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 { 46 .id = LZMA_FILTER_LZMA1EXT, 47 .options_size = sizeof(lzma_options_lzma), 48 .non_last_ok = false, 49 .last_ok = true, 50 .changes_size = true, 51 }, 52 #endif 53 #if defined(HAVE_ENCODER_LZMA2) || defined(HAVE_DECODER_LZMA2) 54 { 55 .id = LZMA_FILTER_LZMA2, 56 .options_size = sizeof(lzma_options_lzma), 57 .non_last_ok = false, 58 .last_ok = true, 59 .changes_size = true, 60 }, 61 #endif 62 #if defined(HAVE_ENCODER_X86) || defined(HAVE_DECODER_X86) 63 { 64 .id = LZMA_FILTER_X86, 65 .options_size = sizeof(lzma_options_bcj), 66 .non_last_ok = true, 67 .last_ok = false, 68 .changes_size = false, 69 }, 70 #endif 71 #if defined(HAVE_ENCODER_POWERPC) || defined(HAVE_DECODER_POWERPC) 72 { 73 .id = LZMA_FILTER_POWERPC, 74 .options_size = sizeof(lzma_options_bcj), 75 .non_last_ok = true, 76 .last_ok = false, 77 .changes_size = false, 78 }, 79 #endif 80 #if defined(HAVE_ENCODER_IA64) || defined(HAVE_DECODER_IA64) 81 { 82 .id = LZMA_FILTER_IA64, 83 .options_size = sizeof(lzma_options_bcj), 84 .non_last_ok = true, 85 .last_ok = false, 86 .changes_size = false, 87 }, 88 #endif 89 #if defined(HAVE_ENCODER_ARM) || defined(HAVE_DECODER_ARM) 90 { 91 .id = LZMA_FILTER_ARM, 92 .options_size = sizeof(lzma_options_bcj), 93 .non_last_ok = true, 94 .last_ok = false, 95 .changes_size = false, 96 }, 97 #endif 98 #if defined(HAVE_ENCODER_ARMTHUMB) || defined(HAVE_DECODER_ARMTHUMB) 99 { 100 .id = LZMA_FILTER_ARMTHUMB, 101 .options_size = sizeof(lzma_options_bcj), 102 .non_last_ok = true, 103 .last_ok = false, 104 .changes_size = false, 105 }, 106 #endif 107 #if defined(HAVE_ENCODER_ARM64) || defined(HAVE_DECODER_ARM64) 108 { 109 .id = LZMA_FILTER_ARM64, 110 .options_size = sizeof(lzma_options_bcj), 111 .non_last_ok = true, 112 .last_ok = false, 113 .changes_size = false, 114 }, 115 #endif 116 #if defined(HAVE_ENCODER_SPARC) || defined(HAVE_DECODER_SPARC) 117 { 118 .id = LZMA_FILTER_SPARC, 119 .options_size = sizeof(lzma_options_bcj), 120 .non_last_ok = true, 121 .last_ok = false, 122 .changes_size = false, 123 }, 124 #endif 125 #if defined(HAVE_ENCODER_DELTA) || defined(HAVE_DECODER_DELTA) 126 { 127 .id = LZMA_FILTER_DELTA, 128 .options_size = sizeof(lzma_options_delta), 129 .non_last_ok = true, 130 .last_ok = false, 131 .changes_size = false, 132 }, 133 #endif 134 { 135 .id = LZMA_VLI_UNKNOWN 136 } 137 }; 138 139 140 extern LZMA_API(lzma_ret) 141 lzma_filters_copy(const lzma_filter *src, lzma_filter *real_dest, 142 const lzma_allocator *allocator) 143 { 144 if (src == NULL || real_dest == NULL) 145 return LZMA_PROG_ERROR; 146 147 // Use a temporary destination so that the real destination 148 // will never be modied if an error occurs. 149 lzma_filter dest[LZMA_FILTERS_MAX + 1]; 150 151 lzma_ret ret; 152 size_t i; 153 for (i = 0; src[i].id != LZMA_VLI_UNKNOWN; ++i) { 154 // There must be a maximum of four filters plus 155 // the array terminator. 156 if (i == LZMA_FILTERS_MAX) { 157 ret = LZMA_OPTIONS_ERROR; 158 goto error; 159 } 160 161 dest[i].id = src[i].id; 162 163 if (src[i].options == NULL) { 164 dest[i].options = NULL; 165 } else { 166 // See if the filter is supported only when the 167 // options is not NULL. This might be convenient 168 // sometimes if the app is actually copying only 169 // a partial filter chain with a place holder ID. 170 // 171 // When options is not NULL, the Filter ID must be 172 // supported by us, because otherwise we don't know 173 // how big the options are. 174 size_t j; 175 for (j = 0; src[i].id != features[j].id; ++j) { 176 if (features[j].id == LZMA_VLI_UNKNOWN) { 177 ret = LZMA_OPTIONS_ERROR; 178 goto error; 179 } 180 } 181 182 // Allocate and copy the options. 183 dest[i].options = lzma_alloc(features[j].options_size, 184 allocator); 185 if (dest[i].options == NULL) { 186 ret = LZMA_MEM_ERROR; 187 goto error; 188 } 189 190 memcpy(dest[i].options, src[i].options, 191 features[j].options_size); 192 } 193 } 194 195 // Terminate the filter array. 196 assert(i < LZMA_FILTERS_MAX + 1); 197 dest[i].id = LZMA_VLI_UNKNOWN; 198 dest[i].options = NULL; 199 200 // Copy it to the caller-supplied array now that we know that 201 // no errors occurred. 202 memcpy(real_dest, dest, (i + 1) * sizeof(lzma_filter)); 203 204 return LZMA_OK; 205 206 error: 207 // Free the options which we have already allocated. 208 while (i-- > 0) 209 lzma_free(dest[i].options, allocator); 210 211 return ret; 212 } 213 214 215 extern LZMA_API(void) 216 lzma_filters_free(lzma_filter *filters, const lzma_allocator *allocator) 217 { 218 if (filters == NULL) 219 return; 220 221 for (size_t i = 0; filters[i].id != LZMA_VLI_UNKNOWN; ++i) { 222 if (i == LZMA_FILTERS_MAX) { 223 // The API says that LZMA_FILTERS_MAX + 1 is the 224 // maximum allowed size including the terminating 225 // element. Thus, we should never get here but in 226 // case there is a bug and we do anyway, don't go 227 // past the (probable) end of the array. 228 assert(0); 229 break; 230 } 231 232 lzma_free(filters[i].options, allocator); 233 filters[i].options = NULL; 234 filters[i].id = LZMA_VLI_UNKNOWN; 235 } 236 237 return; 238 } 239 240 241 extern lzma_ret 242 lzma_validate_chain(const lzma_filter *filters, size_t *count) 243 { 244 // There must be at least one filter. 245 if (filters == NULL || filters[0].id == LZMA_VLI_UNKNOWN) 246 return LZMA_PROG_ERROR; 247 248 // Number of non-last filters that may change the size of the data 249 // significantly (that is, more than 1-2 % or so). 250 size_t changes_size_count = 0; 251 252 // True if it is OK to add a new filter after the current filter. 253 bool non_last_ok = true; 254 255 // True if the last filter in the given chain is actually usable as 256 // the last filter. Only filters that support embedding End of Payload 257 // Marker can be used as the last filter in the chain. 258 bool last_ok = false; 259 260 size_t i = 0; 261 do { 262 size_t j; 263 for (j = 0; filters[i].id != features[j].id; ++j) 264 if (features[j].id == LZMA_VLI_UNKNOWN) 265 return LZMA_OPTIONS_ERROR; 266 267 // If the previous filter in the chain cannot be a non-last 268 // filter, the chain is invalid. 269 if (!non_last_ok) 270 return LZMA_OPTIONS_ERROR; 271 272 non_last_ok = features[j].non_last_ok; 273 last_ok = features[j].last_ok; 274 changes_size_count += features[j].changes_size; 275 276 } while (filters[++i].id != LZMA_VLI_UNKNOWN); 277 278 // There must be 1-4 filters. The last filter must be usable as 279 // the last filter in the chain. A maximum of three filters are 280 // allowed to change the size of the data. 281 if (i > LZMA_FILTERS_MAX || !last_ok || changes_size_count > 3) 282 return LZMA_OPTIONS_ERROR; 283 284 *count = i; 285 return LZMA_OK; 286 } 287 288 289 extern lzma_ret 290 lzma_raw_coder_init(lzma_next_coder *next, const lzma_allocator *allocator, 291 const lzma_filter *options, 292 lzma_filter_find coder_find, bool is_encoder) 293 { 294 // Do some basic validation and get the number of filters. 295 size_t count; 296 return_if_error(lzma_validate_chain(options, &count)); 297 298 // Set the filter functions and copy the options pointer. 299 lzma_filter_info filters[LZMA_FILTERS_MAX + 1]; 300 if (is_encoder) { 301 for (size_t i = 0; i < count; ++i) { 302 // The order of the filters is reversed in the 303 // encoder. It allows more efficient handling 304 // of the uncompressed data. 305 const size_t j = count - i - 1; 306 307 const lzma_filter_coder *const fc 308 = coder_find(options[i].id); 309 if (fc == NULL || fc->init == NULL) 310 return LZMA_OPTIONS_ERROR; 311 312 filters[j].id = options[i].id; 313 filters[j].init = fc->init; 314 filters[j].options = options[i].options; 315 } 316 } else { 317 for (size_t i = 0; i < count; ++i) { 318 const lzma_filter_coder *const fc 319 = coder_find(options[i].id); 320 if (fc == NULL || fc->init == NULL) 321 return LZMA_OPTIONS_ERROR; 322 323 filters[i].id = options[i].id; 324 filters[i].init = fc->init; 325 filters[i].options = options[i].options; 326 } 327 } 328 329 // Terminate the array. 330 filters[count].id = LZMA_VLI_UNKNOWN; 331 filters[count].init = NULL; 332 333 // Initialize the filters. 334 const lzma_ret ret = lzma_next_filter_init(next, allocator, filters); 335 if (ret != LZMA_OK) 336 lzma_next_end(next, allocator); 337 338 return ret; 339 } 340 341 342 extern uint64_t 343 lzma_raw_coder_memusage(lzma_filter_find coder_find, 344 const lzma_filter *filters) 345 { 346 // The chain has to have at least one filter. 347 { 348 size_t tmp; 349 if (lzma_validate_chain(filters, &tmp) != LZMA_OK) 350 return UINT64_MAX; 351 } 352 353 uint64_t total = 0; 354 size_t i = 0; 355 356 do { 357 const lzma_filter_coder *const fc 358 = coder_find(filters[i].id); 359 if (fc == NULL) 360 return UINT64_MAX; // Unsupported Filter ID 361 362 if (fc->memusage == NULL) { 363 // This filter doesn't have a function to calculate 364 // the memory usage and validate the options. Such 365 // filters need only little memory, so we use 1 KiB 366 // as a good estimate. They also accept all possible 367 // options, so there's no need to worry about lack 368 // of validation. 369 total += 1024; 370 } else { 371 // Call the filter-specific memory usage calculation 372 // function. 373 const uint64_t usage 374 = fc->memusage(filters[i].options); 375 if (usage == UINT64_MAX) 376 return UINT64_MAX; // Invalid options 377 378 total += usage; 379 } 380 } while (filters[++i].id != LZMA_VLI_UNKNOWN); 381 382 // Add some fixed amount of extra. It's to compensate memory usage 383 // of Stream, Block etc. coders, malloc() overhead, stack etc. 384 return total + LZMA_MEMUSAGE_BASE; 385 } 386