--- /dev/null
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <string.h>
+#include <errno.h>
+#include <corax/spinlock.h>
+#include <crxstd.h>
+
+#define FLAG_IN_USE (1 << 0)
+#define FLAG_INSECURE (1 << 1)
+#define WASTE_MAX 32
+#define HEAP_FLAGS_DEFAULT 0
+#define PAGE_SIZE 4096
+
+struct heap_node {
+ struct heap_node *next;
+ size_t size;
+ unsigned int flags;
+ unsigned char data[];
+};
+
+static struct {
+ void *base;
+ size_t size;
+ size_t free;
+ struct heap_node *nodes;
+ cx_spinlock_t lock;
+ unsigned int flags;
+} _heap;
+
+#define HEAP_ADDR ((void*)0x80000000)
+#define HEAP_INITSIZE 8192
+#define HEAP_MAXSIZE (1024 * 1024 * 1024)
+
+static inline void _heap_lock(void)
+{
+ cx_spinlock_lock(&_heap.lock);
+ return;
+}
+
+static inline void _heap_unlock(void)
+{
+ cx_spinlock_unlock(&_heap.lock);
+ return;
+}
+
+/*
+ * _heap_grow() - Increase the size of the heap
+ *
+ * SYNOPSIS
+ * int _heap_grow(size_t size);
+ *
+ * DESCRIPTION
+ * The _heap_grow() function increases the size of the heap by `size' bytes. Internally, memory
+ * is allocated by adding new pages to the end of the heap by means of the mmap() syscall. For
+ * that reason, the value of `size' will be rounded up to multiples of the page size of the
+ * process's page directory.
+ *
+ */
+int _heap_grow(size_t size)
+{
+ void *map_base;
+ int ret_val;
+ size_t new_size;
+ size_t map_size;
+
+ new_size = _heap.size + size;
+ map_size = size + sizeof(struct heap_node);
+
+ if(map_size & (PAGE_SIZE - 1)) {
+ map_size &= ~(PAGE_SIZE - 1);
+ map_size += PAGE_SIZE;
+ }
+
+ if(new_size < _heap.size || map_size < size) {
+ return(-EOVERFLOW);
+ }
+
+ ret_val = -ENOMEM;
+
+ if(new_size < HEAP_MAXSIZE) {
+ map_base = _heap.base + _heap.size;
+
+ map_base = mmap(map_base, map_size, PROT_READ | PROT_WRITE, 0, -1, 0);
+
+ if(map_base != (void*)-1) {
+ struct heap_node *node;
+ struct heap_node **cur;
+
+ node = (struct heap_node*)map_base;
+ node->flags = 0;
+ node->size = map_size - sizeof(*node);
+
+ _heap.size += node->size;
+ _heap.free += node->size;
+
+ for(cur = &_heap.nodes; *cur; cur = &(*cur)->next);
+ *cur = node;
+
+ ret_val = 0;
+ }
+ }
+
+ return(ret_val);
+}
+
+int heap_init(void)
+{
+ int ret_val;
+
+ ret_val = -EALREADY;
+
+ if(!_heap.base) {
+ _heap.base = HEAP_ADDR;
+ _heap.size = 0;
+ _heap.nodes = NULL;
+ _heap.flags = HEAP_FLAGS_DEFAULT;
+
+ ret_val = _heap_grow(HEAP_INITSIZE);
+ }
+
+ return(ret_val);
+}
+
+/*
+ * _heap_node_split() - Split a heap node
+ *
+ * SYNOPSIS
+ * int _heap_node_split(struct heap_node *old_node, const size_t size);
+ *
+ * DESCRIPTION
+ * The _heap_node_split() function splits the heap node pointed to by `old_node' into two nodes,
+ * reducing the size of `old_node' to `size' and creating a new node from the remaining memory.
+ *
+ * RETURN VALUE
+ * This function returns zero upon success, or a negative error number if an error has occured.
+ *
+ * ERRORS
+ * -ERANGE The value of `size' is too large
+ * -ENOMEM The node is too small to be split
+ */
+static inline int _heap_node_split(struct heap_node *old_node, const size_t size)
+{
+ struct heap_node *new_node;
+ size_t threshold;
+ int ret_val;
+
+ ret_val = -ERANGE;
+ threshold = size + sizeof(struct heap_node) + WASTE_MAX;
+
+ if(threshold > size) {
+ ret_val = -ENOMEM;
+
+ if(old_node->size > threshold) {
+ _heap.size -= old_node->size;
+
+ new_node = (struct heap_node*)(old_node->data + size);
+ new_node->next = old_node->next;
+ new_node->size = old_node->size - size - sizeof(*new_node);
+ new_node->flags = 0;
+
+ _heap.size += new_node->size;
+
+ old_node->next = new_node;
+ old_node->size = size;
+
+ ret_val = 0;
+ }
+ }
+
+ return(ret_val);
+}
+
+int heap_alloc(size_t size, void **dst)
+{
+ struct heap_node *cur;
+ int ret_val;
+
+ ret_val = -ENOMEM;
+
+ if(_heap.free < size) {
+ _heap_grow(size);
+ }
+
+ for(cur = _heap.nodes; cur; cur = cur->next) {
+ if(cur->flags & FLAG_IN_USE) {
+ continue;
+ }
+
+ if(cur->size < size) {
+ continue;
+ }
+
+ ret_val = _heap_node_split(cur, size);
+
+ /*
+ * The node is definitely big enough for us, but it is possible that it is not big
+ * enough to be split. The latter case is not fatal for the allocation though.
+ */
+ if(ret_val == 0 || ret_val == -ENOMEM) {
+ cur->flags = _heap.flags | FLAG_IN_USE;
+ *dst = (void*)cur->data;
+ ret_val = 0;
+ }
+
+ break;
+ }
+
+ return(ret_val);
+}
+
+int heap_free(void *ptr)
+{
+ struct heap_node *cur;
+ int ret_val;
+
+ ret_val = -EINVAL;
+
+ for(cur = _heap.nodes; cur; cur = cur->next) {
+ if(ptr >= (void*)cur->data && ptr < (void*)cur->data + cur->size) {
+ ret_val = -EALREADY;
+
+ if(cur->flags & FLAG_IN_USE) {
+ cur->flags &= ~FLAG_IN_USE;
+
+ if(!(cur->flags & FLAG_INSECURE)) {
+ memset(cur->data, 0, cur->size);
+ }
+
+ ret_val = 0;
+ }
+ }
+ }
+
+ return(ret_val);
+}