cxalloc: customizable allocator framework
authorMarko Kreen <markokr@gmail.com>
Fri, 23 Jul 2010 10:06:24 +0000 (13:06 +0300)
committerMarko Kreen <markokr@gmail.com>
Wed, 15 Sep 2010 11:53:17 +0000 (14:53 +0300)
There is a need to sometimes have different allocation policy
to some generic data structure than usual.

Instead of having each of them having allocator abstraction
on their own and both simple and customisable API, lets
have simple generic allocation API.

Initialized will take another CxMem *cx argument, and when
no customization is needed, put USUAL_ALLOC there.

25 files changed:
Makefile
test/Makefile
test/compile.c
test/test_cbtree.c
test/test_common.c
test/test_common.h
test/test_cxalloc.c [new file with mode: 0644]
test/test_pgutil.c
test/test_string.c
test/test_strpool.c
usual/cbtree.c
usual/cbtree.h
usual/cxalloc.c [new file with mode: 0644]
usual/cxalloc.h [new file with mode: 0644]
usual/cxextra.c [new file with mode: 0644]
usual/cxextra.h [new file with mode: 0644]
usual/hashtab-impl.h
usual/pgutil.c
usual/pgutil.h
usual/slab.c
usual/slab.h
usual/string.c
usual/string.h
usual/strpool.c
usual/strpool.h

index e807d3d1658140b7be73e2885d8dd0b0a6bb5dc9..7f79a4f673e6775f4e2bbed48c84e1f06b5b8d4c 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -114,6 +114,8 @@ dbg:
        @echo srcs=$(srcs)
        @echo objs=$(objs)
        @echo hdrs=$(hdrs)
+       @echo DEFS=$(DEFS)
+       @echo CFLAGS=$(CFLAGS)
        @echo CPPFLAGS=$(CPPFLAGS)
 
 # requires 8.4+
index 6b7de7900b3fb1eca5ee2f37f1f3b0e0faf93427..fff9c90911a886dcdea55420b1002daee9c8c787 100644 (file)
@@ -11,7 +11,8 @@ override USUAL_DIR = ..
 override DEFS = -DUSUAL_TEST_CONFIG
 OBJS = test_string.o test_crypto.o test_aatree.o test_heap.o \
        test_common.o test_list.o tinytest.o test_cbtree.o \
-       test_utf8.o test_strpool.o test_pgutil.o test_regex.o
+       test_utf8.o test_strpool.o test_pgutil.o test_regex.o \
+       test_cxalloc.o
 
 test-all: regtest
 
index 7c24e6b2946ed2348890211268fcedbdcfb87bdf..be28720491a5e8499f9a210cb35f94cb2830d215 100644 (file)
@@ -40,7 +40,7 @@ int main(void)
        char buf[128];
 
        aatree_init(&aatree, NULL, NULL);
-       cbtree = cbtree_create(NULL);
+       cbtree = cbtree_create(NULL, NULL, NULL, USUAL_ALLOC);
        daemonize(NULL, NULL);
        hash_lookup3("foo", 3);
        if (!event_init())
index 9904e9e3b1888d4d76957f604ddd5fd80898cb55..480170f3b72b2856643c6bac44b295981b9ae312 100644 (file)
@@ -10,7 +10,7 @@ struct MyNode {
        int len;
 };
 
-static unsigned int my_getkey(void *obj, const void **dst_p)
+static unsigned int my_getkey(void *ctx, void *obj, const void **dst_p)
 {
        struct MyNode *node = obj;
        *dst_p = node->str;
@@ -81,7 +81,7 @@ static void test_cbtree_basic(void *p)
        struct CBTree *tree;
        int i;
 
-       tree = cbtree_create(my_getkey);
+       tree = cbtree_create(my_getkey, NULL, NULL, USUAL_ALLOC);
 
        str_check(my_search(tree, 1), "not found");
 
@@ -138,7 +138,7 @@ static void test_cbtree_random(void *p)
        srandom(123123);
        memset(is_added, 0, sizeof(is_added));
 
-       tree = cbtree_create(my_getkey);
+       tree = cbtree_create(my_getkey, NULL, NULL, USUAL_ALLOC);
 
        while (total < 100000) {
                int r = random() & 15;
index e91f638a8e5d66cfbe97e274b5c1707792e2b464..e3cea2e514d6488db8534e5bc9b773e990ea62da 100644 (file)
@@ -4,6 +4,7 @@
 
 struct testgroup_t groups[] = {
        { "aatree/", aatree_tests },
+       { "cxalloc/", cxalloc_tests },
        { "cbtree/", cbtree_tests },
        { "crypto/", crypto_tests },
        { "string/", string_tests },
index 3622b69f2b534c73d07d72d344d8c670e206a363..ae48cc278288bf325048636f7114410f8da79c0c 100644 (file)
@@ -17,4 +17,5 @@ extern struct testcase_t utf8_tests[];
 extern struct testcase_t strpool_tests[];
 extern struct testcase_t pgutil_tests[];
 extern struct testcase_t regex_tests[];
+extern struct testcase_t cxalloc_tests[];
 
diff --git a/test/test_cxalloc.c b/test/test_cxalloc.c
new file mode 100644 (file)
index 0000000..54d9169
--- /dev/null
@@ -0,0 +1,120 @@
+#include "test_common.h"
+
+#include <stdarg.h>
+#include <usual/string.h>
+#include <usual/cxextra.h>
+
+static int delta = 0;
+
+static char logbuf[1024];
+
+static void reset(void)
+{
+       logbuf[0] = 0;
+}
+
+static void m_log(const char *fmt, ...)
+{
+       size_t len = strlen(logbuf);
+       va_list ap;
+
+       if (len && len < sizeof(logbuf) - 1)
+               logbuf[len++] = ' ';
+
+       va_start(ap, fmt);
+       vsnprintf(logbuf + len, sizeof(logbuf) - len, fmt, ap);
+       va_end(ap);
+
+       //printf("\n%s: delta=%d\n", logbuf + len, delta);
+}
+
+static void *log_alloc(void *ctx, size_t len)
+{
+       void *p;
+       m_log("A(%d)", (int)len);
+       delta += len;
+       p = cx_alloc(ctx, len + 8);
+       *(int*)p = len;
+       return (char *)p + 8;
+}
+
+static void *log_realloc(void *ctx, void *ptr, size_t len)
+{
+       char *p = (char *)ptr - 8;
+       int olen = *(int*)p;
+       m_log("R(%d)", (int)len);
+       p = cx_realloc(ctx, p, len + 8);
+       *(int*)p = len;
+       delta += len - olen;
+       return p + 8;
+}
+
+static void log_free(void *ctx, const void *ptr)
+{
+       char *p = (char *)ptr - 8;
+       int len = *(int*)p;
+       delta -= len;
+       m_log("F(%d)", len);
+       cx_free(ctx, p);
+}
+
+static const struct CxOps log_ops = {
+       .c_alloc = log_alloc,
+       .c_realloc = log_realloc,
+       .c_free = log_free,
+};
+
+static const struct CxMem log_libc = {
+       .ops = &log_ops,
+       .ctx = (void*)&cx_libc_allocator,
+};
+
+#define log_check(x) str_check(logbuf, x); reset();
+
+static void test_cxalloc_basic(void *zzz)
+{
+       CxMem *cx = &log_libc;
+       void *p;
+       delta = 0;
+       p = cx_alloc(cx, 16);
+       log_check("A(16)")
+       p = cx_realloc(cx, p, 500);
+       log_check("R(500)")
+       cx_free(cx, p);
+       log_check("F(500)");
+       int_check(delta, 0);
+end:
+       reset();
+}
+
+static void test_cxalloc_tree(void *zzz)
+{
+       CxMem *cx1, *cx2;
+       void *p;
+       delta = 0;
+       cx1 = cx_new_tree(&log_libc);
+       p = cx_alloc(cx1, 16);
+       p = cx_realloc(cx1, p, 500);
+       p = cx_realloc(cx1, p, 1500);
+       p = cx_alloc(cx1, 55);
+       cx_free(cx1, p);
+
+       cx2 = cx_new_tree(cx1);
+       p = cx_realloc(cx2, NULL, 2500);
+       cx2 = cx_new_tree(cx2);
+       p = cx_realloc(cx2, NULL, 3500);
+
+       cx_destroy(cx1);
+
+       //str_check(logbuf, "A(16)R(500)F()");
+       int_check(delta, 0);
+end:
+       reset();
+}
+
+struct testcase_t cxalloc_tests[] = {
+       { "basic", test_cxalloc_basic },
+       { "tree", test_cxalloc_tree },
+       END_OF_TESTCASES
+};
+
index 27663f3f569b50b835918a7c733bb94770cfdb2c..f4e3ede0e5c5b291ac71b799c759d70e7640f899 100644 (file)
@@ -118,7 +118,7 @@ end:;
 
 static char *aparse(const char *src)
 {
-       struct StrList *sl = pg_parse_array(src);
+       struct StrList *sl = pg_parse_array(src, USUAL_ALLOC);
        static char buf[1024];
        char *dst = buf;
        const char *s;
index 59673500899aec6fc71a6bcbec4121e411ebfa8d..40fd01d714b483129ca14e14605e9ab865a8b031 100644 (file)
@@ -1,4 +1,11 @@
 
+#include <usual/base.h>
+#include <string.h>
+#include <libgen.h>
+
+#undef basename
+#undef dirname
+
 #include <usual/string.h>
 
 #include "test_common.h"
index ca055460f0efab080d20dc8681464c35a3204f02..e0cd0c1e7c57281f9074428300e4681c0888d6de 100644 (file)
@@ -10,11 +10,11 @@ static void test_strpool(void *p)
        struct StrPool *pool;
        struct PStr *s;
 
-       pool = strpool_create();
+       pool = strpool_create(USUAL_ALLOC);
        tt_assert(pool);
        strpool_free(pool);
 
-       pool = strpool_create();
+       pool = strpool_create(USUAL_ALLOC);
        tt_assert(pool);
        int_check(strpool_total(pool), 0);
 
@@ -42,7 +42,7 @@ static void test_strpool(void *p)
        strpool_free(pool);
 
        /* free strc with strings */
-       pool = strpool_create();
+       pool = strpool_create(USUAL_ALLOC);
        tt_assert(pool);
        s = strpool_get(pool, "foo", -1);
        s = strpool_get(pool, "bar", 3);
index 390e9c968f79e3aa0d6f40c4936d86f33978cde9..c97dfe531763007a5819999b48a819f17bf97682 100644 (file)
@@ -44,11 +44,11 @@ struct Node {
 
 struct CBTree {
        struct Node *root;
-       cbtree_getkey_func get_key;
+       cbtree_getkey_func obj_key_cb;
+       cbtree_walker_func obj_free_cb;
+       void *cb_ctx;
 
-       cbtree_alloc_func cb_alloc;
-       cbtree_free_func cb_free;
-       void *alloc_arg;
+       CxMem *cx;
 };
 
 #define SAME_KEY 0xFFFFFFFF
@@ -86,7 +86,7 @@ static inline unsigned get_bit(unsigned bitpos, const unsigned char *key, unsign
 /* use callback to get key for a stored object */
 static inline unsigned get_key(struct CBTree *tree, void *obj, const void **key_p)
 {
-       return tree->get_key(obj, key_p);
+       return tree->obj_key_cb(tree->cb_ctx, obj, key_p);
 }
 
 /* check if object key matches argument */
@@ -175,7 +175,7 @@ void *cbtree_lookup(struct CBTree *tree, const void *key, unsigned klen)
 /* node allocation */
 static struct Node *new_node(struct CBTree *tree)
 {
-       struct Node *node = tree->cb_alloc(tree->alloc_arg, sizeof(*node));
+       struct Node *node = cx_alloc(tree->cx, sizeof(*node));
        memset(node, 0, sizeof(*node));
        return node;
 }
@@ -211,7 +211,7 @@ static bool insert_at(struct CBTree *tree, unsigned newbit, const void *key, uns
        return true;
 }
 
-/* actual insert: returns true -> insert ok or key found, false -> malloc failure */
+/* actual insert: returns true -> insert ok or key found, false -> alloc failure */
 bool cbtree_insert(struct CBTree *tree, void *obj)
 {
        const void *key, *old_key;
@@ -265,12 +265,14 @@ bool cbtree_delete(struct CBTree *tree, const void *key, unsigned klen)
        if (!key_matches(tree, obj, key, klen))
                return false;
 
+       if (tree->obj_free_cb)
+               tree->obj_free_cb(tree->cb_ctx, obj);
+
        /* drop the internal node pointing to our key */
        if (prev_pos) {
                tmp = *prev_pos;
                *prev_pos = (*prev_pos)->child[bit ^ 1];
-               if (tree->cb_free)
-                       tree->cb_free(tree->alloc_arg, tmp);
+               cx_free(tree->cx, tmp);
        } else {
                tree->root = NULL;
        }
@@ -281,57 +283,42 @@ bool cbtree_delete(struct CBTree *tree, const void *key, unsigned klen)
  * Management.
  */
 
-struct CBTree *cbtree_create_custom(cbtree_getkey_func get_key_fn,
-                                   void *arg,
-                                   cbtree_alloc_func f_alloc,
-                                   cbtree_free_func f_free)
+struct CBTree *cbtree_create(cbtree_getkey_func obj_key_cb,
+                            cbtree_walker_func obj_free_cb,
+                            void *cb_ctx,
+                            CxMem *cx)
 {
-       struct CBTree *tree = f_alloc(arg, sizeof(*tree));
+       struct CBTree *tree = cx_alloc(cx, sizeof(*tree));
        if (!tree)
                return NULL;
        tree->root = NULL;
-       tree->get_key = get_key_fn;
-       tree->alloc_arg = arg;
-       tree->cb_alloc = f_alloc;
-       tree->cb_free = f_free;
+       tree->cb_ctx = cb_ctx;
+       tree->obj_key_cb = obj_key_cb;
+       tree->obj_free_cb = obj_free_cb;
+       tree->cx = cx;
        return tree;
 }
 
-static void *std_alloc(void *arg, unsigned len)
-{
-       return malloc(len);
-}
-
-static void std_free(void *arg, void *ptr)
-{
-       free(ptr);
-}
-
-/* create takes user function to query object for it's key */
-struct CBTree *cbtree_create(cbtree_getkey_func get_key_fn)
-{
-       return cbtree_create_custom(get_key_fn, NULL, std_alloc, std_free);
-}
-
 /* recursive freeing */
 static void destroy_node(struct CBTree *tree, struct Node *node)
 {
-       if (is_node(node->child[0]))
+       if (is_node(node)) {
                destroy_node(tree, node->child[0]);
-       if (is_node(node->child[1]))
                destroy_node(tree, node->child[1]);
-       tree->cb_free(tree->alloc_arg, node);
+               cx_free(tree->cx, node);
+       } else if (tree->obj_free_cb) {
+               void *obj = get_external(node);
+               tree->obj_free_cb(tree->cb_ctx, obj);
+       }
 }
 
 /* Free tree and all it's internal nodes. */
 void cbtree_destroy(struct CBTree *tree)
 {
-       if (!tree->cb_free)
-               return;
-       if (tree->root && is_node(tree->root))
+       if (tree->root)
                destroy_node(tree, tree->root);
        tree->root = NULL;
-       tree->cb_free(tree->alloc_arg, tree);
+       cx_free(tree->cx, tree);
 }
 
 /*
index cb0f62b1143dd4cfea38e25908ec017014f92e25..bd58dbba7ff9372e0848455d1a27c077b1e0e6ac 100644 (file)
 #ifndef _USUAL_CBTREE_H_
 #define _USUAL_CBTREE_H_
 
-#include <usual/base.h>
+#include <usual/cxalloc.h>
 
 /* returns length of the key */
-typedef unsigned int   (*cbtree_getkey_func)(void *obj, const void **dst_p);
-/* custom alloc */
-typedef void *         (*cbtree_alloc_func)(void *arg, unsigned len);
-typedef void           (*cbtree_free_func)(void *arg, void *ptr);
+typedef unsigned int   (*cbtree_getkey_func)(void *ctx, void *obj, const void **dst_p);
 /* walk */
-typedef bool           (*cbtree_walker_func)(void *arg, void *obj);
+typedef bool           (*cbtree_walker_func)(void *ctx, void *obj);
 
 struct CBTree;
 
-struct CBTree *cbtree_create(cbtree_getkey_func get_key_fn);
-struct CBTree *cbtree_create_custom(cbtree_getkey_func get_key_fn,
-                                   void *arg,
-                                   cbtree_alloc_func f_alloc,
-                                   cbtree_free_func f_free);
+struct CBTree *cbtree_create(cbtree_getkey_func obj_key_cb,
+                            cbtree_walker_func obj_free_cb,
+                            void *cb_ctx,
+                            CxMem *cx);
 void cbtree_destroy(struct CBTree *tree);
 
 bool cbtree_insert(struct CBTree *tree, void *obj) _MUSTCHECK;
diff --git a/usual/cxalloc.c b/usual/cxalloc.c
new file mode 100644 (file)
index 0000000..6ea4abc
--- /dev/null
@@ -0,0 +1,115 @@
+
+/*
+ * Context Allocator framework.
+ *
+ * The idea is that each data structure is given a context to allocate from,
+ * and it can create subcontext for that.  It is slightly more work to
+ * use than palloc (PostgreSQL) or talloc (Samba), but it avoids the need
+ * to have big fully-featured framework, that does everything at once.
+ *
+ * Instead you have small task-specific allocators, and you can always fall
+ * back to raw malloc if you want to valgrind the code.
+ *
+ * Potential variants:
+ * - fully-featured pooled
+ * - randomly failing
+ * - logging
+ * - locking
+ * - guard signatures
+ * - palloc / talloc like API
+ */
+
+#include <usual/cxalloc.h>
+#include <usual/statlist.h>
+
+#include <string.h>
+
+/*
+ * Utility routines for cx_* API.
+ */
+
+void *cx_alloc(CxMem *cx, size_t len)
+{
+       if (!len)
+               return NULL;
+       return cx->ops->c_alloc(cx->ctx, len);
+}
+
+void *cx_realloc(CxMem *cx, void *ptr, size_t len)
+{
+       if (!ptr)
+               return cx_alloc(cx, len);
+       if (!len) {
+               cx_free(cx, ptr);
+               return NULL;
+       }
+       return cx->ops->c_realloc(cx->ctx, ptr, len);
+}
+
+void cx_free(CxMem *cx, const void *ptr)
+{
+       if (ptr)
+               cx->ops->c_free(cx->ctx, ptr);
+}
+
+void cx_destroy(CxMem *cx)
+{
+       if (!cx)
+               return;
+       if (!cx->ops->c_destroy)
+               abort();
+       cx->ops->c_destroy(cx->ctx);
+}
+
+void *cx_alloc0(CxMem *cx, size_t len)
+{
+       void *p = cx_alloc(cx, len);
+       if (p)
+               memset(p, 0, len);
+       return p;
+}
+
+void *cx_memdup(CxMem *cx, const void *src, size_t len)
+{
+       void *p = cx_alloc(cx, len);
+       if (p)
+               memcpy(p, src, len);
+       return p;
+}
+
+void *cx_strdup(CxMem *cx, const char *s)
+{
+       return cx_memdup(cx, s, strlen(s));
+}
+
+/*
+ * Base allocator that uses libc routines.
+ */
+
+static void *libc_alloc(void *ctx, size_t len)
+{
+       return malloc(len);
+}
+
+static void *libc_realloc(void *ctx, void *ptr, size_t len)
+{
+       return realloc(ptr, len);
+}
+
+static void libc_free(void *ctx, const void *ptr)
+{
+       free(ptr);
+}
+
+static const struct CxOps libc_alloc_ops = {
+       .c_alloc = libc_alloc,
+       .c_realloc = libc_realloc,
+       .c_free = libc_free,
+};
+
+const struct CxMem cx_libc_allocator = {
+       .ops = &libc_alloc_ops,
+       .ctx = NULL,
+};
+
+
diff --git a/usual/cxalloc.h b/usual/cxalloc.h
new file mode 100644 (file)
index 0000000..72325bf
--- /dev/null
@@ -0,0 +1,49 @@
+
+#ifndef _USUAL_CXALLOC_H_
+#define _USUAL_CXALLOC_H_
+
+#include <usual/base.h>
+
+/*
+ * Ops for allocator that takes context.
+ */
+struct CxOps {
+       void *(*c_alloc)(void *ctx, size_t len);
+       void *(*c_realloc)(void *ctx, void *p, size_t len);
+       void (*c_free)(void *ctx, const void *p);
+       void (*c_destroy)(void *ctx);
+};
+
+/*
+ * Specific memory allocation context.
+ */
+struct CxMem {
+       const struct CxOps *ops;
+       void *ctx;
+};
+
+typedef const struct CxMem CxMem;
+
+/*
+ * Basic operations on allocation context.
+ */
+
+void *cx_alloc(CxMem *cx, size_t len) _MALLOC;
+void *cx_realloc(CxMem *cx, void *ptr, size_t len);
+void cx_free(CxMem *cx, const void *ptr);
+void cx_destroy(CxMem *cx);
+void *cx_alloc0(CxMem *cx, size_t len) _MALLOC;
+void *cx_memdup(CxMem *cx, const void *src, size_t len) _MALLOC;
+void *cx_strdup(CxMem *cx, const char *str) _MALLOC;
+
+
+/* Allocator that uses libc malloc/realloc/free */
+extern CxMem cx_libc_allocator;
+
+/* Default allocator */
+#ifndef USUAL_ALLOC
+#define USUAL_ALLOC (&cx_libc_allocator)
+#endif
+
+#endif
+
diff --git a/usual/cxextra.c b/usual/cxextra.c
new file mode 100644 (file)
index 0000000..f2fe3b8
--- /dev/null
@@ -0,0 +1,316 @@
+
+/*
+ * Extra allocators
+ */
+
+#include <usual/cxextra.h>
+#include <usual/list.h>
+
+#include <string.h>
+
+/*
+ * Tools for allocators.
+ */
+
+static inline void *p_move(const void *p, int ofs)
+{
+       return (char *)p + ofs;
+}
+
+
+/*
+ * sample exit-on-failure wrapper
+ */
+
+static void *nofail_alloc(void *next, size_t len)
+{
+       void *p = cx_alloc(next, len);
+       if (!p)
+               exit(1);
+       return p;
+}
+
+static void *nofail_realloc(void *next, void *ptr, size_t len)
+{
+       void *p = cx_realloc(next, ptr, len);
+       if (!p)
+               exit(1);
+       return p;
+}
+
+static void nofail_free(void *next, const void *ptr)
+{
+       cx_free(next, ptr);
+}
+
+static void nofail_destroy(void *next)
+{
+       cx_destroy(next);
+}
+
+const struct CxOps cx_nofail_ops = {
+       .c_alloc = nofail_alloc,
+       .c_realloc = nofail_realloc,
+       .c_free = nofail_free,
+       .c_destroy = nofail_destroy,
+};
+
+const struct CxMem cx_libc_nofail = {
+       .ops = &cx_nofail_ops,
+       .ctx = (void*)&cx_libc_allocator,
+};
+
+/*
+ * Append-only pool.
+ */
+
+struct CxPoolSeg {
+       struct CxPoolSeg *prev;
+       unsigned size;
+       unsigned used;
+};
+
+struct CxPool {
+       struct CxMem this;
+       const struct CxMem *parent;
+       struct CxPoolSeg *last;
+       void *last_ptr;
+};
+#define POOL_HDR  ALIGN(sizeof(struct CxPoolSeg))
+
+static void *pool_alloc(void *ctx, size_t size)
+{
+       struct CxPool *pool = ctx;
+       struct CxPoolSeg *seg = pool->last;
+       void *ptr;
+       unsigned nsize;
+
+       size = ALIGN(size);
+       if (seg && seg->used + size <= seg->size) {
+               ptr = p_move(seg, POOL_HDR + seg->used);
+               seg->used += size;
+               pool->last_ptr = ptr;
+               return ptr;
+       } else {
+               nsize = seg ? (2 * seg->size) : 512;
+               while (nsize < size)
+                       nsize *= 2;
+               seg = cx_alloc(pool->parent, POOL_HDR + nsize);
+               if (seg == NULL)
+                       return NULL;
+               seg->used = size;
+               seg->size = nsize;
+               seg->prev = pool->last;
+               pool->last = seg;
+               ptr = p_move(seg, POOL_HDR);
+               pool->last_ptr = ptr;
+               return ptr;
+       }
+}
+
+/* free only last item */
+static void pool_free(void *ctx, const void *ptr)
+{
+       struct CxPool *pool = ctx;
+       struct CxPoolSeg *cur = pool->last;
+       const char *cstart;
+
+       if (pool->last_ptr != ptr)
+               return;
+       cstart = p_move(cur, POOL_HDR);
+       cur->used = (char *)ptr - cstart;
+       pool->last_ptr = NULL;
+}
+
+/* realloc only last item */
+static void *pool_realloc(void *ctx, void *ptr, size_t len)
+{
+       struct CxPool *pool = ctx;
+       struct CxPoolSeg *seg = pool->last;
+       char *cstart, *cused, *p = ptr;
+       size_t olen;
+
+       if (pool->last_ptr != ptr)
+               return NULL;
+
+       cstart = p_move(seg, POOL_HDR);
+       cused = cstart + seg->used;
+
+       olen = cused - p;
+       if (seg->used - olen + len <= seg->size) {
+               seg->used = p + len - cstart;
+               return p;
+       } else {
+               p = pool_alloc(ctx, len);
+               if (!p)
+                       return NULL;
+               memcpy(p, ptr, olen);
+               return p;
+       }
+}
+
+static void pool_destroy(void *ctx)
+{
+       struct CxPool *pool = ctx;
+       struct CxPoolSeg *cur, *tmp;
+       if (!pool)
+               return;
+       for (cur = pool->last; cur; ) {
+               tmp = cur->prev;
+               cx_free(pool->parent, cur);
+               cur = tmp;
+       }
+       cx_free(pool->parent, pool);
+}
+
+static const struct CxOps pool_ops = {
+       .c_alloc = pool_alloc,
+       .c_realloc = pool_realloc,
+       .c_free = pool_free,
+       .c_destroy = pool_destroy,
+};
+
+/*
+ * public functions
+ */
+
+CxMem *cx_new_pool(CxMem *parent)
+{
+       struct CxPool *head;
+
+       head = cx_alloc(parent, sizeof(*head));
+       if (!head)
+               return NULL;
+       head->parent = parent;
+       head->this.ops = &pool_ops;
+       head->this.ctx = head;
+       head->last = NULL;
+       return &head->this;
+}
+
+/*
+ * tree alloc
+ */
+
+#define TREE_HDR (int)(sizeof(struct CxTreeItem))
+
+struct CxTree {
+       struct CxMem this;
+       CxMem *real;
+       struct List alloc_list;
+       struct List subtree_node;
+       struct List subtree_list;
+};
+
+/* header for each allocation */
+struct CxTreeItem {
+       struct List node;
+};
+
+static void *tree_alloc(void *ctx, size_t len)
+{
+       struct CxTree *tree = ctx;
+       struct CxTreeItem *item;
+
+       item = cx_alloc(tree->real, TREE_HDR + len);
+       if (!item)
+               return NULL;
+       list_init(&item->node);
+       list_append(&tree->alloc_list, &item->node);
+
+       return p_move(item, TREE_HDR);
+}
+
+static void *tree_realloc(void *ctx, void *ptr, size_t len)
+{
+       struct CxTree *t = ctx;
+       struct CxTreeItem *item, *item2;
+       item = p_move(ptr, -TREE_HDR);
+
+       list_del(&item->node);
+       item2 = cx_realloc(t->real, item, TREE_HDR + len);
+       if (item2) {
+               list_append(&t->alloc_list, &item2->node);
+               return p_move(item2, TREE_HDR);
+       } else {
+               list_append(&t->alloc_list, &item->node);
+               return NULL;
+       }
+}
+
+static void tree_free(void *ctx, const void *ptr)
+{
+       struct CxTree *t = ctx;
+       struct CxTreeItem *item;
+
+       item = p_move(ptr, -TREE_HDR);
+       list_del(&item->node);
+       cx_free(t->real, item);
+}
+
+static void tree_destroy(void *ctx)
+{
+       struct CxTree *tree = ctx, *sub;
+       struct CxTreeItem *item;
+       struct List *el, *tmp;
+
+       /* unregister from parent */
+       list_del(&tree->subtree_node);
+
+       /* free elements */
+       list_for_each_safe(el, &tree->alloc_list, tmp) {
+               list_del(el);
+               item = container_of(el, struct CxTreeItem, node);
+               cx_free(tree->real, item);
+       }
+
+       /* free subtrees */
+       list_for_each_safe(el, &tree->subtree_list, tmp) {
+               sub = container_of(el, struct CxTree, subtree_node);
+               tree_destroy(sub);
+       }
+
+       /* free base struct */
+       cx_free(tree->real, tree);
+}
+
+static const struct CxOps tree_ops = {
+       .c_alloc = tree_alloc,
+       .c_realloc = tree_realloc,
+       .c_free = tree_free,
+       .c_destroy = tree_destroy,
+};
+
+
+CxMem *cx_new_tree(CxMem *cx)
+{
+       struct CxTree *t, *parent = NULL;
+       CxMem *real = cx;
+
+       /*
+        * Try to allocate from real allocator.  Otherwise allocations
+        * will have double headers.
+        */
+       if (cx->ops == &tree_ops) {
+               parent = cx->ctx;
+               real = parent->real;
+       }
+
+       /* initialize */
+       t = cx_alloc(real, sizeof(*t));
+       if (!t)
+               return NULL;
+       t->real = real;
+       t->this.ops = &tree_ops;
+       t->this.ctx = t;
+       list_init(&t->alloc_list);
+       list_init(&t->subtree_node);
+       list_init(&t->subtree_list);
+
+       /* register at parent */
+       if (parent)
+               list_append(&parent->subtree_list, &t->subtree_node);
+
+       return &t->this;
+}
+
diff --git a/usual/cxextra.h b/usual/cxextra.h
new file mode 100644 (file)
index 0000000..fb50bdf
--- /dev/null
@@ -0,0 +1,31 @@
+
+#ifndef _USUAL_CXEXTRA_H_
+#define _USUAL_CXEXTRA_H_
+
+#include <usual/cxalloc.h>
+
+/* wraps another allocator, exits on allocation failure */
+extern const struct CxOps cx_nofail_ops;
+/* nofail for libc */
+extern CxMem cx_libc_nofail;
+
+/*
+ * Creates allocator that pools all memory together,
+ * without keeping track of single objects, to be
+ * freed all together in one shot.
+ *
+ * realloc(), free() are partially supported for the last
+ * objec only.
+ */
+CxMem *cx_new_pool(CxMem *parent);
+
+/*
+ * Creates allocator that remebers all allocations done
+ * under it and allows all of it to be freed together.
+ *
+ * Supports hierarchical trees.
+ */
+CxMem *cx_new_tree(CxMem *parent);
+
+#endif
+
index a9482a6ab82b850c70fc8b732f8df6ce2bf9da1f..9fc5e400aeed3c4031744fee97dc54c46096805e 100644 (file)
@@ -26,7 +26,7 @@
  *   HashItem must not be split across cachelines.
  */
 
-#include <usual/base.h>
+#include <usual/cxalloc.h>
 
 #include <string.h>
 
@@ -69,18 +69,22 @@ typedef bool (*hash_cmp_fn)(const htab_val_t curval, const void *arg);
 struct HashTab {
        struct HashTab *next;
        hash_cmp_fn cmp_fn;
+       CxMem *ca;
        unsigned size;
        unsigned used;
        struct HashItem tab[];
 };
 
-static struct HashTab *hashtab_create(unsigned size, hash_cmp_fn cmp_fn)
+static struct HashTab *hashtab_create(unsigned size, hash_cmp_fn cmp_fn, CxMem *ca)
 {
        struct HashTab *h;
        unsigned len = size * sizeof(struct HashItem) + offsetof(struct HashTab, tab);
-       h = zmalloc(len);
-       h->size = size;
-       h->cmp_fn = cmp_fn;
+       h = cx_alloc(ca, len);
+       if (h) {
+               h->size = size;
+               h->cmp_fn = cmp_fn;
+               h->ca = ca;
+       }
        return h;
 }
 
@@ -89,7 +93,7 @@ static void hashtab_destroy(struct HashTab *h)
        struct HashTab *tmp;
        while (h) {
                tmp = h->next;
-               free(h);
+               cx_free(h->ca, h);
                h = tmp;
        }
 }
@@ -124,7 +128,7 @@ loop:
        /* insert */
        if (h->used >= MAX_USED(h)) {
                struct HashTab *tmp;
-               tmp = hashtab_create(h->size, h->cmp_fn);
+               tmp = hashtab_create(h->size, h->cmp_fn, h->ca);
                if (!tmp)
                        return NULL;
                h->next = tmp;
@@ -205,7 +209,7 @@ static struct HashTab *hashtab_copy(struct HashTab *h_old, unsigned newsize)
        struct HashTab *h_new;
        unsigned i;
 
-       h_new = hashtab_create(newsize, h_old->cmp_fn);
+       h_new = hashtab_create(newsize, h_old->cmp_fn, h_old->ca);
        for (; h_old; h_old = h_old->next) {
                for (i = 0; i < h_old->size; i++) {
                        struct HashItem *s = &h_old->tab[i];
@@ -230,7 +234,7 @@ static inline void _hashtab_example(void)
        unsigned nitem, nlink;
        struct HashTab *h, *h2;
        
-       h = hashtab_create(1024, NULL);
+       h = hashtab_create(1024, NULL, USUAL_ALLOC);
        hashtab_lookup(h, 123, true, NULL);
        hashtab_stats(h, &nitem, &nlink);
        h2 = hashtab_copy(h, 2048);
index 3e71a6c325c40ab4743828edb1e9ad3d3ed44650..a81f74bbbd18d51332e659e3e5bdd5bf94d85897 100644 (file)
@@ -143,7 +143,8 @@ bool pg_quote_fqident(char *_dst, const char *_src, int dstlen)
  * pgarray parsing
  */
 
-static bool parse_value(struct StrList *arr, const char *val, const char *vend)
+static bool parse_value(struct StrList *arr, const char *val, const char *vend,
+                       CxMem *cx)
 {
        int len;
        const char *s;
@@ -161,7 +162,7 @@ static bool parse_value(struct StrList *arr, const char *val, const char *vend)
        if (len == 4 && !strncasecmp(val, "null", len)) {
                return strlist_append_ref(arr, NULL);
        }
-       p = str = malloc(len + 1);
+       p = str = cx_alloc(cx, len + 1);
        if (!str)
                return false;
 
@@ -185,13 +186,13 @@ static bool parse_value(struct StrList *arr, const char *val, const char *vend)
        }
        *p++ = 0;
        if (!strlist_append_ref(arr, str)) {
-               free(str);
+               cx_free(cx, str);
                return false;
        }
        return true;
 }
 
-struct StrList *pg_parse_array(const char *pgarr)
+struct StrList *pg_parse_array(const char *pgarr, CxMem *cx)
 {
        const char *s = pgarr;
        struct StrList *lst;
@@ -208,7 +209,7 @@ struct StrList *pg_parse_array(const char *pgarr)
        if (*s++ != '{')
                return NULL;
 
-       lst = strlist_new();
+       lst = strlist_new(cx);
        if (!lst)
                return NULL;
 
@@ -219,7 +220,7 @@ struct StrList *pg_parse_array(const char *pgarr)
                                goto failed;
                        }
                        if (val) {
-                               if (!parse_value(lst, val, s))
+                               if (!parse_value(lst, val, s, cx))
                                        goto failed;
                        }
                        return lst;
@@ -231,7 +232,7 @@ struct StrList *pg_parse_array(const char *pgarr)
 
                /* val done? */
                if (*s == ',') {
-                       if (!parse_value(lst, val, s))
+                       if (!parse_value(lst, val, s, cx))
                                goto failed;
                        val = ++s;
                        continue;
index 999bc600fb5d9c4a90c4f2ac50dbb985e05cb278..822eb3f7efa3da291a411bc18f17e83a80604b92 100644 (file)
@@ -9,7 +9,7 @@ bool pg_is_reserved_word(const char *str);
 bool pg_quote_literal(char *_dst, const char *_src, int dstlen);
 bool pg_quote_ident(char *_dst, const char *_src, int dstlen);
 bool pg_quote_fqident(char *_dst, const char *_src, int dstlen);
-struct StrList *pg_parse_array(const char *pgarr);
+struct StrList *pg_parse_array(const char *pgarr, CxMem *cx);
 
 #endif
 
index 19db61a8703dcc89866092419a20eddb7db7e4af..20a1ad373d287985375c38df327749c3150ce734 100644 (file)
@@ -45,6 +45,7 @@ struct Slab {
        unsigned final_size;
        unsigned total_count;
        slab_init_fn  init_func;
+       CxMem *cx;
 };
 
 
@@ -74,7 +75,8 @@ static void slab_list_remove(struct Slab *slab)
 
 /* fill struct contents */
 static void init_slab(struct Slab *slab, const char *name, unsigned obj_size,
-                     unsigned align, slab_init_fn init_func)
+                     unsigned align, slab_init_fn init_func,
+                     CxMem *cx)
 {
        unsigned slen = strlen(name);
 
@@ -83,6 +85,7 @@ static void init_slab(struct Slab *slab, const char *name, unsigned obj_size,
        statlist_init(&slab->fraglist, name);
        slab->total_count = 0;
        slab->init_func = init_func;
+       slab->cx = cx;
 
        if (slen >= sizeof(slab->name))
                slen = sizeof(slab->name) - 1;
@@ -99,14 +102,15 @@ static void init_slab(struct Slab *slab, const char *name, unsigned obj_size,
 
 /* make new slab */
 struct Slab *slab_create(const char *name, unsigned obj_size, unsigned align,
-                        slab_init_fn init_func)
+                        slab_init_fn init_func,
+                        CxMem *cx)
 {
        struct Slab *slab;
 
        /* new slab object */
-       slab = calloc(1, sizeof(*slab));
+       slab = cx_alloc(cx, sizeof(*slab));
        if (slab)
-               init_slab(slab, name, obj_size, align, init_func);
+               init_slab(slab, name, obj_size, align, init_func, cx);
        return slab;
 }
 
@@ -122,9 +126,9 @@ void slab_destroy(struct Slab *slab)
        slab_list_remove(slab);
        statlist_for_each_safe(item, &slab->fraglist, tmp) {
                frag = container_of(item, struct SlabFrag, head);
-               free(frag);
+               cx_free(slab->cx, frag);
        }
-       free(slab);
+       cx_free(slab->cx, slab);
 }
 
 /* add new block of objects to slab */
@@ -143,7 +147,7 @@ static void grow(struct Slab *slab)
        size = count * slab->final_size;
 
        /* allocate & init */
-       frag = calloc(1, size + sizeof(struct SlabFrag));
+       frag = cx_alloc(slab->cx, size + sizeof(struct SlabFrag));
        if (!frag)
                return;
        list_init(&frag->head);
@@ -229,16 +233,19 @@ struct Slab {
        int size;
        struct StatList obj_list;
        slab_init_fn init_func;
+       CxMem *cx;
 };
 
 
 struct Slab *slab_create(const char *name, unsigned obj_size, unsigned align,
-                            slab_init_fn init_func)
+                            slab_init_fn init_func,
+                            CxMem *cx)
 {
-       struct Slab *s = malloc(sizeof(*s));
+       struct Slab *s = cx_alloc(cx, sizeof(*s));
        if (s) {
                s->size = obj_size;
                s->init_func = init_func;
+               s->cx = cx;
                statlist_init(&s->obj_list, "obj_list");
        }
        return s;
@@ -249,16 +256,16 @@ void slab_destroy(struct Slab *slab)
        struct List *el, *tmp;
        statlist_for_each_safe(el, &slab->obj_list, tmp) {
                statlist_remove(&slab->obj_list, el);
-               free(el);
+               cx_free(slab->cx, el);
        }
-       free(slab);
+       cx_free(slab->cx, slab);
 }
 
 void *slab_alloc(struct Slab *slab)
 {
        struct List *o;
        void *res;
-       o = calloc(1, sizeof(struct List) + slab->size);
+       o = cx_alloc(slab->cx, sizeof(struct List) + slab->size);
        if (!o)
                return NULL;
        list_init(o);
@@ -274,7 +281,7 @@ void slab_free(struct Slab *slab, void *obj)
        if (obj) {
                struct List *el = obj;
                statlist_remove(&slab->obj_list, el - 1);
-               free(el - 1);
+               cx_free(slab->cx, el - 1);
        }
 }
 
index dcf3f8eec7791766774ab8a1eb0556250d0bd3a8..0c1660cde6faac17fa5b3c2e55319ba4821d5408 100644 (file)
 #ifndef _USUAL_SLAB_H_
 #define _USUAL_SLAB_H_
 
-#include <usual/base.h>
+#include <usual/cxalloc.h>
 
 struct Slab;
 
 typedef void (*slab_init_fn)(void *obj);
 
 struct Slab *slab_create(const char *name, unsigned obj_size, unsigned align,
-                            slab_init_fn init_func);
+                            slab_init_fn init_func,
+                            CxMem *cx);
 void slab_destroy(struct Slab *slab);
 
 void * slab_alloc(struct Slab *slab) _MALLOC _MUSTCHECK;
index 3eb93ff14dedc3c1a150767a2c6fbf3f243dedd0..5ce75c019c9e848b038ab0bc0c6c26207a963042 100644 (file)
@@ -30,6 +30,7 @@
 
 struct StrList {
        struct StatList list;
+       CxMem *ca;
 };
 
 struct StrItem {
@@ -47,19 +48,19 @@ bool strlist_append(struct StrList *slist, const char *str)
        const char *nstr = NULL;
        bool ok;
        if (str) {
-               nstr = strdup(str);
+               nstr = cx_strdup(slist->ca, str);
                if (!nstr)
                        return false;
        }
        ok = strlist_append_ref(slist, nstr);
        if (!ok)
-               free(nstr);
+               cx_free(slist->ca, nstr);
        return ok;
 }
 
 bool strlist_append_ref(struct StrList *slist, const char *str)
 {
-       struct StrItem *item = calloc(1, sizeof(*item));
+       struct StrItem *item = cx_alloc(slist->ca, sizeof(*item));
        if (!item)
                return false;
        list_init(&item->node);
@@ -80,16 +81,17 @@ const char *strlist_pop(struct StrList *slist)
 
        item = container_of(el, struct StrItem, node);
        str = item->str;
-       free(item);
+       cx_free(slist->ca, item);
        return str;
 }
 
-struct StrList *strlist_new(void)
+struct StrList *strlist_new(CxMem *ca)
 {
-       struct StrList *slist = calloc(1, sizeof(*slist));
+       struct StrList *slist = cx_alloc0(ca, sizeof(*slist));
        if (!slist)
                return NULL;
        statlist_init(&slist->list, "strlist");
+       slist->ca = ca;
        return slist;
 }
 
@@ -101,9 +103,9 @@ void strlist_free(struct StrList *slist)
        while (!strlist_empty(slist)) {
                s = strlist_pop(slist);
                if (s)
-                       free(s);
+                       cx_free(slist->ca, s);
        }
-       free(slist);
+       cx_free(slist->ca, slist);
 }
 
 bool strlist_foreach(const struct StrList *slist, str_cb func, void *arg)
index 80f5f356238e5f15b11e76ceb14f519fad9a3f09..e9fcc3f186dbc506e35525179e572feb53cd1521 100644 (file)
@@ -19,7 +19,7 @@
 #ifndef _USUAL_STRING_H_
 #define _USUAL_STRING_H_
 
-#include <usual/base.h>
+#include <usual/cxalloc.h>
 
 #include <string.h>
 
@@ -45,7 +45,7 @@ void *memrchr(const void *s, int c, size_t n);
 typedef bool (*str_cb)(void *arg, const char *s);
 
 struct StrList;
-struct StrList *strlist_new(void);
+struct StrList *strlist_new(CxMem *ca);
 void strlist_free(struct StrList *slist);
 bool strlist_empty(struct StrList *slist);
 bool strlist_append(struct StrList *slist, const char *str);
index 85cceba318ca32ead79ed2a1b7c656cd8f12cae9..9388ecbef76768d5f92833236aeca8bfeeb87af4 100644 (file)
  * Put all strings into cbtree.
  */
 struct StrPool {
+       CxMem *ca;
        struct CBTree *tree;
        int count;
 };
 
 /* pass key info to cbtree */
-static unsigned get_key(void *obj, const void **dst_p)
+static unsigned get_key(void *ctx, void *obj, const void **dst_p)
 {
        struct PStr *s = obj;
        *dst_p = s->str;
@@ -41,21 +42,26 @@ static unsigned get_key(void *obj, const void **dst_p)
 static bool free_str(void *arg, void *obj)
 {
        struct PStr *p = obj;
+       struct StrPool *sp = p->pool;
+
        memset(p, 0, offsetof(struct PStr, str) + 1);
-       free(obj);
+       cx_free(sp->ca, obj);
        return true;
 }
 
 /* create main structure */
-struct StrPool *strpool_create(void)
+struct StrPool *strpool_create(CxMem *ca)
 {
        struct StrPool *sp;
 
-       sp = malloc(sizeof(*sp));
+       sp = cx_alloc(ca, sizeof(*sp));
+       if (!sp)
+               return NULL;
        sp->count = 0;
-       sp->tree = cbtree_create(get_key);
+       sp->ca = ca;
+       sp->tree = cbtree_create(get_key, NULL, NULL, ca);
        if (!sp->tree) {
-               free(sp);
+               cx_free(ca, sp);
                return NULL;
        }
        return sp;
@@ -67,7 +73,7 @@ void strpool_free(struct StrPool *sp)
        if (sp) {
                cbtree_walk(sp->tree, free_str, sp);
                cbtree_destroy(sp->tree);
-               free(sp);
+               cx_free(sp->ca, sp);
        }
 }
 
@@ -94,7 +100,7 @@ struct PStr *strpool_get(struct StrPool *sp, const char *str, int len)
        }
 
        /* create */
-       cstr = malloc(sizeof(*cstr) + len + 1);
+       cstr = cx_alloc(sp->ca, sizeof(*cstr) + len + 1);
        if (!cstr)
                return NULL;
        cstr->pool = sp;
@@ -105,7 +111,7 @@ struct PStr *strpool_get(struct StrPool *sp, const char *str, int len)
        /* insert */
        ok = cbtree_insert(sp->tree, cstr);
        if (!ok) {
-               free(cstr);
+               cx_free(sp->ca, cstr);
                return NULL;
        }
        sp->count++;
index 02d7a5ae531cacc64ae24cf2e9e0bb36ec2ba250..2cf13e2aadbfd344077ee94626827454ad02e9b0 100644 (file)
@@ -19,7 +19,7 @@
 #ifndef _USUAL_STRPOOL_H_
 #define _USUAL_STRPOOL_H_
 
-#include <usual/base.h>
+#include <usual/cxalloc.h>
 
 struct StrPool;
 
@@ -32,7 +32,7 @@ struct PStr {
 };
 
 
-struct StrPool *strpool_create(void);
+struct StrPool *strpool_create(CxMem *ca);
 void strpool_free(struct StrPool *sp);
 
 struct PStr *strpool_get(struct StrPool *sp, const char *str, int len);