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