summaryrefslogtreecommitdiffhomepage
path: root/src/dalist_memfn.c
diff options
context:
space:
mode:
authormidipix <writeonce@midipix.org>2015-04-12 12:23:25 -0400
committermidipix <writeonce@midipix.org>2015-04-12 12:23:25 -0400
commite09104e6e294bed185227d5a2065d7a1877562b9 (patch)
tree653a13f382f8d3616af9e33d307958d6888ac93f /src/dalist_memfn.c
parentcb9b22e21865f75bb968ec6c27952a230e4dc527 (diff)
downloaddalist-e09104e6e294bed185227d5a2065d7a1877562b9.tar.bz2
dalist-e09104e6e294bed185227d5a2065d7a1877562b9.tar.xz
dalist: initial commit.
Diffstat (limited to 'src/dalist_memfn.c')
-rw-r--r--src/dalist_memfn.c267
1 files changed, 267 insertions, 0 deletions
diff --git a/src/dalist_memfn.c b/src/dalist_memfn.c
new file mode 100644
index 0000000..e1fa9f0
--- /dev/null
+++ b/src/dalist_memfn.c
@@ -0,0 +1,267 @@
+/*****************************************************************************/
+/* dalist: a zero-dependency book-keeping library */
+/* Copyright (C) 2013,2014,2015 Z. Gilboa */
+/* Released under GPLv2 and GPLv3; see COPYING.DALIST. */
+/*****************************************************************************/
+
+#include <dalist/dalist.h>
+#include "dalist_impl.h"
+
+/* private prototypes */
+static void * dalist_alloc(struct dalist_ex * dlist);
+
+static int dalist_memfn_internal(
+ struct dalist_ex * dlist,
+ void ** addr,
+ size_t * alloc_size);
+
+
+dalist_api
+int dalist_get_free_node(
+ struct dalist_ex * dlist,
+ void ** fnode)
+{
+ int ret;
+ struct dalist_node * nfree;
+
+ /* in case we fail */
+ *fnode = (void *)0;
+
+ if (dlist->free)
+ /* allocation is not needed */
+ nfree = (struct dalist_node *)dlist->free;
+ else
+ nfree = (struct dalist_node *)dalist_alloc(dlist);
+
+ if (nfree) {
+ *fnode = nfree;
+ dlist->free = nfree->next;
+
+ if (nfree->next) /* new head of dlist->free */
+ nfree->next->prev = 0;
+
+ /* bookkeeping */
+ dlist->info.free_nodes--;
+
+ /* and for good measure */
+ nfree->prev = nfree->next = 0;
+ ret = DALIST_OK;
+ } else
+ ret = DALIST_EMEMFN;
+
+ return ret;
+}
+
+
+dalist_api
+int dalist_deposit_free_node(
+ struct dalist_ex * dlist,
+ void * fnode)
+{
+ int ret;
+ struct dalist_node * nfree;
+
+ if (fnode) {
+ nfree = (struct dalist_node *)fnode;
+ nfree->next = (struct dalist_node *)dlist->free;
+
+ /* fnode is now the head free node */
+ nfree->prev = 0;
+ dlist->free = fnode;
+
+ /* bookkeeping */
+ dlist->info.free_nodes++;
+ ret = DALIST_OK;
+ } else {
+ ret = DALIST_ENODE;
+ }
+
+ return ret;
+}
+
+
+dalist_api
+int dalist_deposit_memory_block(
+ struct dalist_ex * dlist,
+ void * addr,
+ size_t alloc_size)
+{
+ struct dalist_node * fnode;
+ struct dalist_node * fnode_next;
+ char * naddr;
+ char * naddr_next;
+ char * naddr_upper;
+ size_t node_size;
+
+ /* node_size: before alignment */
+ if (dlist->dblock_size == 0)
+ node_size = sizeof(struct dalist_node);
+ else
+ node_size = (size_t)&((struct dalist_node_ex *)0)->dblock
+ + dlist->dblock_size;
+
+ /* node_size: after alignment */
+ node_size += sizeof(uintptr_t)-1;
+ node_size |= sizeof(uintptr_t)-1;
+ node_size ^= sizeof(uintptr_t)-1;
+
+ /* validate block size */
+ if (alloc_size < node_size)
+ return DALIST_EMEMBLOCK;
+
+ /* marks */
+ naddr = (char *)addr;
+ naddr_next = naddr + node_size;
+ naddr_upper = naddr + alloc_size;
+
+ /* chain of free nodes */
+ while ((naddr_next + node_size) < naddr_upper) {
+ fnode = (struct dalist_node *)naddr;
+ fnode_next = (struct dalist_node *)naddr_next;
+
+ fnode->next = fnode_next;
+ fnode_next->prev = fnode;
+
+ naddr += node_size;
+ naddr_next += node_size;
+ }
+
+ /* free nodes block: head and tail */
+ fnode = (struct dalist_node *)addr;
+ fnode_next = (struct dalist_node *)naddr;
+
+ if (dlist->free) {
+ /* link with pre-allocated free nodes */
+ fnode_next->next = (struct dalist_node *)dlist->free;
+ ((struct dalist_node *)dlist->free)->prev = fnode_next;
+ } else {
+ /* no pre-allocated free nodes */
+ fnode_next->next = 0;
+ }
+
+ /* fnode is now the head free node */
+ fnode->prev = 0;
+ dlist->free = fnode;
+
+ /* bookkeeping */
+ dlist->info.free_nodes += (alloc_size / node_size);
+
+ return DALIST_OK;
+}
+
+
+static void * __cdecl dalist_alloc(struct dalist_ex * dlist)
+{
+ int ret;
+ void * addr;
+ size_t alloc_size;
+ memfn_custom * pmemfn;
+ struct dalist_node * fnode;
+
+ /* pmemfn */
+ if (dlist->memfn_method == DALIST_MEMFN_CUSTOM)
+ pmemfn = (dalist_memfn_custom *)dlist->memfn_ptr;
+ else
+ pmemfn = dalist_memfn_internal;
+
+ /* allocate: try */
+ fnode = 0;
+ ret = pmemfn(dlist,&addr,&alloc_size);
+
+ /* allocate: verify */
+ if (ret == DALIST_OK) {
+ if (dlist->memfn_method == DALIST_MEMFN_MALLOC)
+ fnode = (struct dalist_node *)addr;
+ else if (dalist_deposit_memory_block(
+ dlist,
+ addr,
+ alloc_size) == DALIST_OK)
+ fnode = (struct dalist_node *)dlist->free;
+ }
+
+ return fnode;
+}
+
+
+static int __cdecl dalist_memfn_internal(
+ struct dalist_ex * dlist,
+ void ** address,
+ size_t * alloc_size)
+{
+ int ret;
+ void * addr;
+ size_t size;
+
+ memfn_mmap * pfn_mmap;
+ memfn_malloc * pfn_malloc;
+ memfn_allocvm * pfn_allocvm;
+
+ switch (dlist->memfn_method) {
+ case DALIST_MEMFN_MALLOC:
+ if (dlist->dblock_size == 0)
+ size = sizeof(struct dalist_node);
+ else
+ size = (size_t)((struct dalist_node_ex *)0)->dblock
+ + dlist->dblock_size;
+
+ pfn_malloc = (memfn_malloc * )dlist->memfn_ptr;
+ addr = pfn_malloc(size);
+
+ if (addr) {
+ *address = addr;
+ *alloc_size = size;
+ ret = dlist->memfn_status = DALIST_OK;
+ } else {
+ ret = DALIST_EMEMFN;
+ dlist->memfn_status = dalist_errno(ret);
+ }
+ break;
+
+ case DALIST_MEMFN_MMAP:
+ pfn_mmap = (memfn_mmap * )dlist->memfn_ptr;
+
+ addr = pfn_mmap(
+ (void *)0,
+ dlist->lblock_size,
+ PROT_READ | PROT_WRITE,
+ MAP_ANON | MAP_SHARED,
+ 0,0);
+
+ if (addr) {
+ *address = addr;
+ *alloc_size = dlist->lblock_size;
+ ret = dlist->memfn_status = DALIST_OK;
+ } else {
+ ret = DALIST_EMEMFN;
+ dlist->memfn_status = dalist_errno(ret);
+ }
+ break;
+
+ case DALIST_MEMFN_NT_ALLOCATE_VIRTUAL_MEMORY:
+ addr = (void *)0;
+ size = dlist->lblock_size;
+ pfn_allocvm = (memfn_allocvm * )dlist->memfn_ptr;
+
+ dlist->memfn_status = pfn_allocvm(
+ NT_CURRENT_PROCESS_HANDLE,
+ &addr,
+ 0,
+ &size,
+ NT_MEM_COMMIT,
+ NT_PAGE_READWRITE);
+
+ if (dlist->memfn_status == NT_STATUS_SUCCESS) {
+ *address = addr;
+ *alloc_size = size;
+ ret = DALIST_OK;
+ } else {
+ ret = DALIST_EMEMFN;
+ }
+ break;
+ default:
+ ret = DALIST_EINTERNAL;
+ break;
+ }
+
+ return ret;
+}