#include "aplwc_internal.h" #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) { aplwc->data = NULL; aplwc->unaligned_data = NULL; aplwc->size = 0; aplwc->memptrs = NULL; aplwc->rootptrs = NULL; } void memory_destroy(struct aplwc *aplwc) { if(aplwc->fns.dealloc) aplwc->fns.dealloc(aplwc->unaligned_data); } static void fixrefs(struct aplwc *aplwc, uint8_t *new, uint8_t *old) { #define FIX(ptr) do{ \ if(ptr){ \ (ptr) = (void*)(((uint8_t*)(ptr))+add); \ (ptr) = (void*)(((uint8_t*)(ptr))-sub); \ } \ }while(0) #define FIX_DATA(type, ptr) FIX(((type *)c->data)->ptr) 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; 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) adjust = ALIGNMENT - adjust; aligned_newsize = unaligned_newsize - adjust; aligned_data = unaligned_data + adjust; aligned_newsize -= aligned_newsize % ALIGNMENT; assert(aligned_newsize >= (aplwc->size + extra)); 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 = (struct chunk **)&aligned_data; (*free); free = &(*free)->next) assert((*free)->tag != CHUNK_TAG_FREE); }else free = (struct chunk **)&aligned_data; (*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; } static struct chunk * find_free(struct aplwc *aplwc, size_t min) { struct chunk *c; 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; } static void * alloc(struct aplwc *aplwc, size_t size, enum chunk_tag tag) { 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_ref memory_root_new(struct aplwc *aplwc, void *p) { struct aplwc_ref ref; struct root_ptr *root; size_t size; 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_root_free(struct aplwc_ref ref) { 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; } } } void memptrs_add(struct aplwc *aplwc, struct memory_ptr_frame *frame, void ***ptrs, size_t count) { size_t i; frame->ptrs = ptrs; frame->link = aplwc->memptrs; frame->count = count; aplwc->memptrs = frame; for(i = 0; i < count; i++) frame->ptrs[i] = NULL; } void memptrs_remove(struct aplwc *aplwc, struct memory_ptr_frame *frame) { assert(aplwc->memptrs == frame); aplwc->memptrs = frame->link; } struct aplwc_line * memory_alloc_aplwc_line(struct aplwc *aplwc) { struct aplwc_line *p; p = alloc(aplwc, sizeof(struct aplwc_line), CHUNK_TAG_APLWC_LINE); return p; } struct string * memory_alloc_string(struct aplwc *aplwc, size_t bytes) { struct string *p; p = alloc(aplwc, sizeof(struct string) + bytes, CHUNK_TAG_STRING); return p; } struct token_list * memory_alloc_token_list(struct aplwc *aplwc, size_t tokens) { struct token_list *p; p = alloc(aplwc, sizeof(struct token_list) + (tokens * sizeof(struct token)), CHUNK_TAG_TOKEN_LIST); return p; } struct aplwc_value * memory_alloc_aplwc_value(struct aplwc *aplwc) { 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 aplwc *aplwc) { struct aplwc_module *p; p = alloc(aplwc, sizeof(struct aplwc_module), CHUNK_TAG_APLWC_MODULE); return p; } void clear_ref(struct aplwc_ref *ref) { ref->id = -1; ref->aplwc = NULL; } int valid_ref(struct aplwc_ref ref) { return ref.id >= 0; }