Coverage Report

Created: 2025-06-13 06:48

/src/libwebp/src/utils/utils.c
Line
Count
Source (jump to first uncovered line)
1
// Copyright 2012 Google Inc. All Rights Reserved.
2
//
3
// Use of this source code is governed by a BSD-style license
4
// that can be found in the COPYING file in the root of the source
5
// tree. An additional intellectual property rights grant can be found
6
// in the file PATENTS. All contributing project authors may
7
// be found in the AUTHORS file in the root of the source tree.
8
// -----------------------------------------------------------------------------
9
//
10
// Misc. common utility functions
11
//
12
// Author: Skal (pascal.massimino@gmail.com)
13
14
#include "src/utils/utils.h"
15
16
#include <assert.h>
17
#include <stdlib.h>
18
#include <string.h>  // for memcpy()
19
20
#include "src/webp/types.h"
21
#include "src/utils/palette.h"
22
#include "src/webp/encode.h"
23
24
// If PRINT_MEM_INFO is defined, extra info (like total memory used, number of
25
// alloc/free etc) is printed. For debugging/tuning purpose only (it's slow,
26
// and not multi-thread safe!).
27
// An interesting alternative is valgrind's 'massif' tool:
28
//    https://valgrind.org/docs/manual/ms-manual.html
29
// Here is an example command line:
30
/*    valgrind --tool=massif --massif-out-file=massif.out \
31
               --stacks=yes --alloc-fn=WebPSafeMalloc --alloc-fn=WebPSafeCalloc
32
      ms_print massif.out
33
*/
34
// In addition:
35
// * if PRINT_MEM_TRAFFIC is defined, all the details of the malloc/free cycles
36
//   are printed.
37
// * if MALLOC_FAIL_AT is defined, the global environment variable
38
//   $MALLOC_FAIL_AT is used to simulate a memory error when calloc or malloc
39
//   is called for the nth time. Example usage:
40
//   export MALLOC_FAIL_AT=50 && ./examples/cwebp input.png
41
// * if MALLOC_LIMIT is defined, the global environment variable $MALLOC_LIMIT
42
//   sets the maximum amount of memory (in bytes) made available to libwebp.
43
//   This can be used to emulate environment with very limited memory.
44
//   Example: export MALLOC_LIMIT=64000000 && ./examples/dwebp picture.webp
45
46
// #define PRINT_MEM_INFO
47
// #define PRINT_MEM_TRAFFIC
48
// #define MALLOC_FAIL_AT
49
// #define MALLOC_LIMIT
50
51
//------------------------------------------------------------------------------
52
// Checked memory allocation
53
54
#if defined(PRINT_MEM_INFO)
55
56
#include <stdio.h>
57
58
static int num_malloc_calls = 0;
59
static int num_calloc_calls = 0;
60
static int num_free_calls = 0;
61
static int countdown_to_fail = 0;     // 0 = off
62
63
typedef struct MemBlock MemBlock;
64
struct MemBlock {
65
  void* ptr;
66
  size_t size;
67
  MemBlock* next;
68
};
69
70
static MemBlock* all_blocks = NULL;
71
static size_t total_mem = 0;
72
static size_t total_mem_allocated = 0;
73
static size_t high_water_mark = 0;
74
static size_t mem_limit = 0;
75
76
static int exit_registered = 0;
77
78
static void PrintMemInfo(void) {
79
  fprintf(stderr, "\nMEMORY INFO:\n");
80
  fprintf(stderr, "num calls to: malloc = %4d\n", num_malloc_calls);
81
  fprintf(stderr, "              calloc = %4d\n", num_calloc_calls);
82
  fprintf(stderr, "              free   = %4d\n", num_free_calls);
83
  fprintf(stderr, "total_mem: %u\n", (uint32_t)total_mem);
84
  fprintf(stderr, "total_mem allocated: %u\n", (uint32_t)total_mem_allocated);
85
  fprintf(stderr, "high-water mark: %u\n", (uint32_t)high_water_mark);
86
  while (all_blocks != NULL) {
87
    MemBlock* b = all_blocks;
88
    all_blocks = b->next;
89
    free(b);
90
  }
91
}
92
93
static void Increment(int* const v) {
94
  if (!exit_registered) {
95
#if defined(MALLOC_FAIL_AT)
96
    {
97
      const char* const malloc_fail_at_str = getenv("MALLOC_FAIL_AT");
98
      if (malloc_fail_at_str != NULL) {
99
        countdown_to_fail = atoi(malloc_fail_at_str);
100
      }
101
    }
102
#endif
103
#if defined(MALLOC_LIMIT)
104
    {
105
      const char* const malloc_limit_str = getenv("MALLOC_LIMIT");
106
#if MALLOC_LIMIT > 1
107
      mem_limit = (size_t)MALLOC_LIMIT;
108
#endif
109
      if (malloc_limit_str != NULL) {
110
        mem_limit = atoi(malloc_limit_str);
111
      }
112
    }
113
#endif
114
    (void)countdown_to_fail;
115
    (void)mem_limit;
116
    atexit(PrintMemInfo);
117
    exit_registered = 1;
118
  }
119
  ++*v;
120
}
121
122
static void AddMem(void* ptr, size_t size) {
123
  if (ptr != NULL) {
124
    MemBlock* const b = (MemBlock*)malloc(sizeof(*b));
125
    if (b == NULL) abort();
126
    b->next = all_blocks;
127
    all_blocks = b;
128
    b->ptr = ptr;
129
    b->size = size;
130
    total_mem += size;
131
    total_mem_allocated += size;
132
#if defined(PRINT_MEM_TRAFFIC)
133
#if defined(MALLOC_FAIL_AT)
134
    fprintf(stderr, "fail-count: %5d [mem=%u]\n",
135
            num_malloc_calls + num_calloc_calls, (uint32_t)total_mem);
136
#else
137
    fprintf(stderr, "Mem: %u (+%u)\n", (uint32_t)total_mem, (uint32_t)size);
138
#endif
139
#endif
140
    if (total_mem > high_water_mark) high_water_mark = total_mem;
141
  }
142
}
143
144
static void SubMem(void* ptr) {
145
  if (ptr != NULL) {
146
    MemBlock** b = &all_blocks;
147
    // Inefficient search, but that's just for debugging.
148
    while (*b != NULL && (*b)->ptr != ptr) b = &(*b)->next;
149
    if (*b == NULL) {
150
      fprintf(stderr, "Invalid pointer free! (%p)\n", ptr);
151
      abort();
152
    }
153
    {
154
      MemBlock* const block = *b;
155
      *b = block->next;
156
      total_mem -= block->size;
157
#if defined(PRINT_MEM_TRAFFIC)
158
      fprintf(stderr, "Mem: %u (-%u)\n",
159
              (uint32_t)total_mem, (uint32_t)block->size);
160
#endif
161
      free(block);
162
    }
163
  }
164
}
165
166
#else
167
0
#define Increment(v) do {} while (0)
168
0
#define AddMem(p, s) do {} while (0)
169
0
#define SubMem(p)    do {} while (0)
170
#endif
171
172
// Returns 0 in case of overflow of nmemb * size.
173
0
static int CheckSizeArgumentsOverflow(uint64_t nmemb, size_t size) {
174
0
  const uint64_t total_size = nmemb * size;
175
0
  if (nmemb == 0) return 1;
176
0
  if ((uint64_t)size > WEBP_MAX_ALLOCABLE_MEMORY / nmemb) return 0;
177
0
  if (!CheckSizeOverflow(total_size)) return 0;
178
#if defined(PRINT_MEM_INFO) && defined(MALLOC_FAIL_AT)
179
  if (countdown_to_fail > 0 && --countdown_to_fail == 0) {
180
    return 0;    // fake fail!
181
  }
182
#endif
183
#if defined(PRINT_MEM_INFO) && defined(MALLOC_LIMIT)
184
  if (mem_limit > 0) {
185
    const uint64_t new_total_mem = (uint64_t)total_mem + total_size;
186
    if (!CheckSizeOverflow(new_total_mem) ||
187
        new_total_mem > mem_limit) {
188
      return 0;   // fake fail!
189
    }
190
  }
191
#endif
192
193
0
  return 1;
194
0
}
195
196
0
void* WebPSafeMalloc(uint64_t nmemb, size_t size) {
197
0
  void* ptr;
198
0
  Increment(&num_malloc_calls);
199
0
  if (!CheckSizeArgumentsOverflow(nmemb, size)) return NULL;
200
0
  assert(nmemb * size > 0);
201
0
  ptr = malloc((size_t)(nmemb * size));
202
0
  AddMem(ptr, (size_t)(nmemb * size));
203
0
  return ptr;
204
0
}
205
206
0
void* WebPSafeCalloc(uint64_t nmemb, size_t size) {
207
0
  void* ptr;
208
0
  Increment(&num_calloc_calls);
209
0
  if (!CheckSizeArgumentsOverflow(nmemb, size)) return NULL;
210
0
  assert(nmemb * size > 0);
211
0
  ptr = calloc((size_t)nmemb, size);
212
0
  AddMem(ptr, (size_t)(nmemb * size));
213
0
  return ptr;
214
0
}
215
216
0
void WebPSafeFree(void* const ptr) {
217
0
  if (ptr != NULL) {
218
0
    Increment(&num_free_calls);
219
0
    SubMem(ptr);
220
0
  }
221
0
  free(ptr);
222
0
}
223
224
// Public API functions.
225
226
0
void* WebPMalloc(size_t size) {
227
0
  return WebPSafeMalloc(1, size);
228
0
}
229
230
0
void WebPFree(void* ptr) {
231
0
  WebPSafeFree(ptr);
232
0
}
233
234
//------------------------------------------------------------------------------
235
236
void WebPCopyPlane(const uint8_t* src, int src_stride,
237
0
                   uint8_t* dst, int dst_stride, int width, int height) {
238
0
  assert(src != NULL && dst != NULL);
239
0
  assert(abs(src_stride) >= width && abs(dst_stride) >= width);
240
0
  while (height-- > 0) {
241
0
    memcpy(dst, src, width);
242
0
    src += src_stride;
243
0
    dst += dst_stride;
244
0
  }
245
0
}
246
247
0
void WebPCopyPixels(const WebPPicture* const src, WebPPicture* const dst) {
248
0
  assert(src != NULL && dst != NULL);
249
0
  assert(src->width == dst->width && src->height == dst->height);
250
0
  assert(src->use_argb && dst->use_argb);
251
0
  WebPCopyPlane((uint8_t*)src->argb, 4 * src->argb_stride, (uint8_t*)dst->argb,
252
0
                4 * dst->argb_stride, 4 * src->width, src->height);
253
0
}
254
255
//------------------------------------------------------------------------------
256
257
0
int WebPGetColorPalette(const WebPPicture* const pic, uint32_t* const palette) {
258
0
  return GetColorPalette(pic, palette);
259
0
}
260
261
//------------------------------------------------------------------------------
262
263
#if defined(WEBP_NEED_LOG_TABLE_8BIT)
264
const uint8_t WebPLogTable8bit[256] = {   // 31 ^ clz(i)
265
  0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
266
  4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
267
  5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
268
  5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
269
  6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
270
  6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
271
  6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
272
  6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
273
  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
274
  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
275
  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
276
  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
277
  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
278
  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
279
  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
280
  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
281
};
282
#endif
283
284
//------------------------------------------------------------------------------