From 65f96eba32b93ced0717c2639007bba59da55fa4 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Sun, 2 Jan 2005 07:47:34 +0000 Subject: r4473: - moved talloc into its own lib/talloc/ area - added gcov flags to Makefile.talloc - expanded talloc testsuite to add a test for realloc with a child ptr - fixed a bug in talloc_realloc() with realloc of a ptr that has child ptrs (This used to be commit 98b5f73c1ba34d7576c5995069b485c1c5ede324) --- source4/lib/ldb/common/talloc.c | 997 ---------------------------------------- 1 file changed, 997 deletions(-) delete mode 100644 source4/lib/ldb/common/talloc.c (limited to 'source4/lib/ldb/common') diff --git a/source4/lib/ldb/common/talloc.c b/source4/lib/ldb/common/talloc.c deleted file mode 100644 index 7f50264801..0000000000 --- a/source4/lib/ldb/common/talloc.c +++ /dev/null @@ -1,997 +0,0 @@ -/* - Samba Unix SMB/CIFS implementation. - - Samba trivial allocation library - new interface - - NOTE: Please read talloc_guide.txt for full documentation - - Copyright (C) Andrew Tridgell 2004 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -/* - inspired by http://swapped.cc/halloc/ -*/ - - -#ifdef _SAMBA_BUILD_ -#include "includes.h" -#else -#include -#include -#include -#include -#include "talloc.h" -#endif - -/* use this to force every realloc to change the pointer, to stress test - code that might not cope */ -#define ALWAYS_REALLOC 0 - - -#define MAX_TALLOC_SIZE 0x10000000 -#define TALLOC_MAGIC 0xe814ec4f -#define TALLOC_MAGIC_FREE 0x7faebef3 -#define TALLOC_MAGIC_REFERENCE ((const char *)1) - -/* by default we abort when given a bad pointer (such as when talloc_free() is called - on a pointer that came from malloc() */ -#ifndef TALLOC_ABORT -#define TALLOC_ABORT(reason) abort() -#endif - -#ifndef discard_const_p -#define discard_const_p(type, ptr) ((type *)(ptr)) -#endif - -/* this null_context is only used if talloc_enable_leak_report() or - talloc_enable_leak_report_full() is called, otherwise it remains - NULL -*/ -static const void *null_context; - - -struct talloc_reference_handle { - struct talloc_reference_handle *next, *prev; - void *ptr; -}; - -typedef int (*talloc_destructor_t)(void *); - -struct talloc_chunk { - struct talloc_chunk *next, *prev; - struct talloc_chunk *parent, *child; - struct talloc_reference_handle *refs; - size_t size; - unsigned magic; - talloc_destructor_t destructor; - const char *name; -}; - -/* panic if we get a bad magic value */ -static struct talloc_chunk *talloc_chunk_from_ptr(const void *ptr) -{ - struct talloc_chunk *tc = discard_const_p(struct talloc_chunk, ptr)-1; - if (tc->magic != TALLOC_MAGIC) { - if (tc->magic == TALLOC_MAGIC_FREE) { - TALLOC_ABORT("Bad talloc magic value - double free"); - } else { - TALLOC_ABORT("Bad talloc magic value - unknown value"); - } - } - - return tc; -} - -/* hook into the front of the list */ -#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) - -/* remove an element from a list - element doesn't have to be in list. */ -#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) - - -/* - return the parent chunk of a pointer -*/ -static struct talloc_chunk *talloc_parent_chunk(const void *ptr) -{ - struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); - while (tc->prev) tc=tc->prev; - return tc->parent; -} - -/* - Allocate a bit of memory as a child of an existing pointer -*/ -void *_talloc(const void *context, size_t size) -{ - struct talloc_chunk *tc; - - if (context == NULL) { - context = null_context; - } - - if (size >= MAX_TALLOC_SIZE) { - return NULL; - } - - tc = malloc(sizeof(*tc)+size); - if (tc == NULL) return NULL; - - tc->size = size; - tc->magic = TALLOC_MAGIC; - tc->destructor = NULL; - tc->child = NULL; - tc->name = NULL; - tc->refs = NULL; - - if (context) { - struct talloc_chunk *parent = talloc_chunk_from_ptr(context); - - tc->parent = parent; - - if (parent->child) { - parent->child->parent = NULL; - } - - _TLIST_ADD(parent->child, tc); - } else { - tc->next = tc->prev = tc->parent = NULL; - } - - return (void *)(tc+1); -} - - -/* - setup a destructor to be called on free of a pointer - the destructor should return 0 on success, or -1 on failure. - if the destructor fails then the free is failed, and the memory can - be continued to be used -*/ -void talloc_set_destructor(const void *ptr, int (*destructor)(void *)) -{ - struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); - tc->destructor = destructor; -} - -/* - increase the reference count on a piece of memory. -*/ -void talloc_increase_ref_count(const void *ptr) -{ - talloc_reference(null_context, ptr); -} - -/* - helper for talloc_reference() -*/ -static int talloc_reference_destructor(void *ptr) -{ - struct talloc_reference_handle *handle = ptr; - struct talloc_chunk *tc1 = talloc_chunk_from_ptr(ptr); - struct talloc_chunk *tc2 = talloc_chunk_from_ptr(handle->ptr); - if (tc1->destructor != (talloc_destructor_t)-1) { - tc1->destructor = NULL; - } - _TLIST_REMOVE(tc2->refs, handle); - talloc_free(handle); - return 0; -} - -/* - make a secondary reference to a pointer, hanging off the given context. - the pointer remains valid until both the original caller and this given - context are freed. - - the major use for this is when two different structures need to reference the - same underlying data, and you want to be able to free the two instances separately, - and in either order -*/ -void *talloc_reference(const void *context, const void *ptr) -{ - struct talloc_chunk *tc; - struct talloc_reference_handle *handle; - if (ptr == NULL) return NULL; - - tc = talloc_chunk_from_ptr(ptr); - handle = talloc_named_const(context, sizeof(*handle), TALLOC_MAGIC_REFERENCE); - - if (handle == NULL) return NULL; - - /* note that we hang the destructor off the handle, not the - main context as that allows the caller to still setup their - own destructor on the context if they want to */ - talloc_set_destructor(handle, talloc_reference_destructor); - handle->ptr = discard_const_p(void, ptr); - _TLIST_ADD(tc->refs, handle); - return handle->ptr; -} - -/* - remove a secondary reference to a pointer. This undo's what - talloc_reference() has done. The context and pointer arguments - must match those given to a talloc_reference() -*/ -static int talloc_unreference(const void *context, const void *ptr) -{ - struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); - struct talloc_reference_handle *h; - - if (context == NULL) { - context = null_context; - } - - for (h=tc->refs;h;h=h->next) { - struct talloc_chunk *p = talloc_parent_chunk(h); - if ((p==NULL && context==NULL) || p+1 == context) break; - } - if (h == NULL) { - return -1; - } - - talloc_set_destructor(h, NULL); - _TLIST_REMOVE(tc->refs, h); - talloc_free(h); - return 0; -} - -/* - remove a specific parent context from a pointer. This is a more - controlled varient of talloc_free() -*/ -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(ptr); - } - - new_p = talloc_parent_chunk(tc_p->refs); - if (new_p) { - new_parent = new_p+1; - } else { - new_parent = NULL; - } - - if (talloc_unreference(new_parent, ptr) != 0) { - return -1; - } - - talloc_steal(new_parent, ptr); - - return 0; -} - -/* - add a name to an existing pointer - va_list version -*/ -static void talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); - -static void 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 (tc->name) { - talloc_set_name_const(tc->name, ".name"); - } -} - -/* - add a name to an existing pointer -*/ -void talloc_set_name(const void *ptr, const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - talloc_set_name_v(ptr, fmt, ap); - va_end(ap); -} - -/* - more efficient way to add a name to a pointer - the name must point to a - true string constant -*/ -void talloc_set_name_const(const void *ptr, const char *name) -{ - struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); - tc->name = name; -} - -/* - create a named talloc pointer. Any talloc pointer can be named, and - talloc_named() operates just like talloc() except that it allows you - to name the pointer. -*/ -void *talloc_named(const void *context, size_t size, const char *fmt, ...) -{ - va_list ap; - void *ptr; - - ptr = _talloc(context, size); - if (ptr == NULL) return NULL; - - va_start(ap, fmt); - talloc_set_name_v(ptr, fmt, ap); - va_end(ap); - - return ptr; -} - -/* - create a named talloc pointer. Any talloc pointer can be named, and - talloc_named() operates just like talloc() except that it allows you - to name the pointer. -*/ -void *talloc_named_const(const void *context, size_t size, const char *name) -{ - void *ptr; - - ptr = _talloc(context, size); - if (ptr == NULL) { - return NULL; - } - - talloc_set_name_const(ptr, name); - - return ptr; -} - -/* - return the name of a talloc ptr, or "UNNAMED" -*/ -const char *talloc_get_name(const void *ptr) -{ - struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); - if (tc->name == TALLOC_MAGIC_REFERENCE) { - return ".reference"; - } - if (tc->name) { - return tc->name; - } - return "UNNAMED"; -} - -/* - this is for compatibility with older versions of talloc -*/ -void *talloc_init(const char *fmt, ...) -{ - va_list ap; - void *ptr; - - ptr = _talloc(NULL, 0); - if (ptr == NULL) return NULL; - - va_start(ap, fmt); - talloc_set_name_v(ptr, fmt, ap); - va_end(ap); - - return ptr; -} - - -/* - free a talloc pointer. This also frees all child pointers of this - pointer recursively - - return 0 if the memory is actually freed, otherwise -1. The memory - will not be freed if the ref_count is > 1 or the destructor (if - any) returns non-zero -*/ -int talloc_free(void *ptr) -{ - struct talloc_chunk *tc; - - if (ptr == NULL) { - return -1; - } - - tc = talloc_chunk_from_ptr(ptr); - - if (tc->refs) { - talloc_reference_destructor(tc->refs); - return -1; - } - - if (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; - } - - while (tc->child) { - /* we need to work out who will own an abandoned child - if it cannot be freed. In priority order, the first - choice is owner of any remaining reference to this - pointer, the second choice is our parent, and the - final choice is the null context. */ - void *child = tc->child+1; - const void *new_parent = null_context; - if (tc->child->refs) { - struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs); - if (p) new_parent = p+1; - } - if (talloc_free(child) == -1) { - if (new_parent == null_context) { - struct talloc_chunk *p = talloc_parent_chunk(ptr); - if (p) new_parent = p+1; - } - talloc_steal(new_parent, child); - } - } - - 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->magic = TALLOC_MAGIC_FREE; - - free(tc); - return 0; -} - - - -/* - A talloc version of realloc. The context argument is only used if - ptr is NULL -*/ -void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name) -{ - struct talloc_chunk *tc; - void *new_ptr; - - /* size zero is equivalent to free() */ - if (size == 0) { - talloc_free(ptr); - return NULL; - } - - if (size >= MAX_TALLOC_SIZE) { - return NULL; - } - - /* realloc(NULL) is equavalent to malloc() */ - if (ptr == NULL) { - return talloc_named_const(context, size, name); - } - - tc = talloc_chunk_from_ptr(ptr); - - /* don't allow realloc on referenced pointers */ - if (tc->refs) { - return NULL; - } - - /* by resetting magic we catch users of the old memory */ - tc->magic = TALLOC_MAGIC_FREE; - -#if ALWAYS_REALLOC - new_ptr = malloc(size + sizeof(*tc)); - if (new_ptr) { - memcpy(new_ptr, tc, tc->size + sizeof(*tc)); - free(tc); - } -#else - new_ptr = realloc(tc, size + sizeof(*tc)); -#endif - if (!new_ptr) { - tc->magic = TALLOC_MAGIC; - return NULL; - } - - tc = new_ptr; - tc->magic = TALLOC_MAGIC; - if (tc->parent) { - tc->parent->child = new_ptr; - } - - if (tc->prev) { - tc->prev->next = tc; - } - if (tc->next) { - tc->next->prev = tc; - } - - tc->size = size; - talloc_set_name_const(tc+1, name); - - return (void *)(tc+1); -} - -/* - move a lump of memory from one talloc context to another return the - ptr on success, or NULL if it could not be transferred -*/ -void *talloc_steal(const void *new_ctx, const void *ptr) -{ - struct talloc_chunk *tc, *new_tc; - - if (!ptr) { - return NULL; - } - - if (new_ctx == NULL) { - new_ctx = null_context; - } - - tc = talloc_chunk_from_ptr(ptr); - - if (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 (tc == 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); -} - -/* - return the total size of a talloc pool (subtree) -*/ -off_t talloc_total_size(const void *ptr) -{ - off_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); - - total = tc->size; - for (c=tc->child;c;c=c->next) { - total += talloc_total_size(c+1); - } - return total; -} - -/* - return the total number of blocks in a talloc pool (subtree) -*/ -off_t talloc_total_blocks(const void *ptr) -{ - off_t total = 0; - struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr); - - total++; - for (c=tc->child;c;c=c->next) { - total += talloc_total_blocks(c+1); - } - return total; -} - -/* - return the number of external references to a pointer -*/ -static int talloc_reference_count(const void *ptr) -{ - struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); - struct talloc_reference_handle *h; - int ret = 0; - - for (h=tc->refs;h;h=h->next) { - ret++; - } - return ret; -} - -/* - report on memory usage by all children of a pointer, giving a full tree view -*/ -static void talloc_report_depth(const void *ptr, FILE *f, int depth) -{ - struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr); - - for (c=tc->child;c;c=c->next) { - if (c->name == TALLOC_MAGIC_REFERENCE) { - struct talloc_reference_handle *handle = (void *)(c+1); - const char *name2 = talloc_get_name(handle->ptr); - fprintf(f, "%*sreference to: %s\n", depth*4, "", name2); - } else { - const char *name = talloc_get_name(c+1); - fprintf(f, "%*s%-30s contains %6lu bytes in %3lu blocks (ref %d)\n", - depth*4, "", - name, - (unsigned long)talloc_total_size(c+1), - (unsigned long)talloc_total_blocks(c+1), - talloc_reference_count(c+1)); - talloc_report_depth(c+1, f, depth+1); - } - } - -} - -/* - report on memory usage by all children of a pointer, giving a full tree view -*/ -void talloc_report_full(const void *ptr, FILE *f) -{ - if (ptr == NULL) { - ptr = null_context; - } - if (ptr == NULL) return; - - fprintf(f,"full talloc report on '%s' (total %lu bytes in %lu blocks)\n", - talloc_get_name(ptr), - (unsigned long)talloc_total_size(ptr), - (unsigned long)talloc_total_blocks(ptr)); - - talloc_report_depth(ptr, f, 1); -} - -/* - report on memory usage by all children of a pointer -*/ -void talloc_report(const void *ptr, FILE *f) -{ - struct talloc_chunk *c, *tc; - - if (ptr == NULL) { - ptr = null_context; - } - if (ptr == NULL) return; - - fprintf(f,"talloc report on '%s' (total %lu bytes in %lu blocks)\n", - talloc_get_name(ptr), - (unsigned long)talloc_total_size(ptr), - (unsigned long)talloc_total_blocks(ptr)); - - tc = talloc_chunk_from_ptr(ptr); - - for (c=tc->child;c;c=c->next) { - fprintf(f, "\t%-30s contains %6lu bytes in %3lu blocks\n", - talloc_get_name(c+1), - (unsigned long)talloc_total_size(c+1), - (unsigned long)talloc_total_blocks(c+1)); - } - -} - -/* - report on any memory hanging off the null context -*/ -static void talloc_report_null(void) -{ - if (talloc_total_size(null_context) != 0) { - talloc_report(null_context, stderr); - } -} - -/* - report on any memory hanging off the null context -*/ -static void talloc_report_null_full(void) -{ - if (talloc_total_size(null_context) != 0) { - talloc_report_full(null_context, stderr); - } -} - -/* - enable leak reporting on exit -*/ -void talloc_enable_leak_report(void) -{ - null_context = talloc_named_const(NULL, 0, "null_context"); - atexit(talloc_report_null); -} - -/* - enable full leak reporting on exit -*/ -void talloc_enable_leak_report_full(void) -{ - null_context = talloc_named_const(NULL, 0, "null_context"); - atexit(talloc_report_null_full); -} - -/* - talloc and zero memory. -*/ -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; -} - - -/* - memdup with a talloc. -*/ -void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name) -{ - void *newp = talloc_named_const(t, size, name); - - if (newp) { - memcpy(newp, p, size); - } - - return newp; -} - -/* - strdup with a talloc -*/ -char *talloc_strdup(const void *t, const char *p) -{ - char *ret; - if (!p) { - return NULL; - } - ret = talloc_memdup(t, p, strlen(p) + 1); - if (ret) { - talloc_set_name_const(ret, ret); - } - return ret; -} - -/* - strndup with a talloc -*/ -char *talloc_strndup(const void *t, const char *p, size_t n) -{ - size_t len; - char *ret; - - for (len=0; p[len] && len= MAX_TALLOC_SIZE/el_size) { - return NULL; - } - return talloc_named_const(ctx, el_size * count, name); -} - -/* - alloc an zero array, checking for integer overflow in the array size -*/ -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); -} - - -/* - realloc an array, checking for integer overflow in the array size -*/ -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; - } - ptr = talloc_realloc(ctx, ptr, el_size * count); - if (ptr) { - talloc_set_name_const(ptr, name); - } - return ptr; -} - -/* - a function version of talloc_realloc(), so it can be passed as a function pointer - to libraries that want a realloc function (a realloc function encapsulates - all the basic capabilities of an allocation library, which is why this is useful) -*/ -void *talloc_realloc_fn(const void *context, void *ptr, size_t size) -{ - return _talloc_realloc(context, ptr, size, NULL); -} -- cgit