Coverage Report

Created: 2023-09-25 06:56

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