diff options
-rw-r--r-- | Makefile | 6 | ||||
-rw-r--r-- | ta/README | 39 | ||||
-rw-r--r-- | ta/ta.c | 439 | ||||
-rw-r--r-- | ta/ta.h | 146 | ||||
-rw-r--r-- | ta/ta_talloc.c | 75 | ||||
-rw-r--r-- | ta/ta_talloc.h | 74 | ||||
-rw-r--r-- | ta/ta_utils.c | 320 | ||||
-rw-r--r-- | talloc.c | 1751 | ||||
-rw-r--r-- | talloc.h | 176 |
9 files changed, 1098 insertions, 1928 deletions
@@ -137,8 +137,7 @@ endif SOURCES-$(DLOPEN) += video/filter/vf_dlopen.c -SOURCES = talloc.c \ - audio/audio.c \ +SOURCES = audio/audio.c \ audio/chmap.c \ audio/chmap_sel.c \ audio/fmt-conversion.c \ @@ -241,6 +240,9 @@ SOURCES = talloc.c \ sub/sd_srt.c \ sub/spudec.c \ sub/sub.c \ + ta/ta.c \ + ta/ta_utils.c \ + ta/ta_talloc.c \ video/csputils.c \ video/fmt-conversion.c \ video/image_writer.c \ diff --git a/ta/README b/ta/README new file mode 100644 index 0000000000..cd98c1a5ae --- /dev/null +++ b/ta/README @@ -0,0 +1,39 @@ +TA ("Tree Allocator") is a wrapper around malloc() and related functions, +adding features like automatically free sub-trees of memory allocations if +a parent allocation is freed. + +Generally, the idea is that every TA allocation can have a parent (indicated +by the ta_parent argument in allocation function calls). If a parent is freed, +its child allocations are automatically freed as well. It's also allowed the +free a child before the parent, or to move a child to another parent with +ta_set_parent(). + +It also provides a bunch of convenience macros and debugging facilities. + +The TA functions are documented in the implementation files (ta.c, ta_utils.c). + +TA is intended to be useable as library independent from mpv. It doesn't +depend on anything mpv specific. + +Note: +----- + +mpv doesn't use the TA API yet for two reasons: first, the TA API is not +necessarily finalized yet. Second, it should be easily possible to revert +the commit adding TA, and changing all the code would not allow this. + +Especially the naming schema for some TA functions is still somewhat +undecided. (The talloc naming is a bit verbose at times.) + +For now, mpv goes through a talloc wrapper, which maps the talloc API to TA. +New code should still use talloc as well. At one point, all talloc calls +will be replaced with TA calls, and the talloc wrapper will be removed. + +Documentation for the talloc API is here: + + http://git.samba.org/?p=samba.git;a=blob;f=lib/talloc/talloc.h;hb=HEAD + +There are some minor differences with mpv's talloc bridge. mpv calls abort() +on allocation failures, and the talloc_set_destructor() signature is slightly +different. libtalloc also has a weird 256MB limit per allocation. The talloc +wrapper supports only a strict subset of libtalloc functionality used by mpv. diff --git a/ta/ta.c b/ta/ta.c new file mode 100644 index 0000000000..568f6d5f1b --- /dev/null +++ b/ta/ta.c @@ -0,0 +1,439 @@ +/* Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <assert.h> + +#define TA_NO_WRAPPERS +#include "ta.h" + +// Note: the actual minimum alignment is dictated by malloc(). It doesn't +// make sense to set this value higher than malloc's alignment. +#define MIN_ALIGN 16 + +#ifndef NDEBUG +#define TA_MEMORY_DEBUGGING +#endif + +struct ta_header { + size_t size; // size of the user allocation + struct ta_header *prev; // ring list containing siblings + struct ta_header *next; + struct ta_ext_header *ext; +#ifdef TA_MEMORY_DEBUGGING + unsigned int canary; + struct ta_header *leak_next; + struct ta_header *leak_prev; + const char *name; +#endif +}; + +#define CANARY 0xD3ADB3EF + +union aligned_header { + struct ta_header ta; + // Make sure to satisfy typical alignment requirements + void *align_ptr; + int align_int; + double align_d; + long long align_ll; + char align_min[(sizeof(struct ta_header) + MIN_ALIGN - 1) & ~(MIN_ALIGN - 1)]; +}; + +#define PTR_TO_HEADER(ptr) (&((union aligned_header *)(ptr) - 1)->ta) +#define PTR_FROM_HEADER(h) ((void *)((union aligned_header *)(h) + 1)) + +#define MAX_ALLOC (((size_t)-1) - sizeof(union aligned_header)) + +// Needed for non-leaf allocations, or extended features such as destructors. +struct ta_ext_header { + struct ta_header *header; // points back to normal header + struct ta_header children; // list of children, with this as sentinel + void (*destructor)(void *); +}; + +// ta_ext_header.children.size is set to this +#define CHILDREN_SENTINEL ((size_t)-1) + +static void ta_dbg_add(struct ta_header *h); +static void ta_dbg_check_header(struct ta_header *h); +static void ta_dbg_remove(struct ta_header *h); + +static struct ta_header *get_header(void *ptr) +{ + struct ta_header *h = ptr ? PTR_TO_HEADER(ptr) : NULL; + ta_dbg_check_header(h); + return h; +} + +static struct ta_ext_header *get_or_alloc_ext_header(void *ptr) +{ + struct ta_header *h = get_header(ptr); + if (!h) + return NULL; + if (!h->ext) { + h->ext = malloc(sizeof(struct ta_ext_header)); + if (!h->ext) + return NULL; + *h->ext = (struct ta_ext_header) { + .header = h, + .children = { + .next = &h->ext->children, + .prev = &h->ext->children, + // Needed by ta_find_parent(): + .size = CHILDREN_SENTINEL, + .ext = h->ext, + }, + }; + } + return h->ext; +} + +/* Set the parent allocation of ptr. If parent==NULL, remove the parent. + * Setting parent==NULL (with ptr!=NULL) always succeeds, and unsets the + * parent of ptr. Operations ptr==NULL always succeed and do nothing. + * Returns true on success, false on OOM. + */ +bool ta_set_parent(void *ptr, void *ta_parent) +{ + struct ta_header *ch = get_header(ptr); + if (!ch) + return true; + struct ta_ext_header *parent_eh = get_or_alloc_ext_header(ta_parent); + if (ta_parent && !parent_eh) // do nothing on OOM + return false; + // Unlink from previous parent + if (ch->next) { + ch->next->prev = ch->prev; + ch->prev->next = ch->next; + ch->next = ch->prev = NULL; + } + // Link to new parent - insert at end of list (possibly orders destructors) + if (parent_eh) { + struct ta_header *children = &parent_eh->children; + ch->next = children; + ch->prev = children->prev; + children->prev->next = ch; + children->prev = ch; + } + return true; +} + +/* Allocate size bytes of memory. If ta_parent is not NULL, this is used as + * parent allocation (if ta_parent is freed, this allocation is automatically + * freed as well). size==0 allocates a block of size 0 (i.e. returns non-NULL). + * Returns NULL on OOM. + */ +void *ta_alloc_size(void *ta_parent, size_t size) +{ + if (size >= MAX_ALLOC) + return NULL; + struct ta_header *h = malloc(sizeof(union aligned_header) + size); + if (!h) + return NULL; + *h = (struct ta_header) {.size = size}; + ta_dbg_add(h); + void *ptr = PTR_FROM_HEADER(h); + if (!ta_set_parent(ptr, ta_parent)) { + ta_free(ptr); + return NULL; + } + return ptr; +} + +/* Exactly the same as ta_alloc_size(), but the returned memory block is + * initialized to 0. + */ +void *ta_zalloc_size(void *ta_parent, size_t size) +{ + if (size >= MAX_ALLOC) + return NULL; + struct ta_header *h = calloc(1, sizeof(union aligned_header) + size); + if (!h) + return NULL; + *h = (struct ta_header) {.size = size}; + ta_dbg_add(h); + void *ptr = PTR_FROM_HEADER(h); + if (!ta_set_parent(ptr, ta_parent)) { + ta_free(ptr); + return NULL; + } + return ptr; +} + +/* Reallocate the allocation given by ptr and return a new pointer. Much like + * realloc(), the returned pointer can be different, and on OOM, NULL is + * returned. + * + * size==0 is equivalent to ta_free(ptr). + * ptr==NULL is equivalent to ta_alloc_size(ta_parent, size). + * + * ta_parent is used only in the ptr==NULL case. + * + * Returns NULL if the operation failed. + * NULL is also returned if size==0. + */ +void *ta_realloc_size(void *ta_parent, void *ptr, size_t size) +{ + if (size >= MAX_ALLOC) + return NULL; + if (!size) { + ta_free(ptr); + return NULL; + } + if (!ptr) + return ta_alloc_size(ta_parent, size); + struct ta_header *h = get_header(ptr); + struct ta_header *old_h = h; + if (h->size == size) + return ptr; + ta_dbg_remove(h); + h = realloc(h, sizeof(union aligned_header) + size); + ta_dbg_add(h ? h : old_h); + if (!h) + return NULL; + h->size = size; + if (h != old_h) { + if (h->next) { + // Relink siblings + h->next->prev = h; + h->prev->next = h; + } + if (h->ext) { + // Relink children + h->ext->header = h; + h->ext->children.next->prev = &h->ext->children; + h->ext->children.prev->next = &h->ext->children; + } + } + return PTR_FROM_HEADER(h); +} + +/* Return the allocated size of ptr. This returns the size parameter of the + * most recent ta_alloc.../ta_realloc... call. + * If ptr==NULL, return 0. + */ +size_t ta_get_size(void *ptr) +{ + struct ta_header *h = get_header(ptr); + return h ? h->size : 0; +} + +/* Free all allocations that (recursively) have ptr as parent allocation, but + * do not free ptr itself. + */ +void ta_free_children(void *ptr) +{ + struct ta_header *h = get_header(ptr); + struct ta_ext_header *eh = h ? h->ext : NULL; + if (!eh) + return; + while (eh->children.next != &eh->children) + ta_free(PTR_FROM_HEADER(eh->children.next)); +} + +/* Free the given allocation, and all of its direct and indirect children. + */ +void ta_free(void *ptr) +{ + struct ta_header *h = get_header(ptr); + if (!h) + return; + if (h->ext && h->ext->destructor) + h->ext->destructor(ptr); + ta_free_children(ptr); + if (h->next) { + // Unlink from sibling list + h->next->prev = h->prev; + h->prev->next = h->next; + } + ta_dbg_remove(h); + free(h->ext); + free(h); +} + +/* Set a destructor that is to be called when the given allocation is freed. + * (Whether the allocation is directly freed with ta_free() or indirectly by + * freeing its parent does not matter.) There is only one destructor. If an + * destructor was already set, it's overwritten. + * + * The destructor will be called with ptr as argument. The destructor can do + * almost anything, but it must not attempt to free or realloc ptr. The + * destructor is run before the allocation's children are freed (also, before + * their destructors are run). + * + * Returns false if ptr==NULL, or on OOM. + */ +bool ta_set_destructor(void *ptr, void (*destructor)(void *)) +{ + struct ta_ext_header *eh = get_or_alloc_ext_header(ptr); + if (!eh) + return false; + eh->destructor = destructor; + return true; +} + +/* Return the ptr's parent allocation, or NULL if there isn't any. + * + * Warning: this has O(N) runtime complexity with N sibling allocations! + */ +void *ta_find_parent(void *ptr) +{ + struct ta_header *h = get_header(ptr); + if (!h || !h->next) + return NULL; + for (struct ta_header *cur = h->next; cur != h; cur = cur->next) { + if (cur->size == CHILDREN_SENTINEL) + return PTR_FROM_HEADER(cur->ext->header); + } + return NULL; +} + +#ifdef TA_MEMORY_DEBUGGING + +#include <pthread.h> + +static pthread_mutex_t ta_dbg_mutex = PTHREAD_MUTEX_INITIALIZER; +static bool enable_leak_check; // pretty much constant +static struct ta_header leak_node; +static char allocation_is_string; + +static void ta_dbg_add(struct ta_header *h) +{ + h->canary = CANARY; + if (enable_leak_check) { + pthread_mutex_lock(&ta_dbg_mutex); + h->leak_next = &leak_node; + h->leak_prev = leak_node.leak_prev; + leak_node.leak_prev->leak_next = h; + leak_node.leak_prev = h; + pthread_mutex_unlock(&ta_dbg_mutex); + } +} + +static void ta_dbg_check_header(struct ta_header *h) +{ + if (h) + assert(h->canary == CANARY); +} + +static void ta_dbg_remove(struct ta_header *h) +{ + ta_dbg_check_header(h); + if (h->leak_next) { // assume checking for !=NULL invariant ok without lock + pthread_mutex_lock(&ta_dbg_mutex); + h->leak_next->leak_prev = h->leak_prev; + h->leak_prev->leak_next = h->leak_next; + pthread_mutex_unlock(&ta_dbg_mutex); + h->leak_next = h->leak_prev = NULL; + } + h->canary = 0; +} + +static size_t get_children_size(struct ta_header *h) +{ + size_t size = 0; + if (h->ext) { + struct ta_header *s; + for (s = h->ext->children.next; s != &h->ext->children; s = s->next) + size += s->size + get_children_size(s); + } + return size; +} + +static void print_leak_report(void) +{ + pthread_mutex_lock(&ta_dbg_mutex); + if (leak_node.leak_next && leak_node.leak_next != &leak_node) { + size_t size = 0; + size_t num_blocks = 0; + fprintf(stderr, "Blocks not freed:\n"); + fprintf(stderr, " %-20s %10s %10s %s\n", + "Ptr", "Bytes", "C. Bytes", "Name"); + while (leak_node.leak_next != &leak_node) { + struct ta_header *cur = leak_node.leak_next; + // Don't list those with parent; logically, only parents are listed + if (!cur->next) { + size_t c_size = get_children_size(cur); + char name[30] = {0}; + if (cur->name) + snprintf(name, sizeof(name), "%s", cur->name); + if (cur->name == &allocation_is_string) { + snprintf(name, sizeof(name), "'%.*s'", + (int)cur->size, (char *)PTR_FROM_HEADER(cur)); + } + for (int n = 0; n < sizeof(name); n++) { + if (name[n] && name[n] < 0x20) + name[n] = '.'; + } + fprintf(stderr, " %-20p %10zu %10zu %s\n", + cur, cur->size, c_size, name); + } + size += cur->size; + num_blocks += 1; + // Unlink, and don't confuse valgrind by leaving live pointers. + cur->leak_next->leak_prev = cur->leak_prev; + cur->leak_prev->leak_next = cur->leak_next; + cur->leak_next = cur->leak_prev = NULL; + } + fprintf(stderr, "%zu bytes in %zu blocks.\n", size, num_blocks); + } + pthread_mutex_unlock(&ta_dbg_mutex); +} + +void ta_enable_leak_report(void) +{ + pthread_mutex_lock(&ta_dbg_mutex); + enable_leak_check = true; + if (!leak_node.leak_prev && !leak_node.leak_next) { + leak_node.leak_prev = &leak_node; + leak_node.leak_next = &leak_node; + atexit(print_leak_report); + } + pthread_mutex_unlock(&ta_dbg_mutex); +} + +/* Set a (static) string that will be printed if the memory allocation in ptr + * shows up on the leak report. The string must stay valid until ptr is freed. + * Calling it on ptr==NULL does nothing. + * Typically used to set location info. + * Always returns ptr (useful for chaining function calls). + */ +void *ta_dbg_set_loc(void *ptr, const char *loc) +{ + struct ta_header *h = get_header(ptr); + if (h) + h->name = loc; + return ptr; +} + +/* Mark the allocation as string. The leak report will print it literally. + */ +void *ta_dbg_mark_as_string(void *ptr) +{ + // Specially handled by leak report code. + return ta_dbg_set_loc(ptr, &allocation_is_string); +} + +#else + +static void ta_dbg_add(struct ta_header *h){} +static void ta_dbg_check_header(struct ta_header *h){} +static void ta_dbg_remove(struct ta_header *h){} + +void ta_enable_leak_report(void){} +void *ta_dbg_set_name(void *ptr, const char *name){return ptr;} + +#endif diff --git a/ta/ta.h b/ta/ta.h new file mode 100644 index 0000000000..1a9738a9f0 --- /dev/null +++ b/ta/ta.h @@ -0,0 +1,146 @@ +/* Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef TA_H_ +#define TA_H_ + +#include <stddef.h> +#include <stdbool.h> +#include <stdarg.h> + +#ifdef __GNUC__ +#define TA_PRF(a1, a2) __attribute__ ((format(printf, a1, a2))) +#define TA_TYPEOF(t) __typeof__(t) +#else +#define TA_PRF(a1, a2) +#define TA_TYPEOF(t) void * +#endif + +#define TA_STRINGIFY_(x) # x +#define TA_STRINGIFY(x) TA_STRINGIFY_(x) + +#ifdef NDEBUG +#define TA_LOC "" +#else +#define TA_LOC __FILE__ ":" TA_STRINGIFY(__LINE__) +#endif + +// Core functions +void *ta_alloc_size(void *ta_parent, size_t size); +void *ta_zalloc_size(void *ta_parent, size_t size); +void *ta_realloc_size(void *ta_parent, void *ptr, size_t size); +size_t ta_get_size(void *ptr); +void ta_free(void *ptr); +void ta_free_children(void *ptr); +bool ta_set_destructor(void *ptr, void (*destructor)(void *)); +bool ta_set_parent(void *ptr, void *ta_parent); +void *ta_find_parent(void *ptr); + +// Utility functions +size_t ta_calc_array_size(size_t element_size, size_t count); +void *ta_new_context(void *ta_parent); +void *ta_steal_(void *ta_parent, void *ptr); +void *ta_memdup(void *ta_parent, void *ptr, size_t size); +char *ta_strdup(void *ta_parent, const char *str); +bool ta_strdup_append(char **str, const char *a); +bool ta_strdup_append_buffer(char **str, const char *a); +char *ta_strndup(void *ta_parent, const char *str, size_t n); +bool ta_strndup_append(char **str, const char *a, size_t n); +bool ta_strndup_append_buffer(char **str, const char *a, size_t n); +char *ta_asprintf(void *ta_parent, const char *fmt, ...) TA_PRF(2, 3); +char *ta_vasprintf(void *ta_parent, const char *fmt, va_list ap) TA_PRF(2, 0); +bool ta_asprintf_append(char **str, const char *fmt, ...) TA_PRF(2, 3); +bool ta_vasprintf_append(char **str, const char *fmt, va_list ap) TA_PRF(2, 0); +bool ta_asprintf_append_buffer(char **str, const char *fmt, ...) TA_PRF(2, 3); +bool ta_vasprintf_append_buffer(char **str, const char *fmt, va_list ap) TA_PRF(2, 0); + +#define ta_new(ta_parent, type) (type *)ta_alloc_size(ta_parent, sizeof(type)) +#define ta_znew(ta_parent, type) (type *)ta_zalloc_size(ta_parent, sizeof(type)) + +#define ta_new_array(ta_parent, type, count) \ + (type *)ta_alloc_size(ta_parent, ta_calc_array_size(sizeof(type), count)) + +#define ta_znew_array(ta_parent, type, count) \ + (type *)ta_zalloc_size(ta_parent, ta_calc_array_size(sizeof(type), count)) + +#define ta_new_array_size(ta_parent, element_size, count) \ + ta_alloc_size(ta_parent, ta_calc_array_size(element_size, count)) + +#define ta_realloc(ta_parent, ptr, type, count) \ + (type *)ta_realloc_size(ta_parent, ptr, ta_calc_array_size(sizeof(type), count)) + +#define ta_new_ptrtype(ta_parent, ptr) \ + (TA_TYPEOF(ptr))ta_alloc_size(ta_parent, sizeof(*ptr)) + +#define ta_new_array_ptrtype(ta_parent, ptr, count) \ + (TA_TYPEOF(ptr))ta_new_array_size(ta_parent, sizeof(*(ptr)), count) + +#define ta_steal(ta_parent, ptr) (TA_TYPEOF(ptr))ta_steal_(ta_parent, ptr) + +// Ugly macros that crash on OOM. +// All of these mirror real functions (with a 'x' added after the 'ta_' +// prefix), and the only difference is that they will call abort() on allocation +// failures (such as out of memory conditions), instead of returning an error +// code. +#define ta_xalloc_size(...) ta_oom_p(ta_alloc_size(__VA_ARGS__)) +#define ta_xzalloc_size(...) ta_oom_p(ta_zalloc_size(__VA_ARGS__)) +#define ta_xset_destructor(...) ta_oom_b(ta_set_destructor(__VA_ARGS__)) +#define ta_xset_parent(...) ta_oom_b(ta_set_parent(__VA_ARGS__)) +#define ta_xnew_context(...) ta_oom_p(ta_new_context(__VA_ARGS__)) +#define ta_xstrdup_append(...) ta_oom_b(ta_strdup_append(__VA_ARGS__)) +#define ta_xstrdup_append_buffer(...) ta_oom_b(ta_strdup_append_buffer(__VA_ARGS__)) +#define ta_xstrndup_append(...) ta_oom_b(ta_strndup_append(__VA_ARGS__)) +#define ta_xstrndup_append_buffer(...) ta_oom_b(ta_strndup_append_buffer(__VA_ARGS__)) +#define ta_xasprintf(...) ta_oom_s(ta_asprintf(__VA_ARGS__)) +#define ta_xvasprintf(...) ta_oom_s(ta_vasprintf(__VA_ARGS__)) +#define ta_xasprintf_append(...) ta_oom_b(ta_asprintf_append(__VA_ARGS__)) +#define ta_xvasprintf_append(...) ta_oom_b(ta_vasprintf_append(__VA_ARGS__)) +#define ta_xasprintf_append_buffer(...) ta_oom_b(ta_asprintf_append_buffer(__VA_ARGS__)) +#define ta_xvasprintf_append_buffer(...) ta_oom_b(ta_vasprintf_append_buffer(__VA_ARGS__)) +#define ta_xnew(...) ta_oom_g(ta_new(__VA_ARGS__)) +#define ta_xznew(...) ta_oom_g(ta_znew(__VA_ARGS__)) +#define ta_xnew_array(...) ta_oom_g(ta_new_array(__VA_ARGS__)) +#define ta_xznew_array(...) ta_oom_g(ta_znew_array(__VA_ARGS__)) +#define ta_xnew_array_size(...) ta_oom_p(ta_new_array_size(__VA_ARGS__)) +#define ta_xnew_ptrtype(...) ta_oom_g(ta_new_ptrtype(__VA_ARGS__)) +#define ta_xnew_array_ptrtype(...) ta_oom_g(ta_new_array_ptrtype(__VA_ARGS__)) + +#define ta_xsteal(ta_parent, ptr) (TA_TYPEOF(ptr))ta_xsteal_(ta_parent, ptr) +#define ta_xrealloc(ta_parent, ptr, type, count) \ + (type *)ta_xrealloc_size(ta_parent, ptr, ta_calc_array_size(sizeof(type), count)) + +// Can't be macros, because the OOM logic is slightly less trivial. +char *ta_xstrdup(void *ta_parent, const char *str); +char *ta_xstrndup(void *ta_parent, const char *str, size_t n); +void *ta_xsteal_(void *ta_parent, void *ptr); +void *ta_xmemdup(void *ta_parent, void *ptr, size_t size); +void *ta_xrealloc_size(void *ta_parent, void *ptr, size_t size); + +#ifndef TA_NO_WRAPPERS +#define ta_alloc_size(...) ta_dbg_set_loc(ta_alloc_size(__VA_ARGS__), TA_LOC) +#define ta_zalloc_size(...) ta_dbg_set_loc(ta_zalloc_size(__VA_ARGS__), TA_LOC) +#define ta_realloc_size(...) ta_dbg_set_loc(ta_realloc_size(__VA_ARGS__), TA_LOC) +#define ta_memdup(...) ta_dbg_set_loc(ta_memdup(__VA_ARGS__), TA_LOC) +#endif + +void ta_oom_b(bool b); +char *ta_oom_s(char *s); +void *ta_oom_p(void *p); +// Generic pointer +#define ta_oom_g(ptr) (TA_TYPEOF(ptr))ta_oom_p(ptr) + +void ta_enable_leak_report(void); +void *ta_dbg_set_loc(void *ptr, const char *name); +void *ta_dbg_mark_as_string(void *ptr); + +#endif diff --git a/ta/ta_talloc.c b/ta/ta_talloc.c new file mode 100644 index 0000000000..9c52bbab08 --- /dev/null +++ b/ta/ta_talloc.c @@ -0,0 +1,75 @@ +/* Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <assert.h> + +#include "ta_talloc.h" + +char *ta_talloc_strdup_append(char *s, const char *a) +{ + ta_xstrdup_append(&s, a); + return s; +} + +char *ta_talloc_strdup_append_buffer(char *s, const char *a) +{ + ta_xstrdup_append_buffer(&s, a); + return s; +} + +char *ta_talloc_strndup_append(char *s, const char *a, size_t n) +{ + ta_xstrndup_append(&s, a, n); + return s; +} + +char *ta_talloc_strndup_append_buffer(char *s, const char *a, size_t n) +{ + ta_xstrndup_append_buffer(&s, a, n); + return s; +} + +char *ta_talloc_vasprintf_append(char *s, const char *fmt, va_list ap) +{ + ta_xvasprintf_append(&s, fmt, ap); + return s; +} + +char *ta_talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap) +{ + ta_xvasprintf_append_buffer(&s, fmt, ap); + return s; +} + +char *ta_talloc_asprintf_append(char *s, const char *fmt, ...) +{ + char *res; + va_list ap; + va_start(ap, fmt); + res = talloc_vasprintf_append(s, fmt, ap); + va_end(ap); + return res; +} + +char *ta_talloc_asprintf_append_buffer(char *s, const char *fmt, ...) +{ + char *res; + va_list ap; + va_start(ap, fmt); + res = talloc_vasprintf_append_buffer(s, fmt, ap); + va_end(ap); + return res; +} diff --git a/ta/ta_talloc.h b/ta/ta_talloc.h new file mode 100644 index 0000000000..cd41e63818 --- /dev/null +++ b/ta/ta_talloc.h @@ -0,0 +1,74 @@ +/* Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef TA_TALLOC_H_ +#define TA_TALLOC_H_ + +#include "ta.h" + +// Note: all talloc wrappers are wired to the "x" functions, which abort on OOM. +// libtalloc doesn't do that, but the mplayer2 internal copy of it did. + +#define talloc ta_xnew +#define talloc_zero ta_xznew + +#define talloc_array ta_xnew_array +#define talloc_zero_array ta_xznew_array + +#define talloc_array_size ta_xnew_array_size +#define talloc_realloc ta_xrealloc +#define talloc_ptrtype ta_xnew_ptrtype +#define talloc_array_ptrtype ta_xnew_array_ptrtype + +#define talloc_steal ta_xsteal +#define talloc_realloc_size ta_xrealloc_size +#define talloc_new ta_xnew_context +#define talloc_set_destructor ta_xset_destructor +#define talloc_parent ta_find_parent +#define talloc_enable_leak_report ta_enable_leak_report +#define talloc_size ta_xalloc_size +#define talloc_zero_size ta_xzalloc_size +#define talloc_get_size ta_get_size +#define talloc_free_children ta_free_children +#define talloc_free ta_free +#define talloc_memdup ta_xmemdup +#define talloc_strdup ta_xstrdup +#define talloc_strndup ta_xstrndup +#define talloc_asprintf ta_xasprintf +#define talloc_vasprintf ta_xvasprintf + +// Don't define linker-level symbols, as that would clash with real libtalloc. +#define talloc_strdup_append ta_talloc_strdup_append +#define talloc_strdup_append_buffer ta_talloc_strdup_append_buffer +#define talloc_strndup_append ta_talloc_strndup_append +#define talloc_strndup_append_buffer ta_talloc_strndup_append_buffer +#define talloc_vasprintf_append ta_talloc_vasprintf_append +#define talloc_vasprintf_append_buffer ta_talloc_vasprintf_append_buffer +#define talloc_asprintf_append ta_talloc_asprintf_append +#define talloc_asprintf_append_buffer ta_talloc_asprintf_append_buffer + +char *ta_talloc_strdup(void *t, const char *p); +char *ta_talloc_strdup_append(char *s, const char *a); +char *ta_talloc_strdup_append_buffer(char *s, const char *a); + +char *ta_talloc_strndup(void *t, const char *p, size_t n); +char *ta_talloc_strndup_append(char *s, const char *a, size_t n); +char *ta_talloc_strndup_append_buffer(char *s, const char *a, size_t n); + +char *ta_talloc_vasprintf_append(char *s, const char *fmt, va_list ap) TA_PRF(2, 0); +char *ta_talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap) TA_PRF(2, 0); + +char *ta_talloc_asprintf_append(char *s, const char *fmt, ...) TA_PRF(2, 3); +char *ta_talloc_asprintf_append_buffer(char *s, const char *fmt, ...) TA_PRF(2, 3); + +#endif diff --git a/ta/ta_utils.c b/ta/ta_utils.c new file mode 100644 index 0000000000..19ab77a733 --- /dev/null +++ b/ta/ta_utils.c @@ -0,0 +1,320 @@ +/* Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <assert.h> + +#define TA_NO_WRAPPERS +#include "ta.h" + +// Return element_size * count. If it overflows, return (size_t)-1 (SIZE_MAX). +// I.e. this returns the equivalent of: MIN(element_size * count, SIZE_MAX). +// The idea is that every real memory allocator will reject (size_t)-1, thus +// this is a valid way to handle too large array allocation requests. +size_t ta_calc_array_size(size_t element_size, size_t count) +{ + if (count > (((size_t)-1) / element_size)) + return (size_t)-1; + return element_size * count; +} + +static void dummy_dtor(void *p){} + +/* Create an empty (size 0) TA allocation, which is prepared in a way such that + * using it as parent with ta_set_parent() always succeed. Calling + * ta_set_destructor() on it will always succeed as well. + */ +void *ta_new_context(void *ta_parent) +{ + void *new = ta_alloc_size(ta_parent, 0); + // Force it to allocate an extended header. + if (!ta_set_destructor(new, dummy_dtor)) { + ta_free(new); + new = NULL; + } + return new; +} + +/* Set parent of ptr to ta_parent, return the ptr. + * Note that ta_parent==NULL will simply unset the current parent of ptr. + * If the operation fails (on OOM), return NULL. (That's pretty bad behavior, + * but the only way to signal failure.) + */ +void *ta_steal_(void *ta_parent, void *ptr) +{ + if (!ta_set_parent(ptr, ta_parent)) + return NULL; + return ptr; +} + +/* Duplicate the memory at ptr with the given size. + */ +void *ta_memdup(void *ta_parent, void *ptr, size_t size) +{ + if (!ptr) { + assert(!size); + return NULL; + } + void *res = ta_alloc_size(ta_parent, size); + if (!res) + return NULL; + memcpy(res, ptr, size); + return res; +} + +// *str = *str[0..at] + append[0..append_len] +// (append_len being a maximum length; shorter if embedded \0s are encountered) +static bool strndup_append_at(char **str, size_t at, const char *append, + size_t append_len) +{ + assert(ta_get_size(*str) >= at); + + if (!*str && !append) + return true; // stays NULL, but not an OOM condition + + size_t real_len = append ? strnlen(append, append_len) : 0; + if (append_len > real_len) + append_len = real_len; + + if (ta_get_size(*str) < at + append_len + 1) { + char *t = ta_realloc_size(NULL, *str, at + append_len + 1); + if (!t) + return false; + *str = t; + } + + memcpy(*str + at, append, append_len); + (*str)[at + append_len] = '\0'; + + ta_dbg_mark_as_string(*str); + + return true; +} + +/* Return a copy of str. + * Returns NULL on OOM. + */ +char *ta_strdup(void *ta_parent, const char *str) +{ + return ta_strndup(ta_parent, str, str ? strlen(str) : 0); +} + +/* Return a copy of str. If the string is longer than n, copy only n characters + * (the returned allocation will be n+1 bytes and contain a terminating '\0'). + * The returned string will have the length MIN(strlen(str), n) + * If str==NULL, return NULL. Returns NULL on OOM as well. + */ +char *ta_strndup(void *ta_parent, const char *str, size_t n) +{ + if (!str) + return NULL; + char *new = NULL; + strndup_append_at(&new, 0, str, n); + if (!ta_set_parent(new, ta_parent)) { + ta_free(new); + new = NULL; + } + return new; +} + +/* Append a to *str. If *str is NULL, the string is newly allocated, otherwise + * ta_realloc() is used on *str as needed. + * Return success or failure (it can fail due to OOM only). + */ +bool ta_strdup_append(char **str, const char *a) +{ + return strndup_append_at(str, *str ? strlen(*str) : 0, a, (size_t)-1); +} + +/* Like ta_strdup_append(), but use ta_get_size(*str)-1 instead of strlen(*str). + * (See also: ta_asprintf_append_buffer()) + */ +bool ta_strdup_append_buffer(char **str, const char *a) +{ + size_t size = ta_get_size(*str); + if (size > 0) + size -= 1; + return strndup_append_at(str, size, a, (size_t)-1); +} + +/* Like ta_strdup_append(), but limit the length of a with n. + * (See also: ta_strndup()) + */ +bool ta_strndup_append(char **str, const char *a, size_t n) +{ + return strndup_append_at(str, *str ? strlen(*str) : 0, a, n); +} + +/* Like ta_strdup_append_buffer(), but limit the length of a with n. + * (See also: ta_strndup()) + */ +bool ta_strndup_append_buffer(char **str, const char *a, size_t n) +{ + size_t size = ta_get_size(*str); + if (size > 0) + size -= 1; + return strndup_append_at(str, size, a, n); +} + +static bool ta_vasprintf_append_at(char **str, size_t at, const char *fmt, + va_list ap) +{ + assert(ta_get_size(*str) >= at); + + int size; + va_list copy; + va_copy(copy, ap); + char c; + size = vsnprintf(&c, 1, fmt, copy); + va_end(copy); + + if (size < 0) + return false; + + if (ta_get_size(*str) < at + size + 1) { + char *t = ta_realloc_size(NULL, *str, at + size + 1); + if (!t) + return false; + *str = t; + } + vsnprintf(*str + at, size + 1, fmt, ap); + + ta_dbg_mark_as_string(*str); + + return true; +} + +/* Like snprintf(); returns the formatted string as allocation (or NULL on OOM + * or snprintf() errors). + */ +char *ta_asprintf(void *ta_parent, const char *fmt, ...) +{ + char *res; + va_list ap; + va_start(ap, fmt); + res = ta_vasprintf(ta_parent, fmt, ap); + va_end(ap); + return res; +} + +char *ta_vasprintf(void *ta_parent, const char *fmt, va_list ap) +{ + char *res = NULL; + ta_vasprintf_append_at(&res, 0, fmt, ap); + if (!res || !ta_set_parent(res, ta_parent)) { + ta_free(res); + return NULL; + } + return res; +} + +/* Append the formatted string to *str (after strlen(*str)). The allocation is + * ta_realloced if needed. + * Returns false on OOM or snprintf() errors, with *str left untouched. + */ +bool ta_asprintf_append(char **str, const char *fmt, ...) +{ + bool res; + va_list ap; + va_start(ap, fmt); + res = ta_vasprintf_append(str, fmt, ap); + va_end(ap); + return res; +} + +bool ta_vasprintf_append(char **str, const char *fmt, va_list ap) +{ + return ta_vasprintf_append_at(str, str && *str ? strlen(*str) : 0, fmt, ap); +} + +/* Append the formatted string at the end of the allocation of *str. It + * overwrites the last byte of the allocation too (which is assumed to be the + * '\0' terminating the sttring). Compared to ta_asprintf_append(), this is + * useful if you know that the string ends with the allocation, so that the + * extra strlen() can be avoided for better performance. + * Returns false on OOM or snprintf() errors, with *str left untouched. + */ +bool ta_asprintf_append_buffer(char **str, const char *fmt, ...) +{ + bool res; + va_list ap; + va_start(ap, fmt); + res = ta_vasprintf_append_buffer(str, fmt, ap); + va_end(ap); + return res; +} + +bool ta_vasprintf_append_buffer(char **str, const char *fmt, va_list ap) +{ + size_t size = ta_get_size(*str); + if (size > 0) + size -= 1; + return ta_vasprintf_append_at(str, size, fmt, ap); +} + + +void *ta_oom_p(void *p) +{ + if (!p) + abort(); + return p; +} + +void ta_oom_b(bool b) +{ + if (!b) + abort(); +} + +char *ta_oom_s(char *s) +{ + if (!s) + abort(); + return s; +} + +void *ta_xsteal_(void *ta_parent, void *ptr) +{ + ta_oom_b(ta_set_parent(ptr, ta_parent)); + return ptr; +} + +void *ta_xmemdup(void *ta_parent, void *ptr, size_t size) +{ + void *new = ta_memdup(ta_parent, ptr, size); + ta_oom_b(new || !ptr); + return new; +} + +void *ta_xrealloc_size(void *ta_parent, void *ptr, size_t size) +{ + ptr = ta_realloc_size(ta_parent, ptr, size); + ta_oom_b(ptr || !size); + return ptr; +} + +char *ta_xstrdup(void *ta_parent, const char *str) +{ + char *res = ta_strdup(ta_parent, str); + ta_oom_b(res || !str); + return res; +} + +char *ta_xstrndup(void *ta_parent, const char *str, size_t n) +{ + char *res = ta_strndup(ta_parent, str, n); + ta_oom_b(res || !str); + return res; +} diff --git a/talloc.c b/talloc.c deleted file mode 100644 index ec017d9c89..0000000000 --- a/talloc.c +++ /dev/null @@ -1,1751 +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 - Copyright (C) Stefan Metzmacher 2006 - - ** NOTE! The following LGPL license applies to the talloc - ** library. This does NOT imply that all of Samba is released - ** under the LGPL - - 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 <http://www.gnu.org/licenses/>. -*/ - -/* - inspired by http://swapped.cc/halloc/ -*/ - -// Hardcode these for MPlayer assuming a working system. -// Original used autoconf detection with workarounds for broken systems. -#define HAVE_VA_COPY -#include <stdio.h> -#include <stdlib.h> -#include <stdarg.h> -#include <stdint.h> -#include <string.h> -#include <stdbool.h> -#define MIN(a,b) ((a)<(b)?(a):(b)) -#define strnlen rep_strnlen -static size_t rep_strnlen(const char *s, size_t max) -{ - size_t len; - - for (len = 0; len < max; len++) { - if (s[len] == '\0') { - break; - } - } - return len; -} - - - -#ifdef _SAMBA_BUILD_ -#include "version.h" -#if (SAMBA_VERSION_MAJOR<4) -#include "includes.h" -/* This is to circumvent SAMBA3's paranoid malloc checker. Here in this file - * we trust ourselves... */ -#ifdef malloc -#undef malloc -#endif -#ifdef realloc -#undef realloc -#endif -#define _TALLOC_SAMBA3 -#endif /* (SAMBA_VERSION_MAJOR<4) */ -#endif /* _SAMBA_BUILD_ */ - -#ifndef _TALLOC_SAMBA3 -// Workarounds for missing standard features, not used in MPlayer -// #include "replace.h" -#include "talloc.h" -#endif /* not _TALLOC_SAMBA3 */ - -/* 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 0xe814ec70 -#define TALLOC_FLAG_FREE 0x01 -#define TALLOC_FLAG_LOOP 0x02 -#define TALLOC_FLAG_POOL 0x04 /* This is a talloc pool */ -#define TALLOC_FLAG_POOLMEM 0x08 /* This is allocated in a pool */ -#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 -#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 - -/* these macros gain us a few percent of speed on gcc */ -#if (__GNUC__ >= 3) -/* the strange !! is to ensure that __builtin_expect() takes either 0 or 1 - as its first argument */ -#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 - -/* this null_context is only used if talloc_enable_leak_report() or - talloc_enable_leak_report_full() is called, otherwise it remains - NULL -*/ -static void *null_context; -static void *autofree_context; - -struct talloc_reference_handle { - struct talloc_reference_handle *next, *prev; - void *ptr; -}; - -typedef void (*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; - - /* - * "pool" has dual use: - * - * For the talloc pool itself (i.e. TALLOC_FLAG_POOL is set), "pool" - * marks the end of the currently allocated area. - * - * For members of the pool (i.e. TALLOC_FLAG_POOLMEM is set), "pool" - * is a pointer to the struct talloc_chunk of the pool that it was - * allocated from. This way children can quickly find the pool to chew - * from. - */ - void *pool; -}; - -/* 16 byte alignment seems to keep everyone happy */ -#define TC_HDR_SIZE ((sizeof(struct talloc_chunk)+15)&~15) -#define TC_PTR_FROM_CHUNK(tc) ((void *)(TC_HDR_SIZE + (char*)tc)) - -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"); -} - -/* panic if we get a bad magic 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 & TALLOC_FLAG_FREE) { - talloc_abort_double_free(); - } else { - talloc_abort_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 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; -} - -/* - find parents name -*/ -const char *talloc_parent_name(const void *ptr) -{ - struct talloc_chunk *tc = talloc_parent_chunk(ptr); - return tc? tc->name : NULL; -} - -/* - A pool carries an in-pool object count count in the first 16 bytes. - bytes. This is done to support talloc_steal() to a parent outside of the - pool. The count includes the pool itself, so a talloc_free() on a pool will - only destroy the pool if the count has dropped to zero. A talloc_free() of a - pool member will reduce the count, and eventually also call free(3) on the - pool memory. - - The object count is not put into "struct talloc_chunk" because it is only - relevant for talloc pools and the alignment to 16 bytes would increase the - memory footprint of each talloc chunk by those 16 bytes. -*/ - -#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)); -} - -/* - Allocate from a pool -*/ - -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); - - /* - * Align size to 16 bytes - */ - 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; -} - -/* - Allocate a bit of memory as a child of an existing pointer -*/ -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)) { - abort(); // 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)) abort(); // 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); -} - -/* - * Create a talloc pool - */ - -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; -} - -/* - setup a destructor to be called on free of a pointer -*/ -void _talloc_set_destructor(const void *ptr, void (*destructor)(void *)) -{ - struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); - tc->destructor = destructor; -} - -/* - increase the reference count on a piece of memory. -*/ -int talloc_increase_ref_count(const void *ptr) -{ - if (unlikely(!talloc_reference(null_context, ptr))) { - return -1; - } - return 0; -} - -/* - helper for talloc_reference() - - this is referenced by a function pointer and should not be inline -*/ -static void talloc_reference_destructor(void *ptr) -{ - struct talloc_reference_handle *handle = ptr; - struct talloc_chunk *ptr_tc = talloc_chunk_from_ptr(handle->ptr); - _TLIST_REMOVE(ptr_tc->refs, handle); -} - -/* - more efficient way to add a name to a pointer - the name must point to a - true string constant -*/ -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; -} - -/* - internal talloc_named_const() -*/ -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; -} - -/* - 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 (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; - - /* 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; -} - - -/* - internal talloc_free call -*/ -static inline int _talloc_free(void *ptr) -{ - struct talloc_chunk *tc; - - if (unlikely(ptr == NULL)) { - return -1; - } - - tc = talloc_chunk_from_ptr(ptr); - - if (unlikely(tc->refs)) { - int is_child; - /* check this is a reference from a child or grantchild - * back to it's parent or grantparent - * - * in that case we need to remove the reference and - * call another instance of talloc_free() on the current - * pointer. - */ - is_child = talloc_is_parent(tc->refs, ptr); - _talloc_free(tc->refs); - if (is_child) { - return _talloc_free(ptr); - } - return -1; - } - - if (unlikely(tc->flags & TALLOC_FLAG_LOOP)) { - /* we have a free loop - stop looping */ - 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; - d(ptr); - 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) { - /* 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_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(new_parent, child); - } - } - - tc->flags |= TALLOC_FLAG_FREE; - - 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!"); - } - - *pool_object_count -= 1; - - if (*pool_object_count == 0) { - free(pool); - } - } - else { - free(tc); - } - return 0; -} - -/* - move a lump of memory from one talloc context to another return the - ptr on success, or NULL if it could not be transferred. - passing NULL as ptr will always return NULL with no side effects. -*/ -void *_talloc_steal(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); -} - - - -/* - 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 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(h); -} - -/* - 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 = TC_PTR_FROM_CHUNK(new_p); - } 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 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; -} - -/* - add a name to an existing pointer -*/ -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; -} - - -/* - 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; - 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(ptr); - return NULL; - } - - 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 (unlikely(tc->name == TALLOC_MAGIC_REFERENCE)) { - return ".reference"; - } - if (likely(tc->name)) { - return tc->name; - } - return "UNNAMED"; -} - - -/* - check if a pointer has the given name. If it does, return the pointer, - otherwise return NULL -*/ -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; -} - - -/* - this is for compatibility with older versions of talloc -*/ -void *talloc_init(const char *fmt, ...) -{ - va_list ap; - void *ptr; - const char *name; - - /* - * samba3 expects talloc_report_depth_cb(NULL, ...) - * reports all talloc'ed memory, so we need to enable - * null_tracking - */ - 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(ptr); - return NULL; - } - - return ptr; -} - -/* - this is a replacement for the Samba3 talloc_destroy_pool functionality. It - should probably not be used in new code. It's in here to keep the talloc - code consistent across Samba 3 and 4. -*/ -void talloc_free_children(void *ptr) -{ - struct talloc_chunk *tc; - - if (unlikely(ptr == NULL)) { - return; - } - - tc = talloc_chunk_from_ptr(ptr); - - 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_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(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 - } -} - -/* - Allocate a bit of memory as a child of an existing pointer -*/ -void *_talloc(const void *context, size_t size) -{ - return __talloc(context, size); -} - -/* - externally callable talloc_set_name_const() -*/ -void talloc_set_name_const(const void *ptr, const char *name) -{ - _talloc_set_name_const(ptr, 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(const void *context, size_t size, const char *name) -{ - return _talloc_named_const(context, size, name); -} - -/* - 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) -{ - return _talloc_free(ptr); -} - - - -/* - 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; - bool malloced = false; - - /* size zero is equivalent to free() */ - if (unlikely(size == 0)) { - _talloc_free(ptr); - return NULL; - } - - if (unlikely(size >= MAX_TALLOC_SIZE)) { - abort(); // return NULL; - } - - /* realloc(NULL) is equivalent 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 (unlikely(tc->refs)) { - abort(); // return NULL; - } - - /* don't shrink if we have less than 1k to gain */ - if ((size < tc->size) && ((tc->size - size) < 1024)) { - tc->size = size; - return ptr; - } - - /* by resetting magic we catch users of the old memory */ - 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 = true; - } - - if (new_ptr) { - memcpy(new_ptr, tc, MIN(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; - abort(); // 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); -} - -/* - a wrapper around talloc_steal() for situations where you are moving a pointer - between two structures, and want the old pointer to be set to NULL -*/ -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, *pptr); - (*pptr) = NULL; - return ret; -} - -/* - return the total size of a talloc pool (subtree) -*/ -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; - - 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; -} - -/* - return the total number of blocks in a talloc pool (subtree) -*/ -size_t talloc_total_blocks(const void *ptr) -{ - size_t total = 0; - struct talloc_chunk *c, *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; -} - -/* - return the number of external references to a pointer -*/ -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; -} - -/* - report on memory usage by all children of a pointer, giving a full tree view -*/ -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 -} - -/* - report on memory usage by all children of a pointer, giving a full tree view -*/ -void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f) -{ - talloc_report_depth_cb(ptr, depth, max_depth, talloc_report_depth_FILE_helper, f); - fflush(f); -} - -/* - report on memory usage by all children of a pointer, giving a full tree view -*/ -void talloc_report_full(const void *ptr, FILE *f) -{ - talloc_report_depth_file(ptr, 0, -1, f); -} - -/* - report on memory usage by all children of a pointer -*/ -void talloc_report(const void *ptr, FILE *f) -{ - talloc_report_depth_file(ptr, 0, 1, f); -} - -/* - 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 tracking of the NULL context -*/ -void talloc_enable_null_tracking(void) -{ - if (null_context == NULL) { - null_context = _talloc_named_const(NULL, 0, "null_context"); - } -} - -/* - disable tracking of the NULL context -*/ -void talloc_disable_null_tracking(void) -{ - _talloc_free(null_context); - null_context = NULL; -} - -/* - enable leak reporting on exit -*/ -void talloc_enable_leak_report(void) -{ - talloc_enable_null_tracking(); - atexit(talloc_report_null); -} - -/* - enable full leak reporting on exit -*/ -void talloc_enable_leak_report_full(void) -{ - talloc_enable_null_tracking(); - 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 (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; -} - -/* - strdup with a talloc -*/ -char *talloc_strdup(const void *t, const char *p) -{ - if (unlikely(!p)) return NULL; - return __talloc_strlendup(t, p, strlen(p)); -} - -/* - strndup with a talloc -*/ -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; - - /* append the string and the trailing \0 */ - memcpy(&ret[slen], a, alen); - ret[slen+alen] = 0; - - _talloc_set_name_const(ret, ret); - return ret; -} - -/* - * Appends at the end of the string. - */ -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)); -} - -/* - * Appends at the end of the talloc'ed buffer, - * not the end of the string. - */ -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)); -} - -/* - * Appends at the end of the string. - */ -char *talloc_strndup_append(char *s, const char *a, size_t n) -{ - if (unlikely(!s)) { - return talloc_strndup(NULL, a, n); - } - - if (unlikely(!a)) { - return s; - } - - return __talloc_strlendup_append(s, strlen(s), a, strnlen(a, n)); -} - -/* - * Appends at the end of the talloc'ed buffer, - * not the end of the string. - */ -char *talloc_strndup_append_buffer(char *s, const char *a, size_t n) -{ - size_t slen; - - if (unlikely(!s)) { - return talloc_strndup(NULL, a, n); - } - - 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; - char c; - - /* this call looks strange, but it makes it work on older solaris boxes */ - va_copy(ap2, ap); - len = vsnprintf(&c, 1, fmt, ap2); - va_end(ap2); - if (unlikely(len < 0)) { - abort(); // 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; -} - - -/* - Perform string formatting, and return a pointer to newly allocated - memory holding the result, inside a memory pool. - */ -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) -{ - ssize_t alen; - va_list ap2; - char c; - - va_copy(ap2, ap); - alen = vsnprintf(&c, 1, fmt, ap2); - va_end(ap2); - - if (alen <= 0) { - /* Either the vsnprintf failed or the format resulted in - * no characters being formatted. In the former case, we - * ought to return NULL, in the latter we ought to return - * the original string. Most current callers of this - * function expect it to never return NULL. - */ - 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; -} - -/** - * Realloc @p s to append the formatted result of @p fmt and @p ap, - * and return @p s, which may have moved. Good for gradually - * accumulating output into a string buffer. Appends at the end - * of the string. - **/ -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); -} - -/** - * Realloc @p s to append the formatted result of @p fmt and @p ap, - * and return @p s, which may have moved. Always appends at the - * end of the talloc'ed buffer, not the end of the string. - **/ -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); -} - -/* - Realloc @p s to append the formatted result of @p fmt and return @p - s, which may have moved. Good for gradually accumulating output - into a string buffer. - */ -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; -} - -/* - Realloc @p s to append the formatted result of @p fmt and return @p - s, which may have moved. Good for gradually accumulating output - into a buffer. - */ -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; -} - -/* - alloc an array, checking for integer overflow in the array size -*/ -void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name) -{ - if (count >= MAX_TALLOC_SIZE/el_size) { - abort(); // 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) { - abort(); // 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) { - abort(); // return NULL; - } - return _talloc_realloc(ctx, ptr, el_size * count, name); -} - -/* - 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); -} - - -static void talloc_autofree_destructor(void *ptr) -{ - autofree_context = NULL; -} - -static void talloc_autofree(void) -{ - _talloc_free(autofree_context); -} - -/* - return a context which will be auto-freed on exit - this is useful for reducing the noise in leak reports -*/ -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) - return 0; - - tc = talloc_chunk_from_ptr(context); - - return tc->size; -} - -/* - find a parent of this context that has the given name, if any -*/ -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; -} - -/* - show the parentage of a context -*/ -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); -} - -/* - return 1 if ptr is a parent of context -*/ -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; -} @@ -1,181 +1,7 @@ -#ifndef _TALLOC_H_ -#define _TALLOC_H_ -/* - Unix SMB/CIFS implementation. - Samba temporary memory allocation functions - - Copyright (C) Andrew Tridgell 2004-2005 - Copyright (C) Stefan Metzmacher 2006 - - ** NOTE! The following LGPL license applies to the talloc - ** library. This does NOT imply that all of Samba is released - ** under the LGPL - - 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 <http://www.gnu.org/licenses/>. -*/ - #include <stdlib.h> #include <stdio.h> #include <stdarg.h> #include "compat/compiler.h" -/* HACK: libsmbclient uses dynamically linked libtalloc.so which has - * identically named symbols. This name collision caused a crash under - * stream_smb when trying to play anything with smb://. This hack - * prevents the symbols declared here from being visible to outside - * shared libraries and fixes the crash. - */ -#pragma GCC visibility push(hidden) - -/* this is only needed for compatibility with the old talloc */ -typedef void TALLOC_CTX; - -/* - this uses a little trick to allow __LINE__ to be stringified -*/ -#ifndef __location__ -#define __TALLOC_STRING_LINE1__(s) #s -#define __TALLOC_STRING_LINE2__(s) __TALLOC_STRING_LINE1__(s) -#define __TALLOC_STRING_LINE3__ __TALLOC_STRING_LINE2__(__LINE__) -#define __location__ __FILE__ ":" __TALLOC_STRING_LINE3__ -#endif - -#ifndef TALLOC_DEPRECATED -#define TALLOC_DEPRECATED 0 -#endif - -/* try to make talloc_set_destructor() and talloc_steal() type safe, - if we have a recent gcc */ -#if defined(__clang__) && defined(__llvm__) -#define _TALLOC_TYPEOF(ptr) void * -#define talloc_steal(ctx, ptr) _talloc_steal((ctx),(ptr)) -#elif (__GNUC__ >= 3) -#define _TALLOC_TYPEOF(ptr) __typeof__(ptr) -/* this extremely strange macro is to avoid some braindamaged warning - stupidity in gcc 4.1.x */ -#define talloc_steal(ctx, ptr) ({ _TALLOC_TYPEOF(ptr) __talloc_steal_ret = (_TALLOC_TYPEOF(ptr))_talloc_steal((ctx),(ptr)); __talloc_steal_ret; }) -#else -#define _TALLOC_TYPEOF(ptr) void * -#define talloc_steal(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_steal((ctx),(ptr)) -#endif -#define talloc_set_destructor(ptr, function) \ - _talloc_set_destructor((ptr), (function)) - -#define talloc_reference(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_reference((ctx),(ptr)) -#define talloc_move(ctx, ptr) (_TALLOC_TYPEOF(*(ptr)))_talloc_move((ctx),(void *)(ptr)) - -/* useful macros for creating type checked pointers */ -#define talloc(ctx, type) (type *)talloc_named_const(ctx, sizeof(type), #type) -#define talloc_size(ctx, size) talloc_named_const(ctx, size, __location__) -#define talloc_ptrtype(ctx, ptr) (_TALLOC_TYPEOF(ptr))talloc_size(ctx, sizeof(*(ptr))) - -#define talloc_new(ctx) talloc_named_const(ctx, 0, "talloc_new: " __location__) - -#define talloc_zero(ctx, type) (type *)_talloc_zero(ctx, sizeof(type), #type) -#define talloc_zero_size(ctx, size) _talloc_zero(ctx, size, __location__) - -#define talloc_zero_array(ctx, type, count) (type *)_talloc_zero_array(ctx, sizeof(type), count, #type) -#define talloc_array(ctx, type, count) (type *)_talloc_array(ctx, sizeof(type), count, #type) -#define talloc_array_size(ctx, size, count) _talloc_array(ctx, size, count, __location__) -#define talloc_array_ptrtype(ctx, ptr, count) (_TALLOC_TYPEOF(ptr))talloc_array_size(ctx, sizeof(*(ptr)), count) - -#define talloc_realloc(ctx, p, type, count) (type *)_talloc_realloc_array(ctx, p, sizeof(type), count, #type) -#define talloc_realloc_size(ctx, ptr, size) _talloc_realloc(ctx, ptr, size, __location__) - -#define talloc_memdup(t, p, size) _talloc_memdup(t, p, size, __location__) - -#define talloc_set_type(ptr, type) talloc_set_name_const(ptr, #type) -#define talloc_get_type(ptr, type) (type *)talloc_check_name(ptr, #type) - -#define talloc_find_parent_bytype(ptr, type) (type *)talloc_find_parent_byname(ptr, #type) - -#if TALLOC_DEPRECATED -#define talloc_zero_p(ctx, type) talloc_zero(ctx, type) -#define talloc_p(ctx, type) talloc(ctx, type) -#define talloc_array_p(ctx, type, count) talloc_array(ctx, type, count) -#define talloc_realloc_p(ctx, p, type, count) talloc_realloc(ctx, p, type, count) -#define talloc_destroy(ctx) talloc_free(ctx) -#define talloc_append_string(c, s, a) (s?talloc_strdup_append(s,a):talloc_strdup(c, a)) -#endif - -/* The following definitions come from talloc.c */ -void *_talloc(const void *context, size_t size); -void *talloc_pool(const void *context, size_t size); -void _talloc_set_destructor(const void *ptr, void (*destructor)(void *)); -int talloc_increase_ref_count(const void *ptr); -size_t talloc_reference_count(const void *ptr); -void *_talloc_reference(const void *context, const void *ptr); -int talloc_unlink(const void *context, void *ptr); -const char *talloc_set_name(const void *ptr, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); -void talloc_set_name_const(const void *ptr, const char *name); -void *talloc_named(const void *context, size_t size, - const char *fmt, ...) PRINTF_ATTRIBUTE(3,4); -void *talloc_named_const(const void *context, size_t size, const char *name); -const char *talloc_get_name(const void *ptr); -void *talloc_check_name(const void *ptr, const char *name); -void *talloc_parent(const void *ptr); -const char *talloc_parent_name(const void *ptr); -void *talloc_init(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2); -int talloc_free(void *ptr); -void talloc_free_children(void *ptr); -void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name); -void *_talloc_steal(const void *new_ctx, const void *ptr); -void *_talloc_move(const void *new_ctx, const void *pptr); -size_t talloc_total_size(const void *ptr); -size_t talloc_total_blocks(const void *ptr); -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); -void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f); -void talloc_report_full(const void *ptr, FILE *f); -void talloc_report(const void *ptr, FILE *f); -void talloc_enable_null_tracking(void); -void talloc_disable_null_tracking(void); -void talloc_enable_leak_report(void); -void talloc_enable_leak_report_full(void); -void *_talloc_zero(const void *ctx, size_t size, const char *name); -void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name); -void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name); -void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name); -void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name); -void *talloc_realloc_fn(const void *context, void *ptr, size_t size); -void *talloc_autofree_context(void); -size_t talloc_get_size(const void *ctx); -void *talloc_find_parent_byname(const void *ctx, const char *name); -void talloc_show_parents(const void *context, FILE *file); -int talloc_is_parent(const void *context, const void *ptr); - -char *talloc_strdup(const void *t, const char *p); -char *talloc_strdup_append(char *s, const char *a); -char *talloc_strdup_append_buffer(char *s, const char *a); - -char *talloc_strndup(const void *t, const char *p, size_t n); -char *talloc_strndup_append(char *s, const char *a, size_t n); -char *talloc_strndup_append_buffer(char *s, const char *a, size_t n); - -char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); -char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); -char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); - -char *talloc_asprintf(const void *t, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); -char *talloc_asprintf_append(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); -char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); - -// end of visibility hack from above -#pragma GCC visibility pop -#endif +#include "ta/ta_talloc.h" |