diff options
Diffstat (limited to 'src/lib/memory.c')
| -rw-r--r-- | src/lib/memory.c | 48 |
1 files changed, 47 insertions, 1 deletions
diff --git a/src/lib/memory.c b/src/lib/memory.c index 006c409..b61f844 100644 --- a/src/lib/memory.c +++ b/src/lib/memory.c @@ -438,14 +438,60 @@ compact(struct aplwc *aplwc) } } +static void +shuffle(struct aplwc *aplwc) +{ + struct chunk *c, *prev, *prev_prev; + size_t move; + + compact(aplwc); + + prev_prev = prev = NULL; + for(c = (struct chunk *)aplwc->data; c; prev_prev = prev, prev = c, c = next(c)) + assert((next(c) == NULL) || (c->tag != CHUNK_TAG_FREE)); + + if(prev == NULL) /* No chunks */ + return; + + if(prev->tag != CHUNK_TAG_FREE){ /* No free space at the end */ + if(grow(aplwc, HEADER_SIZE*8)) + shuffle(aplwc); + return; + } + + if(prev_prev == NULL) /* Only free space */ + return; + + assert(prev); + assert(prev->tag == CHUNK_TAG_FREE); + move = prev->size; + prev_prev->last = 1; + memmove(aplwc->data+move, aplwc->data, aplwc->size-move); + + aplwc->data += move; + aplwc->size -= move; + fixrefs(aplwc, aplwc->data-move); + + aplwc->data -= move; + aplwc->size += move; + + c = (struct chunk *)aplwc->data; + c->stack = NULL; + c->tag = CHUNK_TAG_FREE; + c->size = move; + c->last = 0; +} + static void * alloc(struct aplwc *aplwc, size_t size, enum chunk_tag tag) { struct chunk *free, *new; size_t pad; - int attempt; + + FAILCASE((void)0, shuffle(aplwc)); + pad = size % ALIGNMENT; if(pad) size += ALIGNMENT - pad; |