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.c48
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;