Coverage Report

Created: 2025-06-15 00:57

/src/cmp_tool/test/test_common/chunk_round_trip.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * @file chunk_round_trip.c
3
 * @date 2024
4
 *
5
 * @copyright GPLv2
6
 * This program is free software; you can redistribute it and/or modify it
7
 * under the terms and conditions of the GNU General Public License,
8
 * version 2, as published by the Free Software Foundation.
9
 *
10
 * This program is distributed in the hope it will be useful, but WITHOUT
11
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13
 * more details.
14
 *
15
 * @brief chunk compression/decompression test
16
 *
17
 */
18
19
#include <string.h>
20
#include <stdlib.h>
21
22
#include "../test_common/test_common.h"
23
#include "../../lib/cmp_chunk.h"
24
#include "../../lib/decmp.h"
25
#include "chunk_round_trip.h"
26
27
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
28
#  include "../fuzz/fuzz_helpers.h"
29
11.1k
#  define TEST_ASSERT(cond) FUZZ_ASSERT(cond)
30
#else
31
#  include <unity.h>
32
#endif
33
34
35
36
/**
37
 * @brief allocates memory safely for tests
38
 *
39
 * @param size the size of memory to allocate
40
 *
41
 * @returns a pointer to the allocated memory, or NULL if allocation fails
42
 */
43
44
static void *TEST_malloc(size_t size)
45
2.02k
{
46
2.02k
  if (size > 0) {
47
2.02k
    void *mem = malloc(size);
48
49
2.02k
    TEST_ASSERT(mem);
50
2.02k
    return mem;
51
2.02k
  }
52
0
  return NULL;
53
2.02k
}
54
55
56
/**
57
 * @brief performs chunk compression and checks if a decompression is possible
58
 *
59
 * @param chunk     pointer to the chunk to be compressed
60
 * @param chunk_size    byte size of the chunk
61
 * @param chunk_model   pointer to a model of a chunk; has the same size
62
 *        as the chunk (can be NULL if no model compression
63
 *        mode is used)
64
 * @param updated_chunk_model pointer to store the updated model for the next
65
 *        model mode compression; has the same size as the
66
 *        chunk (can be the same as the model_of_data
67
 *        buffer for in-place update or NULL if updated
68
 *        model is not needed)
69
 * @param dst     destination pointer to the compressed data
70
 *        buffer; has to be 4-byte aligned; can be NULL to
71
 *        only get the compressed data size
72
 * @param dst_capacity    capacity of the dst buffer; it's recommended to
73
 *        provide a dst_capacity >=
74
 *        compress_chunk_cmp_size_bound(chunk, chunk_size)
75
 *        as it eliminates one potential failure scenario:
76
 *        not enough space in the dst buffer to write the
77
 *        compressed data; size is internally rounded down
78
 *        to a multiple of 4
79
 * @param use_decmp_buf   if non-zero, a buffer is allocated for the
80
 *        decompressed data
81
 * @param use_decmp_up_model  if non-zero, a buffer for the updated model is
82
 *        allocated during the decompression
83
 * @param cmp_par   pointer to a compression parameters struct
84
 *
85
 * @returns the return value of the compress_chunk() function
86
 */
87
88
uint32_t chunk_round_trip(const void *chunk, uint32_t chunk_size,
89
        const void *chunk_model, void *updated_chunk_model,
90
        uint32_t *dst, uint32_t dst_capacity,
91
        const struct cmp_par *cmp_par,
92
        int use_decmp_buf, int use_decmp_up_model)
93
2.70k
{
94
2.70k
  uint32_t cmp_size;
95
2.70k
  void *model_cpy_p = NULL;
96
2.70k
  const void *model_cpy = NULL;
97
98
  /* if in-place model update is used (up_model == model), the model
99
   * needed for decompression is destroyed; therefore we make a copy
100
   */
101
2.70k
  if (chunk_model) {
102
1.37k
    if (updated_chunk_model == chunk_model) {
103
0
      model_cpy_p = TEST_malloc(chunk_size);
104
0
      memcpy(model_cpy_p, chunk_model, chunk_size);
105
0
      model_cpy = model_cpy_p;
106
1.37k
    } else {
107
1.37k
      model_cpy = chunk_model;
108
1.37k
    }
109
1.37k
  }
110
111
2.70k
  cmp_size = compress_chunk(chunk, chunk_size, chunk_model, updated_chunk_model,
112
2.70k
          dst, dst_capacity, cmp_par);
113
114
2.70k
  if (!cmp_is_error(cmp_size)) { /* second run with dst = NULL */
115
1.65k
    uint32_t cmp_size2;
116
117
    /* reset model if in-place update was used */
118
1.65k
    if (chunk_model && updated_chunk_model == chunk_model)
119
0
      memcpy(updated_chunk_model, model_cpy, chunk_size);
120
121
1.65k
    cmp_size2 = compress_chunk(chunk, chunk_size, chunk_model, updated_chunk_model,
122
1.65k
             NULL, dst_capacity, cmp_par);
123
1.65k
    if (cmp_get_error_code(cmp_size) != CMP_ERROR_SMALL_BUFFER)
124
1.65k
      TEST_ASSERT(cmp_size == cmp_size2);
125
1.65k
  }
126
127
128
  /* decompress the data if the compression was successful */
129
2.70k
  if (!cmp_is_error(cmp_size) && dst) {
130
1.31k
    void *decmp_data = NULL;
131
1.31k
    void *up_model_decmp = NULL;
132
1.31k
    int decmp_size;
133
134
1.31k
    decmp_size = decompress_cmp_entiy((struct cmp_entity *)dst, model_cpy, NULL, NULL);
135
1.31k
    TEST_ASSERT(decmp_size >= 0);
136
1.31k
    TEST_ASSERT((uint32_t)decmp_size == chunk_size);
137
138
1.31k
    if (use_decmp_buf)
139
1.11k
      decmp_data = TEST_malloc(chunk_size);
140
1.31k
    if (use_decmp_up_model) {
141
905
      up_model_decmp = TEST_malloc(chunk_size);
142
905
      if (!model_mode_is_used(cmp_par->cmp_mode))
143
307
        memset(up_model_decmp, 0, chunk_size); /* up_model is not used */
144
905
    }
145
146
1.31k
    decmp_size = decompress_cmp_entiy((struct cmp_entity *)dst, model_cpy,
147
1.31k
              up_model_decmp, decmp_data);
148
1.31k
    TEST_ASSERT(decmp_size >= 0);
149
1.31k
    TEST_ASSERT((uint32_t)decmp_size == chunk_size);
150
151
1.31k
    if (use_decmp_buf) {
152
1.11k
      TEST_ASSERT(!memcmp(chunk, decmp_data, chunk_size));
153
154
      /*
155
       * the model is only updated when the decompressed_data
156
       * buffer is set
157
       */
158
1.11k
      if (use_decmp_up_model && updated_chunk_model)
159
1.11k
        TEST_ASSERT(!memcmp(updated_chunk_model, up_model_decmp, chunk_size));
160
1.11k
    }
161
162
0
    free(decmp_data);
163
1.31k
    free(up_model_decmp);
164
1.31k
  }
165
166
2.70k
  if (updated_chunk_model == chunk_model)
167
614
    free(model_cpy_p);
168
169
2.70k
  return cmp_size;
170
2.70k
}