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