#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));
}