]> git.ozlabs.org Git - ccan/blobdiff - ccan/tal/tal.c
tal/str: new module.
[ccan] / ccan / tal / tal.c
index ff25db19afb8a3b16da50ef254687a85b50cc71e..5ab6cb682cd5349b060b8731e99d9f18c45bb15e 100644 (file)
@@ -3,12 +3,14 @@
 #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
 
@@ -102,12 +104,14 @@ static struct group *next_group(struct group *group)
        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) {
@@ -115,6 +119,15 @@ static void unlink_null(void)
                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
@@ -331,9 +344,10 @@ static bool add_child(struct tal_hdr *parent, struct tal_hdr *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;
@@ -455,6 +469,7 @@ static struct tal_hdr *remove_node(struct tal_hdr *t)
 
                /* 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) {
@@ -462,9 +477,9 @@ static struct tal_hdr *remove_node(struct tal_hdr *t)
                        } 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. */
@@ -478,16 +493,17 @@ static struct tal_hdr *remove_node(struct tal_hdr *t)
        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)
@@ -653,22 +669,29 @@ tal_t *tal_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. */
@@ -688,35 +711,58 @@ void *tal_realloc_(tal_t *ctx, size_t size)
                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;
@@ -736,15 +782,16 @@ char *tal_asprintf(const tal_t *ctx, const char *fmt, ...)
 
 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;
 
@@ -754,9 +801,10 @@ char *tal_vasprintf(const tal_t *ctx, const char *fmt, va_list ap)
 
                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;
 }