xref: /netbsd-src/external/bsd/jemalloc/dist/test/unit/ph.c (revision 7bdf38e5b7a28439665f2fdeff81e36913eef7dd)
1a0698ed9Schristos #include "test/jemalloc_test.h"
2a0698ed9Schristos 
3a0698ed9Schristos #include "jemalloc/internal/ph.h"
4a0698ed9Schristos 
5a0698ed9Schristos typedef struct node_s node_t;
6*7bdf38e5Schristos ph_structs(heap, node_t);
7a0698ed9Schristos 
8a0698ed9Schristos struct node_s {
9a0698ed9Schristos #define NODE_MAGIC 0x9823af7e
10a0698ed9Schristos 	uint32_t magic;
11*7bdf38e5Schristos 	heap_link_t link;
12a0698ed9Schristos 	uint64_t key;
13a0698ed9Schristos };
14a0698ed9Schristos 
15a0698ed9Schristos static int
16a0698ed9Schristos node_cmp(const node_t *a, const node_t *b) {
17a0698ed9Schristos 	int ret;
18a0698ed9Schristos 
19a0698ed9Schristos 	ret = (a->key > b->key) - (a->key < b->key);
20a0698ed9Schristos 	if (ret == 0) {
21a0698ed9Schristos 		/*
22a0698ed9Schristos 		 * Duplicates are not allowed in the heap, so force an
23a0698ed9Schristos 		 * arbitrary ordering for non-identical items with equal keys.
24a0698ed9Schristos 		 */
25a0698ed9Schristos 		ret = (((uintptr_t)a) > ((uintptr_t)b))
26a0698ed9Schristos 		    - (((uintptr_t)a) < ((uintptr_t)b));
27a0698ed9Schristos 	}
28a0698ed9Schristos 	return ret;
29a0698ed9Schristos }
30a0698ed9Schristos 
31a0698ed9Schristos static int
32a0698ed9Schristos node_cmp_magic(const node_t *a, const node_t *b) {
33a0698ed9Schristos 
34*7bdf38e5Schristos 	expect_u32_eq(a->magic, NODE_MAGIC, "Bad magic");
35*7bdf38e5Schristos 	expect_u32_eq(b->magic, NODE_MAGIC, "Bad magic");
36a0698ed9Schristos 
37a0698ed9Schristos 	return node_cmp(a, b);
38a0698ed9Schristos }
39a0698ed9Schristos 
40*7bdf38e5Schristos ph_gen(static, heap, node_t, link, node_cmp_magic);
41*7bdf38e5Schristos 
42*7bdf38e5Schristos static node_t *
43*7bdf38e5Schristos node_next_get(const node_t *node) {
44*7bdf38e5Schristos 	return phn_next_get((node_t *)node, offsetof(node_t, link));
45*7bdf38e5Schristos }
46*7bdf38e5Schristos 
47*7bdf38e5Schristos static node_t *
48*7bdf38e5Schristos node_prev_get(const node_t *node) {
49*7bdf38e5Schristos 	return phn_prev_get((node_t *)node, offsetof(node_t, link));
50*7bdf38e5Schristos }
51*7bdf38e5Schristos 
52*7bdf38e5Schristos static node_t *
53*7bdf38e5Schristos node_lchild_get(const node_t *node) {
54*7bdf38e5Schristos 	return phn_lchild_get((node_t *)node, offsetof(node_t, link));
55*7bdf38e5Schristos }
56a0698ed9Schristos 
57a0698ed9Schristos static void
58a0698ed9Schristos node_print(const node_t *node, unsigned depth) {
59a0698ed9Schristos 	unsigned i;
60a0698ed9Schristos 	node_t *leftmost_child, *sibling;
61a0698ed9Schristos 
62a0698ed9Schristos 	for (i = 0; i < depth; i++) {
63a0698ed9Schristos 		malloc_printf("\t");
64a0698ed9Schristos 	}
65a0698ed9Schristos 	malloc_printf("%2"FMTu64"\n", node->key);
66a0698ed9Schristos 
67*7bdf38e5Schristos 	leftmost_child = node_lchild_get(node);
68a0698ed9Schristos 	if (leftmost_child == NULL) {
69a0698ed9Schristos 		return;
70a0698ed9Schristos 	}
71a0698ed9Schristos 	node_print(leftmost_child, depth + 1);
72a0698ed9Schristos 
73*7bdf38e5Schristos 	for (sibling = node_next_get(leftmost_child); sibling !=
74*7bdf38e5Schristos 	    NULL; sibling = node_next_get(sibling)) {
75a0698ed9Schristos 		node_print(sibling, depth + 1);
76a0698ed9Schristos 	}
77a0698ed9Schristos }
78a0698ed9Schristos 
79a0698ed9Schristos static void
80a0698ed9Schristos heap_print(const heap_t *heap) {
81a0698ed9Schristos 	node_t *auxelm;
82a0698ed9Schristos 
83a0698ed9Schristos 	malloc_printf("vvv heap %p vvv\n", heap);
84*7bdf38e5Schristos 	if (heap->ph.root == NULL) {
85a0698ed9Schristos 		goto label_return;
86a0698ed9Schristos 	}
87a0698ed9Schristos 
88*7bdf38e5Schristos 	node_print(heap->ph.root, 0);
89a0698ed9Schristos 
90*7bdf38e5Schristos 	for (auxelm = node_next_get(heap->ph.root); auxelm != NULL;
91*7bdf38e5Schristos 	    auxelm = node_next_get(auxelm)) {
92*7bdf38e5Schristos 		expect_ptr_eq(node_next_get(node_prev_get(auxelm)), auxelm,
93a0698ed9Schristos 		    "auxelm's prev doesn't link to auxelm");
94a0698ed9Schristos 		node_print(auxelm, 0);
95a0698ed9Schristos 	}
96a0698ed9Schristos 
97a0698ed9Schristos label_return:
98a0698ed9Schristos 	malloc_printf("^^^ heap %p ^^^\n", heap);
99a0698ed9Schristos }
100a0698ed9Schristos 
101a0698ed9Schristos static unsigned
102a0698ed9Schristos node_validate(const node_t *node, const node_t *parent) {
103a0698ed9Schristos 	unsigned nnodes = 1;
104a0698ed9Schristos 	node_t *leftmost_child, *sibling;
105a0698ed9Schristos 
106a0698ed9Schristos 	if (parent != NULL) {
107*7bdf38e5Schristos 		expect_d_ge(node_cmp_magic(node, parent), 0,
108a0698ed9Schristos 		    "Child is less than parent");
109a0698ed9Schristos 	}
110a0698ed9Schristos 
111*7bdf38e5Schristos 	leftmost_child = node_lchild_get(node);
112a0698ed9Schristos 	if (leftmost_child == NULL) {
113a0698ed9Schristos 		return nnodes;
114a0698ed9Schristos 	}
115*7bdf38e5Schristos 	expect_ptr_eq(node_prev_get(leftmost_child),
116a0698ed9Schristos 	    (void *)node, "Leftmost child does not link to node");
117a0698ed9Schristos 	nnodes += node_validate(leftmost_child, node);
118a0698ed9Schristos 
119*7bdf38e5Schristos 	for (sibling = node_next_get(leftmost_child); sibling !=
120*7bdf38e5Schristos 	    NULL; sibling = node_next_get(sibling)) {
121*7bdf38e5Schristos 		expect_ptr_eq(node_next_get(node_prev_get(sibling)), sibling,
122a0698ed9Schristos 		    "sibling's prev doesn't link to sibling");
123a0698ed9Schristos 		nnodes += node_validate(sibling, node);
124a0698ed9Schristos 	}
125a0698ed9Schristos 	return nnodes;
126a0698ed9Schristos }
127a0698ed9Schristos 
128a0698ed9Schristos static unsigned
129a0698ed9Schristos heap_validate(const heap_t *heap) {
130a0698ed9Schristos 	unsigned nnodes = 0;
131a0698ed9Schristos 	node_t *auxelm;
132a0698ed9Schristos 
133*7bdf38e5Schristos 	if (heap->ph.root == NULL) {
134a0698ed9Schristos 		goto label_return;
135a0698ed9Schristos 	}
136a0698ed9Schristos 
137*7bdf38e5Schristos 	nnodes += node_validate(heap->ph.root, NULL);
138a0698ed9Schristos 
139*7bdf38e5Schristos 	for (auxelm = node_next_get(heap->ph.root); auxelm != NULL;
140*7bdf38e5Schristos 	    auxelm = node_next_get(auxelm)) {
141*7bdf38e5Schristos 		expect_ptr_eq(node_next_get(node_prev_get(auxelm)), auxelm,
142a0698ed9Schristos 		    "auxelm's prev doesn't link to auxelm");
143a0698ed9Schristos 		nnodes += node_validate(auxelm, NULL);
144a0698ed9Schristos 	}
145a0698ed9Schristos 
146a0698ed9Schristos label_return:
147a0698ed9Schristos 	if (false) {
148a0698ed9Schristos 		heap_print(heap);
149a0698ed9Schristos 	}
150a0698ed9Schristos 	return nnodes;
151a0698ed9Schristos }
152a0698ed9Schristos 
153a0698ed9Schristos TEST_BEGIN(test_ph_empty) {
154a0698ed9Schristos 	heap_t heap;
155a0698ed9Schristos 
156a0698ed9Schristos 	heap_new(&heap);
157*7bdf38e5Schristos 	expect_true(heap_empty(&heap), "Heap should be empty");
158*7bdf38e5Schristos 	expect_ptr_null(heap_first(&heap), "Unexpected node");
159*7bdf38e5Schristos 	expect_ptr_null(heap_any(&heap), "Unexpected node");
160a0698ed9Schristos }
161a0698ed9Schristos TEST_END
162a0698ed9Schristos 
163a0698ed9Schristos static void
164a0698ed9Schristos node_remove(heap_t *heap, node_t *node) {
165a0698ed9Schristos 	heap_remove(heap, node);
166a0698ed9Schristos 
167a0698ed9Schristos 	node->magic = 0;
168a0698ed9Schristos }
169a0698ed9Schristos 
170a0698ed9Schristos static node_t *
171a0698ed9Schristos node_remove_first(heap_t *heap) {
172a0698ed9Schristos 	node_t *node = heap_remove_first(heap);
173a0698ed9Schristos 	node->magic = 0;
174a0698ed9Schristos 	return node;
175a0698ed9Schristos }
176a0698ed9Schristos 
177a0698ed9Schristos static node_t *
178a0698ed9Schristos node_remove_any(heap_t *heap) {
179a0698ed9Schristos 	node_t *node = heap_remove_any(heap);
180a0698ed9Schristos 	node->magic = 0;
181a0698ed9Schristos 	return node;
182a0698ed9Schristos }
183a0698ed9Schristos 
184a0698ed9Schristos TEST_BEGIN(test_ph_random) {
185a0698ed9Schristos #define NNODES 25
186a0698ed9Schristos #define NBAGS 250
187a0698ed9Schristos #define SEED 42
188a0698ed9Schristos 	sfmt_t *sfmt;
189a0698ed9Schristos 	uint64_t bag[NNODES];
190a0698ed9Schristos 	heap_t heap;
191a0698ed9Schristos 	node_t nodes[NNODES];
192a0698ed9Schristos 	unsigned i, j, k;
193a0698ed9Schristos 
194a0698ed9Schristos 	sfmt = init_gen_rand(SEED);
195a0698ed9Schristos 	for (i = 0; i < NBAGS; i++) {
196a0698ed9Schristos 		switch (i) {
197a0698ed9Schristos 		case 0:
198a0698ed9Schristos 			/* Insert in order. */
199a0698ed9Schristos 			for (j = 0; j < NNODES; j++) {
200a0698ed9Schristos 				bag[j] = j;
201a0698ed9Schristos 			}
202a0698ed9Schristos 			break;
203a0698ed9Schristos 		case 1:
204a0698ed9Schristos 			/* Insert in reverse order. */
205a0698ed9Schristos 			for (j = 0; j < NNODES; j++) {
206a0698ed9Schristos 				bag[j] = NNODES - j - 1;
207a0698ed9Schristos 			}
208a0698ed9Schristos 			break;
209a0698ed9Schristos 		default:
210a0698ed9Schristos 			for (j = 0; j < NNODES; j++) {
211a0698ed9Schristos 				bag[j] = gen_rand64_range(sfmt, NNODES);
212a0698ed9Schristos 			}
213a0698ed9Schristos 		}
214a0698ed9Schristos 
215a0698ed9Schristos 		for (j = 1; j <= NNODES; j++) {
216a0698ed9Schristos 			/* Initialize heap and nodes. */
217a0698ed9Schristos 			heap_new(&heap);
218*7bdf38e5Schristos 			expect_u_eq(heap_validate(&heap), 0,
219a0698ed9Schristos 			    "Incorrect node count");
220a0698ed9Schristos 			for (k = 0; k < j; k++) {
221a0698ed9Schristos 				nodes[k].magic = NODE_MAGIC;
222a0698ed9Schristos 				nodes[k].key = bag[k];
223a0698ed9Schristos 			}
224a0698ed9Schristos 
225a0698ed9Schristos 			/* Insert nodes. */
226a0698ed9Schristos 			for (k = 0; k < j; k++) {
227a0698ed9Schristos 				heap_insert(&heap, &nodes[k]);
228a0698ed9Schristos 				if (i % 13 == 12) {
229*7bdf38e5Schristos 					expect_ptr_not_null(heap_any(&heap),
230a0698ed9Schristos 					    "Heap should not be empty");
231a0698ed9Schristos 					/* Trigger merging. */
232*7bdf38e5Schristos 					expect_ptr_not_null(heap_first(&heap),
233a0698ed9Schristos 					    "Heap should not be empty");
234a0698ed9Schristos 				}
235*7bdf38e5Schristos 				expect_u_eq(heap_validate(&heap), k + 1,
236a0698ed9Schristos 				    "Incorrect node count");
237a0698ed9Schristos 			}
238a0698ed9Schristos 
239*7bdf38e5Schristos 			expect_false(heap_empty(&heap),
240a0698ed9Schristos 			    "Heap should not be empty");
241a0698ed9Schristos 
242a0698ed9Schristos 			/* Remove nodes. */
243a0698ed9Schristos 			switch (i % 6) {
244a0698ed9Schristos 			case 0:
245a0698ed9Schristos 				for (k = 0; k < j; k++) {
246*7bdf38e5Schristos 					expect_u_eq(heap_validate(&heap), j - k,
247a0698ed9Schristos 					    "Incorrect node count");
248a0698ed9Schristos 					node_remove(&heap, &nodes[k]);
249*7bdf38e5Schristos 					expect_u_eq(heap_validate(&heap), j - k
250a0698ed9Schristos 					    - 1, "Incorrect node count");
251a0698ed9Schristos 				}
252a0698ed9Schristos 				break;
253a0698ed9Schristos 			case 1:
254a0698ed9Schristos 				for (k = j; k > 0; k--) {
255a0698ed9Schristos 					node_remove(&heap, &nodes[k-1]);
256*7bdf38e5Schristos 					expect_u_eq(heap_validate(&heap), k - 1,
257a0698ed9Schristos 					    "Incorrect node count");
258a0698ed9Schristos 				}
259a0698ed9Schristos 				break;
260a0698ed9Schristos 			case 2: {
261a0698ed9Schristos 				node_t *prev = NULL;
262a0698ed9Schristos 				for (k = 0; k < j; k++) {
263a0698ed9Schristos 					node_t *node = node_remove_first(&heap);
264*7bdf38e5Schristos 					expect_u_eq(heap_validate(&heap), j - k
265a0698ed9Schristos 					    - 1, "Incorrect node count");
266a0698ed9Schristos 					if (prev != NULL) {
267*7bdf38e5Schristos 						expect_d_ge(node_cmp(node,
268a0698ed9Schristos 						    prev), 0,
269a0698ed9Schristos 						    "Bad removal order");
270a0698ed9Schristos 					}
271a0698ed9Schristos 					prev = node;
272a0698ed9Schristos 				}
273a0698ed9Schristos 				break;
274a0698ed9Schristos 			} case 3: {
275a0698ed9Schristos 				node_t *prev = NULL;
276a0698ed9Schristos 				for (k = 0; k < j; k++) {
277a0698ed9Schristos 					node_t *node = heap_first(&heap);
278*7bdf38e5Schristos 					expect_u_eq(heap_validate(&heap), j - k,
279a0698ed9Schristos 					    "Incorrect node count");
280a0698ed9Schristos 					if (prev != NULL) {
281*7bdf38e5Schristos 						expect_d_ge(node_cmp(node,
282a0698ed9Schristos 						    prev), 0,
283a0698ed9Schristos 						    "Bad removal order");
284a0698ed9Schristos 					}
285a0698ed9Schristos 					node_remove(&heap, node);
286*7bdf38e5Schristos 					expect_u_eq(heap_validate(&heap), j - k
287a0698ed9Schristos 					    - 1, "Incorrect node count");
288a0698ed9Schristos 					prev = node;
289a0698ed9Schristos 				}
290a0698ed9Schristos 				break;
291a0698ed9Schristos 			} case 4: {
292a0698ed9Schristos 				for (k = 0; k < j; k++) {
293a0698ed9Schristos 					node_remove_any(&heap);
294*7bdf38e5Schristos 					expect_u_eq(heap_validate(&heap), j - k
295a0698ed9Schristos 					    - 1, "Incorrect node count");
296a0698ed9Schristos 				}
297a0698ed9Schristos 				break;
298a0698ed9Schristos 			} case 5: {
299a0698ed9Schristos 				for (k = 0; k < j; k++) {
300a0698ed9Schristos 					node_t *node = heap_any(&heap);
301*7bdf38e5Schristos 					expect_u_eq(heap_validate(&heap), j - k,
302a0698ed9Schristos 					    "Incorrect node count");
303a0698ed9Schristos 					node_remove(&heap, node);
304*7bdf38e5Schristos 					expect_u_eq(heap_validate(&heap), j - k
305a0698ed9Schristos 					    - 1, "Incorrect node count");
306a0698ed9Schristos 				}
307a0698ed9Schristos 				break;
308a0698ed9Schristos 			} default:
309a0698ed9Schristos 				not_reached();
310a0698ed9Schristos 			}
311a0698ed9Schristos 
312*7bdf38e5Schristos 			expect_ptr_null(heap_first(&heap),
313a0698ed9Schristos 			    "Heap should be empty");
314*7bdf38e5Schristos 			expect_ptr_null(heap_any(&heap),
315a0698ed9Schristos 			    "Heap should be empty");
316*7bdf38e5Schristos 			expect_true(heap_empty(&heap), "Heap should be empty");
317a0698ed9Schristos 		}
318a0698ed9Schristos 	}
319a0698ed9Schristos 	fini_gen_rand(sfmt);
320a0698ed9Schristos #undef NNODES
321a0698ed9Schristos #undef SEED
322a0698ed9Schristos }
323a0698ed9Schristos TEST_END
324a0698ed9Schristos 
325a0698ed9Schristos int
326a0698ed9Schristos main(void) {
327a0698ed9Schristos 	return test(
328a0698ed9Schristos 	    test_ph_empty,
329a0698ed9Schristos 	    test_ph_random);
330a0698ed9Schristos }
331