/* babl - dynamically extendable universal pixel conversion library.
* Copyright (C) 2005, Øyvind Kolås.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see
* .
*/
#include "config.h"
#include
#include
#include
#include
#include "babl-internal.h"
static BablMallocFunc malloc_f = malloc;
static BablFreeFunc free_f = free;
static void *first_malloc_used = NULL;
static void *first_free_used = NULL;
void
babl_set_malloc (BablMallocFunc malloc_function)
{
malloc_f = malloc_function;
}
void
babl_set_free (BablFreeFunc free_function)
{
free_f = free_function;
}
static char *signature = "babl-memory";
static char *freed = "So long and thanks for all the fish.";
typedef struct
{
char *signature;
size_t size;
int (*destructor)(void *ptr);
} BablAllocInfo;
#define BABL_ALIGN 16
#define BABL_ALLOC (sizeof (BablAllocInfo) + sizeof (void *))
#define BAI(ptr) ((BablAllocInfo *) *((void **) ptr - 1))
#define IS_BAI(ptr) (BAI (ptr)->signature == signature)
#if BABL_DEBUG_MEM
/* runtime statistics: */
static int mallocs = 0;
static int frees = 0;
static int strdups = 0;
static int reallocs = 0;
static int callocs = 0;
static int dups = 0;
static const char *
mem_stats (void)
{
static char buf[128];
snprintf (buf, sizeof (buf), "mallocs:%i callocs:%i strdups:%i dups:%i allocs:%i frees:%i reallocs:%i\t|",
mallocs, callocs, strdups, dups, mallocs + callocs + strdups + dups, frees, reallocs);
return buf;
}
#endif
static void
functions_sanity (void)
{
if (first_malloc_used != malloc_f ||
first_free_used != free_f)
{
static int displayed = 0;
if (first_malloc_used == NULL)
{
first_malloc_used = malloc_f;
first_free_used = free_f;
}
else if (!displayed)
{
fprintf (stderr, "HMM....\nSomething strange is happening,\n%s function pointer changing between invocations in babl.\n",
first_malloc_used == malloc_f ? "free" :
(first_free_used == free_f ? "malloc" : "malloc and free"));
displayed = 1;
}
}
}
/* Allocate /size/ bytes of memory
*
* contents of memory undefined.
*/
void *
babl_malloc (size_t size)
{
char *ret;
int offset;
functions_sanity ();
ret = malloc_f (BABL_ALLOC + BABL_ALIGN + size);
if (!ret)
babl_fatal ("args=(%i): failed", size);
offset = BABL_ALIGN - ((uintptr_t) ret + BABL_ALLOC) % BABL_ALIGN;
ret = ret + BABL_ALLOC + offset;
*((void **) ret - 1) = ret - BABL_ALLOC - offset;
BAI (ret)->signature = signature;
BAI (ret)->size = size;
BAI (ret)->destructor = NULL;
#if BABL_DEBUG_MEM
babl_mutex_lock (babl_debug_mutex);
mallocs++;
babl_mutex_unlock (babl_debug_mutex);
#endif
return (void *) (ret);
}
/* set a callback to be called when the segment is freed.
*/
void
babl_set_destructor (void *ptr,
int (*destructor)(void *ptr))
{
babl_assert (IS_BAI (ptr));
BAI(ptr)->destructor = destructor;
}
/* Create a duplicate allocation of the same size, note
* that the exact location of the allocation needs to be
* passed.
*/
void *
babl_dup (void *ptr)
{
void *ret;
babl_assert (IS_BAI (ptr));
ret = babl_malloc (BAI (ptr)->size);
memcpy (ret, ptr, BAI (ptr)->size);
#if BABL_DEBUG_MEM
babl_mutex_lock (babl_debug_mutex);
dups++;
mallocs--;
babl_mutex_unlock (babl_debug_mutex);
#endif
return NULL;
}
/* Free memory allocated by a babl function (note: babl_free
* will complain if memory not allocated by babl is passed.)
*
* Note: the function is made variadic to be a legal callback
* function in some circumstances.
*/
void
babl_free (void *ptr,
...)
{
functions_sanity ();
if (!ptr)
return;
if (!IS_BAI (ptr))
{
#define IS_BAI(ptr) (BAI (ptr)->signature == signature)
if (freed == BAI (ptr)->signature)
fprintf (stderr, "\nbabl:double free detected\n");
else
fprintf (stderr, "\nbabl_free passed unknown pointer, bailing and leaking it\n");
return;
//assert(0);
}
if (BAI (ptr)->destructor)
if (BAI (ptr)->destructor (ptr))
return; /* bail out on non 0 return from destructor */
BAI (ptr)->signature = freed;
free_f (BAI (ptr));
#if BABL_DEBUG_MEM
babl_mutex_lock (babl_debug_mutex);
frees++;
babl_mutex_unlock (babl_debug_mutex);
#endif
}
/* reallocate allocation to be in size instead, contents of
* common allocated memory between old and new size is preserved.
*/
void *
babl_realloc (void *ptr,
size_t size)
{
void *ret = NULL;
if (!ptr)
{
return babl_malloc (size);
}
babl_assert (IS_BAI (ptr));
if (size == 0)
{
babl_free (ptr);
return NULL;
}
if (babl_sizeof (ptr) >= size)
{
return ptr;
}
else if (babl_sizeof (ptr) < size)
{
#ifdef USE_REALLOC_CLEAR
/* not needed yet by babl, if aviodable, preferred, since
* it has performance hits where it isn't wanted, a special
* function might be better when needd.
*/
ret = babl_calloc (size, 1);
#else
ret = babl_malloc (size);
#endif
memcpy (ret, ptr, babl_sizeof (ptr));
BAI (ret)->destructor = BAI (ptr)->destructor;
BAI (ptr)->destructor = NULL;
babl_free (ptr);
#if BABL_DEBUG_MEM
babl_mutex_lock (babl_debug_mutex);
reallocs++;
babl_mutex_unlock (babl_debug_mutex);
#endif
return ret;
}
if (!ret)
babl_fatal ("args=(%p, %i): failed", ptr, size);
return NULL;
}
/* allocate nmemb*size bytes and set it to all zeros. */
void *
babl_calloc (size_t nmemb,
size_t size)
{
void *ret = babl_malloc (nmemb * size);
if (!ret)
babl_fatal ("args=(%i, %i): failed", nmemb, size);
memset (ret, 0, nmemb * size);
#if BABL_DEBUG_MEM
babl_mutex_lock (babl_debug_mutex);
callocs++;
mallocs--;
babl_mutex_unlock (babl_debug_mutex);
#endif
return ret;
}
/* Returns the size of an allocation.
*/
size_t
babl_sizeof (void *ptr)
{
babl_assert (IS_BAI (ptr));
return BAI (ptr)->size;
}
/* duplicate allocation needed for a string, and
* copy string contents, string is zero terminated.
*/
char *
babl_strdup (const char *s)
{
char *ret;
ret = babl_malloc (strlen (s) + 1);
if (!ret)
babl_log ("args=(%s): failed", s);
strcpy (ret, s);
#if BABL_DEBUG_MEM
babl_mutex_lock (babl_debug_mutex);
strdups++;
mallocs--;
babl_mutex_unlock (babl_debug_mutex);
#endif
return ret;
}
/* append string to babl allocated string dest, the returned
* string is the new canonical position with src added to dest
* if the dest allocation needed to be resized. Passing NULL
* causes a new allocation (thus babl-memory sees NULL as the empty
* string).
*/
char *
babl_strcat (char *dest,
const char *src)
{
char *ret;
int src_len;
int dst_len;
if (NULL == src)
return dest;
src_len = strlen (src);
if (!dest)
{
ret = babl_malloc (src_len + 1);
strcpy (ret, src);
return ret;
}
babl_assert (IS_BAI (dest));
dst_len = strlen (dest);
ret = dest;
if (babl_sizeof (dest) < src_len + dst_len + 1)
{
size_t new_size = babl_sizeof (dest);
while (new_size < src_len + dst_len + 1)
new_size *= 2;
ret = babl_realloc (dest, new_size);
}
strcpy (&ret[dst_len], src);
return ret;
}
#if BABL_DEBUG_MEM
/* performs a sanity check on memory, (checks if number of
* allocations and frees on babl memory evens out to zero).
*/
int
babl_memory_sanity (void)
{
if (frees != mallocs + strdups + callocs)
{
babl_log ("memory usage does not add up!\n"
"%s\n"
"\tbalance: %i-%i=%i\n",
mem_stats (), (strdups + mallocs + callocs), frees, (strdups + mallocs + callocs) - frees);
return -1;
}
return 0;
}
#endif