Coverage Report

Created: 2024-07-27 06:28

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