#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
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;
return NULL;
}
-void tal_free(const tal_t *ctx)
+void *tal_free(const tal_t *ctx)
{
- struct tal_hdr *t;
-
- if (!ctx)
- return;
-
- t = debug_tal(to_tal_hdr(ctx));
- remove_node(t);
- del_tree(t);
+ if (ctx) {
+ struct tal_hdr *t;
+ int saved_errno = errno;
+ t = debug_tal(to_tal_hdr(ctx));
+ remove_node(t);
+ del_tree(t);
+ errno = saved_errno;
+ }
+ return NULL;
}
void *tal_steal_(const tal_t *new_parent, const tal_t *ctx)
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;
- if (ctx == TAL_TAKE)
- return (void *)p;
+ /* 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;
+ }
- ret = tal_arr(ctx, char, n);
+ if (taken(p)) {
+ if (unlikely(!p))
+ return NULL;
+ if (unlikely(!tal_resize_((void **)&p, n + extra)))
+ return tal_free(p);
+ if (unlikely(!tal_steal(ctx, p)))
+ return tal_free(p);
+ 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;
+ size_t max;
char *buf;
int ret;
- if (ctx == TAL_TAKE)
- buf = tal_arr(tal_parent(fmt), char, max);
- else
- buf = tal_arr(ctx, char, max);
+ 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))
+ buf = tal_free(buf);
}
- if (ctx == TAL_TAKE)
+ if (taken(fmt))
tal_free(fmt);
return buf;
}