#include <assert.h>
#include <limits.h>
-//#include <stdio.h> // temporary
+#include <stdio.h> // temporary
#include <stdlib.h>
#include "pool.h"
return true;
}
+bool pool_realloc_base(
+ struct pool_head *head,
+ struct pool_item *item,
+ int size
+) {
+ struct pool_item *p, *q;
+ int size_change, avail, gap;
+
+ // item must be already allocated
+ assert(item->prev && item->next);
+
+ // try to change current block size without copying
+ size_change = size + item->base - item->limit;
+ if (item->limit - size >= item->prev->limit)
+ goto resize;
+
+ // check size, disregarding fragmentation
+ avail = head->avail - size_change;
+ if (avail < 0)
+ return false;
+
+ // last-fit algorithm, until our own list entry
+ p = &head->item;
+ do {
+ q = p;
+ p = q->prev;
+ if (p == item)
+ goto ourself;
+ gap = q->base - p->limit;
+ if (size <= gap)
+ goto found;
+ assert(p != &head->item);
+ avail -= gap;
+ } while (avail >= 0);
+ return false;
+
+ourself:
+ // first-fit algorithm, including and after our own list entry,
+ // the first iteration (ours) is different because p != q->prev,
+ // this causes us to ignore our own entry in the gap calculation
+ p = p->prev;
+ goto loop_entry;
+ do {
+ q = p;
+ p = q->prev;
+ loop_entry:
+ gap = q->base - p->limit;
+ if (size <= gap)
+ goto found;
+ if (p == &head->item)
+ break;
+ avail -= gap;
+ } while (avail >= 0);
+ return false;
+
+found:
+ // if realloc is used, then user must provide move_up routine
+ // (when called from here, it is always non-overlapping or upwards)
+ assert(head->move_up);
+ head->move_up(item, q->base);
+ //item->base += q->base - item->limit;
+ item->limit = q->base;
+
+ // remove from list
+ item->prev->next = item->next;
+ item->next->prev = item->prev;
+
+ // insert into list at found position
+ item->prev = p;
+ p->next = item;
+ item->next = q;
+ q->prev = item;
+
+resize:
+ item->base = item->limit - size;
+
+ // keep track of total allocation
+ head->avail -= size_change;
+ check_invariants(head);
+ return true;
+}
+
void pool_free(struct pool_head *head, struct pool_item *item) {
// item must be already allocated
assert(item->prev && item->next);
p = q;
q = p->next;
if (q == item)
- goto ourself;
+ break;
// calculate gap at current position
gap = q->base - p->limit;
assert(q != &head->item);
}
-ourself:
#ifdef TRY_REALLOC
// optional step: try an ordinary realloc in rest of list
// this is because if the pool consists of a tightly packed region
return true;
}
+bool pool_realloc_base_moveable(
+ struct pool_head *head,
+ struct pool_item *item,
+ int size
+) {
+ struct pool_item *p, *q;
+ int size_change, avail, gap;
+#ifdef TRY_BLOCKER
+ struct pool_item *blocker;
+ int blocker_size;
+#endif
+#ifdef TRY_REALLOC
+ int avail_save;
+#endif
+
+ // item must be already allocated
+ assert(item->prev && item->next);
+
+ // try to change current block size without copying
+ size_change = size + item->base - item->limit;
+#ifndef TRY_BLOCKER
+ if (item->limit - size >= item->prev->limit)
+ goto resize;
+#endif
+
+ // check size, disregarding fragmentation
+ avail = head->avail - size_change;
+ if (avail < 0)
+ return false;
+
+ // last-fit algorithm, until our own list entry
+ p = &head->item;
+
+#ifdef TRY_BLOCKER
+blocker_test:
+ // enter here when blocker has changed
+ blocker = item->prev;
+
+ // see if we fit, given the new blocker
+ if (item->limit - size >= blocker->limit)
+ goto resize;
+
+ // otherwise, see if blocker is moveable
+ blocker_size =
+ blocker == &head->item ? INT_MAX : blocker->limit - blocker->base;
+#endif
+
+ while (true) {
+ q = p;
+ p = q->prev;
+ if (p == item)
+ break;
+
+ // calculate gap at current position
+ gap = q->base - p->limit;
+
+ // see if request fits in gap
+ if (size <= gap)
+ goto found;
+
+#ifdef TRY_BLOCKER
+ // see if blocker fits in gap
+ if (blocker_size <= gap) {
+ // if moveable realloc is used, then user must provide move_up routine
+ assert(head->move_up);
+ head->move_up(blocker, q->base);
+ blocker->base += q->base - blocker->limit;
+ blocker->limit = q->base;
+
+ // remove from list
+ blocker->prev->next = blocker->next;
+ blocker->next->prev = blocker->prev;
+
+ // insert into list at gap position
+ blocker->prev = p;
+ p->next = blocker;
+ blocker->next = q;
+ q->prev = blocker;
+
+ // calculate new blocker
+ p = blocker;
+ goto blocker_test;
+ }
+#endif
+
+ // if gap is too large, move block before gap up into gap
+ avail -= gap;
+ if (avail < 0) {
+ avail += gap;
+
+ // if moveable realloc is used, then user must provide move_up routine
+ assert(head->move_up);
+ head->move_up(p, q->base);
+ p->base += q->base - p->limit;
+ p->limit = q->base;
+ }
+
+ assert(p != &head->item);
+ }
+
+#ifdef TRY_REALLOC
+ // optional step: try an ordinary realloc in rest of list
+ // this is because if the pool consists of a tightly packed region
+ // preceded by a mainly free region, it's best to move us to there
+ avail_save = avail;
+
+ // last-fit algorithm, including and before our own list entry,
+ // the first iteration (ours) is different because p != q->prev,
+ // this causes us to ignore our own entry in the gap calculation
+ p = p->prev;
+ goto loop_entry;
+ do {
+ q = p;
+ p = q->prev;
+ loop_entry:
+ gap = q->base - p->limit;
+ if (size <= gap)
+ goto found;
+ if (p == &head->item)
+ break;
+ avail -= gap;
+ } while (avail >= 0);
+
+ avail = avail_save;
+ q = item->next;
+#endif
+
+ // calculate gap that would be left if we didn't move ourself,
+ // if too large, move block before gap (ourself) up into gap
+ gap = q->base - item->limit;
+ avail -= gap;
+ if (avail < 0) {
+ avail += gap;
+
+ // if moveable realloc is used, then user must provide move_up routine
+ assert(head->move_up);
+ head->move_up(item, q->base);
+ item->base += q->base - item->limit;
+ item->limit = q->base;
+
+#ifndef TRY_REALLOC
+ // if didn't do ordinary realloc above, see if we now fit
+ if (item->limit - size >= item->prev->limit)
+ goto resize;
+#endif
+ }
+
+ // at this point we will not move our own block anymore (because
+ // a first-fit approach is risky, given the likelihood that we will
+ // soon want to resize again), compact the remainder up from bottom
+ q = &head->item;
+ while (true) {
+ p = q;
+ q = p->next;
+ if (p == item) {
+ assert(item->limit - size >= item->prev->limit);
+ goto resize;
+ }
+
+ // calculate gap at current position
+ gap = q->base - p->limit;
+
+#ifdef TRY_BLOCKER
+ // see if blocker fits in gap
+ if (blocker_size <= gap && blocker != q) {
+ // if moveable realloc is used, then user must provide move routine
+ assert(head->move);
+ head->move(blocker, p->limit);
+ blocker->limit += p->limit - blocker->base;
+ blocker->base = p->limit;
+
+ // remove from list
+ blocker->prev->next = blocker->next;
+ blocker->next->prev = blocker->prev;
+
+ // insert into list at gap position
+ blocker->prev = p;
+ p->next = blocker;
+ blocker->next = q;
+ q->prev = blocker;
+
+ // calculate new blocker
+ q = blocker;
+ blocker = item->prev;
+
+ // see if we fit, given the new blocker
+ if (item->limit - size >= blocker->limit)
+ goto resize;
+
+ // otherwise, see if blocker is moveable
+ blocker_size =
+ blocker == &head->item ? INT_MAX : blocker->limit - blocker->base;
+ continue;
+ }
+#endif
+
+ // if gap too large, move block after gap down into gap
+ avail -= gap;
+ if (avail < 0) {
+ avail += gap;
+
+ // if moveable realloc is used, user must provide move routine
+ assert(head->move);
+ head->move(q, p->limit);
+ q->limit += p->limit - q->base;
+ q->base = p->limit;
+ }
+
+ assert(q != &head->item);
+ }
+
+found:
+ // if moveable realloc is used, then user must provide move_up routine
+ // (when called from here, it is always non-overlapping or upwards)
+ assert(head->move_up);
+ head->move_up(item, q->base);
+ //item->base += q->base - item->limit;
+ item->limit = q->base;
+
+ // remove from list
+ item->prev->next = item->next;
+ item->next->prev = item->prev;
+
+ // insert into list at found position
+ item->prev = p;
+ p->next = item;
+ item->next = q;
+ q->prev = item;
+
+resize:
+ item->base = item->limit - size;
+
+ // keep track of total allocation
+ head->avail -= size_change;
+ check_invariants(head);
+ return true;
+}
+
void pool_move_item(struct pool_item *from, struct pool_item *to) {
// from-item must be already allocated
assert(from->prev && from->next);
}
int n_items = atoi(argv[1]);
int pool_size = atoi(argv[2]);
- bool moveable = argc >= 4 ? (bool)atoi(argv[3]) : false;
+ bool moveable = argc >= 4 ? strcmp(argv[3], "false") != 0 : false;
struct pool_head head;
pool_init(&head, 0, pool_size, move, move_up);
}
}
}
+ else if (strcmp(buf, "realloc_base") == 0) {
+ int item, old_size, size;
+ rassert(scanf("%d %d %d %s", &item, &old_size, &size, buf) == 4);
+ rassert(item >= 0 && item < n_items);
+ rassert(old_size >= 0);
+ rassert(size >= 0);
+ bool success;
+ if (strcmp(buf, "false") == 0)
+ success = false;
+ else if (strcmp(buf, "true") == 0)
+ success = true;
+ else
+ rassert(false);
+ printf(
+ "realloc_base %d %d %d %s\n",
+ item,
+ old_size,
+ size,
+ success ? "true" : "false"
+ );
+ if (items[item].prev == NULL)
+ printf("... not allocated, ignore\n");
+ else {
+ int base = items[item].base;
+ int actual_old_size = items[item].limit - items[item].base;
+ if (actual_old_size != old_size) {
+ printf(
+ "... old size %d, should be %d\n",
+ actual_old_size,
+ old_size
+ );
+ rassert(actual_old_size <= old_size);
+ }
+ printf("old region [%d,%d)\n", base, base + actual_old_size);
+ hash_verify(item, base, 0, actual_old_size - size);
+ bool result =
+ moveable ?
+ pool_realloc_base_moveable(&head, items + item, size) :
+ pool_realloc_base(&head, items + item, size);
+ printf(
+ "... %s\n",
+ result == success ?
+ "ok" :
+ result ? "succeeded, should fail" : "failed, should succeed"
+ );
+ if (result) {
+ if (!success) {
+ printf("... undo\n");
+ rassert(pool_realloc_base(&head, items + item, actual_old_size));
+ }
+ else {
+ base = items[item].base;
+ rassert(items[item].limit == base + size);
+ printf("new region [%d,%d)\n", base, base + size);
+ hash_verify(
+ item,
+ base + size - actual_old_size,
+ size >= actual_old_size ? 0 : actual_old_size - size,
+ actual_old_size
+ );
+ hash_init(item, base, 0, size);
+ }
+ }
+ }
+ }
else if (strcmp(buf, "free") == 0) {
int item, old_size;
rassert(scanf("%d %d", &item, &old_size) == 2);