From 3c164cd21a13bd3904117f6a33e06945f0f1850c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 3 Jan 2013 11:34:22 +1030 Subject: [PATCH] tal/talloc: new module for backending tal onto talloc. David Gibson points out that a project may already be using talloc, so this gives a simple adapter if one module uses tal. Signed-off-by: Rusty Russell --- Makefile-ccan | 1 + ccan/tal/talloc/LICENSE | 1 + ccan/tal/talloc/_info | 40 +++ ccan/tal/talloc/talloc.c | 256 ++++++++++++++++++ ccan/tal/talloc/talloc.h | 305 ++++++++++++++++++++++ ccan/tal/talloc/test/run-array.c | 46 ++++ ccan/tal/talloc/test/run-count.c | 33 +++ ccan/tal/talloc/test/run-destructor.c | 67 +++++ ccan/tal/talloc/test/run-expand.c | 35 +++ ccan/tal/talloc/test/run-free.c | 26 ++ ccan/tal/talloc/test/run-named-debug.c | 34 +++ ccan/tal/talloc/test/run-named-nolabels.c | 30 +++ ccan/tal/talloc/test/run-named.c | 33 +++ ccan/tal/talloc/test/run-overflow.c | 100 +++++++ ccan/tal/talloc/test/run-steal.c | 40 +++ ccan/tal/talloc/test/run-take.c | 56 ++++ ccan/tal/talloc/test/run.c | 44 ++++ 17 files changed, 1147 insertions(+) create mode 120000 ccan/tal/talloc/LICENSE create mode 100644 ccan/tal/talloc/_info create mode 100644 ccan/tal/talloc/talloc.c create mode 100644 ccan/tal/talloc/talloc.h create mode 100644 ccan/tal/talloc/test/run-array.c create mode 100644 ccan/tal/talloc/test/run-count.c create mode 100644 ccan/tal/talloc/test/run-destructor.c create mode 100644 ccan/tal/talloc/test/run-expand.c create mode 100644 ccan/tal/talloc/test/run-free.c create mode 100644 ccan/tal/talloc/test/run-named-debug.c create mode 100644 ccan/tal/talloc/test/run-named-nolabels.c create mode 100644 ccan/tal/talloc/test/run-named.c create mode 100644 ccan/tal/talloc/test/run-overflow.c create mode 100644 ccan/tal/talloc/test/run-steal.c create mode 100644 ccan/tal/talloc/test/run-take.c create mode 100644 ccan/tal/talloc/test/run.c diff --git a/Makefile-ccan b/Makefile-ccan index 249d0bb0..70f7c936 100644 --- a/Makefile-ccan +++ b/Makefile-ccan @@ -92,6 +92,7 @@ MODS_EXTERNAL_WITH_SRC:=jmap \ jset \ nfs \ ogg_to_pcm \ + tal/talloc \ wwviaudio MODS_EXTERNAL:=$(MODS_EXTERNAL_NO_SRC) $(MODS_EXTERNAL_WITH_SRC) diff --git a/ccan/tal/talloc/LICENSE b/ccan/tal/talloc/LICENSE new file mode 120000 index 00000000..ebc58940 --- /dev/null +++ b/ccan/tal/talloc/LICENSE @@ -0,0 +1 @@ +../../../licenses/LGPL-3 \ No newline at end of file diff --git a/ccan/tal/talloc/_info b/ccan/tal/talloc/_info new file mode 100644 index 00000000..cf01d6cc --- /dev/null +++ b/ccan/tal/talloc/_info @@ -0,0 +1,40 @@ +#include +#include +#include "config.h" + +/** + * tal/talloc - an implementation of the tal interface in terms of talloc. + * + * Tal and talloc are both hierarchical allocators, but have different APIs. + * The tal API is mostly a subset of talloc, but if your project already + * uses talloc then having both tal and talloc pointers is confusing, and + * a waste of resources. + * + * The standard convention to tell ccan modules to use this instead of + * ccan/tal is to define TAL_USE_TALLOC, usually on the commandline. + * + * Bugs: + * tal_first() and tal_next() can't be implemented. + * tal_set_backend() can only change the error function. + * + * License: LGPL + */ +int main(int argc, char *argv[]) +{ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { + printf("ccan/take\n"); + printf("ccan/typesafe_cb\n"); + printf("talloc\n"); + return 0; + } + + if (strcmp(argv[1], "libs") == 0) { + printf("talloc\n"); + return 0; + } + + return 1; +} diff --git a/ccan/tal/talloc/talloc.c b/ccan/tal/talloc/talloc.c new file mode 100644 index 00000000..ad21b704 --- /dev/null +++ b/ccan/tal/talloc/talloc.c @@ -0,0 +1,256 @@ +/* Licensed under LGPL - see LICENSE file for details */ +#include +#include +#include +#include + +static void (*errorfn)(const char *msg) = (void *)abort; + +static void COLD call_error(const char *msg) +{ + errorfn(msg); +} + +static void *error_on_null(void *p, const char *msg) +{ + if (!p) + call_error(msg); + return p; +} + +void *tal_talloc_(const tal_t *ctx, size_t bytes, bool clear, + const char *label) +{ + void *ret; + + if (clear) + ret = _talloc_zero(ctx, bytes, label); + else + ret = talloc_named_const(ctx, bytes, label); + + return error_on_null(ret, "allocation failure"); +} + +void *tal_talloc_arr_(const tal_t *ctx, size_t bytes, size_t count, bool clear, + const char *label) +{ + void *ret; + + if (clear) + ret = _talloc_zero_array(ctx, bytes, count, label); + else + ret = _talloc_array(ctx, bytes, count, label); + + return error_on_null(ret, "array allocation failure"); +} + +void *tal_talloc_free_(const tal_t *ctx) +{ + int saved_errno = errno; + talloc_free((void *)ctx); + errno = saved_errno; + return NULL; +} + +bool tal_talloc_set_name_(tal_t *ctx, const char *name, bool literal) +{ + if (!literal) { + name = talloc_strdup(ctx, name); + if (!name) { + call_error("set_name allocation failure"); + return false; + } + } + talloc_set_name_const(ctx, name); + return true; +} + +const char *tal_talloc_name_(const tal_t *ctx) +{ + const char *p = talloc_get_name(ctx); + if (p && unlikely(strcmp(p, "UNNAMED") == 0)) + p = NULL; + return p; +} + +static bool adjust_size(size_t *size, size_t count) +{ + /* Multiplication wrap */ + if (count && unlikely(*size * count / *size != count)) + goto overflow; + + *size *= count; + + /* Make sure we don't wrap adding header. */ + if (*size + 1024 < 1024) + goto overflow; + return true; +overflow: + call_error("allocation size overflow"); + return false; +} + +void *tal_talloc_dup_(const tal_t *ctx, const void *p, size_t size, + size_t n, size_t extra, const char *label) +{ + void *ret; + size_t nbytes = size; + + if (!adjust_size(&nbytes, n)) { + if (taken(p)) + tal_free(p); + return NULL; + } + + /* Beware addition overflow! */ + if (n + extra < n) { + call_error("dup size overflow"); + if (taken(p)) + tal_free(p); + return NULL; + } + + if (taken(p)) { + if (unlikely(!p)) + return NULL; + if (unlikely(!tal_talloc_resize_((void **)&p, size, n + extra))) + return tal_free(p); + if (unlikely(!tal_steal(ctx, p))) + return tal_free(p); + return (void *)p; + } + + ret = tal_talloc_arr_(ctx, size, n + extra, false, label); + if (ret) + memcpy(ret, p, nbytes); + return ret; +} + +bool tal_talloc_resize_(tal_t **ctxp, size_t size, size_t count) +{ + tal_t *newp; + + if (unlikely(count == 0)) { + /* Don't free it! */ + newp = talloc_size(talloc_parent(*ctxp), 0); + if (!newp) { + call_error("Resize failure"); + return false; + } + talloc_free(*ctxp); + *ctxp = newp; + return true; + } + newp = _talloc_realloc_array(NULL, *ctxp, size, count, NULL); + if (!newp) { + call_error("Resize failure"); + return false; + } + *ctxp = newp; + return true; +} + +bool tal_talloc_expand_(tal_t **ctxp, const void *src, size_t size, size_t count) +{ + bool ret = false; + size_t old_count = talloc_get_size(*ctxp) / size; + + /* Check for additive overflow */ + if (old_count + count < count) { + call_error("dup size overflow"); + goto out; + } + + /* Don't point src inside thing we're expanding! */ + assert(src < *ctxp + || (char *)src >= (char *)(*ctxp) + (size * old_count)); + + if (!tal_talloc_resize_(ctxp, size, old_count + count)) + goto out; + + memcpy((char *)*ctxp + size * old_count, src, count * size); + ret = true; + +out: + if (taken(src)) + tal_free(src); + return ret; +} + +/* Sucky inline hash table implementation, to avoid deps. */ +#define HTABLE_BITS 10 +struct destructor { + struct destructor *next; + const tal_t *ctx; + void (*destroy)(void *me); +}; +static struct destructor *destr_hash[1 << HTABLE_BITS]; + +static unsigned int hash_ptr(const void *p) +{ + unsigned long h = (unsigned long)p / sizeof(void *); + + return (h ^ (h >> HTABLE_BITS)) & ((1 << HTABLE_BITS) - 1); +} + +static int tal_talloc_destroy(const tal_t *ctx) +{ + struct destructor **d = &destr_hash[hash_ptr(ctx)]; + while (*d) { + if ((*d)->ctx == ctx) { + struct destructor *this = *d; + this->destroy((void *)ctx); + *d = this->next; + talloc_free(this); + } + } + return 0; +} + +bool tal_talloc_add_destructor_(const tal_t *ctx, void (*destroy)(void *me)) +{ + struct destructor *d = talloc(ctx, struct destructor); + if (!d) + return false; + + d->next = destr_hash[hash_ptr(ctx)]; + d->ctx = ctx; + d->destroy = destroy; + destr_hash[hash_ptr(ctx)] = d; + talloc_set_destructor(ctx, tal_talloc_destroy); + return true; +} + +bool tal_talloc_del_destructor_(const tal_t *ctx, void (*destroy)(void *me)) +{ + struct destructor **d = &destr_hash[hash_ptr(ctx)]; + + while (*d) { + if ((*d)->ctx == ctx && (*d)->destroy == destroy) { + struct destructor *this = *d; + *d = this->next; + talloc_free(this); + return true; + } + d = &(*d)->next; + } + return false; +} + +void tal_talloc_set_backend_(void *(*alloc_fn)(size_t size), + void *(*resize_fn)(void *, size_t size), + void (*free_fn)(void *), + void (*error_fn)(const char *msg)) +{ + assert(!alloc_fn); + assert(!resize_fn); + assert(!free_fn); + errorfn = error_fn; + talloc_set_abort_fn(error_fn); +} + +bool tal_talloc_check_(const tal_t *ctx, const char *errorstr) +{ + /* We can't really check, but this iterates (and may abort). */ + return !ctx || talloc_total_blocks(ctx) >= 1; +} diff --git a/ccan/tal/talloc/talloc.h b/ccan/tal/talloc/talloc.h new file mode 100644 index 00000000..718f12c4 --- /dev/null +++ b/ccan/tal/talloc/talloc.h @@ -0,0 +1,305 @@ +/* Licensed under LGPL - see LICENSE file for details */ +#ifndef CCAN_TAL_TALLOC_H +#define CCAN_TAL_TALLOC_H +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * tal_t - convenient alias for void to mark tal pointers. + * + * Since any pointer can be a tal-allocated pointer, it's often + * useful to use this typedef to mark them explicitly. + */ +typedef TALLOC_CTX tal_t; + +/** + * tal - basic allocator function + * @ctx: NULL, or tal allocated object to be parent. + * @type: the type to allocate. + * + * Allocates a specific type, with a given parent context. The name + * of the object is a string of the type, but if CCAN_TAL_DEBUG is + * defined it also contains the file and line which allocated it. + * + * Example: + * int *p = tal(NULL, int); + * *p = 1; + */ +#define tal(ctx, type) \ + ((type *)tal_talloc_((ctx), sizeof(type), false, \ + TAL_LABEL(type, ""))) + +/** + * talz - zeroing allocator function + * @ctx: NULL, or tal allocated object to be parent. + * @type: the type to allocate. + * + * Equivalent to tal() followed by memset() to zero. + * + * Example: + * p = talz(NULL, int); + * assert(*p == 0); + */ +#define talz(ctx, type) \ + ((type *)tal_talloc_((ctx), sizeof(type), true, \ + TAL_LABEL(type, ""))) + +/** + * tal_free - free a tal-allocated pointer. + * @p: NULL, or tal allocated object to free. + * + * This calls the destructors for p (if any), then does the same for all its + * children (recursively) before finally freeing the memory. It returns + * NULL, for convenience. + * + * Note: errno is preserved by this call. + * + * Example: + * p = tal_free(p); + */ +#define tal_free(p) tal_talloc_free_(p) + +/** + * tal_arr - allocate an array of objects. + * @ctx: NULL, or tal allocated object to be parent. + * @type: the type to allocate. + * @count: the number to allocate. + * + * Note that an object allocated with tal_arr() has a length property; + * see tal_count(). + * + * Example: + * p = tal_arr(NULL, int, 2); + * p[0] = 0; + * p[1] = 1; + */ +#define tal_arr(ctx, type, count) \ + ((type *)tal_talloc_arr_((ctx), sizeof(type), (count), false, \ + TAL_LABEL(type, "[]"))) + +/** + * tal_arrz - allocate an array of zeroed objects. + * @ctx: NULL, or tal allocated object to be parent. + * @type: the type to allocate. + * @count: the number to allocate. + * + * Note that an object allocated with tal_arrz() has a length property; + * see tal_count(). + * + * Example: + * p = tal_arrz(NULL, int, 2); + * assert(p[0] == 0 && p[1] == 0); + */ +#define tal_arrz(ctx, type, count) \ + ((type *)tal_talloc_arr_((ctx), sizeof(type), (count), true, \ + TAL_LABEL(type, "[]"))) + +/** + * tal_resize - enlarge or reduce a tal_arr[z]. + * @p: A pointer to the tal allocated array to resize. + * @count: the number to allocate. + * + * This returns true on success (and may move *@p), or false on failure. + * If @p has a length property, it is updated on success. + * + * Example: + * tal_resize(&p, 100); + */ +#define tal_resize(p, count) \ + tal_talloc_resize_((void **)(p), sizeof**(p), (count)) + +/** + * tal_steal - change the parent of a tal-allocated pointer. + * @ctx: The new parent. + * @ptr: The tal allocated object to move. + * + * This may need to perform an allocation, in which case it may fail; thus + * it can return NULL, otherwise returns @ptr. + */ +#define tal_steal(ctx, ptr) talloc_steal((ctx), (ptr)) + +/** + * tal_add_destructor - add a callback function when this context is destroyed. + * @ptr: The tal allocated object. + * @function: the function to call before it's freed. + * + * This is a more convenient form of tal_add_notifier(@ptr, + * TAL_NOTIFY_FREE, ...), in that the function prototype takes only @ptr. + */ +#define tal_add_destructor(ptr, function) \ + tal_talloc_add_destructor_((ptr), typesafe_cb(void, void *, \ + (function), (ptr))) + +/** + * tal_del_destructor - remove a destructor callback function. + * @ptr: The tal allocated object. + * @function: the function to call before it's freed. + * + * If @function has not been successfully added as a destructor, this returns + * false. + * + * Note: you can't add more than one destructor with the talloc backend! + */ +#define tal_del_destructor(ptr, function) \ + tal_talloc_del_destructor_((ptr), typesafe_cb(void, void *, \ + (function), (ptr))) + +/** + * tal_set_name - attach a name to a tal pointer. + * @ptr: The tal allocated object. + * @name: The name to use. + * + * The name is copied, unless we're certain it's a string literal. + */ +#define tal_set_name(ptr, name) \ + tal_talloc_set_name_((ptr), (name), TAL_TALLOC_IS_LITERAL(name)) + +/** + * tal_name - get the name for a tal pointer. + * @ptr: The tal allocated object. + * + * Returns NULL if no name has been set. + */ +#define tal_name(ptr) \ + tal_talloc_name_(ptr) + +/** + * tal_count - get the count of objects in a tal_arr. + * @ptr: The tal allocated object array. + */ +#define tal_count(ptr) talloc_array_length(ptr) + +/** + * tal_parent - get the parent of a tal object. + * @ctx: The tal allocated object. + * + * Returns the parent, which may be NULL. Returns NULL if @ctx is NULL. + */ +#define tal_parent(ctx) talloc_parent(ctx) + +/** + * tal_dup - duplicate an array. + * @ctx: The tal allocated object to be parent of the result (may be NULL). + * @type: the type (should match type of @p!) + * @p: the array to copy (or resized & reparented if take()) + * @n: the number of sizeof(type) entries to copy. + * @extra: the number of extra sizeof(type) entries to allocate. + */ +#define tal_dup(ctx, type, p, n, extra) \ + ((type *)tal_talloc_dup_((ctx), tal_talloc_typechk_(p, type *), \ + sizeof(type), (n), (extra), \ + TAL_LABEL(type, "[]"))) + + +/** + * tal_set_backend - set the allocation or error functions to use + * @alloc_fn: NULL + * @resize_fn: NULL + * @free_fn: NULL + * @error_fn: called on errors or NULL (default is abort) + * + * The defaults are set up so tal functions never return NULL, but you + * can override error_fn to change that. error_fn can return, and is + * called if malloc or realloc fail. + */ +#define tal_set_backend(alloc_fn, resize_fn, free_fn, error_fn) \ + tal_talloc_set_backend_((alloc_fn), (resize_fn), (free_fn), (error_fn)) + +/** + * tal_expand - expand a tal array with contents. + * @a1p: a pointer to the tal array to expand. + * @a2: the second array (can be take()). + * @num2: the number of elements in the second array. + * + * Note that *@a1 and @a2 should be the same type. tal_count(@a1) will + * be increased by @num2. + * + * Example: + * int *arr1 = tal_arrz(NULL, int, 2); + * int arr2[2] = { 1, 3 }; + * + * tal_expand(&arr1, arr2, 2); + * assert(tal_count(arr1) == 4); + * assert(arr1[2] == 1); + * assert(arr1[3] == 3); + */ +#define tal_expand(a1p, a2, num2) \ + tal_talloc_expand_((void **)(a1p), (a2), sizeof**(a1p), \ + (num2) + 0*sizeof(*(a1p) == (a2))) + + +/** + * tal_check - set the allocation or error functions to use + * @ctx: a tal context, or NULL. + * @errorstr: a string to prepend calls to error_fn, or NULL. + * + * This sanity-checks a tal tree (unless NDEBUG is defined, in which case + * it simply returns true). If errorstr is not null, error_fn is called + * when a problem is found, otherwise it is not. + */ +#define tal_check(ctx, errorstr) \ + tal_talloc_check_((ctx), (errorstr)) + + +/* Internal support functions */ +#ifndef TAL_TALLOC_LABEL +#ifdef CCAN_TAL_NO_LABELS +#define TAL_LABEL(type, arr) NULL +#else +#ifdef CCAN_TAL_DEBUG +#define TAL_LABEL(type, arr) \ + __FILE__ ":" stringify(__LINE__) ":" stringify(type) arr +#else +#define TAL_LABEL(type, arr) stringify(type) arr +#endif /* CCAN_TAL_DEBUG */ +#endif +#endif + +#if HAVE_BUILTIN_CONSTANT_P +#define TAL_TALLOC_IS_LITERAL(str) __builtin_constant_p(str) +#else +#define TAL_TALLOC_IS_LITERAL(str) false +#endif + +#if HAVE_TYPEOF && HAVE_STATEMENT_EXPR +/* Careful: ptr can be const foo *, ptype is foo *. Also, ptr could + * be an array, eg "hello". */ +#define tal_talloc_typechk_(ptr, ptype) ({ __typeof__((ptr)+0) _p = (ptype)(ptr); _p; }) +#else +#define tal_talloc_typechk_(ptr, ptype) (ptr) +#endif + +void *tal_talloc_(const tal_t *ctx, size_t bytes, bool clear, + const char *label); +void *tal_talloc_arr_(const tal_t *ctx, size_t bytes, size_t count, bool clear, + const char *label); +void *tal_talloc_free_(const tal_t *ctx); +const char *tal_talloc_name_(const tal_t *ctx); +bool tal_talloc_set_name_(tal_t *ctx, const char *name, bool literal); + +bool tal_talloc_add_destructor_(const tal_t *ctx, void (*destroy)(void *me)); +bool tal_talloc_del_destructor_(const tal_t *ctx, void (*destroy)(void *me)); + +/* ccan/tal/str uses this, so define it. */ +#define tal_dup_(ctx, p, size, n, extra, add_count, label) \ + tal_talloc_dup_((ctx), (p), (size), (n), (extra), (label)) +void *tal_talloc_dup_(const tal_t *ctx, const void *p, size_t size, + size_t n, size_t extra, const char *label); + +bool tal_talloc_resize_(tal_t **ctxp, size_t size, size_t count); +bool tal_talloc_expand_(tal_t **ctxp, const void *src, size_t size, size_t count); +bool tal_talloc_check_(const tal_t *ctx, const char *errorstr); + +void tal_talloc_set_backend_(void *(*alloc_fn)(size_t size), + void *(*resize_fn)(void *, size_t size), + void (*free_fn)(void *), + void (*error_fn)(const char *msg)); + +#endif /* CCAN_TAL_TALLOC_H */ diff --git a/ccan/tal/talloc/test/run-array.c b/ccan/tal/talloc/test/run-array.c new file mode 100644 index 00000000..61202f6d --- /dev/null +++ b/ccan/tal/talloc/test/run-array.c @@ -0,0 +1,46 @@ +#include +#include +#include + +int main(void) +{ + char *parent, *c[4]; + int i; + + plan_tests(11); + + parent = tal(NULL, char); + ok1(parent); + + /* Zeroing allocations. */ + for (i = 0; i < 4; i++) { + c[i] = talz(parent, char); + ok1(*c[i] == '\0'); + tal_free(c[i]); + } + + /* Array allocation. */ + for (i = 0; i < 4; i++) { + c[i] = tal_arr(parent, char, 4); + strcpy(c[i], "abc"); + tal_free(c[i]); + } + + /* Zeroing array allocation. */ + for (i = 0; i < 4; i++) { + c[i] = tal_arrz(parent, char, 4); + ok1(!c[i][0] && !c[i][1] && !c[i][2] && !c[i][3]); + strcpy(c[i], "abc"); + tal_free(c[i]); + } + + /* Resizing. */ + c[0] = tal_arrz(parent, char, 4); + ok1(tal_resize(&c[0], 6)); + strcpy(c[0], "hello"); + tal_free(c[0]); + ok1(talloc_total_blocks(parent) == 1); + tal_free(parent); + + return exit_status(); +} diff --git a/ccan/tal/talloc/test/run-count.c b/ccan/tal/talloc/test/run-count.c new file mode 100644 index 00000000..9e8a4e76 --- /dev/null +++ b/ccan/tal/talloc/test/run-count.c @@ -0,0 +1,33 @@ +#include +#include +#include + +int main(void) +{ + char *p1, *p2; + + plan_tests(12); + + p1 = tal(NULL, char); + ok1(p1); + ok1(tal_count(p1) == 1); + + p2 = tal_arr(p1, char, 1); + ok1(p2); + ok1(tal_count(p2) == 1); + ok1(tal_resize(&p2, 2)); + ok1(tal_count(p2) == 2); + ok1(tal_check(NULL, NULL)); + tal_free(p2); + + p2 = tal_arrz(p1, char, 7); + ok1(p2); + ok1(tal_count(p2) == 7); + ok1(tal_resize(&p2, 0)); + ok1(tal_count(p2) == 0); + ok1(tal_check(NULL, NULL)); + tal_free(p2); + tal_free(p1); + + return exit_status(); +} diff --git a/ccan/tal/talloc/test/run-destructor.c b/ccan/tal/talloc/test/run-destructor.c new file mode 100644 index 00000000..55e0de21 --- /dev/null +++ b/ccan/tal/talloc/test/run-destructor.c @@ -0,0 +1,67 @@ +#include +#include +#include + +static char *parent, *child; +static int destroy_count; + +/* Parent gets destroyed first. */ +static void destroy_parent(char *p) +{ + ok1(p == parent); + ok1(destroy_count == 0); + /* Can still access child. */ + *child = '1'; + destroy_count++; +} + +static void destroy_child(char *p) +{ + ok1(p == child); + ok1(destroy_count == 1); + /* Can still access parent (though destructor has been called). */ + *parent = '1'; + destroy_count++; +} + +static void destroy_inc(char *p) +{ + destroy_count++; +} + +int main(void) +{ + char *child2; + + plan_tests(18); + + destroy_count = 0; + parent = tal(NULL, char); + child = tal(parent, char); + ok1(tal_add_destructor(parent, destroy_parent)); + ok1(tal_add_destructor(child, destroy_child)); + tal_free(parent); + ok1(destroy_count == 2); + + destroy_count = 0; + parent = tal(NULL, char); + child = tal(parent, char); + ok1(tal_add_destructor(parent, destroy_parent)); + ok1(tal_add_destructor(child, destroy_child)); + ok1(tal_del_destructor(child, destroy_child)); + tal_free(parent); + ok1(destroy_count == 1); + + destroy_count = 0; + parent = tal(NULL, char); + child = tal(parent, char); + child2 = tal(parent, char); + ok1(tal_add_destructor(parent, destroy_inc)); + ok1(tal_add_destructor(parent, destroy_inc)); + ok1(tal_add_destructor(child, destroy_inc)); + ok1(tal_add_destructor(child2, destroy_inc)); + tal_free(parent); + ok1(destroy_count == 4); + + return exit_status(); +} diff --git a/ccan/tal/talloc/test/run-expand.c b/ccan/tal/talloc/test/run-expand.c new file mode 100644 index 00000000..4708768c --- /dev/null +++ b/ccan/tal/talloc/test/run-expand.c @@ -0,0 +1,35 @@ +#include +#include +#include + +int main(void) +{ + int *a; + const int arr[] = { 1, 2 }; + + plan_tests(14); + talloc_enable_null_tracking_no_autofree(); + + a = tal_arrz(NULL, int, 1); + ok1(a); + + ok1(tal_expand(&a, arr, 2)); + ok1(tal_count(a) == 3); + ok1(a[0] == 0); + ok1(a[1] == 1); + ok1(a[2] == 2); + + ok1(tal_expand(&a, take(tal_arrz(NULL, int, 1)), 1)); + ok1(tal_count(a) == 4); + ok1(a[0] == 0); + ok1(a[1] == 1); + ok1(a[2] == 2); + ok1(a[3] == 0); + ok1(talloc_total_blocks(NULL) == 2); + ok1(talloc_total_blocks(a) == 1); + + tal_free(a); + + talloc_disable_null_tracking(); + return exit_status(); +} diff --git a/ccan/tal/talloc/test/run-free.c b/ccan/tal/talloc/test/run-free.c new file mode 100644 index 00000000..0dee0a70 --- /dev/null +++ b/ccan/tal/talloc/test/run-free.c @@ -0,0 +1,26 @@ +#include +#include +#include +#include + +static void destroy_errno(char *p) +{ + errno = ENOENT; +} + +int main(void) +{ + char *p; + + plan_tests(2); + + p = tal(NULL, char); + ok1(tal_add_destructor(p, destroy_errno)); + + /* Errno save/restored across free. */ + errno = EINVAL; + tal_free(p); + ok1(errno == EINVAL); + + return exit_status(); +} diff --git a/ccan/tal/talloc/test/run-named-debug.c b/ccan/tal/talloc/test/run-named-debug.c new file mode 100644 index 00000000..625fd776 --- /dev/null +++ b/ccan/tal/talloc/test/run-named-debug.c @@ -0,0 +1,34 @@ +#define CCAN_TAL_DEBUG +#include +#include +#include + +int main(void) +{ + int *p; + char name[] = "test name"; + + plan_tests(6); + + p = tal(NULL, int); + ok1(strcmp(tal_name(p), __FILE__ ":13:int") == 0); + + tal_set_name(p, "some literal"); + ok1(strcmp(tal_name(p), "some literal") == 0); + + tal_set_name(p, name); + ok1(strcmp(tal_name(p), name) == 0); + /* You can't reuse my pointer though! */ + ok1(tal_name(p) != name); + + tal_set_name(p, "some other literal"); + ok1(strcmp(tal_name(p), "some other literal") == 0); + + tal_free(p); + + p = tal_arr(NULL, int, 2); + ok1(strcmp(tal_name(p), __FILE__ ":29:int[]") == 0); + tal_free(p); + + return exit_status(); +} diff --git a/ccan/tal/talloc/test/run-named-nolabels.c b/ccan/tal/talloc/test/run-named-nolabels.c new file mode 100644 index 00000000..2897cd37 --- /dev/null +++ b/ccan/tal/talloc/test/run-named-nolabels.c @@ -0,0 +1,30 @@ +#define CCAN_TAL_NO_LABELS +#include +#include +#include + +int main(void) +{ + int *p; + char name[] = "test name"; + + plan_tests(5); + + p = tal(NULL, int); + ok1(tal_name(p) == NULL); + + tal_set_name(p, "some literal"); + ok1(strcmp(tal_name(p), "some literal") == 0); + + tal_set_name(p, name); + ok1(strcmp(tal_name(p), name) == 0); + /* You can't reuse my pointer though! */ + ok1(tal_name(p) != name); + + tal_set_name(p, "some other literal"); + ok1(strcmp(tal_name(p), "some other literal") == 0); + + tal_free(p); + + return exit_status(); +} diff --git a/ccan/tal/talloc/test/run-named.c b/ccan/tal/talloc/test/run-named.c new file mode 100644 index 00000000..57bc5568 --- /dev/null +++ b/ccan/tal/talloc/test/run-named.c @@ -0,0 +1,33 @@ +#include +#include +#include + +int main(void) +{ + int *p; + char name[] = "test name"; + + plan_tests(6); + + p = tal(NULL, int); + ok1(strcmp(tal_name(p), "int") == 0); + + tal_set_name(p, "some literal"); + ok1(strcmp(tal_name(p), "some literal") == 0); + + tal_set_name(p, name); + ok1(strcmp(tal_name(p), name) == 0); + /* You can't reuse my pointer though! */ + ok1(tal_name(p) != name); + + tal_set_name(p, "some other literal"); + ok1(strcmp(tal_name(p), "some other literal") == 0); + + tal_free(p); + + p = tal_arr(NULL, int, 2); + ok1(strcmp(tal_name(p), "int[]") == 0); + tal_free(p); + + return exit_status(); +} diff --git a/ccan/tal/talloc/test/run-overflow.c b/ccan/tal/talloc/test/run-overflow.c new file mode 100644 index 00000000..c7f9c18f --- /dev/null +++ b/ccan/tal/talloc/test/run-overflow.c @@ -0,0 +1,100 @@ +#include +#include +#include + +static int error_count; + +static void my_error(const char *msg) +{ + error_count++; +} + +int main(void) +{ + void *p; + int *pi, *origpi; + char *cp; + + plan_tests(30); + + tal_set_backend(NULL, NULL, NULL, my_error); + talloc_enable_null_tracking_no_autofree(); + + p = tal_arr(NULL, int, (size_t)-1); + ok1(!p); + ok1(error_count == 1); + + p = tal_arr(NULL, char, (size_t)-2); + ok1(!p); + ok1(error_count == 2); + + /* Now try overflow cases for tal_dup. */ + error_count = 0; + origpi = tal_arr(NULL, int, 100); + ok1(origpi); + ok1(error_count == 0); + pi = tal_dup(NULL, int, origpi, (size_t)-1, 0); + ok1(!pi); + ok1(error_count == 1); + pi = tal_dup(NULL, int, origpi, 0, (size_t)-1); + ok1(!pi); + ok1(error_count == 2); + + pi = tal_dup(NULL, int, origpi, (size_t)-1UL / sizeof(int), + (size_t)-1UL / sizeof(int)); + ok1(!pi); + ok1(error_count == 3); + /* This will still overflow when tal_hdr is added. */ + pi = tal_dup(NULL, int, origpi, (size_t)-1UL / sizeof(int) / 2, + (size_t)-1UL / sizeof(int) / 2); + ok1(!pi); + ok1(error_count == 4); + ok1(talloc_total_blocks(NULL) == 2); + tal_free(origpi); + + /* Now, check that with taltk() we free old one on failure. */ + origpi = tal_arr(NULL, int, 100); + error_count = 0; + pi = tal_dup(NULL, int, take(origpi), (size_t)-1, 0); + ok1(!pi); + ok1(error_count == 1); + + origpi = tal_arr(NULL, int, 100); + error_count = 0; + pi = tal_dup(NULL, int, take(origpi), 0, (size_t)-1); + ok1(!pi); + ok1(error_count == 1); + ok1(talloc_total_blocks(NULL) == 1); + + origpi = tal_arr(NULL, int, 100); + error_count = 0; + pi = tal_dup(NULL, int, take(origpi), (size_t)-1UL / sizeof(int), + (size_t)-1UL / sizeof(int)); + ok1(!pi); + ok1(error_count == 1); + ok1(talloc_total_blocks(NULL) == 1); + + origpi = tal_arr(NULL, int, 100); + error_count = 0; + /* This will still overflow when tal_hdr is added. */ + pi = tal_dup(NULL, int, take(origpi), (size_t)-1UL / sizeof(int) / 2, + (size_t)-1UL / sizeof(int) / 2); + ok1(!pi); + ok1(error_count == 1); + ok1(talloc_total_blocks(NULL) == 1); + + /* Overflow on expand addition. */ + cp = tal_arr(p, char, 100); + ok1(!tal_expand(&cp, NULL, (size_t)-99UL)); + ok1(error_count == 2); + tal_free(cp); + + /* Overflow when multiplied by size */ + origpi = tal_arr(NULL, int, 100); + ok1(!tal_expand(&origpi, NULL, (size_t)-1UL / sizeof(int))); + ok1(error_count == 3); + tal_free(origpi); + + talloc_disable_null_tracking(); + return exit_status(); +} diff --git a/ccan/tal/talloc/test/run-steal.c b/ccan/tal/talloc/test/run-steal.c new file mode 100644 index 00000000..10efadda --- /dev/null +++ b/ccan/tal/talloc/test/run-steal.c @@ -0,0 +1,40 @@ +#include +#include +#include + +int main(void) +{ + char *p[5]; + unsigned int i; + + plan_tests(9); + + p[0] = tal(NULL, char); + for (i = 1; i < 5; i++) + p[i] = tal(p[i-1], char); + + tal_check(NULL, "check"); + /* Steal node with no children. */ + ok1(tal_steal(p[0], p[4]) == p[4]); + tal_check(NULL, "check"); + /* Noop steal. */ + ok1(tal_steal(p[0], p[4]) == p[4]); + tal_check(NULL, "check"); + /* Steal with children. */ + ok1(tal_steal(p[0], p[1]) == p[1]); + tal_check(NULL, "check"); + /* Noop steal. */ + ok1(tal_steal(p[0], p[1]) == p[1]); + tal_check(NULL, "check"); + /* Steal from direct child. */ + ok1(tal_steal(p[0], p[2]) == p[2]); + tal_check(NULL, "check"); + + ok1(tal_parent(p[1]) == p[0]); + ok1(tal_parent(p[2]) == p[0]); + ok1(tal_parent(p[3]) == p[2]); + ok1(tal_parent(p[4]) == p[0]); + tal_free(p[0]); + + return exit_status(); +} diff --git a/ccan/tal/talloc/test/run-take.c b/ccan/tal/talloc/test/run-take.c new file mode 100644 index 00000000..7283c16e --- /dev/null +++ b/ccan/tal/talloc/test/run-take.c @@ -0,0 +1,56 @@ +#include +#include +#include + +int main(void) +{ + char *parent, *c; + + plan_tests(21); + + /* We can take NULL. */ + ok1(take(NULL) == NULL); + ok1(is_taken(NULL)); + ok1(taken(NULL)); /* Undoes take() */ + ok1(!is_taken(NULL)); + ok1(!taken(NULL)); + + parent = tal(NULL, char); + ok1(parent); + + ok1(take(parent) == parent); + ok1(is_taken(parent)); + ok1(taken(parent)); /* Undoes take() */ + ok1(!is_taken(parent)); + ok1(!taken(parent)); + + c = tal(parent, char); + *c = 'h'; + c = tal_dup(parent, char, take(c), 1, 0); + ok1(c[0] == 'h'); + ok1(tal_parent(c) == parent); + + c = tal_dup(parent, char, take(c), 1, 2); + ok1(c[0] == 'h'); + strcpy(c, "hi"); + ok1(tal_parent(c) == parent); + + /* dup must reparent child. */ + c = tal_dup(NULL, char, take(c), 1, 0); + ok1(c[0] == 'h'); + ok1(tal_parent(c) == NULL); + + /* No leftover allocations. */ + tal_free(c); + ok1(talloc_total_blocks(parent) == 1); + + tal_free(parent); + ok1(!taken_any()); + + /* NULL pass-through. */ + c = NULL; + ok1(tal_dup(NULL, char, take(c), 5, 5) == NULL); + ok1(!taken_any()); + + return exit_status(); +} diff --git a/ccan/tal/talloc/test/run.c b/ccan/tal/talloc/test/run.c new file mode 100644 index 00000000..bc963442 --- /dev/null +++ b/ccan/tal/talloc/test/run.c @@ -0,0 +1,44 @@ +#include +#include +#include + +int main(void) +{ + char *parent, *c[4]; + int i, j; + + plan_tests(9); + + /* tal_free(NULL) works. */ + ok1(tal_free(NULL) == NULL); + + parent = tal(NULL, char); + ok1(parent); + ok1(tal_parent(parent) == NULL); + ok1(tal_parent(NULL) == NULL); + + for (i = 0; i < 4; i++) + c[i] = tal(parent, char); + + for (i = 0; i < 4; i++) + ok1(tal_parent(c[i]) == parent); + + /* Free parent. */ + ok1(tal_free(parent) == NULL); + + parent = tal(NULL, char); + + /* Test freeing in every order */ + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) + c[j] = tal(parent, char); + + tal_free(c[i]); + tal_free(c[(i+1) % 4]); + tal_free(c[(i+2) % 4]); + tal_free(c[(i+3) % 4]); + } + tal_free(parent); + + return exit_status(); +} -- 2.39.2