]> git.ozlabs.org Git - ccan/blobdiff - ccan/tal/talloc/talloc.c
tal/talloc: new module for backending tal onto talloc.
[ccan] / ccan / tal / talloc / talloc.c
diff --git a/ccan/tal/talloc/talloc.c b/ccan/tal/talloc/talloc.c
new file mode 100644 (file)
index 0000000..ad21b70
--- /dev/null
@@ -0,0 +1,256 @@
+/* Licensed under LGPL - see LICENSE file for details */
+#include <ccan/tal/talloc/talloc.h>
+#include <ccan/take/take.h>
+#include <errno.h>
+#include <assert.h>
+
+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;
+}