asearch, like the standard library bsearch, takes a comparison callback.
Like bsearch() that callback doesn't include a user supplied context
pointer. As well as being generally limiting, that makes the comparison
functions used by asearch gratuitously different from those used by the
asort module.
This patch alters this. Note that this is an incompatible change to the
asearch interface. I think this is worth it to correct the oversight, but
others might disagree. At present the only user within ccan is ntdb, which
is corrected to match.
This means actually supplying a binary search implementation, rather than
relying on bsearch() from the standard library. We follow the lead of the
asort module and steal^H^H^H^H^Hadapt the implementation from glibc.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
* #include <stdio.h>
* #include <string.h>
*
- * static int cmp(const char *key, char *const *elem)
+ * static int cmp(const char *key, char *const *elem, void *ctx)
* {
* return strcmp(key, *elem);
* }
* exit(1);
* }
*
- * p = asearch(argv[1], &argv[2], argc-2, cmp);
+ * p = asearch(argv[1], &argv[2], argc-2, cmp, NULL);
* if (!p) {
* printf("Not found!\n");
* return 1;
--- /dev/null
+#include <ccan/asearch/asearch.h>
+
+#include <stddef.h>
+
+/* Steal glibc's code. */
+
+/* Perform binary search - inline version.
+ Copyright (C) 1991-2015 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+void *
+_asearch (const void *__key, const void *__base, size_t __nmemb, size_t __size,
+ asearch_cmp __compar, void *ctx)
+{
+ size_t __l, __u, __idx;
+ const void *__p;
+ int __comparison;
+
+ __l = 0;
+ __u = __nmemb;
+ while (__l < __u)
+ {
+ __idx = (__l + __u) / 2;
+ __p = (void *) (((const char *) __base) + (__idx * __size));
+ __comparison = (*__compar) (__key, __p, ctx);
+ if (__comparison < 0)
+ __u = __idx;
+ else if (__comparison > 0)
+ __l = __idx + 1;
+ else
+ return (void *) __p;
+ }
+
+ return NULL;
+}
#include <stdlib.h>
#include <ccan/typesafe_cb/typesafe_cb.h>
+typedef int (*asearch_cmp)(const void *, const void *, void *);
+
/**
* asearch - search an array of elements
* @key: pointer to item being searched for
* the same comparison function for both sort() and asearch().
*/
#if HAVE_TYPEOF
-#define asearch(key, base, num, cmp) \
- ((__typeof__(*(base))*)(bsearch((key), (base), (num), sizeof(*(base)), \
- typesafe_cb_cast(int (*)(const void *, const void *), \
+#define asearch(key, base, num, cmp, ctx) \
+ ((__typeof__(*(base))*)(_asearch((key), (base), (num), sizeof(*(base)), \
+ typesafe_cb_cast(asearch_cmp, \
int (*)(const __typeof__(*(key)) *, \
- const __typeof__(*(base)) *), \
- (cmp)))))
+ const __typeof__(*(base)) *, \
+ __typeof__(*(ctx)) *), \
+ (cmp)), (ctx))))
#else
-#define asearch(key, base, num, cmp) \
- (bsearch((key), (base), (num), sizeof(*(base)), \
- (int (*)(const void *, const void *))(cmp)))
+#define asearch(key, base, num, cmp, ctx) \
+ (_asearch((key), (base), (num), sizeof(*(base)), \
+ (int (*)(const void *, const void *, void *))(cmp), (ctx)))
#endif
+void *_asearch(const void *key, const void *base,
+ size_t nmemb, size_t size,
+ asearch_cmp compar, void *ctx);
+
#endif /* CCAN_ASEARCH_H */
#include <ccan/array_size/array_size.h>
#include <string.h>
-static int cmp(const char *key, const char *const *elem)
+#include <ccan/asearch/asearch.c>
+
+static int cmp(const char *key, const char *const *elem, void *ctx)
{
return strcmp(key, *elem);
}
#else
const char **p;
#endif
- p = asearch(key, elems, ARRAY_SIZE(elems), cmp);
+ p = asearch(key, elems, ARRAY_SIZE(elems), cmp, NULL);
return p ? 0 : 1;
}
#include <ccan/asearch/asearch.h>
-static int cmp(const char *key, char *const *elem)
+#include <ccan/asearch/asearch.c>
+
+static int cmp(const char *key, char *const *elem, void *ctx)
{
return 0;
}
#else
char **p;
#endif
- p = asearch(key, argv+1, argc-1, cmp);
+ p = asearch(key, argv+1, argc-1, cmp, NULL);
return p ? 0 : 1;
}
#include <ccan/tap/tap.h>
#include <stdlib.h>
-static int cmp(const int *key, const char *const *elem)
+#include <ccan/asearch/asearch.c>
+
+static int cmp(const int *key, const char *const *elem, void *ctx)
{
return *key - atoi(*elem);
}
const char **p;
plan_tests(1);
- p = asearch(&key, args, ARRAY_SIZE(args), cmp);
+ p = asearch(&key, args, ARRAY_SIZE(args), cmp, NULL);
ok1(p == &args[2]);
return exit_status();
#include <ccan/tap/tap.h>
#include <limits.h>
-static int test_cmp(const int *key, const int *elt)
+#include <ccan/asearch/asearch.c>
+
+static int test_cmp(const int *key, const int *elt, void *ctx)
{
if (*key < *elt)
return -1;
for (start = 0; start < ARRAY_SIZE(arr); start++) {
for (num = 0; num < ARRAY_SIZE(arr) - start; num++) {
key = 7;
- ok1(asearch(&key, &arr[start], num, test_cmp) == NULL);
+ ok1(asearch(&key, &arr[start], num, test_cmp,
+ NULL) == NULL);
total++;
for (i = start; i < start+num; i++) {
const int *ret;
key = arr[i];
- ret = asearch(&key, &arr[start], num, test_cmp);
+ ret = asearch(&key, &arr[start], num,
+ test_cmp, NULL);
ok1(ret);
ok1(ret && *ret == key);
total++;
return NTDB_SUCCESS;
}
-static int off_cmp(const ntdb_off_t *a, const ntdb_off_t *b)
+static int off_cmp(const ntdb_off_t *a, const ntdb_off_t *b, void *ctx)
{
/* Can overflow an int. */
return *a > *b ? 1
" %llu", (long long)off_and_hash);
}
- p = asearch(&off, used, num_used, off_cmp);
+ p = asearch(&off, used, num_used, off_cmp, NULL);
if (!p) {
return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT, NTDB_LOG_ERROR,
"ntdb_check: Invalid offset"
}
/* FIXME: Check hash bits */
- p = asearch(&off, fr, num_free, off_cmp);
+ p = asearch(&off, fr, num_free, off_cmp, NULL);
if (!p) {
return ntdb_logerr(ntdb, NTDB_ERR_CORRUPT,
NTDB_LOG_ERROR,