Blame src/utils/utils.c

Packit 9c6abc
// Copyright 2012 Google Inc. All Rights Reserved.
Packit 9c6abc
//
Packit 9c6abc
// Use of this source code is governed by a BSD-style license
Packit 9c6abc
// that can be found in the COPYING file in the root of the source
Packit 9c6abc
// tree. An additional intellectual property rights grant can be found
Packit 9c6abc
// in the file PATENTS. All contributing project authors may
Packit 9c6abc
// be found in the AUTHORS file in the root of the source tree.
Packit 9c6abc
// -----------------------------------------------------------------------------
Packit 9c6abc
//
Packit 9c6abc
// Misc. common utility functions
Packit 9c6abc
//
Packit 9c6abc
// Author: Skal (pascal.massimino@gmail.com)
Packit 9c6abc
Packit 9c6abc
#include <stdlib.h>
Packit 9c6abc
#include <string.h>  // for memcpy()
Packit 9c6abc
#include "src/webp/decode.h"
Packit 9c6abc
#include "src/webp/encode.h"
Packit 9c6abc
#include "src/webp/format_constants.h"  // for MAX_PALETTE_SIZE
Packit 9c6abc
#include "src/utils/color_cache_utils.h"
Packit 9c6abc
#include "src/utils/utils.h"
Packit 9c6abc
Packit 9c6abc
// If PRINT_MEM_INFO is defined, extra info (like total memory used, number of
Packit 9c6abc
// alloc/free etc) is printed. For debugging/tuning purpose only (it's slow,
Packit 9c6abc
// and not multi-thread safe!).
Packit 9c6abc
// An interesting alternative is valgrind's 'massif' tool:
Packit 9c6abc
//    http://valgrind.org/docs/manual/ms-manual.html
Packit 9c6abc
// Here is an example command line:
Packit 9c6abc
/*    valgrind --tool=massif --massif-out-file=massif.out \
Packit 9c6abc
               --stacks=yes --alloc-fn=WebPSafeMalloc --alloc-fn=WebPSafeCalloc
Packit 9c6abc
      ms_print massif.out
Packit 9c6abc
*/
Packit 9c6abc
// In addition:
Packit 9c6abc
// * if PRINT_MEM_TRAFFIC is defined, all the details of the malloc/free cycles
Packit 9c6abc
//   are printed.
Packit 9c6abc
// * if MALLOC_FAIL_AT is defined, the global environment variable
Packit 9c6abc
//   $MALLOC_FAIL_AT is used to simulate a memory error when calloc or malloc
Packit 9c6abc
//   is called for the nth time. Example usage:
Packit 9c6abc
//   export MALLOC_FAIL_AT=50 && ./examples/cwebp input.png
Packit 9c6abc
// * if MALLOC_LIMIT is defined, the global environment variable $MALLOC_LIMIT
Packit 9c6abc
//   sets the maximum amount of memory (in bytes) made available to libwebp.
Packit 9c6abc
//   This can be used to emulate environment with very limited memory.
Packit 9c6abc
//   Example: export MALLOC_LIMIT=64000000 && ./examples/dwebp picture.webp
Packit 9c6abc
Packit 9c6abc
// #define PRINT_MEM_INFO
Packit 9c6abc
// #define PRINT_MEM_TRAFFIC
Packit 9c6abc
// #define MALLOC_FAIL_AT
Packit 9c6abc
// #define MALLOC_LIMIT
Packit 9c6abc
Packit 9c6abc
//------------------------------------------------------------------------------
Packit 9c6abc
// Checked memory allocation
Packit 9c6abc
Packit 9c6abc
#if defined(PRINT_MEM_INFO)
Packit 9c6abc
Packit 9c6abc
#include <stdio.h>
Packit 9c6abc
Packit 9c6abc
static int num_malloc_calls = 0;
Packit 9c6abc
static int num_calloc_calls = 0;
Packit 9c6abc
static int num_free_calls = 0;
Packit 9c6abc
static int countdown_to_fail = 0;     // 0 = off
Packit 9c6abc
Packit 9c6abc
typedef struct MemBlock MemBlock;
Packit 9c6abc
struct MemBlock {
Packit 9c6abc
  void* ptr_;
Packit 9c6abc
  size_t size_;
Packit 9c6abc
  MemBlock* next_;
Packit 9c6abc
};
Packit 9c6abc
Packit 9c6abc
static MemBlock* all_blocks = NULL;
Packit 9c6abc
static size_t total_mem = 0;
Packit 9c6abc
static size_t total_mem_allocated = 0;
Packit 9c6abc
static size_t high_water_mark = 0;
Packit 9c6abc
static size_t mem_limit = 0;
Packit 9c6abc
Packit 9c6abc
static int exit_registered = 0;
Packit 9c6abc
Packit 9c6abc
static void PrintMemInfo(void) {
Packit 9c6abc
  fprintf(stderr, "\nMEMORY INFO:\n");
Packit 9c6abc
  fprintf(stderr, "num calls to: malloc = %4d\n", num_malloc_calls);
Packit 9c6abc
  fprintf(stderr, "              calloc = %4d\n", num_calloc_calls);
Packit 9c6abc
  fprintf(stderr, "              free   = %4d\n", num_free_calls);
Packit 9c6abc
  fprintf(stderr, "total_mem: %u\n", (uint32_t)total_mem);
Packit 9c6abc
  fprintf(stderr, "total_mem allocated: %u\n", (uint32_t)total_mem_allocated);
Packit 9c6abc
  fprintf(stderr, "high-water mark: %u\n", (uint32_t)high_water_mark);
Packit 9c6abc
  while (all_blocks != NULL) {
Packit 9c6abc
    MemBlock* b = all_blocks;
Packit 9c6abc
    all_blocks = b->next_;
Packit 9c6abc
    free(b);
Packit 9c6abc
  }
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static void Increment(int* const v) {
Packit 9c6abc
  if (!exit_registered) {
Packit 9c6abc
#if defined(MALLOC_FAIL_AT)
Packit 9c6abc
    {
Packit 9c6abc
      const char* const malloc_fail_at_str = getenv("MALLOC_FAIL_AT");
Packit 9c6abc
      if (malloc_fail_at_str != NULL) {
Packit 9c6abc
        countdown_to_fail = atoi(malloc_fail_at_str);
Packit 9c6abc
      }
Packit 9c6abc
    }
Packit 9c6abc
#endif
Packit 9c6abc
#if defined(MALLOC_LIMIT)
Packit 9c6abc
    {
Packit 9c6abc
      const char* const malloc_limit_str = getenv("MALLOC_LIMIT");
Packit 9c6abc
      if (malloc_limit_str != NULL) {
Packit 9c6abc
        mem_limit = atoi(malloc_limit_str);
Packit 9c6abc
      }
Packit 9c6abc
    }
Packit 9c6abc
#endif
Packit 9c6abc
    (void)countdown_to_fail;
Packit 9c6abc
    (void)mem_limit;
Packit 9c6abc
    atexit(PrintMemInfo);
Packit 9c6abc
    exit_registered = 1;
Packit 9c6abc
  }
Packit 9c6abc
  ++*v;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static void AddMem(void* ptr, size_t size) {
Packit 9c6abc
  if (ptr != NULL) {
Packit 9c6abc
    MemBlock* const b = (MemBlock*)malloc(sizeof(*b));
Packit 9c6abc
    if (b == NULL) abort();
Packit 9c6abc
    b->next_ = all_blocks;
Packit 9c6abc
    all_blocks = b;
Packit 9c6abc
    b->ptr_ = ptr;
Packit 9c6abc
    b->size_ = size;
Packit 9c6abc
    total_mem += size;
Packit 9c6abc
    total_mem_allocated += size;
Packit 9c6abc
#if defined(PRINT_MEM_TRAFFIC)
Packit 9c6abc
#if defined(MALLOC_FAIL_AT)
Packit 9c6abc
    fprintf(stderr, "fail-count: %5d [mem=%u]\n",
Packit 9c6abc
            num_malloc_calls + num_calloc_calls, (uint32_t)total_mem);
Packit 9c6abc
#else
Packit 9c6abc
    fprintf(stderr, "Mem: %u (+%u)\n", (uint32_t)total_mem, (uint32_t)size);
Packit 9c6abc
#endif
Packit 9c6abc
#endif
Packit 9c6abc
    if (total_mem > high_water_mark) high_water_mark = total_mem;
Packit 9c6abc
  }
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
static void SubMem(void* ptr) {
Packit 9c6abc
  if (ptr != NULL) {
Packit 9c6abc
    MemBlock** b = &all_blocks;
Packit 9c6abc
    // Inefficient search, but that's just for debugging.
Packit 9c6abc
    while (*b != NULL && (*b)->ptr_ != ptr) b = &(*b)->next_;
Packit 9c6abc
    if (*b == NULL) {
Packit 9c6abc
      fprintf(stderr, "Invalid pointer free! (%p)\n", ptr);
Packit 9c6abc
      abort();
Packit 9c6abc
    }
Packit 9c6abc
    {
Packit 9c6abc
      MemBlock* const block = *b;
Packit 9c6abc
      *b = block->next_;
Packit 9c6abc
      total_mem -= block->size_;
Packit 9c6abc
#if defined(PRINT_MEM_TRAFFIC)
Packit 9c6abc
      fprintf(stderr, "Mem: %u (-%u)\n",
Packit 9c6abc
              (uint32_t)total_mem, (uint32_t)block->size_);
Packit 9c6abc
#endif
Packit 9c6abc
      free(block);
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
#else
Packit 9c6abc
#define Increment(v) do {} while (0)
Packit 9c6abc
#define AddMem(p, s) do {} while (0)
Packit 9c6abc
#define SubMem(p)    do {} while (0)
Packit 9c6abc
#endif
Packit 9c6abc
Packit 9c6abc
// Returns 0 in case of overflow of nmemb * size.
Packit 9c6abc
static int CheckSizeArgumentsOverflow(uint64_t nmemb, size_t size) {
Packit 9c6abc
  const uint64_t total_size = nmemb * size;
Packit 9c6abc
  if (nmemb == 0) return 1;
Packit 9c6abc
  if ((uint64_t)size > WEBP_MAX_ALLOCABLE_MEMORY / nmemb) return 0;
Packit 9c6abc
  if (total_size != (size_t)total_size) return 0;
Packit 9c6abc
#if defined(PRINT_MEM_INFO) && defined(MALLOC_FAIL_AT)
Packit 9c6abc
  if (countdown_to_fail > 0 && --countdown_to_fail == 0) {
Packit 9c6abc
    return 0;    // fake fail!
Packit 9c6abc
  }
Packit 9c6abc
#endif
Packit 9c6abc
#if defined(MALLOC_LIMIT)
Packit 9c6abc
  if (mem_limit > 0) {
Packit 9c6abc
    const uint64_t new_total_mem = (uint64_t)total_mem + total_size;
Packit 9c6abc
    if (new_total_mem != (size_t)new_total_mem ||
Packit 9c6abc
        new_total_mem > mem_limit) {
Packit 9c6abc
      return 0;   // fake fail!
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
#endif
Packit 9c6abc
Packit 9c6abc
  return 1;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
void* WebPSafeMalloc(uint64_t nmemb, size_t size) {
Packit 9c6abc
  void* ptr;
Packit 9c6abc
  Increment(&num_malloc_calls);
Packit 9c6abc
  if (!CheckSizeArgumentsOverflow(nmemb, size)) return NULL;
Packit 9c6abc
  assert(nmemb * size > 0);
Packit 9c6abc
  ptr = malloc((size_t)(nmemb * size));
Packit 9c6abc
  AddMem(ptr, (size_t)(nmemb * size));
Packit 9c6abc
  return ptr;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
void* WebPSafeCalloc(uint64_t nmemb, size_t size) {
Packit 9c6abc
  void* ptr;
Packit 9c6abc
  Increment(&num_calloc_calls);
Packit 9c6abc
  if (!CheckSizeArgumentsOverflow(nmemb, size)) return NULL;
Packit 9c6abc
  assert(nmemb * size > 0);
Packit 9c6abc
  ptr = calloc((size_t)nmemb, size);
Packit 9c6abc
  AddMem(ptr, (size_t)(nmemb * size));
Packit 9c6abc
  return ptr;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
void WebPSafeFree(void* const ptr) {
Packit 9c6abc
  if (ptr != NULL) {
Packit 9c6abc
    Increment(&num_free_calls);
Packit 9c6abc
    SubMem(ptr);
Packit 9c6abc
  }
Packit 9c6abc
  free(ptr);
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// Public API function.
Packit 9c6abc
void WebPFree(void* ptr) {
Packit 9c6abc
  free(ptr);
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
//------------------------------------------------------------------------------
Packit 9c6abc
Packit 9c6abc
void WebPCopyPlane(const uint8_t* src, int src_stride,
Packit 9c6abc
                   uint8_t* dst, int dst_stride, int width, int height) {
Packit 9c6abc
  assert(src != NULL && dst != NULL);
Packit 9c6abc
  assert(src_stride >= width && dst_stride >= width);
Packit 9c6abc
  while (height-- > 0) {
Packit 9c6abc
    memcpy(dst, src, width);
Packit 9c6abc
    src += src_stride;
Packit 9c6abc
    dst += dst_stride;
Packit 9c6abc
  }
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
void WebPCopyPixels(const WebPPicture* const src, WebPPicture* const dst) {
Packit 9c6abc
  assert(src != NULL && dst != NULL);
Packit 9c6abc
  assert(src->width == dst->width && src->height == dst->height);
Packit 9c6abc
  assert(src->use_argb && dst->use_argb);
Packit 9c6abc
  WebPCopyPlane((uint8_t*)src->argb, 4 * src->argb_stride, (uint8_t*)dst->argb,
Packit 9c6abc
                4 * dst->argb_stride, 4 * src->width, src->height);
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
//------------------------------------------------------------------------------
Packit 9c6abc
Packit 9c6abc
#define COLOR_HASH_SIZE         (MAX_PALETTE_SIZE * 4)
Packit 9c6abc
#define COLOR_HASH_RIGHT_SHIFT  22  // 32 - log2(COLOR_HASH_SIZE).
Packit 9c6abc
Packit 9c6abc
int WebPGetColorPalette(const WebPPicture* const pic, uint32_t* const palette) {
Packit 9c6abc
  int i;
Packit 9c6abc
  int x, y;
Packit 9c6abc
  int num_colors = 0;
Packit 9c6abc
  uint8_t in_use[COLOR_HASH_SIZE] = { 0 };
Packit 9c6abc
  uint32_t colors[COLOR_HASH_SIZE];
Packit 9c6abc
  const uint32_t* argb = pic->argb;
Packit 9c6abc
  const int width = pic->width;
Packit 9c6abc
  const int height = pic->height;
Packit 9c6abc
  uint32_t last_pix = ~argb[0];   // so we're sure that last_pix != argb[0]
Packit 9c6abc
  assert(pic != NULL);
Packit 9c6abc
  assert(pic->use_argb);
Packit 9c6abc
Packit 9c6abc
  for (y = 0; y < height; ++y) {
Packit 9c6abc
    for (x = 0; x < width; ++x) {
Packit 9c6abc
      int key;
Packit 9c6abc
      if (argb[x] == last_pix) {
Packit 9c6abc
        continue;
Packit 9c6abc
      }
Packit 9c6abc
      last_pix = argb[x];
Packit 9c6abc
      key = VP8LHashPix(last_pix, COLOR_HASH_RIGHT_SHIFT);
Packit 9c6abc
      while (1) {
Packit 9c6abc
        if (!in_use[key]) {
Packit 9c6abc
          colors[key] = last_pix;
Packit 9c6abc
          in_use[key] = 1;
Packit 9c6abc
          ++num_colors;
Packit 9c6abc
          if (num_colors > MAX_PALETTE_SIZE) {
Packit 9c6abc
            return MAX_PALETTE_SIZE + 1;  // Exact count not needed.
Packit 9c6abc
          }
Packit 9c6abc
          break;
Packit 9c6abc
        } else if (colors[key] == last_pix) {
Packit 9c6abc
          break;  // The color is already there.
Packit 9c6abc
        } else {
Packit 9c6abc
          // Some other color sits here, so do linear conflict resolution.
Packit 9c6abc
          ++key;
Packit 9c6abc
          key &= (COLOR_HASH_SIZE - 1);  // Key mask.
Packit 9c6abc
        }
Packit 9c6abc
      }
Packit 9c6abc
    }
Packit 9c6abc
    argb += pic->argb_stride;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  if (palette != NULL) {  // Fill the colors into palette.
Packit 9c6abc
    num_colors = 0;
Packit 9c6abc
    for (i = 0; i < COLOR_HASH_SIZE; ++i) {
Packit 9c6abc
      if (in_use[i]) {
Packit 9c6abc
        palette[num_colors] = colors[i];
Packit 9c6abc
        ++num_colors;
Packit 9c6abc
      }
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
  return num_colors;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
#undef COLOR_HASH_SIZE
Packit 9c6abc
#undef COLOR_HASH_RIGHT_SHIFT
Packit 9c6abc
Packit 9c6abc
//------------------------------------------------------------------------------
Packit 9c6abc
Packit 9c6abc
#if defined(WEBP_NEED_LOG_TABLE_8BIT)
Packit 9c6abc
const uint8_t WebPLogTable8bit[256] = {   // 31 ^ clz(i)
Packit 9c6abc
  0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
Packit 9c6abc
  4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
Packit 9c6abc
  5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
Packit 9c6abc
  5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
Packit 9c6abc
  6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
Packit 9c6abc
  6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
Packit 9c6abc
  6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
Packit 9c6abc
  6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
Packit 9c6abc
  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
Packit 9c6abc
  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
Packit 9c6abc
  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
Packit 9c6abc
  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
Packit 9c6abc
  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
Packit 9c6abc
  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
Packit 9c6abc
  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
Packit 9c6abc
  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
Packit 9c6abc
};
Packit 9c6abc
#endif
Packit 9c6abc
Packit 9c6abc
//------------------------------------------------------------------------------