Files
systemd/src/basic/sort-util.c
T
Michael Vogt bb94af45c1 many: use POINTER_MAY_BE_NULL in inner sort _cmp funcs
Some functions are in the hot path of a sort. While it is more
correct to also use assert() here for the passed pointers it is
also a runtime cost that should never be needed when e.g. qsort()
is used as it will always pass non-NULL pointers.

So for these critical functions use POINTER_MAY_BE_NULL() which
is compiled out and add a comment to them. Not all _compare()
functions are changed, it was a bit of a judgement call because
some (like ether_addr_compare) are not called with that much data.

But happy to get advise here and tweak accordingly.
2026-03-31 18:30:40 +02:00

84 lines
2.5 KiB
C

/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <stdlib.h>
#include "alloc-util.h"
#include "sort-util.h"
/* hey glibc, APIs with callbacks without a user pointer are so useless */
void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
comparison_userdata_fn_t compar, void *arg) {
size_t l, u, idx;
const void *p;
int comparison;
assert(!size_multiply_overflow(nmemb, size));
l = 0;
u = nmemb;
while (l < u) {
idx = (l + u) / 2;
p = (const uint8_t*) base + idx * size;
comparison = compar(key, p, arg);
if (comparison < 0)
u = idx;
else if (comparison > 0)
l = idx + 1;
else
return (void *)p;
}
return NULL;
}
void* bsearch_safe_internal(const void *key, const void *base, size_t nmemb, size_t size, comparison_fn_t compar) {
/**
* Normal bsearch requires base to be nonnull. Here were require
* that only if nmemb > 0.
*/
if (nmemb <= 0)
return NULL;
assert(base);
return (void*) bsearch(key, base, nmemb, size, compar);
}
void qsort_safe(void *base, size_t nmemb, size_t size, comparison_fn_t compar) {
/**
* Normal qsort requires base to be nonnull. Here were require
* that only if nmemb > 0.
*/
if (nmemb <= 1)
return;
assert(base);
qsort(base, nmemb, size, compar);
}
void qsort_r_safe(void *base, size_t nmemb, size_t size, comparison_userdata_fn_t compar, void *userdata) {
if (nmemb <= 1)
return;
assert(base);
qsort_r(base, nmemb, size, compar, userdata);
}
int cmp_int(const int *a, const int *b) {
/* This is called from qsort()s inner loops. Correctly implemented qsort will never pass NULL so we
just suppress the check via POINTER_MAY_BE_NULL instead of assert() to avoid the runtime cost. */
POINTER_MAY_BE_NULL(a);
POINTER_MAY_BE_NULL(b);
return CMP(*a, *b);
}
int cmp_uint16(const uint16_t *a, const uint16_t *b) {
/* This is called from qsort()s inner loops. Correctly implemented qsort will never pass NULL so we
just suppress the check via POINTER_MAY_BE_NULL instead of assert() to avoid the runtime cost. */
POINTER_MAY_BE_NULL(a);
POINTER_MAY_BE_NULL(b);
return CMP(*a, *b);
}