]> git.ozlabs.org Git - ccan/blobdiff - ccan/tal/tal.c
tal: tal_dup()
[ccan] / ccan / tal / tal.c
index d7b7b4b22e6ad34ad54e21f6b8bd2683d97c4337..88bcb5ab6135257529eda0e999df024507c0386d 100644 (file)
@@ -9,6 +9,7 @@
 #include <stddef.h>
 #include <string.h>
 #include <limits.h>
+#include <errno.h>
 
 //#define TAL_DEBUG 1
 
@@ -294,6 +295,7 @@ static struct children *add_child_property(struct tal_hdr *parent,
 
                init_group_property(&prop->group, prop, child);
                list_head_init(&prop->group.list);
+               update_bounds(&prop->group);
        }
        return prop;
 }
@@ -316,8 +318,7 @@ static bool add_child(struct tal_hdr *parent, struct tal_hdr *child)
                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;
@@ -417,7 +418,7 @@ static void del_tree(struct tal_hdr *t)
         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));
 
@@ -426,7 +427,7 @@ void *tal_alloc_(const tal_t *ctx, size_t size, bool clear)
                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;
@@ -455,6 +456,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 +464,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. */
@@ -481,6 +483,7 @@ static struct tal_hdr *remove_node(struct tal_hdr *t)
 void tal_free(const tal_t *ctx)
 {
         struct tal_hdr *t;
+       int saved_errno = errno;
 
         if (!ctx)
                 return;
@@ -488,6 +491,7 @@ void tal_free(const tal_t *ctx)
         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)
@@ -638,7 +642,12 @@ tal_t *tal_next(const tal_t *root, const tal_t *prev)
 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;
@@ -648,22 +657,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. */
@@ -683,13 +699,13 @@ 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);
+       return tal_dup(ctx, char, p, strlen(p)+1, 0);
 }
 
 char *tal_strndup(const tal_t *ctx, const char *p, size_t n)
@@ -698,20 +714,35 @@ char *tal_strndup(const tal_t *ctx, const char *p, size_t n)
 
        if (strlen(p) < n)
                n = strlen(p);
-       ret = tal_memdup(ctx, p, n+1);
+       ret = tal_dup(ctx, char, p, n, 1);
        if (ret)
                ret[n] = '\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 (ctx == TAL_TAKE)
+                       tal_free(p);
+               return NULL;
+       }
 
-       ret = tal_arr(ctx, char, n);
+       if (ctx == TAL_TAKE) {
+               if (unlikely(!p))
+                       return NULL;
+               if (!tal_resize_((void **)&p, n + extra)) {
+                       tal_free(p);
+                       return NULL;
+               }
+               return (void *)p;
+       }
+       ret = tal_alloc_(ctx, n + extra, false, label);
        if (ret)
                memcpy(ret, p, n);
        return ret;
@@ -749,7 +780,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)) {
+                       tal_free(buf);
+                       buf = NULL;
+               }
        }
        if (ctx == TAL_TAKE)
                tal_free(fmt);
@@ -887,7 +921,8 @@ static bool check_node(struct group *group,
                        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");