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