2 * Copyright (c) 2019 David Lamparter, for NetDEF, Inc.
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
28 DEFINE_MTYPE_STATIC(LIB
, TYPEDHASH_BUCKET
, "Typed-hash bucket");
29 DEFINE_MTYPE_STATIC(LIB
, SKIPLIST_OFLOW
, "Skiplist overflow");
30 DEFINE_MTYPE_STATIC(LIB
, HEAP_ARRAY
, "Typed-heap array");
32 struct slist_item typesafe_slist_sentinel
= { NULL
};
34 bool typesafe_list_member(const struct slist_head
*head
,
35 const struct slist_item
*item
)
37 struct slist_item
*fromhead
= head
->first
;
38 struct slist_item
**fromnext
= (struct slist_item
**)&item
->next
;
40 while (fromhead
!= _SLIST_LAST
) {
41 if (fromhead
== item
|| fromnext
== head
->last_next
)
44 fromhead
= fromhead
->next
;
45 if (!*fromnext
|| *fromnext
== _SLIST_LAST
)
47 fromnext
= &(*fromnext
)->next
;
53 bool typesafe_dlist_member(const struct dlist_head
*head
,
54 const struct dlist_item
*item
)
56 const struct dlist_item
*fromhead
= head
->hitem
.next
;
57 const struct dlist_item
*fromitem
= item
->next
;
59 if (!item
->prev
|| !item
->next
)
62 while (fromhead
!= &head
->hitem
&& fromitem
!= item
) {
63 if (fromitem
== &head
->hitem
|| fromhead
== item
)
65 fromhead
= fromhead
->next
;
66 fromitem
= fromitem
->next
;
73 static void hash_consistency_check(struct thash_head
*head
)
76 struct thash_item
*item
, *prev
;
78 for (i
= 0; i
< HASH_SIZE(*head
); i
++) {
79 item
= head
->entries
[i
];
82 assert(HASH_KEY(*head
, item
->hashval
) == i
);
83 assert(!prev
|| item
->hashval
>= prev
->hashval
);
90 #define hash_consistency_check(x)
93 void typesafe_hash_grow(struct thash_head
*head
)
95 uint32_t newsize
= head
->count
, i
, j
;
96 uint8_t newshift
, delta
;
98 hash_consistency_check(head
);
100 newsize
|= newsize
>> 1;
101 newsize
|= newsize
>> 2;
102 newsize
|= newsize
>> 4;
103 newsize
|= newsize
>> 8;
104 newsize
|= newsize
>> 16;
106 newshift
= __builtin_ctz(newsize
) + 1;
108 if (head
->maxshift
&& newshift
> head
->maxshift
)
109 newshift
= head
->maxshift
;
110 if (newshift
== head
->tabshift
)
112 newsize
= _HASH_SIZE(newshift
);
114 head
->entries
= XREALLOC(MTYPE_TYPEDHASH_BUCKET
, head
->entries
,
115 sizeof(head
->entries
[0]) * newsize
);
116 memset(head
->entries
+ HASH_SIZE(*head
), 0,
117 sizeof(head
->entries
[0]) *
118 (newsize
- HASH_SIZE(*head
)));
120 delta
= newshift
- head
->tabshift
;
122 i
= HASH_SIZE(*head
);
126 struct thash_item
**apos
, *item
;
129 apos
= &head
->entries
[i
];
131 for (j
= 0; j
< (1U << delta
); j
++) {
135 head
->entries
[(i
<< delta
) + j
] = item
;
136 apos
= &head
->entries
[(i
<< delta
) + j
];
138 while ((item
= *apos
)) {
140 midbits
= _HASH_KEY(newshift
, item
->hashval
);
141 midbits
&= (1 << delta
) - 1;
150 head
->tabshift
= newshift
;
151 hash_consistency_check(head
);
154 void typesafe_hash_shrink(struct thash_head
*head
)
156 uint32_t newsize
= head
->count
, i
, j
;
157 uint8_t newshift
, delta
;
159 hash_consistency_check(head
);
162 XFREE(MTYPE_TYPEDHASH_BUCKET
, head
->entries
);
167 newsize
|= newsize
>> 1;
168 newsize
|= newsize
>> 2;
169 newsize
|= newsize
>> 4;
170 newsize
|= newsize
>> 8;
171 newsize
|= newsize
>> 16;
173 newshift
= __builtin_ctz(newsize
) + 1;
175 if (head
->minshift
&& newshift
< head
->minshift
)
176 newshift
= head
->minshift
;
177 if (newshift
== head
->tabshift
)
179 newsize
= _HASH_SIZE(newshift
);
181 delta
= head
->tabshift
- newshift
;
183 for (i
= 0; i
< newsize
; i
++) {
184 struct thash_item
**apos
= &head
->entries
[i
];
186 for (j
= 0; j
< (1U << delta
); j
++) {
187 *apos
= head
->entries
[(i
<< delta
) + j
];
189 apos
= &(*apos
)->next
;
192 head
->entries
= XREALLOC(MTYPE_TYPEDHASH_BUCKET
, head
->entries
,
193 sizeof(head
->entries
[0]) * newsize
);
194 head
->tabshift
= newshift
;
196 hash_consistency_check(head
);
201 static inline struct sskip_item
*sl_level_get(const struct sskip_item
*item
,
204 if (level
< SKIPLIST_OVERFLOW
)
205 return item
->next
[level
];
206 if (level
== SKIPLIST_OVERFLOW
&& !((uintptr_t)item
->next
[level
] & 1))
207 return item
->next
[level
];
209 uintptr_t ptrval
= (uintptr_t)item
->next
[SKIPLIST_OVERFLOW
];
210 ptrval
&= UINTPTR_MAX
- 3;
211 struct sskip_overflow
*oflow
= (struct sskip_overflow
*)ptrval
;
212 return oflow
->next
[level
- SKIPLIST_OVERFLOW
];
215 static inline void sl_level_set(struct sskip_item
*item
, size_t level
,
216 struct sskip_item
*value
)
218 if (level
< SKIPLIST_OVERFLOW
)
219 item
->next
[level
] = value
;
220 else if (level
== SKIPLIST_OVERFLOW
&& !((uintptr_t)item
->next
[level
] & 1))
221 item
->next
[level
] = value
;
223 uintptr_t ptrval
= (uintptr_t)item
->next
[SKIPLIST_OVERFLOW
];
224 ptrval
&= UINTPTR_MAX
- 3;
225 struct sskip_overflow
*oflow
= (struct sskip_overflow
*)ptrval
;
226 oflow
->next
[level
- SKIPLIST_OVERFLOW
] = value
;
230 struct sskip_item
*typesafe_skiplist_add(struct sskip_head
*head
,
231 struct sskip_item
*item
,
232 int (*cmpfn
)(const struct sskip_item
*a
,
233 const struct sskip_item
*b
))
235 size_t level
= SKIPLIST_MAXDEPTH
, newlevel
, auxlevel
;
236 struct sskip_item
*prev
= &head
->hitem
, *next
, *auxprev
, *auxnext
;
239 /* level / newlevel are 1-counted here */
240 newlevel
= __builtin_ctz(frr_weak_random()) + 1;
241 if (newlevel
> SKIPLIST_MAXDEPTH
)
242 newlevel
= SKIPLIST_MAXDEPTH
;
245 while (level
>= newlevel
) {
246 next
= sl_level_get(prev
, level
- 1);
251 cmpval
= cmpfn(next
, item
);
255 } else if (cmpval
== 0) {
261 /* check for duplicate item - could be removed if code doesn't rely
262 * on it, but not really work the complication. */
267 auxnext
= sl_level_get(auxprev
, auxlevel
);
269 while (auxnext
&& (cmpval
= cmpfn(auxnext
, item
)) < 0) {
271 auxnext
= sl_level_get(auxprev
, auxlevel
);
278 memset(item
, 0, sizeof(*item
));
279 if (newlevel
> SKIPLIST_EMBED
) {
280 struct sskip_overflow
*oflow
;
281 oflow
= XMALLOC(MTYPE_SKIPLIST_OFLOW
, sizeof(void *)
282 * (newlevel
- SKIPLIST_OVERFLOW
));
283 item
->next
[SKIPLIST_OVERFLOW
] = (struct sskip_item
*)
284 ((uintptr_t)oflow
| 1);
287 sl_level_set(item
, level
, next
);
288 sl_level_set(prev
, level
, item
);
289 /* level is now 0-counted and < newlevel*/
292 next
= sl_level_get(prev
, level
);
293 while (next
&& cmpfn(next
, item
) < 0) {
295 next
= sl_level_get(prev
, level
);
298 sl_level_set(item
, level
, next
);
299 sl_level_set(prev
, level
, item
);
304 /* NOTE: level counting below is 1-based since that makes the code simpler! */
306 const struct sskip_item
*typesafe_skiplist_find(
307 const struct sskip_head
*head
,
308 const struct sskip_item
*item
, int (*cmpfn
)(
309 const struct sskip_item
*a
,
310 const struct sskip_item
*b
))
312 size_t level
= SKIPLIST_MAXDEPTH
;
313 const struct sskip_item
*prev
= &head
->hitem
, *next
;
317 next
= sl_level_get(prev
, level
- 1);
322 cmpval
= cmpfn(next
, item
);
334 const struct sskip_item
*typesafe_skiplist_find_gteq(
335 const struct sskip_head
*head
,
336 const struct sskip_item
*item
, int (*cmpfn
)(
337 const struct sskip_item
*a
,
338 const struct sskip_item
*b
))
340 size_t level
= SKIPLIST_MAXDEPTH
;
341 const struct sskip_item
*prev
= &head
->hitem
, *next
;
345 next
= sl_level_get(prev
, level
- 1);
350 cmpval
= cmpfn(next
, item
);
362 const struct sskip_item
*typesafe_skiplist_find_lt(
363 const struct sskip_head
*head
,
364 const struct sskip_item
*item
, int (*cmpfn
)(
365 const struct sskip_item
*a
,
366 const struct sskip_item
*b
))
368 size_t level
= SKIPLIST_MAXDEPTH
;
369 const struct sskip_item
*prev
= &head
->hitem
, *next
, *best
= NULL
;
373 next
= sl_level_get(prev
, level
- 1);
378 cmpval
= cmpfn(next
, item
);
388 struct sskip_item
*typesafe_skiplist_del(
389 struct sskip_head
*head
, struct sskip_item
*item
,
390 int (*cmpfn
)(const struct sskip_item
*a
, const struct sskip_item
*b
))
392 size_t level
= SKIPLIST_MAXDEPTH
;
393 struct sskip_item
*prev
= &head
->hitem
, *next
;
398 next
= sl_level_get(prev
, level
- 1);
404 sl_level_set(prev
, level
- 1,
405 sl_level_get(item
, level
- 1));
410 cmpval
= cmpfn(next
, item
);
421 /* TBD: assert when trying to remove non-existing item? */
424 if ((uintptr_t)item
->next
[SKIPLIST_OVERFLOW
] & 1) {
425 uintptr_t ptrval
= (uintptr_t)item
->next
[SKIPLIST_OVERFLOW
];
426 ptrval
&= UINTPTR_MAX
- 3;
427 struct sskip_overflow
*oflow
= (struct sskip_overflow
*)ptrval
;
428 XFREE(MTYPE_SKIPLIST_OFLOW
, oflow
);
430 memset(item
, 0, sizeof(*item
));
435 struct sskip_item
*typesafe_skiplist_pop(struct sskip_head
*head
)
437 size_t level
= SKIPLIST_MAXDEPTH
;
438 struct sskip_item
*prev
= &head
->hitem
, *next
, *item
;
440 item
= sl_level_get(prev
, 0);
447 next
= sl_level_get(prev
, level
);
451 sl_level_set(prev
, level
, sl_level_get(item
, level
));
456 if ((uintptr_t)item
->next
[SKIPLIST_OVERFLOW
] & 1) {
457 uintptr_t ptrval
= (uintptr_t)item
->next
[SKIPLIST_OVERFLOW
];
458 ptrval
&= UINTPTR_MAX
- 3;
459 struct sskip_overflow
*oflow
= (struct sskip_overflow
*)ptrval
;
460 XFREE(MTYPE_SKIPLIST_OFLOW
, oflow
);
462 memset(item
, 0, sizeof(*item
));
470 static void heap_consistency_check(struct heap_head
*head
,
471 int (*cmpfn
)(const struct heap_item
*a
,
472 const struct heap_item
*b
),
475 uint32_t rghtpos
= pos
+ 1;
476 uint32_t downpos
= HEAP_NARY
* (pos
+ 1);
478 if (pos
+ 1 > ~0U / HEAP_NARY
)
481 if ((pos
& (HEAP_NARY
- 1)) != HEAP_NARY
- 1 && rghtpos
< head
->count
) {
482 assert(cmpfn(head
->array
[rghtpos
], head
->array
[pos
]) >= 0);
483 heap_consistency_check(head
, cmpfn
, rghtpos
);
485 if (downpos
< head
->count
) {
486 assert(cmpfn(head
->array
[downpos
], head
->array
[pos
]) >= 0);
487 heap_consistency_check(head
, cmpfn
, downpos
);
491 #define heap_consistency_check(head, cmpfn, pos)
494 void typesafe_heap_resize(struct heap_head
*head
, bool grow
)
499 newsize
= head
->arraysz
;
502 else if (newsize
< 262144)
503 newsize
+= newsize
/ 2;
504 else if (newsize
< 0xaaaa0000)
505 newsize
+= newsize
/ 3;
508 } else if (head
->count
> 0) {
509 newsize
= head
->count
;
511 XFREE(MTYPE_HEAP_ARRAY
, head
->array
);
516 newsize
+= HEAP_NARY
- 1;
517 newsize
&= ~(HEAP_NARY
- 1);
518 if (newsize
== head
->arraysz
)
521 head
->array
= XREALLOC(MTYPE_HEAP_ARRAY
, head
->array
,
522 newsize
* sizeof(struct heap_item
*));
523 head
->arraysz
= newsize
;
526 void typesafe_heap_pushdown(struct heap_head
*head
, uint32_t pos
,
527 struct heap_item
*item
,
528 int (*cmpfn
)(const struct heap_item
*a
,
529 const struct heap_item
*b
))
531 uint32_t rghtpos
, downpos
, moveto
;
534 /* rghtpos: neighbor to the "right", inside block of NARY.
535 * may be invalid if last in block, check nary_last()
536 * downpos: first neighbor in the "downwards" block further
541 /* make sure we can use the full 4G items */
542 downpos
= HEAP_NARY
* (pos
+ 1);
543 if (pos
+ 1 > ~0U / HEAP_NARY
)
544 /* multiplication overflowed. ~0U is guaranteed
545 * to be an invalid index; size limit is enforced in
550 /* only used on break */
553 #define nary_last(x) (((x) & (HEAP_NARY - 1)) == HEAP_NARY - 1)
554 if (downpos
>= head
->count
555 || cmpfn(head
->array
[downpos
], item
) >= 0) {
556 /* not moving down; either at end or down is >= item */
557 if (nary_last(pos
) || rghtpos
>= head
->count
558 || cmpfn(head
->array
[rghtpos
], item
) >= 0)
559 /* not moving right either - got our spot */
564 /* else: downpos is valid and < item. choose between down
565 * or right (if the latter is an option) */
566 } else if (nary_last(pos
) || cmpfn(head
->array
[rghtpos
],
567 head
->array
[downpos
]) >= 0)
573 head
->array
[pos
] = head
->array
[moveto
];
574 head
->array
[pos
]->index
= pos
;
578 head
->array
[moveto
] = item
;
579 item
->index
= moveto
;
581 heap_consistency_check(head
, cmpfn
, 0);
584 void typesafe_heap_pullup(struct heap_head
*head
, uint32_t pos
,
585 struct heap_item
*item
,
586 int (*cmpfn
)(const struct heap_item
*a
,
587 const struct heap_item
*b
))
592 if ((pos
& (HEAP_NARY
- 1)) == 0)
593 moveto
= pos
/ HEAP_NARY
- 1;
597 if (cmpfn(head
->array
[moveto
], item
) <= 0)
600 head
->array
[pos
] = head
->array
[moveto
];
601 head
->array
[pos
]->index
= pos
;
606 head
->array
[pos
] = item
;
609 heap_consistency_check(head
, cmpfn
, 0);