c0cc5db871
This resolves #564.
288 lines
6.4 KiB
C
288 lines
6.4 KiB
C
#include "test/jemalloc_test.h"
|
|
|
|
typedef struct node_s node_t;
|
|
|
|
struct node_s {
|
|
#define NODE_MAGIC 0x9823af7e
|
|
uint32_t magic;
|
|
phn(node_t) link;
|
|
uint64_t key;
|
|
};
|
|
|
|
static int
|
|
node_cmp(const node_t *a, const node_t *b) {
|
|
int ret;
|
|
|
|
ret = (a->key > b->key) - (a->key < b->key);
|
|
if (ret == 0) {
|
|
/*
|
|
* Duplicates are not allowed in the heap, so force an
|
|
* arbitrary ordering for non-identical items with equal keys.
|
|
*/
|
|
ret = (((uintptr_t)a) > ((uintptr_t)b))
|
|
- (((uintptr_t)a) < ((uintptr_t)b));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
node_cmp_magic(const node_t *a, const node_t *b) {
|
|
|
|
assert_u32_eq(a->magic, NODE_MAGIC, "Bad magic");
|
|
assert_u32_eq(b->magic, NODE_MAGIC, "Bad magic");
|
|
|
|
return node_cmp(a, b);
|
|
}
|
|
|
|
typedef ph(node_t) heap_t;
|
|
ph_gen(static, heap_, heap_t, node_t, link, node_cmp_magic);
|
|
|
|
static void
|
|
node_print(const node_t *node, unsigned depth) {
|
|
unsigned i;
|
|
node_t *leftmost_child, *sibling;
|
|
|
|
for (i = 0; i < depth; i++) {
|
|
malloc_printf("\t");
|
|
}
|
|
malloc_printf("%2"FMTu64"\n", node->key);
|
|
|
|
leftmost_child = phn_lchild_get(node_t, link, node);
|
|
if (leftmost_child == NULL) {
|
|
return;
|
|
}
|
|
node_print(leftmost_child, depth + 1);
|
|
|
|
for (sibling = phn_next_get(node_t, link, leftmost_child); sibling !=
|
|
NULL; sibling = phn_next_get(node_t, link, sibling)) {
|
|
node_print(sibling, depth + 1);
|
|
}
|
|
}
|
|
|
|
static void
|
|
heap_print(const heap_t *heap) {
|
|
node_t *auxelm;
|
|
|
|
malloc_printf("vvv heap %p vvv\n", heap);
|
|
if (heap->ph_root == NULL) {
|
|
goto label_return;
|
|
}
|
|
|
|
node_print(heap->ph_root, 0);
|
|
|
|
for (auxelm = phn_next_get(node_t, link, heap->ph_root); auxelm != NULL;
|
|
auxelm = phn_next_get(node_t, link, auxelm)) {
|
|
assert_ptr_eq(phn_next_get(node_t, link, phn_prev_get(node_t,
|
|
link, auxelm)), auxelm,
|
|
"auxelm's prev doesn't link to auxelm");
|
|
node_print(auxelm, 0);
|
|
}
|
|
|
|
label_return:
|
|
malloc_printf("^^^ heap %p ^^^\n", heap);
|
|
}
|
|
|
|
static unsigned
|
|
node_validate(const node_t *node, const node_t *parent) {
|
|
unsigned nnodes = 1;
|
|
node_t *leftmost_child, *sibling;
|
|
|
|
if (parent != NULL) {
|
|
assert_d_ge(node_cmp_magic(node, parent), 0,
|
|
"Child is less than parent");
|
|
}
|
|
|
|
leftmost_child = phn_lchild_get(node_t, link, node);
|
|
if (leftmost_child == NULL) {
|
|
return nnodes;
|
|
}
|
|
assert_ptr_eq((void *)phn_prev_get(node_t, link, leftmost_child),
|
|
(void *)node, "Leftmost child does not link to node");
|
|
nnodes += node_validate(leftmost_child, node);
|
|
|
|
for (sibling = phn_next_get(node_t, link, leftmost_child); sibling !=
|
|
NULL; sibling = phn_next_get(node_t, link, sibling)) {
|
|
assert_ptr_eq(phn_next_get(node_t, link, phn_prev_get(node_t,
|
|
link, sibling)), sibling,
|
|
"sibling's prev doesn't link to sibling");
|
|
nnodes += node_validate(sibling, node);
|
|
}
|
|
return nnodes;
|
|
}
|
|
|
|
static unsigned
|
|
heap_validate(const heap_t *heap) {
|
|
unsigned nnodes = 0;
|
|
node_t *auxelm;
|
|
|
|
if (heap->ph_root == NULL) {
|
|
goto label_return;
|
|
}
|
|
|
|
nnodes += node_validate(heap->ph_root, NULL);
|
|
|
|
for (auxelm = phn_next_get(node_t, link, heap->ph_root); auxelm != NULL;
|
|
auxelm = phn_next_get(node_t, link, auxelm)) {
|
|
assert_ptr_eq(phn_next_get(node_t, link, phn_prev_get(node_t,
|
|
link, auxelm)), auxelm,
|
|
"auxelm's prev doesn't link to auxelm");
|
|
nnodes += node_validate(auxelm, NULL);
|
|
}
|
|
|
|
label_return:
|
|
if (false) {
|
|
heap_print(heap);
|
|
}
|
|
return nnodes;
|
|
}
|
|
|
|
TEST_BEGIN(test_ph_empty) {
|
|
heap_t heap;
|
|
|
|
heap_new(&heap);
|
|
assert_true(heap_empty(&heap), "Heap should be empty");
|
|
assert_ptr_null(heap_first(&heap), "Unexpected node");
|
|
}
|
|
TEST_END
|
|
|
|
static void
|
|
node_remove(heap_t *heap, node_t *node) {
|
|
heap_remove(heap, node);
|
|
|
|
node->magic = 0;
|
|
}
|
|
|
|
static node_t *
|
|
node_remove_first(heap_t *heap) {
|
|
node_t *node = heap_remove_first(heap);
|
|
node->magic = 0;
|
|
return node;
|
|
}
|
|
|
|
TEST_BEGIN(test_ph_random) {
|
|
#define NNODES 25
|
|
#define NBAGS 250
|
|
#define SEED 42
|
|
sfmt_t *sfmt;
|
|
uint64_t bag[NNODES];
|
|
heap_t heap;
|
|
node_t nodes[NNODES];
|
|
unsigned i, j, k;
|
|
|
|
sfmt = init_gen_rand(SEED);
|
|
for (i = 0; i < NBAGS; i++) {
|
|
switch (i) {
|
|
case 0:
|
|
/* Insert in order. */
|
|
for (j = 0; j < NNODES; j++) {
|
|
bag[j] = j;
|
|
}
|
|
break;
|
|
case 1:
|
|
/* Insert in reverse order. */
|
|
for (j = 0; j < NNODES; j++) {
|
|
bag[j] = NNODES - j - 1;
|
|
}
|
|
break;
|
|
default:
|
|
for (j = 0; j < NNODES; j++) {
|
|
bag[j] = gen_rand64_range(sfmt, NNODES);
|
|
}
|
|
}
|
|
|
|
for (j = 1; j <= NNODES; j++) {
|
|
/* Initialize heap and nodes. */
|
|
heap_new(&heap);
|
|
assert_u_eq(heap_validate(&heap), 0,
|
|
"Incorrect node count");
|
|
for (k = 0; k < j; k++) {
|
|
nodes[k].magic = NODE_MAGIC;
|
|
nodes[k].key = bag[k];
|
|
}
|
|
|
|
/* Insert nodes. */
|
|
for (k = 0; k < j; k++) {
|
|
heap_insert(&heap, &nodes[k]);
|
|
if (i % 13 == 12) {
|
|
/* Trigger merging. */
|
|
assert_ptr_not_null(heap_first(&heap),
|
|
"Heap should not be empty");
|
|
}
|
|
assert_u_eq(heap_validate(&heap), k + 1,
|
|
"Incorrect node count");
|
|
}
|
|
|
|
assert_false(heap_empty(&heap),
|
|
"Heap should not be empty");
|
|
|
|
/* Remove nodes. */
|
|
switch (i % 4) {
|
|
case 0:
|
|
for (k = 0; k < j; k++) {
|
|
assert_u_eq(heap_validate(&heap), j - k,
|
|
"Incorrect node count");
|
|
node_remove(&heap, &nodes[k]);
|
|
assert_u_eq(heap_validate(&heap), j - k
|
|
- 1, "Incorrect node count");
|
|
}
|
|
break;
|
|
case 1:
|
|
for (k = j; k > 0; k--) {
|
|
node_remove(&heap, &nodes[k-1]);
|
|
assert_u_eq(heap_validate(&heap), k - 1,
|
|
"Incorrect node count");
|
|
}
|
|
break;
|
|
case 2: {
|
|
node_t *prev = NULL;
|
|
for (k = 0; k < j; k++) {
|
|
node_t *node = node_remove_first(&heap);
|
|
assert_u_eq(heap_validate(&heap), j - k
|
|
- 1, "Incorrect node count");
|
|
if (prev != NULL) {
|
|
assert_d_ge(node_cmp(node,
|
|
prev), 0,
|
|
"Bad removal order");
|
|
}
|
|
prev = node;
|
|
}
|
|
break;
|
|
} case 3: {
|
|
node_t *prev = NULL;
|
|
for (k = 0; k < j; k++) {
|
|
node_t *node = heap_first(&heap);
|
|
assert_u_eq(heap_validate(&heap), j - k,
|
|
"Incorrect node count");
|
|
if (prev != NULL) {
|
|
assert_d_ge(node_cmp(node,
|
|
prev), 0,
|
|
"Bad removal order");
|
|
}
|
|
node_remove(&heap, node);
|
|
assert_u_eq(heap_validate(&heap), j - k
|
|
- 1, "Incorrect node count");
|
|
prev = node;
|
|
}
|
|
break;
|
|
} default:
|
|
not_reached();
|
|
}
|
|
|
|
assert_ptr_null(heap_first(&heap),
|
|
"Heap should be empty");
|
|
assert_true(heap_empty(&heap), "Heap should be empty");
|
|
}
|
|
}
|
|
fini_gen_rand(sfmt);
|
|
#undef NNODES
|
|
#undef SEED
|
|
}
|
|
TEST_END
|
|
|
|
int
|
|
main(void) {
|
|
return test(
|
|
test_ph_empty,
|
|
test_ph_random);
|
|
}
|