summaryrefslogtreecommitdiff
path: root/src/lib/memory.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/memory.c')
-rw-r--r--src/lib/memory.c359
1 files changed, 288 insertions, 71 deletions
diff --git a/src/lib/memory.c b/src/lib/memory.c
index a348b93..694a31b 100644
--- a/src/lib/memory.c
+++ b/src/lib/memory.c
@@ -1,136 +1,353 @@
#include "aplwc_internal.h"
-struct memory
-memory_init(uint8_t *data , size_t size)
+#define ALIGNMENT 8
+#define HEADER_SIZE (sizeof(struct chunk) + (sizeof(struct chunk) % ALIGNMENT))
+
+enum chunk_tag {
+ CHUNK_TAG_FREE,
+ CHUNK_TAG_APLWC_LINE,
+ CHUNK_TAG_STRING,
+ CHUNK_TAG_TOKEN_LIST,
+ CHUNK_TAG_APLWC_VALUE,
+ CHUNK_TAG_APLWC_MODULE
+};
+
+struct chunk {
+ struct chunk *next;
+ size_t size;
+ enum chunk_tag tag;
+ uint8_t *data;
+};
+
+void
+memory_init(struct aplwc *aplwc)
{
- struct memory mem;
+ aplwc->data = NULL;
+ aplwc->unaligned_data = NULL;
+ aplwc->size = 0;
+ aplwc->memptrs = NULL;
+ aplwc->rootptrs = NULL;
+}
- mem.data = data;
- mem.size = size;
- mem.offset = 0;
- return mem;
+void
+memory_destroy(struct aplwc *aplwc)
+{
+ if(aplwc->fns.dealloc)
+ aplwc->fns.dealloc(aplwc->unaligned_data);
}
-static void *
-alloc(struct memory *m, size_t size, size_t align)
+static void
+fixrefs(struct aplwc *aplwc, uint8_t *new, uint8_t *old)
{
- uintptr_t ptr;
- size_t alloc_size, adjust;
- void *p;
+#define FIX(ptr) do{ \
+ if(ptr){ \
+ (ptr) = (void*)(((uint8_t*)(ptr))+add); \
+ (ptr) = (void*)(((uint8_t*)(ptr))-sub); \
+ } \
+}while(0)
- ptr = ((uintptr_t)m->data) + m->offset;
- alloc_size = size;
+#define FIX_DATA(type, ptr) FIX(((type *)c->data)->ptr)
- if(NEVER(align == 0))
- return NULL;
- adjust = ptr % align;
+ struct chunk *c;
+ struct memory_ptr_frame *frame;
+ struct root_ptr *root;
+ size_t add, sub, i;
+
+ if(new > old){
+ add = new-old;
+ sub = 0;
+ }else{
+ add = 0;
+ sub = old-new;
+ }
+ for(c = (struct chunk *)new; c; c = c->next){
+ FIX(c->next);
+ FIX(c->data);
+
+ switch(c->tag){
+ case CHUNK_TAG_APLWC_LINE:
+ FIX_DATA(struct aplwc_line, text);
+ FIX_DATA(struct aplwc_line, tokens);
+ break;
+ case CHUNK_TAG_STRING:
+ break;
+ case CHUNK_TAG_TOKEN_LIST:
+ FIX_DATA(struct token_list, text);
+ break;
+ case CHUNK_TAG_APLWC_VALUE:
+ break;
+ case CHUNK_TAG_APLWC_MODULE:
+ FIX_DATA(struct aplwc_module, name);
+ FIX_DATA(struct aplwc_module, tokens);
+ FIX_DATA(struct aplwc_module, link);
+ break;
+ case CHUNK_TAG_FREE:
+ break;
+ }
+ }
+
+ for(frame = aplwc->memptrs; frame; frame = frame->link)
+ for(i = 0; i < frame->count; i++)
+ if(frame->ptrs[i])
+ FIX(*frame->ptrs[i]);
+
+ for(root = aplwc->rootptrs; root; root = root->link)
+ if(root->ptr)
+ FIX(root->ptr);
+
+#undef FIX
+#undef FIX_DATA
+}
+
+static enum aplwc_rc
+grow(struct aplwc *aplwc, size_t extra)
+{
+ uint8_t *unaligned_data, *aligned_data;
+ size_t unaligned_newsize, aligned_newsize, adjust;
+ struct chunk **free, *head;
+
+ if(aplwc->fns.alloc == NULL)
+ goto error;
+
+ unaligned_data = aplwc->fns.alloc(aplwc->size + extra + HEADER_SIZE + 2*ALIGNMENT, 0, &unaligned_newsize);
+ if(unaligned_data == NULL)
+ goto error;
+
+ adjust = ((uintptr_t)unaligned_data) % ALIGNMENT;
if(adjust)
- alloc_size += align - adjust;
+ adjust = ALIGNMENT - adjust;
+ aligned_newsize = unaligned_newsize - adjust;
+ aligned_data = unaligned_data + adjust;
+
+ aligned_newsize -= aligned_newsize % ALIGNMENT;
- if((m->size - m->offset) >= alloc_size){
- p = m->data+m->offset+alloc_size-size;
- assert((((uintptr_t)p) % align) == 0);
- m->offset += alloc_size;
- memset(p, 0, size);
+ assert(aligned_newsize >= (aplwc->size + extra));
+ head = (struct chunk *)aligned_data;
+
+ if(aplwc->data){
+ memmove(aligned_data, aplwc->data, aplwc->size);
+ fixrefs(aplwc, aligned_data, aplwc->data);
+ if(aplwc->fns.dealloc)
+ aplwc->fns.dealloc(aplwc->unaligned_data);
+ for(free = &head; (*free); free = &(*free)->next)
+ assert((*free)->tag != CHUNK_TAG_FREE);
}else
- p = NULL;
+ free = &head;
- return p;
+
+ (*free) = (struct chunk *)(aligned_data + aplwc->size);
+ (*free)->tag = CHUNK_TAG_FREE;
+ (*free)->data = NULL;
+ (*free)->size = aligned_newsize - aplwc->size;
+ (*free)->next = NULL;
+
+ aplwc->unaligned_data = unaligned_data;
+ aplwc->data = aligned_data;
+ aplwc->size = aligned_newsize;
+ return 1;
+
+error:
+ aplwc->last_error = APLWC_OUT_OF_MEMORY;
+ return 0;
}
-struct aplwc *
-memory_alloc_aplwc(struct memory *m)
+static struct chunk *
+find_free(struct aplwc *aplwc, size_t min)
{
- struct aplwc *p;
+ struct chunk *c;
- p = alloc(m, sizeof(struct aplwc), alignof(struct aplwc));
- return p;
+ min += HEADER_SIZE;
+
+ if(aplwc->data == NULL)
+ return NULL;
+
+ for(c = (struct chunk *)aplwc->data; c; c = c->next){
+ if((c->tag == CHUNK_TAG_FREE) && (c->size >= min))
+ break;
+ }
+
+ return c;
}
-void
-memory_free_aplwc(struct memory *m, struct aplwc *p)
+static void *
+alloc(struct aplwc *aplwc, size_t size, enum chunk_tag tag)
{
- UNUSED_PARAMETER(m);
- UNUSED_PARAMETER(p);
+ struct chunk *free;
+ struct chunk *new;
+
+retry:
+ free = find_free(aplwc, size);
+ if(!free){
+ if(grow(aplwc, size))
+ goto retry;
+ else
+ return NULL;
+ }
+
+ assert(free->data == NULL);
+
+ new = free;
+
+ if(free->size > (size + 2*HEADER_SIZE)){
+ free = (struct chunk *)(((uint8_t*)free) + HEADER_SIZE + size);
+ free->tag = CHUNK_TAG_FREE;
+ free->size = new->size - (HEADER_SIZE + size);
+ free->data = NULL;
+ }
+
+ new->tag = tag;
+ new->size = sizeof(struct chunk) + size;
+ new->data = ((uint8_t*)new) + sizeof(struct chunk);
+
+ new->data += ((uintptr_t)new->data) % ALIGNMENT;
+
+ return new->data;
}
-struct aplwc_line *
-memory_alloc_aplwc_line(struct memory *m)
+struct aplwc_ref
+memory_root_new(struct aplwc *aplwc, void *p)
{
- struct aplwc_line *p;
+ struct aplwc_ref ref;
+ struct root_ptr *root;
+ size_t size;
- p = alloc(m, sizeof(struct aplwc_line), alignof(struct aplwc_line));
- return p;
+ clear_ref(&ref);
+ if(aplwc->fns.alloc == NULL){
+ aplwc->last_error = APLWC_OUT_OF_MEMORY;
+ goto end;
+ }
+
+ root = aplwc->fns.alloc(sizeof(*root), sizeof(*root), &size);
+ if(root == NULL){
+ aplwc->last_error = APLWC_OUT_OF_MEMORY;
+ goto end;
+ }
+
+ root->id = aplwc->rootptrs ? (aplwc->rootptrs->id+1) : 0;
+ root->ptr = p;
+ root->link = aplwc->rootptrs;
+ aplwc->rootptrs = root;
+
+ ref.id = root->id;
+ ref.aplwc = aplwc;
+end:
+ return ref;
+}
+
+void *
+memory_root_get(struct aplwc_ref ref)
+{
+ struct root_ptr *root;
+
+ if(!valid_ref(ref))
+ return NULL;
+
+ for(root = ref.aplwc->rootptrs; root; root = root->link)
+ if(root->id == ref.id)
+ return root->ptr;
+
+ return NULL;
}
void
-memory_free_aplwc_line(struct memory *m, struct aplwc_line *p)
+memory_root_free(struct aplwc_ref ref)
{
- UNUSED_PARAMETER(m);
- UNUSED_PARAMETER(p);
+ struct aplwc *aplwc;
+ struct root_ptr **root, *tmp;
+
+ if(!valid_ref(ref))
+ return;
+
+ aplwc = ref.aplwc;
+
+ for(root = &aplwc->rootptrs; (*root); root = &(*root)->link){
+ if((*root)->id == ref.id){
+ tmp = *root;
+ *root = tmp->link;
+ if(aplwc->fns.dealloc)
+ aplwc->fns.dealloc(tmp);
+ break;
+ }
+ }
}
-struct string *
-memory_alloc_string(struct memory *m, size_t bytes)
+void
+memptrs_add(struct aplwc *aplwc, struct memory_ptr_frame *frame, void ***ptrs, size_t count)
{
- struct string *p;
+ size_t i;
- p = alloc(m, sizeof(struct string) + bytes, alignof(struct string));
- return p;
+ frame->ptrs = ptrs;
+ frame->link = aplwc->memptrs;
+ frame->count = count;
+ aplwc->memptrs = frame;
+
+ for(i = 0; i < count; i++)
+ frame->ptrs[i] = NULL;
}
void
-memory_free_string(struct memory *m, struct string *p)
+memptrs_remove(struct aplwc *aplwc, struct memory_ptr_frame *frame)
{
- UNUSED_PARAMETER(m);
- UNUSED_PARAMETER(p);
+ assert(aplwc->memptrs == frame);
+ aplwc->memptrs = frame->link;
}
-struct token_list *
-memory_alloc_token_list(struct memory *m, size_t tokens)
+struct aplwc_line *
+memory_alloc_aplwc_line(struct aplwc *aplwc)
{
- struct token_list *p;
+ struct aplwc_line *p;
- p = alloc(m, sizeof(struct token_list) + (tokens * sizeof(struct token)), alignof(struct token_list));
+ p = alloc(aplwc, sizeof(struct aplwc_line), CHUNK_TAG_APLWC_LINE);
return p;
}
-void
-memory_free_token_list(struct memory *m, struct token_list *p)
+struct string *
+memory_alloc_string(struct aplwc *aplwc, size_t bytes)
{
- UNUSED_PARAMETER(m);
- UNUSED_PARAMETER(p);
+ struct string *p;
+
+ p = alloc(aplwc, sizeof(struct string) + bytes, CHUNK_TAG_STRING);
+
+ return p;
}
-struct aplwc_value *
-memory_alloc_aplwc_value(struct memory *m)
+struct token_list *
+memory_alloc_token_list(struct aplwc *aplwc, size_t tokens)
{
- struct aplwc_value *p;
+ struct token_list *p;
- p = alloc(m, sizeof(struct aplwc_value), alignof(struct aplwc_value));
+ p = alloc(aplwc, sizeof(struct token_list) + (tokens * sizeof(struct token)), CHUNK_TAG_TOKEN_LIST);
return p;
}
-void
-memory_free_aplwc_value(struct memory *m, struct aplwc_value *p)
+struct aplwc_value *
+memory_alloc_aplwc_value(struct aplwc *aplwc)
{
- UNUSED_PARAMETER(m);
- UNUSED_PARAMETER(p);
+ struct aplwc_value *p;
+
+ p = alloc(aplwc, sizeof(struct aplwc_value), CHUNK_TAG_APLWC_VALUE);
+ return p;
}
struct aplwc_module *
-memory_alloc_aplwc_module(struct memory *m)
+memory_alloc_aplwc_module(struct aplwc *aplwc)
{
struct aplwc_module *p;
- p = alloc(m, sizeof(struct aplwc_module), alignof(struct aplwc_module));
+ p = alloc(aplwc, sizeof(struct aplwc_module), CHUNK_TAG_APLWC_MODULE);
return p;
}
void
-memory_free_aplwc_module(struct memory *m, struct aplwc_module *p)
+clear_ref(struct aplwc_ref *ref)
+{
+ ref->id = -1;
+ ref->aplwc = NULL;
+}
+
+int
+valid_ref(struct aplwc_ref ref)
{
- UNUSED_PARAMETER(m);
- UNUSED_PARAMETER(p);
+ return ref.id >= 0;
}