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 #if defined(HAVE_ENCODER_LZMA2) || defined(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 #if defined(HAVE_ENCODER_X86) || defined(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 #if defined(HAVE_ENCODER_IA64) || defined(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 } 338