Coverage Report

Created: 2025-06-15 00:57

/src/cmp_tool/programs/cmp_guess.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * @file   cmp_guess.c
3
 * @author Dominik Loidolt (dominik.loidolt@univie.ac.at)
4
 * @date   2021
5
 *
6
 * @copyright GPLv2
7
 * This program is free software; you can redistribute it and/or modify it
8
 * under the terms and conditions of the GNU General Public License,
9
 * version 2, as published by the Free Software Foundation.
10
 *
11
 * This program is distributed in the hope it will be useful, but WITHOUT
12
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
14
 * more details.
15
 *
16
 * @brief helps the user find good compression parameters for a given dataset
17
 * @warning this part of the software is not intended to run on-board on the ICU.
18
 */
19
20
#include <limits.h>
21
#include <stdint.h>
22
#include <stdio.h>
23
#include <stdlib.h>
24
#include <string.h>
25
26
#include <cmp_error.h>
27
#include <cmp_debug.h>
28
#include <leon_inttypes.h>
29
#include <cmp_data_types.h>
30
#include <cmp_support.h>
31
#include <cmp_icu.h>
32
#include <cmp_chunk.h>
33
#include <cmp_chunk_type.h>
34
#include <cmp_guess.h>
35
36
0
#define CMP_GUESS_MAX_CAL_STEPS 20274
37
38
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
39
/* Redefine (f)printf to do nothing */
40
__extension__
41
0
#define printf(...) do {} while (0)
42
0
#define fprintf(...) do {} while (0)
43
#endif
44
45
46
/* how often the model is updated before it is rested */
47
static int num_model_updates = CMP_GUESS_N_MODEL_UPDATE_DEF;
48
49
50
/**
51
 * @brief sets how often the model is updated before the model reset;
52
 * @note the default value is CMP_GUESS_N_MODEL_UPDATE_DEF
53
 * @note this is needed to guess a good model_value
54
 *
55
 * @param n_model_updates number of model updates
56
 */
57
58
void cmp_guess_set_model_updates(int n_model_updates)
59
0
{
60
0
  num_model_updates = n_model_updates;
61
0
}
62
63
64
/**
65
 * @brief guess a good model value
66
 *
67
 * @param n_model_updates number of model updates
68
 *
69
 * @returns guessed model_value
70
 */
71
72
uint16_t cmp_guess_model_value(int n_model_updates)
73
46
{
74
46
  if (n_model_updates <= 2)
75
0
    return 8;
76
46
  if (n_model_updates <= 5)
77
0
    return 10;
78
46
  if (n_model_updates <= 11)
79
46
    return 11;
80
0
  if (n_model_updates <= 21)
81
0
    return 12;
82
83
0
  return 13;
84
0
}
85
86
87
/**
88
 * @brief get a good spill threshold parameter for the selected Golomb parameter
89
 *  and compression mode
90
 *
91
 * @param golomb_par  Golomb parameter
92
 * @param cmp_mode  compression mode
93
 *
94
 * @returns a good spill parameter (optimal for zero escape mechanism)
95
 */
96
97
uint32_t cmp_rdcu_get_good_spill(unsigned int golomb_par, enum cmp_mode cmp_mode)
98
2.94k
{
99
2.94k
  const uint32_t LUT_IMA_MULIT[MAX_IMA_GOLOMB_PAR+1] = {0, 8, 16, 23,
100
2.94k
    30, 36, 44, 51, 58, 64, 71, 77, 84, 90, 97, 108, 115, 121, 128,
101
2.94k
    135, 141, 148, 155, 161, 168, 175, 181, 188, 194, 201, 207, 214,
102
2.94k
    229, 236, 242, 250, 256, 263, 269, 276, 283, 290, 296, 303, 310,
103
2.94k
    317, 324, 330, 336, 344, 351, 358, 363, 370, 377, 383, 391, 397,
104
2.94k
    405, 411, 418, 424, 431, 452 };
105
106
2.94k
  if (zero_escape_mech_is_used(cmp_mode))
107
1.60k
    return cmp_ima_max_spill(golomb_par);
108
109
1.34k
  if (cmp_mode == CMP_MODE_MODEL_MULTI) {
110
576
    if (golomb_par > MAX_IMA_GOLOMB_PAR)
111
0
      return 0;
112
576
    else
113
576
      return LUT_IMA_MULIT[golomb_par];
114
576
  }
115
116
768
  if (cmp_mode == CMP_MODE_DIFF_MULTI)
117
576
    return CMP_GOOD_SPILL_DIFF_MULTI;
118
119
192
  return 0;
120
768
}
121
122
123
/**
124
 * @brief guess a good configuration with pre_cal_method
125
 *
126
 * @param rcfg  RDCU compression configuration structure
127
 *
128
 * @returns the size in bits of the compressed data of the guessed
129
 * configuration; 0 on error
130
 */
131
132
static uint32_t pre_cal_method(struct rdcu_cfg *rcfg)
133
46
{
134
46
  uint32_t g;
135
46
  uint32_t cmp_size, cmp_size_best = INT_MAX;
136
46
  uint32_t golomb_par_best = 0;
137
46
  uint32_t spill_best = 0;
138
139
2.89k
  for (g = MIN_IMA_GOLOMB_PAR; g < MAX_IMA_GOLOMB_PAR; g++) {
140
2.85k
    uint32_t s = cmp_rdcu_get_good_spill(g, rcfg->cmp_mode);
141
142
2.85k
    rcfg->golomb_par = g;
143
2.85k
    rcfg->spill = s;
144
2.85k
    cmp_size = compress_like_rdcu(rcfg, NULL);
145
2.85k
    if (cmp_is_error(cmp_size)) {
146
0
      return 0;
147
2.85k
    } else if (cmp_size < cmp_size_best) {
148
231
      cmp_size_best = cmp_size;
149
231
      golomb_par_best = g;
150
231
      spill_best = s;
151
231
    }
152
2.85k
  }
153
46
  rcfg->golomb_par = golomb_par_best;
154
46
  rcfg->spill = spill_best;
155
156
46
  return cmp_size_best;
157
46
}
158
159
160
/**
161
 * @brief guess a good configuration with brute force method
162
 *
163
 * @param rcfg  RDCU compression configuration structure
164
 *
165
 * @returns the size in bits of the compressed data of the guessed
166
 * configuration; 0 on error
167
 */
168
169
static uint32_t brute_force(struct rdcu_cfg *rcfg)
170
0
{
171
0
  uint32_t g, s;
172
0
  uint32_t n_cal_steps = 0, last = 0;
173
0
  const uint32_t max_cal_steps = CMP_GUESS_MAX_CAL_STEPS;
174
0
  uint32_t cmp_size, cmp_size_best = INT_MAX;
175
0
  uint32_t golomb_par_best = 0;
176
0
  uint32_t spill_best = 0;
177
0
  uint32_t percent;
178
179
  /* shortcut for zero escape mechanism */
180
0
  if (zero_escape_mech_is_used(rcfg->cmp_mode))
181
0
    return pre_cal_method(rcfg);
182
183
0
  printf("0%%... ");
184
0
  fflush(stdout);
185
186
0
  for (g = MIN_IMA_GOLOMB_PAR; g < MAX_IMA_GOLOMB_PAR; g++) {
187
0
    for (s = MIN_IMA_SPILL; s < cmp_ima_max_spill(g); s++) {
188
0
      rcfg->golomb_par = g;
189
0
      rcfg->spill = s;
190
191
0
      cmp_size = compress_like_rdcu(rcfg, NULL);
192
0
      if (cmp_is_error(cmp_size)) {
193
0
        return 0;
194
0
      } else if (cmp_size < cmp_size_best) {
195
0
        cmp_size_best = cmp_size;
196
0
        golomb_par_best = g;
197
0
        spill_best = s;
198
0
      }
199
0
    }
200
0
    n_cal_steps += s;
201
202
0
    percent = n_cal_steps*100/max_cal_steps;
203
0
    if (percent > 5+last && percent < 100) {
204
0
      last = percent;
205
0
      printf("%" PRIu32 "%%... ", percent);
206
0
      fflush(stdout);
207
0
    }
208
0
  }
209
0
  printf("100%% ");
210
0
  rcfg->golomb_par = golomb_par_best;
211
0
  rcfg->spill = spill_best;
212
213
0
  return cmp_size_best;
214
0
}
215
216
217
/**
218
 * @brief guessed rdcu specific adaptive parameters
219
 *
220
 * @param rcfg  RDCU compression configuration structure
221
 * @note internal use only
222
 */
223
224
static void add_rdcu_pars_internal(struct rdcu_cfg *rcfg)
225
46
{
226
46
  if (rcfg->golomb_par == MIN_IMA_GOLOMB_PAR) {
227
17
    rcfg->ap1_golomb_par = rcfg->golomb_par + 1;
228
17
    rcfg->ap2_golomb_par = rcfg->golomb_par + 2;
229
230
29
  } else if (rcfg->golomb_par == MAX_IMA_GOLOMB_PAR) {
231
0
    rcfg->ap1_golomb_par = rcfg->golomb_par - 2;
232
0
    rcfg->ap2_golomb_par = rcfg->golomb_par - 1;
233
29
  } else {
234
29
    rcfg->ap1_golomb_par = rcfg->golomb_par - 1;
235
29
    rcfg->ap2_golomb_par = rcfg->golomb_par + 1;
236
29
  }
237
238
46
  rcfg->ap1_spill = cmp_rdcu_get_good_spill(rcfg->ap1_golomb_par, rcfg->cmp_mode);
239
46
  rcfg->ap2_spill = cmp_rdcu_get_good_spill(rcfg->ap2_golomb_par, rcfg->cmp_mode);
240
241
46
  if (model_mode_is_used(rcfg->cmp_mode)) {
242
16
    rcfg->rdcu_data_adr = CMP_DEF_IMA_MODEL_RDCU_DATA_ADR;
243
16
    rcfg->rdcu_model_adr = CMP_DEF_IMA_MODEL_RDCU_MODEL_ADR;
244
16
    rcfg->rdcu_new_model_adr = CMP_DEF_IMA_MODEL_RDCU_UP_MODEL_ADR;
245
16
    rcfg->rdcu_buffer_adr = CMP_DEF_IMA_MODEL_RDCU_BUFFER_ADR;
246
30
  } else {
247
30
    rcfg->rdcu_data_adr = CMP_DEF_IMA_DIFF_RDCU_DATA_ADR;
248
30
    rcfg->rdcu_model_adr = CMP_DEF_IMA_DIFF_RDCU_MODEL_ADR;
249
30
    rcfg->rdcu_new_model_adr = CMP_DEF_IMA_DIFF_RDCU_UP_MODEL_ADR;
250
30
    rcfg->rdcu_buffer_adr = CMP_DEF_IMA_DIFF_RDCU_BUFFER_ADR;
251
30
  }
252
46
}
253
254
255
/**
256
 * @brief guess a good compression configuration
257
 * @details use the samples, input_buf, model_buf and the cmp_mode in rcfg to
258
 *  find a good set of compression parameters
259
 * @note compression parameters in the rcfg struct (golomb_par, spill, model_value,
260
 *  ap1_.., ap2_.., buffer_length, ...) are overwritten by this function
261
 *
262
 * @param rcfg  RDCU compression configuration structure
263
 * @param level guess_level 1 -> fast; 2 -> default; 3 -> slow(brute force)
264
 *
265
 * @returns the size in bits of the compressed data of the guessed
266
 * configuration; 0 on error
267
 */
268
269
uint32_t cmp_guess(struct rdcu_cfg *rcfg, int level)
270
46
{
271
46
  struct rdcu_cfg work_rcfg;
272
46
  uint32_t cmp_size = 0;
273
274
46
  if (!rcfg)
275
0
    return 0;
276
277
46
  if (!rcfg->input_buf)
278
0
    return 0;
279
46
  if (model_mode_is_used(rcfg->cmp_mode))
280
16
    if (!rcfg->model_buf)
281
0
      return 0;
282
283
46
  if (!cmp_mode_is_supported(rcfg->cmp_mode)) {
284
0
    printf("This compression mode is not implied yet.\n");
285
0
    return 0;
286
0
  }
287
  /* make a working copy of the input data (and model) because the
288
   * following function works in-place
289
   */
290
46
  work_rcfg = *rcfg;
291
46
  work_rcfg.icu_new_model_buf = NULL;
292
46
  work_rcfg.icu_output_buf = NULL;
293
46
  work_rcfg.buffer_length = 0;
294
295
46
  if (model_mode_is_used(rcfg->cmp_mode)) {
296
16
    work_rcfg.icu_new_model_buf = malloc(rcfg->samples * sizeof(uint16_t));
297
16
    if (!work_rcfg.icu_new_model_buf) {
298
0
      printf("malloc() failed!\n");
299
0
      goto error;
300
0
    }
301
16
  }
302
303
  /* find the best parameters */
304
46
  switch (level) {
305
0
  case 3:
306
0
    cmp_size = brute_force(&work_rcfg);
307
0
    break;
308
0
  case 1:
309
0
    printf("guess level 1 not implied for RDCU data, I use guess level 2\n");
310
    /* fall through */
311
46
  case 2:
312
46
    cmp_size = pre_cal_method(&work_rcfg);
313
46
    break;
314
0
  default:
315
0
    fprintf(stderr, "cmp_tool: guess level not supported for RDCU guess mode!\n");
316
0
    goto error;
317
46
  }
318
46
  if (!cmp_size)
319
0
    goto error;
320
321
46
  free(work_rcfg.icu_new_model_buf);
322
323
46
  rcfg->golomb_par = work_rcfg.golomb_par;
324
46
  rcfg->spill = work_rcfg.spill;
325
326
46
  rcfg->model_value = cmp_guess_model_value(num_model_updates);
327
328
46
  add_rdcu_pars_internal(rcfg);
329
330
46
  rcfg->buffer_length = ((cmp_size + 32)&~0x1FU)/(size_of_a_sample(DATA_TYPE_IMAGETTE)*8);
331
332
46
  return cmp_size;
333
334
0
error:
335
0
  free(work_rcfg.icu_new_model_buf);
336
0
  return 0;
337
46
}
338
339
340
/**
341
 * @brief get the next Golomb parameter value to try based on the guess level
342
 *
343
 * @param cur_g   current Golomb parameter value
344
 * @param guess_level determines the granularity of the parameter search
345
 *      higher values decrease step size (finer search)
346
 *      lower/negative values increase step size (coarser search)
347
 *      range: [-31, 31], default: 2
348
 *
349
 * @returns next Golomb parameter value to try
350
 */
351
352
static uint32_t get_next_g_par(uint32_t cur_g, int guess_level)
353
0
{
354
0
  uint32_t result = cur_g;
355
356
0
  guess_level--; /* use a better guess level */
357
358
0
  if (guess_level > 31)
359
0
    guess_level = 31;
360
361
0
  if (guess_level < -31)
362
0
    guess_level = -31;
363
364
365
0
  if (guess_level >= 0)
366
0
    result += (1U << ilog_2(cur_g)) >> guess_level;
367
0
  else
368
0
    result = cur_g << -guess_level;
369
370
0
  if (result == cur_g)
371
0
    result++;
372
373
0
  return result;
374
0
}
375
376
377
/**
378
 * @brief estimate the optimal specific compression parameter set for a given chunk type
379
 *
380
 * @param chunk   pointer to the chunk data to analyse
381
 * @param chunk_size  size of the chunk in bytes
382
 * @param chunk_model pointer to the model data (can be NULL)
383
 * @param cmp_par pointer to where to store the optimized compression parameters
384
 * @param guess_level controls the granularity of the parameter search; 2 is the default
385
 *
386
 * @returns the size of the compressed data with the estimated parameters; error
387
 *  code on failure
388
 */
389
390
static uint32_t cmp_guess_chunk_par(const void *chunk, uint32_t chunk_size,
391
            const void *chunk_model, struct cmp_par *cmp_par,
392
            int guess_level)
393
0
{
394
0
  uint32_t *param_ptrs[7] = {0};
395
0
  uint32_t cmp_size_best = ~0U;
396
0
  int i;
397
398
0
  if (cmp_par->lossy_par)
399
0
    debug_print("Warning: lossy compression is not supported for chunk compression, lossy_par will be ignored.");
400
0
  cmp_par->lossy_par = 0;
401
0
  cmp_par->model_value = cmp_guess_model_value(num_model_updates);
402
403
0
  switch (cmp_col_get_chunk_type(chunk)) {
404
0
  case CHUNK_TYPE_NCAM_IMAGETTE:
405
0
    param_ptrs[0] = &cmp_par->nc_imagette;
406
0
    break;
407
0
  case CHUNK_TYPE_SAT_IMAGETTE:
408
0
    param_ptrs[0] = &cmp_par->saturated_imagette;
409
0
    break;
410
0
  case CHUNK_TYPE_SHORT_CADENCE:
411
0
    param_ptrs[0] = &cmp_par->s_exp_flags;
412
0
    param_ptrs[1] = &cmp_par->s_fx;
413
0
    param_ptrs[2] = &cmp_par->s_ncob;
414
0
    param_ptrs[3] = &cmp_par->s_efx;
415
0
    param_ptrs[4] = &cmp_par->s_ecob;
416
0
    break;
417
0
  case CHUNK_TYPE_LONG_CADENCE:
418
0
    param_ptrs[0] = &cmp_par->l_exp_flags;
419
0
    param_ptrs[1] = &cmp_par->l_fx;
420
0
    param_ptrs[2] = &cmp_par->l_ncob;
421
0
    param_ptrs[3] = &cmp_par->l_efx;
422
0
    param_ptrs[4] = &cmp_par->l_ecob;
423
0
    param_ptrs[5] = &cmp_par->l_fx_cob_variance;
424
0
    break;
425
0
  case CHUNK_TYPE_OFFSET_BACKGROUND:
426
0
    param_ptrs[0] = &cmp_par->nc_offset_mean;
427
0
    param_ptrs[1] = &cmp_par->nc_offset_variance;
428
0
    param_ptrs[2] = &cmp_par->nc_background_mean;
429
0
    param_ptrs[3] = &cmp_par->nc_background_variance;
430
0
    param_ptrs[4] = &cmp_par->nc_background_outlier_pixels;
431
0
    break;
432
0
  case CHUNK_TYPE_SMEARING:
433
0
    param_ptrs[0] = &cmp_par->smearing_mean;
434
0
    param_ptrs[1] = &cmp_par->smearing_variance_mean;
435
0
    param_ptrs[2] = &cmp_par->smearing_outlier_pixels;
436
0
    break;
437
0
  case CHUNK_TYPE_F_CHAIN:
438
0
    param_ptrs[0] = &cmp_par->fc_imagette;
439
0
    param_ptrs[1] = &cmp_par->fc_offset_mean;
440
0
    param_ptrs[2] = &cmp_par->fc_offset_variance;
441
0
    param_ptrs[3] = &cmp_par->fc_background_mean;
442
0
    param_ptrs[4] = &cmp_par->fc_background_variance;
443
0
    param_ptrs[5] = &cmp_par->fc_background_outlier_pixels;
444
0
    break;
445
0
  case CHUNK_TYPE_UNKNOWN:
446
0
  default: /*
447
      * default case never reached because cmp_col_get_chunk_type
448
      * returns CHUNK_TYPE_UNKNOWN if the type is unknown
449
      */
450
0
    break;
451
0
  }
452
453
  /* init */
454
0
  for (i = 0; param_ptrs[i] != NULL; i++)
455
0
    *param_ptrs[i] = 1;
456
457
0
  for (i = 0; param_ptrs[i] != NULL; i++) {
458
0
    uint32_t best_g = *param_ptrs[i];
459
0
    uint32_t g;
460
461
0
    for (g = MIN_NON_IMA_GOLOMB_PAR; g < MAX_NON_IMA_GOLOMB_PAR; g =  get_next_g_par(g, guess_level)) {
462
0
      uint32_t cmp_size;
463
464
0
      *param_ptrs[i] = g;
465
0
      cmp_size = compress_chunk(chunk, chunk_size, chunk_model,
466
0
              NULL, NULL, 0, cmp_par);
467
0
      FORWARD_IF_ERROR(cmp_size, "");
468
0
      if (cmp_size < cmp_size_best) {
469
0
        cmp_size_best = cmp_size;
470
0
        best_g = g;
471
0
      }
472
0
    }
473
0
    *param_ptrs[i] = best_g;
474
0
  }
475
476
0
  return cmp_size_best;
477
0
}
478
479
480
/**
481
 * @brief estimate an optimal compression parameters for the given chunk
482
 *
483
 * @param chunk   pointer to the chunk data to analyse
484
 * @param chunk_size  size of the chunk in bytes
485
 * @param chunk_model pointer to the model data (can be NULL)
486
 * @param cmp_par pointer to where to store the optimized compression parameters
487
 * @param guess_level controls the granularity of the parameter search; 2 is
488
 *      the default
489
 *
490
 * @returns the size of the compressed data with the estimated parameters; error
491
 *  code on failure
492
 */
493
494
uint32_t cmp_guess_chunk(const void *chunk, uint32_t chunk_size,
495
       const void *chunk_model, struct cmp_par *cmp_par,
496
       int guess_level)
497
0
{
498
0
  uint32_t cmp_size_zero, cmp_size_multi;
499
0
  struct cmp_par cmp_par_zero;
500
0
  struct cmp_par cmp_par_multi;
501
502
0
  memset(&cmp_par_zero, 0, sizeof(cmp_par_zero));
503
0
  memset(&cmp_par_multi, 0, sizeof(cmp_par_multi));
504
505
0
  if (chunk_model) {
506
0
    cmp_par_zero.cmp_mode = CMP_MODE_DIFF_ZERO;
507
0
    cmp_par_multi.cmp_mode = CMP_MODE_MODEL_MULTI;
508
0
  } else {
509
0
    cmp_par_zero.cmp_mode = CMP_MODE_DIFF_ZERO;
510
0
    cmp_par_multi.cmp_mode = CMP_MODE_DIFF_MULTI;
511
0
  }
512
0
  cmp_size_zero = cmp_guess_chunk_par(chunk, chunk_size, chunk_model,
513
0
              &cmp_par_zero, guess_level);
514
0
  FORWARD_IF_ERROR(cmp_size_zero, "");
515
516
0
  cmp_size_multi = cmp_guess_chunk_par(chunk, chunk_size, chunk_model,
517
0
               &cmp_par_multi, guess_level);
518
0
  FORWARD_IF_ERROR(cmp_size_multi, "");
519
520
0
  if (cmp_size_zero <= cmp_size_multi) {
521
0
    *cmp_par = cmp_par_zero;
522
0
    return cmp_size_zero;
523
0
  }
524
525
0
  *cmp_par = cmp_par_multi;
526
0
  return cmp_size_multi;
527
0
}