filter_common.c revision 292588
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 16static 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 124extern LZMA_API(lzma_ret) 125lzma_filters_copy(const lzma_filter *src, lzma_filter *dest, 126 const 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 182error: 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 193static lzma_ret 194validate_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 241extern lzma_ret 242lzma_raw_coder_init(lzma_next_coder *next, const 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 294extern uint64_t 295lzma_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