/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  |  | //------------------------------------------------------------------------------  |