This source file includes following definitions.
- talloc_version_major
- talloc_version_minor
- talloc_set_log_fn
- talloc_log
- talloc_log_stderr
- talloc_set_log_stderr
- talloc_set_abort_fn
- talloc_abort
- talloc_abort_magic
- talloc_abort_double_free
- talloc_abort_unknown_value
- talloc_chunk_from_ptr
- talloc_parent_chunk
- talloc_parent
- talloc_parent_name
- talloc_pool_objectcount
- talloc_alloc_pool
- __talloc
- talloc_pool
- _talloc_set_destructor
- talloc_increase_ref_count
- talloc_reference_destructor
- _talloc_set_name_const
- _talloc_named_const
- _talloc_reference_loc
- _talloc_free_internal
- _talloc_steal_internal
- _talloc_steal_loc
- talloc_reparent
- talloc_unreference
- talloc_unlink
- talloc_set_name_v
- talloc_set_name
- talloc_named
- talloc_get_name
- talloc_check_name
- talloc_abort_type_missmatch
- _talloc_get_type_abort
- talloc_init
- talloc_free_children
- _talloc
- talloc_set_name_const
- talloc_named_const
- _talloc_free
- min_size
- _talloc_realloc
- _talloc_move
- talloc_total_size
- talloc_total_blocks
- talloc_reference_count
- talloc_report_depth_cb
- talloc_report_depth_FILE_helper
- talloc_report_depth_file
- talloc_report_full
- talloc_report
- talloc_report_null
- talloc_report_null_full
- talloc_enable_null_tracking
- talloc_enable_null_tracking_no_autofree
- talloc_disable_null_tracking
- talloc_enable_leak_report
- talloc_enable_leak_report_full
- _talloc_zero
- _talloc_memdup
- __talloc_strlendup
- talloc_strdup
- rep_strnlen
- talloc_strndup
- __talloc_strlendup_append
- talloc_strdup_append
- talloc_strdup_append_buffer
- talloc_strndup_append
- talloc_strndup_append_buffer
- talloc_vasprintf
- talloc_asprintf
- __talloc_vaslenprintf_append
- talloc_vasprintf_append
- talloc_vasprintf_append_buffer
- talloc_asprintf_append
- talloc_asprintf_append_buffer
- _talloc_array
- _talloc_zero_array
- _talloc_realloc_array
- talloc_realloc_fn
- talloc_autofree_destructor
- talloc_autofree
- talloc_autofree_context
- talloc_get_size
- talloc_find_parent_byname
- talloc_show_parents
- talloc_is_parent
#include "talloc.h"
#ifdef TALLOC_BUILD_VERSION_MAJOR
#if (TALLOC_VERSION_MAJOR != TALLOC_BUILD_VERSION_MAJOR)
#error "TALLOC_VERSION_MAJOR != TALLOC_BUILD_VERSION_MAJOR"
#endif
#endif
#ifdef TALLOC_BUILD_VERSION_MINOR
#if (TALLOC_VERSION_MINOR != TALLOC_BUILD_VERSION_MINOR)
#error "TALLOC_VERSION_MINOR != TALLOC_BUILD_VERSION_MINOR"
#endif
#endif
#define ALWAYS_REALLOC 0
#define MAX_TALLOC_SIZE 0x10000000
#define TALLOC_MAGIC_BASE 0xe814ec70
#define TALLOC_MAGIC ( \
TALLOC_MAGIC_BASE + \
(TALLOC_VERSION_MAJOR << 12) + \
(TALLOC_VERSION_MINOR << 4) \
)
#define TALLOC_FLAG_FREE 0x01
#define TALLOC_FLAG_LOOP 0x02
#define TALLOC_FLAG_POOL 0x04
#define TALLOC_FLAG_POOLMEM 0x08
#define TALLOC_MAGIC_REFERENCE ((const char *)1)
#ifndef TALLOC_ABORT
#define TALLOC_ABORT(reason) abort()
#endif
#ifndef discard_const_p
#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
# define discard_const_p(type, ptr) ((type *)((intptr_t)(ptr)))
#else
# define discard_const_p(type, ptr) ((type *)(ptr))
#endif
#endif
#if (__GNUC__ >= 3)
#ifndef likely
#define likely(x) __builtin_expect(!!(x), 1)
#endif
#ifndef unlikely
#define unlikely(x) __builtin_expect(!!(x), 0)
#endif
#else
#ifndef likely
#define likely(x) (x)
#endif
#ifndef unlikely
#define unlikely(x) (x)
#endif
#endif
#ifdef _MSC_VER
#define INLINE
#else
#define INLINE inline
#endif
static void *null_context;
static void *autofree_context;
struct talloc_reference_handle {
struct talloc_reference_handle *next, *prev;
void *ptr;
const char *location;
};
typedef int (*talloc_destructor_t)(void *);
struct talloc_chunk {
struct talloc_chunk *next, *prev;
struct talloc_chunk *parent, *child;
struct talloc_reference_handle *refs;
talloc_destructor_t destructor;
const char *name;
size_t size;
unsigned flags;
void *pool;
};
#define TC_HDR_SIZE ((sizeof(struct talloc_chunk)+15)&~15)
#define TC_PTR_FROM_CHUNK(tc) ((void *)(TC_HDR_SIZE + (char*)tc))
int talloc_version_major(void)
{
return TALLOC_VERSION_MAJOR;
}
int talloc_version_minor(void)
{
return TALLOC_VERSION_MINOR;
}
static void (*talloc_log_fn)(const char *message);
void talloc_set_log_fn(void (*log_fn)(const char *message))
{
talloc_log_fn = log_fn;
}
static void talloc_log(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2);
static void talloc_log(const char *fmt, ...)
{
va_list ap;
char *message;
if (!talloc_log_fn) {
return;
}
va_start(ap, fmt);
message = talloc_vasprintf(NULL, fmt, ap);
va_end(ap);
talloc_log_fn(message);
talloc_free(message);
}
static void talloc_log_stderr(const char *message)
{
fprintf(stderr, "%s", message);
}
void talloc_set_log_stderr(void)
{
talloc_set_log_fn(talloc_log_stderr);
}
static void (*talloc_abort_fn)(const char *reason);
void talloc_set_abort_fn(void (*abort_fn)(const char *reason))
{
talloc_abort_fn = abort_fn;
}
static void talloc_abort(const char *reason)
{
talloc_log("%s\n", reason);
if (!talloc_abort_fn) {
TALLOC_ABORT(reason);
}
talloc_abort_fn(reason);
}
static void talloc_abort_magic(unsigned magic)
{
unsigned striped = magic - TALLOC_MAGIC_BASE;
unsigned major = (striped & 0xFFFFF000) >> 12;
unsigned minor = (striped & 0x00000FF0) >> 4;
talloc_log("Bad talloc magic[0x%08X/%u/%u] expected[0x%08X/%u/%u]\n",
magic, major, minor,
TALLOC_MAGIC, TALLOC_VERSION_MAJOR, TALLOC_VERSION_MINOR);
talloc_abort("Bad talloc magic value - wrong talloc version used/mixed");
}
static void talloc_abort_double_free(void)
{
talloc_abort("Bad talloc magic value - double free");
}
static void talloc_abort_unknown_value(void)
{
talloc_abort("Bad talloc magic value - unknown value");
}
static INLINE struct talloc_chunk *talloc_chunk_from_ptr(const void *ptr)
{
const char *pp = (const char *)ptr;
struct talloc_chunk *tc = discard_const_p(struct talloc_chunk, pp - TC_HDR_SIZE);
if (unlikely((tc->flags & (TALLOC_FLAG_FREE | ~0xF)) != TALLOC_MAGIC)) {
if ((tc->flags & (~0xFFF)) == TALLOC_MAGIC_BASE) {
talloc_abort_magic(tc->flags & (~0xF));
return NULL;
}
if (tc->flags & TALLOC_FLAG_FREE) {
talloc_log("talloc: double free error - first free may be at %s\n", tc->name);
talloc_abort_double_free();
return NULL;
} else {
talloc_abort_unknown_value();
return NULL;
}
}
return tc;
}
#define _TLIST_ADD(list, p) \
do { \
if (!(list)) { \
(list) = (p); \
(p)->next = (p)->prev = NULL; \
} else { \
(list)->prev = (p); \
(p)->next = (list); \
(p)->prev = NULL; \
(list) = (p); \
}\
} while (0)
#define _TLIST_REMOVE(list, p) \
do { \
if ((p) == (list)) { \
(list) = (p)->next; \
if (list) (list)->prev = NULL; \
} else { \
if ((p)->prev) (p)->prev->next = (p)->next; \
if ((p)->next) (p)->next->prev = (p)->prev; \
} \
if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \
} while (0)
static INLINE struct talloc_chunk *talloc_parent_chunk(const void *ptr)
{
struct talloc_chunk *tc;
if (unlikely(ptr == NULL)) {
return NULL;
}
tc = talloc_chunk_from_ptr(ptr);
while (tc->prev) tc=tc->prev;
return tc->parent;
}
void *talloc_parent(const void *ptr)
{
struct talloc_chunk *tc = talloc_parent_chunk(ptr);
return tc? TC_PTR_FROM_CHUNK(tc) : NULL;
}
const char *talloc_parent_name(const void *ptr)
{
struct talloc_chunk *tc = talloc_parent_chunk(ptr);
return tc? tc->name : NULL;
}
#define TALLOC_POOL_HDR_SIZE 16
static unsigned int *talloc_pool_objectcount(struct talloc_chunk *tc)
{
return (unsigned int *)((char *)tc + sizeof(struct talloc_chunk));
}
static struct talloc_chunk *talloc_alloc_pool(struct talloc_chunk *parent,
size_t size)
{
struct talloc_chunk *pool_ctx = NULL;
size_t space_left;
struct talloc_chunk *result;
size_t chunk_size;
if (parent == NULL) {
return NULL;
}
if (parent->flags & TALLOC_FLAG_POOL) {
pool_ctx = parent;
}
else if (parent->flags & TALLOC_FLAG_POOLMEM) {
pool_ctx = (struct talloc_chunk *)parent->pool;
}
if (pool_ctx == NULL) {
return NULL;
}
space_left = ((char *)pool_ctx + TC_HDR_SIZE + pool_ctx->size)
- ((char *)pool_ctx->pool);
chunk_size = ((size + 15) & ~15);
if (space_left < chunk_size) {
return NULL;
}
result = (struct talloc_chunk *)pool_ctx->pool;
#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_UNDEFINED)
VALGRIND_MAKE_MEM_UNDEFINED(result, size);
#endif
pool_ctx->pool = (void *)((char *)result + chunk_size);
result->flags = TALLOC_MAGIC | TALLOC_FLAG_POOLMEM;
result->pool = pool_ctx;
*talloc_pool_objectcount(pool_ctx) += 1;
return result;
}
static INLINE void *__talloc(const void *context, size_t size)
{
struct talloc_chunk *tc = NULL;
if (unlikely(context == NULL)) {
context = null_context;
}
if (unlikely(size >= MAX_TALLOC_SIZE)) {
return NULL;
}
if (context != NULL) {
tc = talloc_alloc_pool(talloc_chunk_from_ptr(context),
TC_HDR_SIZE+size);
}
if (tc == NULL) {
tc = (struct talloc_chunk *)malloc(TC_HDR_SIZE+size);
if (unlikely(tc == NULL)) return NULL;
tc->flags = TALLOC_MAGIC;
tc->pool = NULL;
}
tc->size = size;
tc->destructor = NULL;
tc->child = NULL;
tc->name = NULL;
tc->refs = NULL;
if (likely(context)) {
struct talloc_chunk *parent = talloc_chunk_from_ptr(context);
if (parent->child) {
parent->child->parent = NULL;
tc->next = parent->child;
tc->next->prev = tc;
} else {
tc->next = NULL;
}
tc->parent = parent;
tc->prev = NULL;
parent->child = tc;
} else {
tc->next = tc->prev = tc->parent = NULL;
}
return TC_PTR_FROM_CHUNK(tc);
}
void *talloc_pool(const void *context, size_t size)
{
void *result = __talloc(context, size + TALLOC_POOL_HDR_SIZE);
struct talloc_chunk *tc;
if (unlikely(result == NULL)) {
return NULL;
}
tc = talloc_chunk_from_ptr(result);
tc->flags |= TALLOC_FLAG_POOL;
tc->pool = (char *)result + TALLOC_POOL_HDR_SIZE;
*talloc_pool_objectcount(tc) = 1;
#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS)
VALGRIND_MAKE_MEM_NOACCESS(tc->pool, size);
#endif
return result;
}
void _talloc_set_destructor(const void *ptr, int (*destructor)(void *))
{
struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
tc->destructor = destructor;
}
int talloc_increase_ref_count(const void *ptr)
{
if (unlikely(!talloc_reference(null_context, ptr))) {
return -1;
}
return 0;
}
static int talloc_reference_destructor(struct talloc_reference_handle *handle)
{
struct talloc_chunk *ptr_tc = talloc_chunk_from_ptr(handle->ptr);
_TLIST_REMOVE(ptr_tc->refs, handle);
return 0;
}
static INLINE void _talloc_set_name_const(const void *ptr, const char *name)
{
struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
tc->name = name;
}
static INLINE void *_talloc_named_const(const void *context, size_t size, const char *name)
{
void *ptr;
ptr = __talloc(context, size);
if (unlikely(ptr == NULL)) {
return NULL;
}
_talloc_set_name_const(ptr, name);
return ptr;
}
void *_talloc_reference_loc(const void *context, const void *ptr, const char *location)
{
struct talloc_chunk *tc;
struct talloc_reference_handle *handle;
if (unlikely(ptr == NULL)) return NULL;
tc = talloc_chunk_from_ptr(ptr);
handle = (struct talloc_reference_handle *)_talloc_named_const(context,
sizeof(struct talloc_reference_handle),
TALLOC_MAGIC_REFERENCE);
if (unlikely(handle == NULL)) return NULL;
talloc_set_destructor(handle, talloc_reference_destructor);
handle->ptr = discard_const_p(void, ptr);
handle->location = location;
_TLIST_ADD(tc->refs, handle);
return handle->ptr;
}
static void *_talloc_steal_internal(const void *new_ctx, const void *ptr);
static INLINE int _talloc_free_internal(void *ptr, const char *location)
{
struct talloc_chunk *tc;
if (unlikely(ptr == NULL)) {
return -1;
}
tc = talloc_chunk_from_ptr(ptr);
if (unlikely(tc->refs)) {
int is_child;
is_child = talloc_is_parent(tc->refs, ptr);
_talloc_free_internal(tc->refs, location);
if (is_child) {
return _talloc_free_internal(ptr, location);
}
return -1;
}
if (unlikely(tc->flags & TALLOC_FLAG_LOOP)) {
return 0;
}
if (unlikely(tc->destructor)) {
talloc_destructor_t d = tc->destructor;
if (d == (talloc_destructor_t)-1) {
return -1;
}
tc->destructor = (talloc_destructor_t)-1;
if (d(ptr) == -1) {
tc->destructor = d;
return -1;
}
tc->destructor = NULL;
}
if (tc->parent) {
_TLIST_REMOVE(tc->parent->child, tc);
if (tc->parent->child) {
tc->parent->child->parent = tc->parent;
}
} else {
if (tc->prev) tc->prev->next = tc->next;
if (tc->next) tc->next->prev = tc->prev;
}
tc->flags |= TALLOC_FLAG_LOOP;
while (tc->child) {
void *child = TC_PTR_FROM_CHUNK(tc->child);
const void *new_parent = null_context;
if (unlikely(tc->child->refs)) {
struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs);
if (p) new_parent = TC_PTR_FROM_CHUNK(p);
}
if (unlikely(_talloc_free_internal(child, location) == -1)) {
if (new_parent == null_context) {
struct talloc_chunk *p = talloc_parent_chunk(ptr);
if (p) new_parent = TC_PTR_FROM_CHUNK(p);
}
_talloc_steal_internal(new_parent, child);
}
}
tc->flags |= TALLOC_FLAG_FREE;
tc->name = location;
if (tc->flags & (TALLOC_FLAG_POOL|TALLOC_FLAG_POOLMEM)) {
struct talloc_chunk *pool;
unsigned int *pool_object_count;
pool = (tc->flags & TALLOC_FLAG_POOL)
? tc : (struct talloc_chunk *)tc->pool;
pool_object_count = talloc_pool_objectcount(pool);
if (*pool_object_count == 0) {
talloc_abort("Pool object count zero!");
return 0;
}
*pool_object_count -= 1;
if (*pool_object_count == 0) {
free(pool);
}
}
else {
free(tc);
}
return 0;
}
static void *_talloc_steal_internal(const void *new_ctx, const void *ptr)
{
struct talloc_chunk *tc, *new_tc;
if (unlikely(!ptr)) {
return NULL;
}
if (unlikely(new_ctx == NULL)) {
new_ctx = null_context;
}
tc = talloc_chunk_from_ptr(ptr);
if (unlikely(new_ctx == NULL)) {
if (tc->parent) {
_TLIST_REMOVE(tc->parent->child, tc);
if (tc->parent->child) {
tc->parent->child->parent = tc->parent;
}
} else {
if (tc->prev) tc->prev->next = tc->next;
if (tc->next) tc->next->prev = tc->prev;
}
tc->parent = tc->next = tc->prev = NULL;
return discard_const_p(void, ptr);
}
new_tc = talloc_chunk_from_ptr(new_ctx);
if (unlikely(tc == new_tc || tc->parent == new_tc)) {
return discard_const_p(void, ptr);
}
if (tc->parent) {
_TLIST_REMOVE(tc->parent->child, tc);
if (tc->parent->child) {
tc->parent->child->parent = tc->parent;
}
} else {
if (tc->prev) tc->prev->next = tc->next;
if (tc->next) tc->next->prev = tc->prev;
}
tc->parent = new_tc;
if (new_tc->child) new_tc->child->parent = NULL;
_TLIST_ADD(new_tc->child, tc);
return discard_const_p(void, ptr);
}
void *_talloc_steal_loc(const void *new_ctx, const void *ptr, const char *location)
{
struct talloc_chunk *tc;
if (unlikely(ptr == NULL)) {
return NULL;
}
tc = talloc_chunk_from_ptr(ptr);
if (unlikely(tc->refs != NULL) && talloc_parent(ptr) != new_ctx) {
struct talloc_reference_handle *h;
talloc_log("WARNING: talloc_steal with references at %s\n",
location);
for (h=tc->refs; h; h=h->next) {
talloc_log("\treference at %s\n",
h->location);
}
}
return _talloc_steal_internal(new_ctx, ptr);
}
void *talloc_reparent(const void *old_parent, const void *new_parent, const void *ptr)
{
struct talloc_chunk *tc;
struct talloc_reference_handle *h;
if (unlikely(ptr == NULL)) {
return NULL;
}
if (old_parent == talloc_parent(ptr)) {
return _talloc_steal_internal(new_parent, ptr);
}
tc = talloc_chunk_from_ptr(ptr);
for (h=tc->refs;h;h=h->next) {
if (talloc_parent(h) == old_parent) {
if (_talloc_steal_internal(new_parent, h) != h) {
return NULL;
}
return discard_const_p(void, ptr);
}
}
return NULL;
}
static INLINE int talloc_unreference(const void *context, const void *ptr)
{
struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
struct talloc_reference_handle *h;
if (unlikely(context == NULL)) {
context = null_context;
}
for (h=tc->refs;h;h=h->next) {
struct talloc_chunk *p = talloc_parent_chunk(h);
if (p == NULL) {
if (context == NULL) break;
} else if (TC_PTR_FROM_CHUNK(p) == context) {
break;
}
}
if (h == NULL) {
return -1;
}
return _talloc_free_internal(h, __location__);
}
int talloc_unlink(const void *context, void *ptr)
{
struct talloc_chunk *tc_p, *new_p;
void *new_parent;
if (ptr == NULL) {
return -1;
}
if (context == NULL) {
context = null_context;
}
if (talloc_unreference(context, ptr) == 0) {
return 0;
}
if (context == NULL) {
if (talloc_parent_chunk(ptr) != NULL) {
return -1;
}
} else {
if (talloc_chunk_from_ptr(context) != talloc_parent_chunk(ptr)) {
return -1;
}
}
tc_p = talloc_chunk_from_ptr(ptr);
if (tc_p->refs == NULL) {
return _talloc_free_internal(ptr, __location__);
}
new_p = talloc_parent_chunk(tc_p->refs);
if (new_p) {
new_parent = TC_PTR_FROM_CHUNK(new_p);
} else {
new_parent = NULL;
}
if (talloc_unreference(new_parent, ptr) != 0) {
return -1;
}
_talloc_steal_internal(new_parent, ptr);
return 0;
}
static INLINE const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
static INLINE const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap)
{
struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
tc->name = talloc_vasprintf(ptr, fmt, ap);
if (likely(tc->name)) {
_talloc_set_name_const(tc->name, ".name");
}
return tc->name;
}
const char *talloc_set_name(const void *ptr, const char *fmt, ...)
{
const char *name;
va_list ap;
va_start(ap, fmt);
name = talloc_set_name_v(ptr, fmt, ap);
va_end(ap);
return name;
}
void *talloc_named(const void *context, size_t size, const char *fmt, ...)
{
va_list ap;
void *ptr;
const char *name;
ptr = __talloc(context, size);
if (unlikely(ptr == NULL)) return NULL;
va_start(ap, fmt);
name = talloc_set_name_v(ptr, fmt, ap);
va_end(ap);
if (unlikely(name == NULL)) {
_talloc_free_internal(ptr, __location__);
return NULL;
}
return ptr;
}
const char *talloc_get_name(const void *ptr)
{
struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
if (unlikely(tc->name == TALLOC_MAGIC_REFERENCE)) {
return ".reference";
}
if (likely(tc->name)) {
return tc->name;
}
return "UNNAMED";
}
void *talloc_check_name(const void *ptr, const char *name)
{
const char *pname;
if (unlikely(ptr == NULL)) return NULL;
pname = talloc_get_name(ptr);
if (likely(pname == name || strcmp(pname, name) == 0)) {
return discard_const_p(void, ptr);
}
return NULL;
}
static void talloc_abort_type_missmatch(const char *location,
const char *name,
const char *expected)
{
const char *reason;
reason = talloc_asprintf(NULL,
"%s: Type mismatch: name[%s] expected[%s]",
location,
name?name:"NULL",
expected);
if (!reason) {
reason = "Type mismatch";
}
talloc_abort(reason);
}
void *_talloc_get_type_abort(const void *ptr, const char *name, const char *location)
{
const char *pname;
if (unlikely(ptr == NULL)) {
talloc_abort_type_missmatch(location, NULL, name);
return NULL;
}
pname = talloc_get_name(ptr);
if (likely(pname == name || strcmp(pname, name) == 0)) {
return discard_const_p(void, ptr);
}
talloc_abort_type_missmatch(location, pname, name);
return NULL;
}
void *talloc_init(const char *fmt, ...)
{
va_list ap;
void *ptr;
const char *name;
talloc_enable_null_tracking();
ptr = __talloc(NULL, 0);
if (unlikely(ptr == NULL)) return NULL;
va_start(ap, fmt);
name = talloc_set_name_v(ptr, fmt, ap);
va_end(ap);
if (unlikely(name == NULL)) {
_talloc_free_internal(ptr, __location__);
return NULL;
}
return ptr;
}
void talloc_free_children(void *ptr)
{
struct talloc_chunk *tc;
if (unlikely(ptr == NULL)) {
return;
}
tc = talloc_chunk_from_ptr(ptr);
while (tc->child) {
void *child = TC_PTR_FROM_CHUNK(tc->child);
const void *new_parent = null_context;
if (unlikely(tc->child->refs)) {
struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs);
if (p) new_parent = TC_PTR_FROM_CHUNK(p);
}
if (unlikely(talloc_free(child) == -1)) {
if (new_parent == null_context) {
struct talloc_chunk *p = talloc_parent_chunk(ptr);
if (p) new_parent = TC_PTR_FROM_CHUNK(p);
}
_talloc_steal_internal(new_parent, child);
}
}
if ((tc->flags & TALLOC_FLAG_POOL)
&& (*talloc_pool_objectcount(tc) == 1)) {
tc->pool = ((char *)tc + TC_HDR_SIZE + TALLOC_POOL_HDR_SIZE);
#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS)
VALGRIND_MAKE_MEM_NOACCESS(
tc->pool, tc->size - TALLOC_POOL_HDR_SIZE);
#endif
}
}
void *_talloc(const void *context, size_t size)
{
return __talloc(context, size);
}
void talloc_set_name_const(const void *ptr, const char *name)
{
_talloc_set_name_const(ptr, name);
}
void *talloc_named_const(const void *context, size_t size, const char *name)
{
return _talloc_named_const(context, size, name);
}
int _talloc_free(void *ptr, const char *location)
{
struct talloc_chunk *tc;
if (unlikely(ptr == NULL)) {
return -1;
}
tc = talloc_chunk_from_ptr(ptr);
if (unlikely(tc->refs != NULL)) {
struct talloc_reference_handle *h;
talloc_log("ERROR: talloc_free with references at %s\n",
location);
for (h=tc->refs; h; h=h->next) {
talloc_log("\treference at %s\n",
h->location);
}
return -1;
}
return _talloc_free_internal(ptr, location);
}
static INLINE size_t min_size(size_t a, size_t b)
{
return a > b ? b : a;
}
void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name)
{
struct talloc_chunk *tc;
void *new_ptr;
int malloced = 0;
if (unlikely(size == 0)) {
talloc_unlink(context, ptr);
return NULL;
}
if (unlikely(size >= MAX_TALLOC_SIZE)) {
return NULL;
}
if (ptr == NULL) {
return _talloc_named_const(context, size, name);
}
tc = talloc_chunk_from_ptr(ptr);
if (unlikely(tc->refs)) {
return NULL;
}
if (unlikely(tc->flags & TALLOC_FLAG_POOL)) {
return NULL;
}
if ((size < tc->size) && ((tc->size - size) < 1024)) {
tc->size = size;
return ptr;
}
tc->flags |= TALLOC_FLAG_FREE;
#if ALWAYS_REALLOC
new_ptr = malloc(size + TC_HDR_SIZE);
if (new_ptr) {
memcpy(new_ptr, tc, tc->size + TC_HDR_SIZE);
free(tc);
}
#else
if (tc->flags & TALLOC_FLAG_POOLMEM) {
new_ptr = talloc_alloc_pool(tc, size + TC_HDR_SIZE);
*talloc_pool_objectcount((struct talloc_chunk *)
(tc->pool)) -= 1;
if (new_ptr == NULL) {
new_ptr = malloc(TC_HDR_SIZE+size);
malloced = 1;
}
if (new_ptr) {
memcpy(new_ptr, tc, min_size(tc->size,size) + TC_HDR_SIZE);
}
}
else {
new_ptr = realloc(tc, size + TC_HDR_SIZE);
}
#endif
if (unlikely(!new_ptr)) {
tc->flags &= ~TALLOC_FLAG_FREE;
return NULL;
}
tc = (struct talloc_chunk *)new_ptr;
tc->flags &= ~TALLOC_FLAG_FREE;
if (malloced) {
tc->flags &= ~TALLOC_FLAG_POOLMEM;
}
if (tc->parent) {
tc->parent->child = tc;
}
if (tc->child) {
tc->child->parent = tc;
}
if (tc->prev) {
tc->prev->next = tc;
}
if (tc->next) {
tc->next->prev = tc;
}
tc->size = size;
_talloc_set_name_const(TC_PTR_FROM_CHUNK(tc), name);
return TC_PTR_FROM_CHUNK(tc);
}
void *_talloc_move(const void *new_ctx, const void *_pptr)
{
const void **pptr = discard_const_p(const void *,_pptr);
void *ret = talloc_steal(new_ctx, discard_const_p(void, *pptr));
(*pptr) = NULL;
return ret;
}
size_t talloc_total_size(const void *ptr)
{
size_t total = 0;
struct talloc_chunk *c, *tc;
if (ptr == NULL) {
ptr = null_context;
}
if (ptr == NULL) {
return 0;
}
tc = talloc_chunk_from_ptr(ptr);
if (tc->flags & TALLOC_FLAG_LOOP) {
return 0;
}
tc->flags |= TALLOC_FLAG_LOOP;
if (likely(tc->name != TALLOC_MAGIC_REFERENCE)) {
total = tc->size;
}
for (c=tc->child;c;c=c->next) {
total += talloc_total_size(TC_PTR_FROM_CHUNK(c));
}
tc->flags &= ~TALLOC_FLAG_LOOP;
return total;
}
size_t talloc_total_blocks(const void *ptr)
{
size_t total = 0;
struct talloc_chunk *c, *tc;
if (ptr == NULL) {
ptr = null_context;
}
if (ptr == NULL) {
return 0;
}
tc = talloc_chunk_from_ptr(ptr);
if (tc->flags & TALLOC_FLAG_LOOP) {
return 0;
}
tc->flags |= TALLOC_FLAG_LOOP;
total++;
for (c=tc->child;c;c=c->next) {
total += talloc_total_blocks(TC_PTR_FROM_CHUNK(c));
}
tc->flags &= ~TALLOC_FLAG_LOOP;
return total;
}
size_t talloc_reference_count(const void *ptr)
{
struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
struct talloc_reference_handle *h;
size_t ret = 0;
for (h=tc->refs;h;h=h->next) {
ret++;
}
return ret;
}
void talloc_report_depth_cb(const void *ptr, int depth, int max_depth,
void (*callback)(const void *ptr,
int depth, int max_depth,
int is_ref,
void *private_data),
void *private_data)
{
struct talloc_chunk *c, *tc;
if (ptr == NULL) {
ptr = null_context;
}
if (ptr == NULL) return;
tc = talloc_chunk_from_ptr(ptr);
if (tc->flags & TALLOC_FLAG_LOOP) {
return;
}
callback(ptr, depth, max_depth, 0, private_data);
if (max_depth >= 0 && depth >= max_depth) {
return;
}
tc->flags |= TALLOC_FLAG_LOOP;
for (c=tc->child;c;c=c->next) {
if (c->name == TALLOC_MAGIC_REFERENCE) {
struct talloc_reference_handle *h = (struct talloc_reference_handle *)TC_PTR_FROM_CHUNK(c);
callback(h->ptr, depth + 1, max_depth, 1, private_data);
} else {
talloc_report_depth_cb(TC_PTR_FROM_CHUNK(c), depth + 1, max_depth, callback, private_data);
}
}
tc->flags &= ~TALLOC_FLAG_LOOP;
}
static void talloc_report_depth_FILE_helper(const void *ptr, int depth, int max_depth, int is_ref, void *_f)
{
const char *name = talloc_get_name(ptr);
FILE *f = (FILE *)_f;
if (is_ref) {
fprintf(f, "%*sreference to: %s\n", depth*4, "", name);
return;
}
if (depth == 0) {
fprintf(f,"%stalloc report on '%s' (total %6lu bytes in %3lu blocks)\n",
(max_depth < 0 ? "full " :""), name,
(unsigned long)talloc_total_size(ptr),
(unsigned long)talloc_total_blocks(ptr));
return;
}
fprintf(f, "%*s%-30s contains %6lu bytes in %3lu blocks (ref %d) %p\n",
depth*4, "",
name,
(unsigned long)talloc_total_size(ptr),
(unsigned long)talloc_total_blocks(ptr),
(int)talloc_reference_count(ptr), ptr);
#if 0
fprintf(f, "content: ");
if (talloc_total_size(ptr)) {
int tot = talloc_total_size(ptr);
int i;
for (i = 0; i < tot; i++) {
if ((((char *)ptr)[i] > 31) && (((char *)ptr)[i] < 126)) {
fprintf(f, "%c", ((char *)ptr)[i]);
} else {
fprintf(f, "~%02x", ((char *)ptr)[i]);
}
}
}
fprintf(f, "\n");
#endif
}
void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f)
{
if (f) {
talloc_report_depth_cb(ptr, depth, max_depth, talloc_report_depth_FILE_helper, f);
fflush(f);
}
}
void talloc_report_full(const void *ptr, FILE *f)
{
talloc_report_depth_file(ptr, 0, -1, f);
}
void talloc_report(const void *ptr, FILE *f)
{
talloc_report_depth_file(ptr, 0, 1, f);
}
static void talloc_report_null(void)
{
if (talloc_total_size(null_context) != 0) {
talloc_report(null_context, stderr);
}
}
static void talloc_report_null_full(void)
{
if (talloc_total_size(null_context) != 0) {
talloc_report_full(null_context, stderr);
}
}
void talloc_enable_null_tracking(void)
{
if (null_context == NULL) {
null_context = _talloc_named_const(NULL, 0, "null_context");
if (autofree_context != NULL) {
talloc_reparent(NULL, null_context, autofree_context);
}
}
}
void talloc_enable_null_tracking_no_autofree(void)
{
if (null_context == NULL) {
null_context = _talloc_named_const(NULL, 0, "null_context");
}
}
void talloc_disable_null_tracking(void)
{
if (null_context != NULL) {
struct talloc_chunk *tc, *tc2;
tc = talloc_chunk_from_ptr(null_context);
for (tc2 = tc->child; tc2; tc2=tc2->next) {
if (tc2->parent == tc) tc2->parent = NULL;
if (tc2->prev == tc) tc2->prev = NULL;
}
for (tc2 = tc->next; tc2; tc2=tc2->next) {
if (tc2->parent == tc) tc2->parent = NULL;
if (tc2->prev == tc) tc2->prev = NULL;
}
tc->child = NULL;
tc->next = NULL;
}
talloc_free(null_context);
null_context = NULL;
}
void talloc_enable_leak_report(void)
{
talloc_enable_null_tracking();
atexit(talloc_report_null);
}
void talloc_enable_leak_report_full(void)
{
talloc_enable_null_tracking();
atexit(talloc_report_null_full);
}
void *_talloc_zero(const void *ctx, size_t size, const char *name)
{
void *p = _talloc_named_const(ctx, size, name);
if (p) {
memset(p, '\0', size);
}
return p;
}
void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name)
{
void *newp = _talloc_named_const(t, size, name);
if (likely(newp)) {
memcpy(newp, p, size);
}
return newp;
}
static INLINE char *__talloc_strlendup(const void *t, const char *p, size_t len)
{
char *ret;
ret = (char *)__talloc(t, len + 1);
if (unlikely(!ret)) return NULL;
memcpy(ret, p, len);
ret[len] = 0;
_talloc_set_name_const(ret, ret);
return ret;
}
char *talloc_strdup(const void *t, const char *p)
{
if (unlikely(!p)) return NULL;
return __talloc_strlendup(t, p, strlen(p));
}
#ifndef HAVE_STRNLEN
#define strnlen rep_strnlen
static size_t rep_strnlen(const char* s, size_t n)
{
if (unlikely(!s)) return 0;
int i = 0;
while (i < n && *s++ != '\0')
++i;
return i;
}
#endif
char *talloc_strndup(const void *t, const char *p, size_t n)
{
if (unlikely(!p)) return NULL;
return __talloc_strlendup(t, p, strnlen(p, n));
}
static INLINE char *__talloc_strlendup_append(char *s, size_t slen,
const char *a, size_t alen)
{
char *ret;
ret = talloc_realloc(NULL, s, char, slen + alen + 1);
if (unlikely(!ret)) return NULL;
memcpy(&ret[slen], a, alen);
ret[slen+alen] = 0;
_talloc_set_name_const(ret, ret);
return ret;
}
char *talloc_strdup_append(char *s, const char *a)
{
if (unlikely(!s)) {
return talloc_strdup(NULL, a);
}
if (unlikely(!a)) {
return s;
}
return __talloc_strlendup_append(s, strlen(s), a, strlen(a));
}
char *talloc_strdup_append_buffer(char *s, const char *a)
{
size_t slen;
if (unlikely(!s)) {
return talloc_strdup(NULL, a);
}
if (unlikely(!a)) {
return s;
}
slen = talloc_get_size(s);
if (likely(slen > 0)) {
slen--;
}
return __talloc_strlendup_append(s, slen, a, strlen(a));
}
char *talloc_strndup_append(char *s, const char *a, size_t n)
{
if (unlikely(!s)) {
return talloc_strdup(NULL, a);
}
if (unlikely(!a)) {
return s;
}
return __talloc_strlendup_append(s, strlen(s), a, strnlen(a, n));
}
char *talloc_strndup_append_buffer(char *s, const char *a, size_t n)
{
size_t slen;
if (unlikely(!s)) {
return talloc_strdup(NULL, a);
}
if (unlikely(!a)) {
return s;
}
slen = talloc_get_size(s);
if (likely(slen > 0)) {
slen--;
}
return __talloc_strlendup_append(s, slen, a, strnlen(a, n));
}
#ifndef HAVE_VA_COPY
#ifdef HAVE___VA_COPY
#define va_copy(dest, src) __va_copy(dest, src)
#else
#define va_copy(dest, src) (dest) = (src)
#endif
#endif
char *talloc_vasprintf(const void *t, const char *fmt, va_list ap)
{
int len;
char *ret;
va_list ap2;
va_copy(ap2, ap);
len = vsnprintf(NULL, 0, fmt, ap2);
va_end(ap2);
if (unlikely(len < 0)) {
return NULL;
}
ret = (char *)__talloc(t, len+1);
if (unlikely(!ret)) return NULL;
va_copy(ap2, ap);
vsnprintf(ret, len+1, fmt, ap2);
va_end(ap2);
_talloc_set_name_const(ret, ret);
return ret;
}
char *talloc_asprintf(const void *t, const char *fmt, ...)
{
va_list ap;
char *ret;
va_start(ap, fmt);
ret = talloc_vasprintf(t, fmt, ap);
va_end(ap);
return ret;
}
static INLINE char *__talloc_vaslenprintf_append(char *s, size_t slen,
const char *fmt, va_list ap)
PRINTF_ATTRIBUTE(3,0);
static INLINE char *__talloc_vaslenprintf_append(char *s, size_t slen,
const char *fmt, va_list ap)
{
#ifndef _MSC_VER
ssize_t alen;
#else
size_t alen;
#endif
va_list ap2;
va_copy(ap2, ap);
alen = vsnprintf(NULL, 0, fmt, ap2);
va_end(ap2);
if (alen <= 0) {
return s;
}
s = talloc_realloc(NULL, s, char, slen + alen + 1);
if (!s) return NULL;
va_copy(ap2, ap);
vsnprintf(s + slen, alen + 1, fmt, ap2);
va_end(ap2);
_talloc_set_name_const(s, s);
return s;
}
char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap)
{
if (unlikely(!s)) {
return talloc_vasprintf(NULL, fmt, ap);
}
return __talloc_vaslenprintf_append(s, strlen(s), fmt, ap);
}
char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap)
{
size_t slen;
if (unlikely(!s)) {
return talloc_vasprintf(NULL, fmt, ap);
}
slen = talloc_get_size(s);
if (likely(slen > 0)) {
slen--;
}
return __talloc_vaslenprintf_append(s, slen, fmt, ap);
}
char *talloc_asprintf_append(char *s, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
s = talloc_vasprintf_append(s, fmt, ap);
va_end(ap);
return s;
}
char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
s = talloc_vasprintf_append_buffer(s, fmt, ap);
va_end(ap);
return s;
}
void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name)
{
if (count >= MAX_TALLOC_SIZE/el_size) {
return NULL;
}
return _talloc_named_const(ctx, el_size * count, name);
}
void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name)
{
if (count >= MAX_TALLOC_SIZE/el_size) {
return NULL;
}
return _talloc_zero(ctx, el_size * count, name);
}
void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name)
{
if (count >= MAX_TALLOC_SIZE/el_size) {
return NULL;
}
return _talloc_realloc(ctx, ptr, el_size * count, name);
}
void *talloc_realloc_fn(const void *context, void *ptr, size_t size)
{
return _talloc_realloc(context, ptr, size, NULL);
}
static int talloc_autofree_destructor(void *ptr)
{
autofree_context = NULL;
return 0;
}
static void talloc_autofree(void)
{
talloc_free(autofree_context);
}
void *talloc_autofree_context(void)
{
if (autofree_context == NULL) {
autofree_context = _talloc_named_const(NULL, 0, "autofree_context");
talloc_set_destructor(autofree_context, talloc_autofree_destructor);
atexit(talloc_autofree);
}
return autofree_context;
}
size_t talloc_get_size(const void *context)
{
struct talloc_chunk *tc;
if (context == NULL) {
context = null_context;
}
if (context == NULL) {
return 0;
}
tc = talloc_chunk_from_ptr(context);
return tc->size;
}
void *talloc_find_parent_byname(const void *context, const char *name)
{
struct talloc_chunk *tc;
if (context == NULL) {
return NULL;
}
tc = talloc_chunk_from_ptr(context);
while (tc) {
if (tc->name && strcmp(tc->name, name) == 0) {
return TC_PTR_FROM_CHUNK(tc);
}
while (tc && tc->prev) tc = tc->prev;
if (tc) {
tc = tc->parent;
}
}
return NULL;
}
void talloc_show_parents(const void *context, FILE *file)
{
struct talloc_chunk *tc;
if (context == NULL) {
fprintf(file, "talloc no parents for NULL\n");
return;
}
tc = talloc_chunk_from_ptr(context);
fprintf(file, "talloc parents of '%s'\n", talloc_get_name(context));
while (tc) {
fprintf(file, "\t'%s'\n", talloc_get_name(TC_PTR_FROM_CHUNK(tc)));
while (tc && tc->prev) tc = tc->prev;
if (tc) {
tc = tc->parent;
}
}
fflush(file);
}
int talloc_is_parent(const void *context, const void *ptr)
{
struct talloc_chunk *tc;
if (context == NULL) {
return 0;
}
tc = talloc_chunk_from_ptr(context);
while (tc) {
if (TC_PTR_FROM_CHUNK(tc) == ptr) return 1;
while (tc && tc->prev) tc = tc->prev;
if (tc) {
tc = tc->parent;
}
}
return 0;
}