@echo srcs=$(srcs)
@echo objs=$(objs)
@echo hdrs=$(hdrs)
+ @echo DEFS=$(DEFS)
+ @echo CFLAGS=$(CFLAGS)
@echo CPPFLAGS=$(CPPFLAGS)
# requires 8.4+
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
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())
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;
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");
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;
struct testgroup_t groups[] = {
{ "aatree/", aatree_tests },
+ { "cxalloc/", cxalloc_tests },
{ "cbtree/", cbtree_tests },
{ "crypto/", crypto_tests },
{ "string/", string_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[];
--- /dev/null
+#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
+};
+
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;
+#include <usual/base.h>
+#include <string.h>
+#include <libgen.h>
+
+#undef basename
+#undef dirname
+
#include <usual/string.h>
#include "test_common.h"
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);
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);
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
/* 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 */
/* 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;
}
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;
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;
}
* 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);
}
/*
#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;
--- /dev/null
+
+/*
+ * 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,
+};
+
+
--- /dev/null
+
+#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
+
--- /dev/null
+
+/*
+ * 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;
+}
+
--- /dev/null
+
+#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
+
* HashItem must not be split across cachelines.
*/
-#include <usual/base.h>
+#include <usual/cxalloc.h>
#include <string.h>
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;
}
struct HashTab *tmp;
while (h) {
tmp = h->next;
- free(h);
+ cx_free(h->ca, h);
h = tmp;
}
}
/* 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;
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];
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);
* 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;
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;
}
*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;
if (*s++ != '{')
return NULL;
- lst = strlist_new();
+ lst = strlist_new(cx);
if (!lst)
return NULL;
goto failed;
}
if (val) {
- if (!parse_value(lst, val, s))
+ if (!parse_value(lst, val, s, cx))
goto failed;
}
return lst;
/* val done? */
if (*s == ',') {
- if (!parse_value(lst, val, s))
+ if (!parse_value(lst, val, s, cx))
goto failed;
val = ++s;
continue;
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
unsigned final_size;
unsigned total_count;
slab_init_fn init_func;
+ CxMem *cx;
};
/* 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);
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;
/* 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;
}
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 */
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);
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;
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);
if (obj) {
struct List *el = obj;
statlist_remove(&slab->obj_list, el - 1);
- free(el - 1);
+ cx_free(slab->cx, el - 1);
}
}
#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;
struct StrList {
struct StatList list;
+ CxMem *ca;
};
struct StrItem {
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);
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;
}
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)
#ifndef _USUAL_STRING_H_
#define _USUAL_STRING_H_
-#include <usual/base.h>
+#include <usual/cxalloc.h>
#include <string.h>
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);
* 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;
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;
if (sp) {
cbtree_walk(sp->tree, free_str, sp);
cbtree_destroy(sp->tree);
- free(sp);
+ cx_free(sp->ca, sp);
}
}
}
/* create */
- cstr = malloc(sizeof(*cstr) + len + 1);
+ cstr = cx_alloc(sp->ca, sizeof(*cstr) + len + 1);
if (!cstr)
return NULL;
cstr->pool = sp;
/* insert */
ok = cbtree_insert(sp->tree, cstr);
if (!ok) {
- free(cstr);
+ cx_free(sp->ca, cstr);
return NULL;
}
sp->count++;
#ifndef _USUAL_STRPOOL_H_
#define _USUAL_STRPOOL_H_
-#include <usual/base.h>
+#include <usual/cxalloc.h>
struct StrPool;
};
-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);