]>
git.proxmox.com Git - mirror_frr.git/blob - lib/typesafe.c
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");
33 static void hash_consistency_check(struct thash_head
*head
)
36 struct thash_item
*item
, *prev
;
38 for (i
= 0; i
< HASH_SIZE(*head
); i
++) {
39 item
= head
->entries
[i
];
42 assert(HASH_KEY(*head
, item
->hashval
) == i
);
43 assert(!prev
|| item
->hashval
>= prev
->hashval
);
50 #define hash_consistency_check(x)
53 void typesafe_hash_grow(struct thash_head
*head
)
55 uint32_t newsize
= head
->count
, i
, j
;
56 uint8_t newshift
, delta
;
58 hash_consistency_check(head
);
60 newsize
|= newsize
>> 1;
61 newsize
|= newsize
>> 2;
62 newsize
|= newsize
>> 4;
63 newsize
|= newsize
>> 8;
64 newsize
|= newsize
>> 16;
66 newshift
= __builtin_ctz(newsize
) + 1;
68 if (head
->maxshift
&& newshift
> head
->maxshift
)
69 newshift
= head
->maxshift
;
70 if (newshift
== head
->tabshift
)
72 newsize
= _HASH_SIZE(newshift
);
74 head
->entries
= XREALLOC(MTYPE_TYPEDHASH_BUCKET
, head
->entries
,
75 sizeof(head
->entries
[0]) * newsize
);
76 memset(head
->entries
+ HASH_SIZE(*head
), 0,
77 sizeof(head
->entries
[0]) *
78 (newsize
- HASH_SIZE(*head
)));
80 delta
= newshift
- head
->tabshift
;
86 struct thash_item
**apos
, *item
;
89 apos
= &head
->entries
[i
];
91 for (j
= 0; j
< (1U << delta
); j
++) {
95 head
->entries
[(i
<< delta
) + j
] = item
;
96 apos
= &head
->entries
[(i
<< delta
) + j
];
98 while ((item
= *apos
)) {
100 midbits
= _HASH_KEY(newshift
, item
->hashval
);
101 midbits
&= (1 << delta
) - 1;
110 head
->tabshift
= newshift
;
111 hash_consistency_check(head
);
114 void typesafe_hash_shrink(struct thash_head
*head
)
116 uint32_t newsize
= head
->count
, i
, j
;
117 uint8_t newshift
, delta
;
119 hash_consistency_check(head
);
122 XFREE(MTYPE_TYPEDHASH_BUCKET
, head
->entries
);
127 newsize
|= newsize
>> 1;
128 newsize
|= newsize
>> 2;
129 newsize
|= newsize
>> 4;
130 newsize
|= newsize
>> 8;
131 newsize
|= newsize
>> 16;
133 newshift
= __builtin_ctz(newsize
) + 1;
135 if (head
->minshift
&& newshift
< head
->minshift
)
136 newshift
= head
->minshift
;
137 if (newshift
== head
->tabshift
)
139 newsize
= _HASH_SIZE(newshift
);
141 delta
= head
->tabshift
- newshift
;
143 for (i
= 0; i
< newsize
; i
++) {
144 struct thash_item
**apos
= &head
->entries
[i
];
146 for (j
= 0; j
< (1U << delta
); j
++) {
147 *apos
= head
->entries
[(i
<< delta
) + j
];
149 apos
= &(*apos
)->next
;
152 head
->entries
= XREALLOC(MTYPE_TYPEDHASH_BUCKET
, head
->entries
,
153 sizeof(head
->entries
[0]) * newsize
);
154 head
->tabshift
= newshift
;
156 hash_consistency_check(head
);
161 static inline struct sskip_item
*sl_level_get(const struct sskip_item
*item
,
164 if (level
< SKIPLIST_OVERFLOW
)
165 return item
->next
[level
];
166 if (level
== SKIPLIST_OVERFLOW
&& !((uintptr_t)item
->next
[level
] & 1))
167 return item
->next
[level
];
169 uintptr_t ptrval
= (uintptr_t)item
->next
[SKIPLIST_OVERFLOW
];
170 ptrval
&= UINTPTR_MAX
- 3;
171 struct sskip_overflow
*oflow
= (struct sskip_overflow
*)ptrval
;
172 return oflow
->next
[level
- SKIPLIST_OVERFLOW
];
175 static inline void sl_level_set(struct sskip_item
*item
, size_t level
,
176 struct sskip_item
*value
)
178 if (level
< SKIPLIST_OVERFLOW
)
179 item
->next
[level
] = value
;
180 else if (level
== SKIPLIST_OVERFLOW
&& !((uintptr_t)item
->next
[level
] & 1))
181 item
->next
[level
] = value
;
183 uintptr_t ptrval
= (uintptr_t)item
->next
[SKIPLIST_OVERFLOW
];
184 ptrval
&= UINTPTR_MAX
- 3;
185 struct sskip_overflow
*oflow
= (struct sskip_overflow
*)ptrval
;
186 oflow
->next
[level
- SKIPLIST_OVERFLOW
] = value
;
190 struct sskip_item
*typesafe_skiplist_add(struct sskip_head
*head
,
191 struct sskip_item
*item
,
192 int (*cmpfn
)(const struct sskip_item
*a
,
193 const struct sskip_item
*b
))
195 size_t level
= SKIPLIST_MAXDEPTH
, newlevel
, auxlevel
;
196 struct sskip_item
*prev
= &head
->hitem
, *next
, *auxprev
, *auxnext
;
199 /* level / newlevel are 1-counted here */
200 newlevel
= __builtin_ctz(frr_weak_random()) + 1;
201 if (newlevel
> SKIPLIST_MAXDEPTH
)
202 newlevel
= SKIPLIST_MAXDEPTH
;
205 while (level
>= newlevel
) {
206 next
= sl_level_get(prev
, level
- 1);
211 cmpval
= cmpfn(next
, item
);
215 } else if (cmpval
== 0) {
221 /* check for duplicate item - could be removed if code doesn't rely
222 * on it, but not really work the complication. */
227 auxnext
= sl_level_get(auxprev
, auxlevel
);
229 while (auxnext
&& (cmpval
= cmpfn(auxnext
, item
)) < 0) {
231 auxnext
= sl_level_get(auxprev
, auxlevel
);
238 memset(item
, 0, sizeof(*item
));
239 if (newlevel
> SKIPLIST_EMBED
) {
240 struct sskip_overflow
*oflow
;
241 oflow
= XMALLOC(MTYPE_SKIPLIST_OFLOW
, sizeof(void *)
242 * (newlevel
- SKIPLIST_OVERFLOW
));
243 item
->next
[SKIPLIST_OVERFLOW
] = (struct sskip_item
*)
244 ((uintptr_t)oflow
| 1);
247 sl_level_set(item
, level
, next
);
248 sl_level_set(prev
, level
, item
);
249 /* level is now 0-counted and < newlevel*/
252 next
= sl_level_get(prev
, level
);
253 while (next
&& cmpfn(next
, item
) < 0) {
255 next
= sl_level_get(prev
, level
);
258 sl_level_set(item
, level
, next
);
259 sl_level_set(prev
, level
, item
);
264 /* NOTE: level counting below is 1-based since that makes the code simpler! */
266 const struct sskip_item
*typesafe_skiplist_find(
267 const struct sskip_head
*head
,
268 const struct sskip_item
*item
, int (*cmpfn
)(
269 const struct sskip_item
*a
,
270 const struct sskip_item
*b
))
272 size_t level
= SKIPLIST_MAXDEPTH
;
273 const struct sskip_item
*prev
= &head
->hitem
, *next
;
277 next
= sl_level_get(prev
, level
- 1);
282 cmpval
= cmpfn(next
, item
);
294 const struct sskip_item
*typesafe_skiplist_find_gteq(
295 const struct sskip_head
*head
,
296 const struct sskip_item
*item
, int (*cmpfn
)(
297 const struct sskip_item
*a
,
298 const struct sskip_item
*b
))
300 size_t level
= SKIPLIST_MAXDEPTH
;
301 const struct sskip_item
*prev
= &head
->hitem
, *next
;
305 next
= sl_level_get(prev
, level
- 1);
310 cmpval
= cmpfn(next
, item
);
322 const struct sskip_item
*typesafe_skiplist_find_lt(
323 const struct sskip_head
*head
,
324 const struct sskip_item
*item
, int (*cmpfn
)(
325 const struct sskip_item
*a
,
326 const struct sskip_item
*b
))
328 size_t level
= SKIPLIST_MAXDEPTH
;
329 const struct sskip_item
*prev
= &head
->hitem
, *next
, *best
= NULL
;
333 next
= sl_level_get(prev
, level
- 1);
338 cmpval
= cmpfn(next
, item
);
348 struct sskip_item
*typesafe_skiplist_del(
349 struct sskip_head
*head
, struct sskip_item
*item
,
350 int (*cmpfn
)(const struct sskip_item
*a
, const struct sskip_item
*b
))
352 size_t level
= SKIPLIST_MAXDEPTH
;
353 struct sskip_item
*prev
= &head
->hitem
, *next
;
358 next
= sl_level_get(prev
, level
- 1);
364 sl_level_set(prev
, level
- 1,
365 sl_level_get(item
, level
- 1));
370 cmpval
= cmpfn(next
, item
);
381 /* TBD: assert when trying to remove non-existing item? */
384 if ((uintptr_t)item
->next
[SKIPLIST_OVERFLOW
] & 1) {
385 uintptr_t ptrval
= (uintptr_t)item
->next
[SKIPLIST_OVERFLOW
];
386 ptrval
&= UINTPTR_MAX
- 3;
387 struct sskip_overflow
*oflow
= (struct sskip_overflow
*)ptrval
;
388 XFREE(MTYPE_SKIPLIST_OFLOW
, oflow
);
390 memset(item
, 0, sizeof(*item
));
395 struct sskip_item
*typesafe_skiplist_pop(struct sskip_head
*head
)
397 size_t level
= SKIPLIST_MAXDEPTH
;
398 struct sskip_item
*prev
= &head
->hitem
, *next
, *item
;
400 item
= sl_level_get(prev
, 0);
407 next
= sl_level_get(prev
, level
);
411 sl_level_set(prev
, level
, sl_level_get(item
, level
));
416 if ((uintptr_t)item
->next
[SKIPLIST_OVERFLOW
] & 1) {
417 uintptr_t ptrval
= (uintptr_t)item
->next
[SKIPLIST_OVERFLOW
];
418 ptrval
&= UINTPTR_MAX
- 3;
419 struct sskip_overflow
*oflow
= (struct sskip_overflow
*)ptrval
;
420 XFREE(MTYPE_SKIPLIST_OFLOW
, oflow
);
422 memset(item
, 0, sizeof(*item
));
430 static void heap_consistency_check(struct heap_head
*head
,
431 int (*cmpfn
)(const struct heap_item
*a
,
432 const struct heap_item
*b
),
435 uint32_t rghtpos
= pos
+ 1;
436 uint32_t downpos
= HEAP_NARY
* (pos
+ 1);
438 if (pos
+ 1 > ~0U / HEAP_NARY
)
441 if ((pos
& (HEAP_NARY
- 1)) != HEAP_NARY
- 1 && rghtpos
< head
->count
) {
442 assert(cmpfn(head
->array
[rghtpos
], head
->array
[pos
]) >= 0);
443 heap_consistency_check(head
, cmpfn
, rghtpos
);
445 if (downpos
< head
->count
) {
446 assert(cmpfn(head
->array
[downpos
], head
->array
[pos
]) >= 0);
447 heap_consistency_check(head
, cmpfn
, downpos
);
451 #define heap_consistency_check(head, cmpfn, pos)
454 void typesafe_heap_resize(struct heap_head
*head
, bool grow
)
459 newsize
= head
->arraysz
;
462 else if (newsize
< 262144)
463 newsize
+= newsize
/ 2;
464 else if (newsize
< 0xaaaa0000)
465 newsize
+= newsize
/ 3;
468 } else if (head
->count
> 0) {
469 newsize
= head
->count
;
471 XFREE(MTYPE_HEAP_ARRAY
, head
->array
);
476 newsize
+= HEAP_NARY
- 1;
477 newsize
&= ~(HEAP_NARY
- 1);
478 if (newsize
== head
->arraysz
)
481 head
->array
= XREALLOC(MTYPE_HEAP_ARRAY
, head
->array
,
482 newsize
* sizeof(struct heap_item
*));
483 head
->arraysz
= newsize
;
486 void typesafe_heap_pushdown(struct heap_head
*head
, uint32_t pos
,
487 struct heap_item
*item
,
488 int (*cmpfn
)(const struct heap_item
*a
,
489 const struct heap_item
*b
))
491 uint32_t rghtpos
, downpos
, moveto
;
494 /* rghtpos: neighbor to the "right", inside block of NARY.
495 * may be invalid if last in block, check nary_last()
496 * downpos: first neighbor in the "downwards" block further
501 /* make sure we can use the full 4G items */
502 downpos
= HEAP_NARY
* (pos
+ 1);
503 if (pos
+ 1 > ~0U / HEAP_NARY
)
504 /* multiplication overflowed. ~0U is guaranteed
505 * to be an invalid index; size limit is enforced in
510 /* only used on break */
513 #define nary_last(x) (((x) & (HEAP_NARY - 1)) == HEAP_NARY - 1)
514 if (downpos
>= head
->count
515 || cmpfn(head
->array
[downpos
], item
) >= 0) {
516 /* not moving down; either at end or down is >= item */
517 if (nary_last(pos
) || rghtpos
>= head
->count
518 || cmpfn(head
->array
[rghtpos
], item
) >= 0)
519 /* not moving right either - got our spot */
524 /* else: downpos is valid and < item. choose between down
525 * or right (if the latter is an option) */
526 } else if (nary_last(pos
) || cmpfn(head
->array
[rghtpos
],
527 head
->array
[downpos
]) >= 0)
533 head
->array
[pos
] = head
->array
[moveto
];
534 head
->array
[pos
]->index
= pos
;
538 head
->array
[moveto
] = item
;
539 item
->index
= moveto
;
541 heap_consistency_check(head
, cmpfn
, 0);
544 void typesafe_heap_pullup(struct heap_head
*head
, uint32_t pos
,
545 struct heap_item
*item
,
546 int (*cmpfn
)(const struct heap_item
*a
,
547 const struct heap_item
*b
))
552 if ((pos
& (HEAP_NARY
- 1)) == 0)
553 moveto
= pos
/ HEAP_NARY
- 1;
557 if (cmpfn(head
->array
[moveto
], item
) <= 0)
560 head
->array
[pos
] = head
->array
[moveto
];
561 head
->array
[pos
]->index
= pos
;
566 head
->array
[pos
] = item
;
569 heap_consistency_check(head
, cmpfn
, 0);