utils.c (8892B)
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 #define Increment(v) do {} while (0) 168 #define AddMem(p, s) do {} while (0) 169 #define SubMem(p) do {} while (0) 170 #endif 171 172 // Returns 0 in case of overflow of nmemb * size. 173 static int CheckSizeArgumentsOverflow(uint64_t nmemb, size_t size) { 174 const uint64_t total_size = nmemb * size; 175 if (nmemb == 0) return 1; 176 if ((uint64_t)size > WEBP_MAX_ALLOCABLE_MEMORY / nmemb) return 0; 177 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 return 1; 194 } 195 196 void* WebPSafeMalloc(uint64_t nmemb, size_t size) { 197 void* ptr; 198 Increment(&num_malloc_calls); 199 if (!CheckSizeArgumentsOverflow(nmemb, size)) return NULL; 200 assert(nmemb * size > 0); 201 ptr = malloc((size_t)(nmemb * size)); 202 AddMem(ptr, (size_t)(nmemb * size)); 203 return ptr; 204 } 205 206 void* WebPSafeCalloc(uint64_t nmemb, size_t size) { 207 void* ptr; 208 Increment(&num_calloc_calls); 209 if (!CheckSizeArgumentsOverflow(nmemb, size)) return NULL; 210 assert(nmemb * size > 0); 211 ptr = calloc((size_t)nmemb, size); 212 AddMem(ptr, (size_t)(nmemb * size)); 213 return ptr; 214 } 215 216 void WebPSafeFree(void* const ptr) { 217 if (ptr != NULL) { 218 Increment(&num_free_calls); 219 SubMem(ptr); 220 } 221 free(ptr); 222 } 223 224 // Public API functions. 225 226 void* WebPMalloc(size_t size) { 227 return WebPSafeMalloc(1, size); 228 } 229 230 void WebPFree(void* ptr) { 231 WebPSafeFree(ptr); 232 } 233 234 //------------------------------------------------------------------------------ 235 236 void WebPCopyPlane(const uint8_t* src, int src_stride, 237 uint8_t* dst, int dst_stride, int width, int height) { 238 assert(src != NULL && dst != NULL); 239 assert(abs(src_stride) >= width && abs(dst_stride) >= width); 240 while (height-- > 0) { 241 memcpy(dst, src, width); 242 src += src_stride; 243 dst += dst_stride; 244 } 245 } 246 247 void WebPCopyPixels(const WebPPicture* const src, WebPPicture* const dst) { 248 assert(src != NULL && dst != NULL); 249 assert(src->width == dst->width && src->height == dst->height); 250 assert(src->use_argb && dst->use_argb); 251 WebPCopyPlane((uint8_t*)src->argb, 4 * src->argb_stride, (uint8_t*)dst->argb, 252 4 * dst->argb_stride, 4 * src->width, src->height); 253 } 254 255 //------------------------------------------------------------------------------ 256 257 int WebPGetColorPalette(const WebPPicture* const pic, uint32_t* const palette) { 258 return GetColorPalette(pic, palette); 259 } 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 //------------------------------------------------------------------------------