1207753Smm/////////////////////////////////////////////////////////////////////////////// 2207753Smm// 3207753Smm/// \file filter_common.c 4207753Smm/// \brief Filter-specific stuff common for both encoder and decoder 5207753Smm// 6207753Smm// Author: Lasse Collin 7207753Smm// 8207753Smm// This file has been put into the public domain. 9207753Smm// You can do whatever you want with this file. 10207753Smm// 11207753Smm/////////////////////////////////////////////////////////////////////////////// 12207753Smm 13207753Smm#include "filter_common.h" 14207753Smm 15207753Smm 16207753Smmstatic const struct { 17207753Smm /// Filter ID 18207753Smm lzma_vli id; 19207753Smm 20207753Smm /// Size of the filter-specific options structure 21207753Smm size_t options_size; 22207753Smm 23207753Smm /// True if it is OK to use this filter as non-last filter in 24207753Smm /// the chain. 25207753Smm bool non_last_ok; 26207753Smm 27207753Smm /// True if it is OK to use this filter as the last filter in 28207753Smm /// the chain. 29207753Smm bool last_ok; 30207753Smm 31207753Smm /// True if the filter may change the size of the data (that is, the 32207753Smm /// amount of encoded output can be different than the amount of 33207753Smm /// uncompressed input). 34207753Smm bool changes_size; 35207753Smm 36207753Smm} features[] = { 37207753Smm#if defined (HAVE_ENCODER_LZMA1) || defined(HAVE_DECODER_LZMA1) 38207753Smm { 39207753Smm .id = LZMA_FILTER_LZMA1, 40207753Smm .options_size = sizeof(lzma_options_lzma), 41207753Smm .non_last_ok = false, 42207753Smm .last_ok = true, 43207753Smm .changes_size = true, 44207753Smm }, 45207753Smm#endif 46223935Smm#if defined(HAVE_ENCODER_LZMA2) || defined(HAVE_DECODER_LZMA2) 47207753Smm { 48207753Smm .id = LZMA_FILTER_LZMA2, 49207753Smm .options_size = sizeof(lzma_options_lzma), 50207753Smm .non_last_ok = false, 51207753Smm .last_ok = true, 52207753Smm .changes_size = true, 53207753Smm }, 54207753Smm#endif 55223935Smm#if defined(HAVE_ENCODER_X86) || defined(HAVE_DECODER_X86) 56207753Smm { 57207753Smm .id = LZMA_FILTER_X86, 58207753Smm .options_size = sizeof(lzma_options_bcj), 59207753Smm .non_last_ok = true, 60207753Smm .last_ok = false, 61207753Smm .changes_size = false, 62207753Smm }, 63207753Smm#endif 64207753Smm#if defined(HAVE_ENCODER_POWERPC) || defined(HAVE_DECODER_POWERPC) 65207753Smm { 66207753Smm .id = LZMA_FILTER_POWERPC, 67207753Smm .options_size = sizeof(lzma_options_bcj), 68207753Smm .non_last_ok = true, 69207753Smm .last_ok = false, 70207753Smm .changes_size = false, 71207753Smm }, 72207753Smm#endif 73223935Smm#if defined(HAVE_ENCODER_IA64) || defined(HAVE_DECODER_IA64) 74207753Smm { 75207753Smm .id = LZMA_FILTER_IA64, 76207753Smm .options_size = sizeof(lzma_options_bcj), 77207753Smm .non_last_ok = true, 78207753Smm .last_ok = false, 79207753Smm .changes_size = false, 80207753Smm }, 81207753Smm#endif 82207753Smm#if defined(HAVE_ENCODER_ARM) || defined(HAVE_DECODER_ARM) 83207753Smm { 84207753Smm .id = LZMA_FILTER_ARM, 85207753Smm .options_size = sizeof(lzma_options_bcj), 86207753Smm .non_last_ok = true, 87207753Smm .last_ok = false, 88207753Smm .changes_size = false, 89207753Smm }, 90207753Smm#endif 91207753Smm#if defined(HAVE_ENCODER_ARMTHUMB) || defined(HAVE_DECODER_ARMTHUMB) 92207753Smm { 93207753Smm .id = LZMA_FILTER_ARMTHUMB, 94207753Smm .options_size = sizeof(lzma_options_bcj), 95207753Smm .non_last_ok = true, 96207753Smm .last_ok = false, 97207753Smm .changes_size = false, 98207753Smm }, 99207753Smm#endif 100207753Smm#if defined(HAVE_ENCODER_SPARC) || defined(HAVE_DECODER_SPARC) 101207753Smm { 102207753Smm .id = LZMA_FILTER_SPARC, 103207753Smm .options_size = sizeof(lzma_options_bcj), 104207753Smm .non_last_ok = true, 105207753Smm .last_ok = false, 106207753Smm .changes_size = false, 107207753Smm }, 108207753Smm#endif 109207753Smm#if defined(HAVE_ENCODER_DELTA) || defined(HAVE_DECODER_DELTA) 110207753Smm { 111207753Smm .id = LZMA_FILTER_DELTA, 112207753Smm .options_size = sizeof(lzma_options_delta), 113207753Smm .non_last_ok = true, 114207753Smm .last_ok = false, 115207753Smm .changes_size = false, 116207753Smm }, 117207753Smm#endif 118207753Smm { 119207753Smm .id = LZMA_VLI_UNKNOWN 120207753Smm } 121207753Smm}; 122207753Smm 123207753Smm 124207753Smmextern LZMA_API(lzma_ret) 125207753Smmlzma_filters_copy(const lzma_filter *src, lzma_filter *dest, 126278433Srpaulo const lzma_allocator *allocator) 127207753Smm{ 128207753Smm if (src == NULL || dest == NULL) 129207753Smm return LZMA_PROG_ERROR; 130207753Smm 131207753Smm lzma_ret ret; 132207753Smm size_t i; 133207753Smm for (i = 0; src[i].id != LZMA_VLI_UNKNOWN; ++i) { 134207753Smm // There must be a maximum of four filters plus 135207753Smm // the array terminator. 136207753Smm if (i == LZMA_FILTERS_MAX) { 137207753Smm ret = LZMA_OPTIONS_ERROR; 138207753Smm goto error; 139207753Smm } 140207753Smm 141207753Smm dest[i].id = src[i].id; 142207753Smm 143207753Smm if (src[i].options == NULL) { 144207753Smm dest[i].options = NULL; 145207753Smm } else { 146207753Smm // See if the filter is supported only when the 147207753Smm // options is not NULL. This might be convenient 148207753Smm // sometimes if the app is actually copying only 149207753Smm // a partial filter chain with a place holder ID. 150207753Smm // 151207753Smm // When options is not NULL, the Filter ID must be 152207753Smm // supported by us, because otherwise we don't know 153207753Smm // how big the options are. 154207753Smm size_t j; 155207753Smm for (j = 0; src[i].id != features[j].id; ++j) { 156207753Smm if (features[j].id == LZMA_VLI_UNKNOWN) { 157207753Smm ret = LZMA_OPTIONS_ERROR; 158207753Smm goto error; 159207753Smm } 160207753Smm } 161207753Smm 162207753Smm // Allocate and copy the options. 163207753Smm dest[i].options = lzma_alloc(features[j].options_size, 164207753Smm allocator); 165207753Smm if (dest[i].options == NULL) { 166207753Smm ret = LZMA_MEM_ERROR; 167207753Smm goto error; 168207753Smm } 169207753Smm 170207753Smm memcpy(dest[i].options, src[i].options, 171207753Smm features[j].options_size); 172207753Smm } 173207753Smm } 174207753Smm 175207753Smm // Terminate the filter array. 176207753Smm assert(i <= LZMA_FILTERS_MAX + 1); 177207753Smm dest[i].id = LZMA_VLI_UNKNOWN; 178207753Smm dest[i].options = NULL; 179207753Smm 180207753Smm return LZMA_OK; 181207753Smm 182207753Smmerror: 183207753Smm // Free the options which we have already allocated. 184207753Smm while (i-- > 0) { 185207753Smm lzma_free(dest[i].options, allocator); 186207753Smm dest[i].options = NULL; 187207753Smm } 188207753Smm 189207753Smm return ret; 190207753Smm} 191207753Smm 192207753Smm 193207753Smmstatic lzma_ret 194207753Smmvalidate_chain(const lzma_filter *filters, size_t *count) 195207753Smm{ 196207753Smm // There must be at least one filter. 197207753Smm if (filters == NULL || filters[0].id == LZMA_VLI_UNKNOWN) 198207753Smm return LZMA_PROG_ERROR; 199207753Smm 200207753Smm // Number of non-last filters that may change the size of the data 201207753Smm // significantly (that is, more than 1-2 % or so). 202207753Smm size_t changes_size_count = 0; 203207753Smm 204207753Smm // True if it is OK to add a new filter after the current filter. 205207753Smm bool non_last_ok = true; 206207753Smm 207207753Smm // True if the last filter in the given chain is actually usable as 208207753Smm // the last filter. Only filters that support embedding End of Payload 209207753Smm // Marker can be used as the last filter in the chain. 210207753Smm bool last_ok = false; 211207753Smm 212207753Smm size_t i = 0; 213207753Smm do { 214207753Smm size_t j; 215207753Smm for (j = 0; filters[i].id != features[j].id; ++j) 216207753Smm if (features[j].id == LZMA_VLI_UNKNOWN) 217207753Smm return LZMA_OPTIONS_ERROR; 218207753Smm 219207753Smm // If the previous filter in the chain cannot be a non-last 220207753Smm // filter, the chain is invalid. 221207753Smm if (!non_last_ok) 222207753Smm return LZMA_OPTIONS_ERROR; 223207753Smm 224207753Smm non_last_ok = features[j].non_last_ok; 225207753Smm last_ok = features[j].last_ok; 226207753Smm changes_size_count += features[j].changes_size; 227207753Smm 228207753Smm } while (filters[++i].id != LZMA_VLI_UNKNOWN); 229207753Smm 230207753Smm // There must be 1-4 filters. The last filter must be usable as 231207753Smm // the last filter in the chain. A maximum of three filters are 232207753Smm // allowed to change the size of the data. 233207753Smm if (i > LZMA_FILTERS_MAX || !last_ok || changes_size_count > 3) 234207753Smm return LZMA_OPTIONS_ERROR; 235207753Smm 236207753Smm *count = i; 237207753Smm return LZMA_OK; 238207753Smm} 239207753Smm 240207753Smm 241207753Smmextern lzma_ret 242278433Srpaulolzma_raw_coder_init(lzma_next_coder *next, const lzma_allocator *allocator, 243207753Smm const lzma_filter *options, 244207753Smm lzma_filter_find coder_find, bool is_encoder) 245207753Smm{ 246207753Smm // Do some basic validation and get the number of filters. 247207753Smm size_t count; 248207753Smm return_if_error(validate_chain(options, &count)); 249207753Smm 250207753Smm // Set the filter functions and copy the options pointer. 251207753Smm lzma_filter_info filters[LZMA_FILTERS_MAX + 1]; 252207753Smm if (is_encoder) { 253207753Smm for (size_t i = 0; i < count; ++i) { 254207753Smm // The order of the filters is reversed in the 255207753Smm // encoder. It allows more efficient handling 256207753Smm // of the uncompressed data. 257207753Smm const size_t j = count - i - 1; 258207753Smm 259207753Smm const lzma_filter_coder *const fc 260207753Smm = coder_find(options[i].id); 261207753Smm if (fc == NULL || fc->init == NULL) 262207753Smm return LZMA_OPTIONS_ERROR; 263207753Smm 264207753Smm filters[j].id = options[i].id; 265207753Smm filters[j].init = fc->init; 266207753Smm filters[j].options = options[i].options; 267207753Smm } 268207753Smm } else { 269207753Smm for (size_t i = 0; i < count; ++i) { 270207753Smm const lzma_filter_coder *const fc 271207753Smm = coder_find(options[i].id); 272207753Smm if (fc == NULL || fc->init == NULL) 273207753Smm return LZMA_OPTIONS_ERROR; 274207753Smm 275207753Smm filters[i].id = options[i].id; 276207753Smm filters[i].init = fc->init; 277207753Smm filters[i].options = options[i].options; 278207753Smm } 279207753Smm } 280207753Smm 281207753Smm // Terminate the array. 282207753Smm filters[count].id = LZMA_VLI_UNKNOWN; 283207753Smm filters[count].init = NULL; 284207753Smm 285207753Smm // Initialize the filters. 286207753Smm const lzma_ret ret = lzma_next_filter_init(next, allocator, filters); 287207753Smm if (ret != LZMA_OK) 288207753Smm lzma_next_end(next, allocator); 289207753Smm 290207753Smm return ret; 291207753Smm} 292207753Smm 293207753Smm 294207753Smmextern uint64_t 295207753Smmlzma_raw_coder_memusage(lzma_filter_find coder_find, 296207753Smm const lzma_filter *filters) 297207753Smm{ 298207753Smm // The chain has to have at least one filter. 299207753Smm { 300207753Smm size_t tmp; 301207753Smm if (validate_chain(filters, &tmp) != LZMA_OK) 302207753Smm return UINT64_MAX; 303207753Smm } 304207753Smm 305207753Smm uint64_t total = 0; 306207753Smm size_t i = 0; 307207753Smm 308207753Smm do { 309207753Smm const lzma_filter_coder *const fc 310207753Smm = coder_find(filters[i].id); 311207753Smm if (fc == NULL) 312207753Smm return UINT64_MAX; // Unsupported Filter ID 313207753Smm 314207753Smm if (fc->memusage == NULL) { 315207753Smm // This filter doesn't have a function to calculate 316207753Smm // the memory usage and validate the options. Such 317207753Smm // filters need only little memory, so we use 1 KiB 318207753Smm // as a good estimate. They also accept all possible 319207753Smm // options, so there's no need to worry about lack 320207753Smm // of validation. 321207753Smm total += 1024; 322207753Smm } else { 323207753Smm // Call the filter-specific memory usage calculation 324207753Smm // function. 325207753Smm const uint64_t usage 326207753Smm = fc->memusage(filters[i].options); 327207753Smm if (usage == UINT64_MAX) 328207753Smm return UINT64_MAX; // Invalid options 329207753Smm 330207753Smm total += usage; 331207753Smm } 332207753Smm } while (filters[++i].id != LZMA_VLI_UNKNOWN); 333207753Smm 334207753Smm // Add some fixed amount of extra. It's to compensate memory usage 335207753Smm // of Stream, Block etc. coders, malloc() overhead, stack etc. 336207753Smm return total + LZMA_MEMUSAGE_BASE; 337207753Smm} 338