#include <ccan/compiler/compiler.h>
#include <ccan/hash/hash.h>
#include <ccan/list/list.h>
+#include <ccan/take/take.h>
#include <assert.h>
#include <stdio.h>
#include <stdarg.h>
#include <stddef.h>
#include <string.h>
#include <limits.h>
+#include <errno.h>
//#define TAL_DEBUG 1
return list_entry(group->list.n.next, struct group, list.n);
}
-static bool atexit_set = false;
+static bool initialized = false;
+
/* This means valgrind can see leaks. */
-static void unlink_null(void)
+static void tal_cleanup(void)
{
struct group *i, *next;
+ /* Unlink null_parent. */
for (i = next_group(&null_parent.c.group);
i != &null_parent.c.group;
i = next) {
freefn(i);
}
null_parent.c.group.first_child = NULL;
+
+ /* Cleanup any taken pointers. */
+ take_cleanup();
+}
+
+/* For allocation failures inside ccan/take */
+static void take_alloc_failed(const void *p)
+{
+ tal_free(p);
}
#ifndef NDEBUG
init_group_property(&prop->group, prop, child);
list_head_init(&prop->group.list);
+ update_bounds(&prop->group);
}
return prop;
}
children = add_child_property(parent, child);
if (!children)
return false;
- children->group.list.n.next = children->group.list.n.prev
- = &children->group.list.n;
+ list_head_init(&children->group.list);
/* Child links to itself. */
child->next = child;
if (unlikely(!group->first_child)) {
assert(group == &children->group);
/* This hits on first child appended to null parent. */
- if (unlikely(!atexit_set)) {
- atexit(unlink_null);
- atexit_set = true;
+ if (unlikely(!initialized)) {
+ atexit(tal_cleanup);
+ take_allocfail(take_alloc_failed);
+ initialized = true;
}
/* Link group into this child, make it the first one. */
group->hdr.next = child->prop;
freefn(t);
}
-void *tal_alloc_(const tal_t *ctx, size_t size, bool clear)
+void *tal_alloc_(const tal_t *ctx, size_t size, bool clear, const char *label)
{
struct tal_hdr *child, *parent = debug_tal(to_tal_hdr_or_null(ctx));
return NULL;
if (clear)
memset(from_tal_hdr(child), 0, size);
- child->prop = NULL;
+ child->prop = (void *)label;
if (!add_child(parent, child)) {
freefn(child);
return NULL;
/* Are we the only one? */
if (prev == t) {
+ struct prop_hdr *next = (*prop)->next;
struct children *c = group->parent_child;
/* Is this the group embedded in the child property? */
if (group == &c->group) {
} else {
/* Empty group, so free it. */
list_del_from(&c->group.list, &group->list.n);
- *prop = group->hdr.next;
freefn(group);
}
+ *prop = next;
return c->parent;
} else {
/* Move property to next node. */
void tal_free(const tal_t *ctx)
{
struct tal_hdr *t;
+ int saved_errno = errno;
if (!ctx)
return;
t = debug_tal(to_tal_hdr(ctx));
remove_node(t);
del_tree(t);
+ errno = saved_errno;
}
void *tal_steal_(const tal_t *new_parent, const tal_t *ctx)
tal_t *tal_parent(const tal_t *ctx)
{
struct group *group;
- struct tal_hdr *t = debug_tal(to_tal_hdr(ctx));
+ struct tal_hdr *t;
+
+ if (!ctx)
+ return NULL;
+
+ t = debug_tal(to_tal_hdr(ctx));
while (!(group = find_property(t, GROUP)))
t = t->next;
return from_tal_hdr(group->parent_child->parent);
}
-void *tal_realloc_(tal_t *ctx, size_t size)
+bool tal_resize_(tal_t **ctxp, size_t size)
{
struct tal_hdr *old_t, *t, **prev;
struct group *group;
struct children *child;
- old_t = debug_tal(to_tal_hdr(ctx));
+ old_t = debug_tal(to_tal_hdr(*ctxp));
+
+ /* Don't hand silly sizes to realloc. */
+ if (size >> (CHAR_BIT*sizeof(size) - 1)) {
+ call_error("Reallocation size overflow");
+ return false;
+ }
t = resizefn(old_t, size + sizeof(struct tal_hdr));
if (!t) {
call_error("Reallocation failure");
- tal_free(old_t);
- return NULL;
+ return false;
}
+
+ /* If it didn't move, we're done! */
if (t == old_t)
- return ctx;
+ return true;
update_bounds(t);
/* Fix up linked list pointer. */
assert(child->parent == old_t);
child->parent = t;
}
-
- return from_tal_hdr(debug_tal(t));
+ *ctxp = from_tal_hdr(debug_tal(t));
+ return true;
}
char *tal_strdup(const tal_t *ctx, const char *p)
{
- return tal_memdup(ctx, p, strlen(p)+1);
+ /* We have to let through NULL for take(). */
+ return tal_dup(ctx, char, p, p ? strlen(p) + 1: 1, 0);
}
char *tal_strndup(const tal_t *ctx, const char *p, size_t n)
{
+ size_t len;
char *ret;
- if (strlen(p) < n)
- n = strlen(p);
- ret = tal_memdup(ctx, p, n+1);
+ /* We have to let through NULL for take(). */
+ if (likely(p)) {
+ len = strlen(p);
+ if (len > n)
+ len = n;
+ } else
+ len = n;
+
+ ret = tal_dup(ctx, char, p, len, 1);
if (ret)
- ret[n] = '\0';
+ ret[len] = '\0';
return ret;
}
-void *tal_memdup(const tal_t *ctx, const void *p, size_t n)
+void *tal_dup_(const tal_t *ctx, const void *p, size_t n, size_t extra,
+ const char *label)
{
- void *ret = tal_arr(ctx, char, n);
+ void *ret;
+
+ /* Beware overflow! */
+ if (n + extra < n || n + extra + sizeof(struct tal_hdr) < 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_resize_((void **)&p, n + extra))) {
+ tal_free(p);
+ return NULL;
+ }
+ if (unlikely(!tal_steal(ctx, p))) {
+ tal_free(p);
+ return NULL;
+ }
+ return (void *)p;
+ }
+ ret = tal_alloc_(ctx, n + extra, false, label);
if (ret)
memcpy(ret, p, n);
return ret;
char *tal_vasprintf(const tal_t *ctx, const char *fmt, va_list ap)
{
- size_t max = strlen(fmt) * 2;
- char *buf = tal_arr(ctx, char, max);
+ size_t max;
+ char *buf;
int ret;
+ if (!fmt && taken(fmt))
+ return NULL;
+
+ /* A decent guess to start. */
+ max = strlen(fmt) * 2;
+ buf = tal_arr(ctx, char, max);
while (buf) {
va_list ap2;
if (ret < max)
break;
- buf = tal_resize(buf, max *= 2);
+ if (!tal_resize(&buf, max *= 2)) {
+ tal_free(buf);
+ buf = NULL;
+ }
}
+ if (taken(fmt))
+ tal_free(fmt);
return buf;
}
name = (struct name *)p;
break;
}
- if (p != &null_parent.c.hdr && !in_bounds(p))
+ if (p != &null_parent.c.hdr && p != &null_parent.c.group.hdr
+ && !in_bounds(p))
return check_err(t, errorstr,
"has bad property pointer");