go-libwebp

Experimental translation from libwebp to Go source.
Log | Files | Refs | README | LICENSE

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