mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-10 07:11:41 +00:00
[util] Add fuzzy and reentrant bsearch
Fuzzy bsearch is useful for finding an entry in a prefix sum array (value is >= ele[0], < ele[1]), and the reentrant version is good when data needs to be passed to the compare function. Adapted from the code used in pr_resolve.
This commit is contained in:
parent
00fb0972a8
commit
bcc5686606
11 changed files with 322 additions and 1 deletions
|
@ -8,7 +8,8 @@ AC_FUNC_MMAP
|
|||
AC_TYPE_SIGNAL
|
||||
AC_FUNC_VPRINTF
|
||||
AC_CHECK_FUNCS(
|
||||
access _access connect dlopen execvp fcntl ftime _ftime getaddrinfo \
|
||||
access _access bsearch_r connect dlopen execvp fcntl ftime _ftime \
|
||||
getaddrinfo \
|
||||
gethostbyname gethostname getnameinfo getpagesize gettimeofday getuid \
|
||||
getwd ioctl mkdir _mkdir mprotect putenv qsort_r select sigaction \
|
||||
snprintf _snprintf socket stat strcasestr strerror strerror_r strndup \
|
||||
|
|
|
@ -10,6 +10,7 @@ EXTRA_DIST += \
|
|||
include/asm_i386.h \
|
||||
include/block16.h \
|
||||
include/block8.h \
|
||||
include/bsearch.h \
|
||||
include/buildnum.h \
|
||||
include/compat.h \
|
||||
include/context_sdl.h \
|
||||
|
|
|
@ -16,6 +16,7 @@ include_qf = \
|
|||
include/QF/dstring.h \
|
||||
include/QF/draw.h \
|
||||
include/QF/entity.h \
|
||||
include/QF/fbsearch.h \
|
||||
include/QF/gib.h \
|
||||
include/QF/hash.h \
|
||||
include/QF/idparse.h \
|
||||
|
|
49
include/QF/fbsearch.h
Normal file
49
include/QF/fbsearch.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
fbsearch.h
|
||||
|
||||
Fuzzy bsearch
|
||||
|
||||
Copyright (C) 2021 Bill Currie <bill@taniwha.org>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
|
||||
Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __QF_bsearch_h
|
||||
#define __QF_bsearch_h
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifndef __compar_fn_t_defined
|
||||
#define __compar_fn_t_defined
|
||||
typedef int (*__compar_fn_t)(const void *, const void *);
|
||||
#endif
|
||||
|
||||
#ifndef __compar_d_fn_t_defined
|
||||
#define __compar_d_fn_t_defined
|
||||
typedef int (*__compar_d_fn_t)(const void *, const void *, void *);
|
||||
#endif
|
||||
|
||||
void *fbsearch (const void *key, const void *base, size_t nmemb, size_t size,
|
||||
__compar_fn_t cmp);
|
||||
|
||||
void *fbsearch_r (const void *key, const void *base, size_t nmemb, size_t size,
|
||||
__compar_d_fn_t cmp, void *arg);
|
||||
|
||||
#endif//__QF_bsearch_h
|
41
include/bsearch.h
Normal file
41
include/bsearch.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
bsearch.h
|
||||
|
||||
Reentrant bsearch for systems that don't have it
|
||||
|
||||
Copyright (C) 2021 Bill Currie <bill@taniwha.org>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
|
||||
Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA
|
||||
|
||||
*/
|
||||
|
||||
#ifndef bsearch_h
|
||||
#define bsearch_h
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifndef __compar_d_fn_t_defined
|
||||
#define __compar_d_fn_t_defined
|
||||
typedef int (*__compar_d_fn_t)(const void *, const void *, void *);
|
||||
#endif
|
||||
|
||||
void *_bsearch(const void *key, const void *base, size_t nmemb, size_t size,
|
||||
__compar_d_fn_t cmp, void *arg);
|
||||
|
||||
#endif// quicksort_h
|
|
@ -107,6 +107,11 @@ size_t strndup (const char *str, size_t len);
|
|||
# include "qstring.h"
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_BSEARCH_R
|
||||
# include "bsearch.h"
|
||||
# define bsearch_r _bsearch
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_QSORT_R
|
||||
# include "quicksort.h"
|
||||
# define qsort_r _quicksort
|
||||
|
|
|
@ -30,7 +30,10 @@
|
|||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifndef __compar_d_fn_t_defined
|
||||
#define __compar_d_fn_t_defined
|
||||
typedef int (*__compar_d_fn_t)(const void *, const void *, void *);
|
||||
#endif
|
||||
|
||||
void _quicksort(void *base, size_t nmemb, size_t size, __compar_d_fn_t cmp,
|
||||
void *arg);
|
||||
|
|
|
@ -40,6 +40,7 @@ libs_util_libQFutil_la_LDFLAGS= $(lib_ldflags)
|
|||
libs_util_libQFutil_la_LIBADD= $(util_asm) $(Z_LIBS) $(DL_LIBS) $(WIN32_LIBS)
|
||||
libs_util_libQFutil_la_DEPENDENCIES= $(util_asm)
|
||||
libs_util_libQFutil_la_SOURCES= \
|
||||
libs/util/bsearch.c \
|
||||
libs/util/bspfile.c \
|
||||
libs/util/buildnum.c \
|
||||
libs/util/cbuf.c \
|
||||
|
|
98
libs/util/bsearch.c
Normal file
98
libs/util/bsearch.c
Normal file
|
@ -0,0 +1,98 @@
|
|||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "QF/fbsearch.h"
|
||||
#include "bsearch.h"
|
||||
|
||||
void *fbsearch (const void *key, const void *_base, size_t nmemb, size_t size,
|
||||
__compar_fn_t cmp)
|
||||
{
|
||||
// fuzzy bsearh
|
||||
const char *base = (const char *) _base;
|
||||
unsigned left = 0;
|
||||
unsigned right = nmemb - 1;
|
||||
unsigned mid;
|
||||
const void *p = 0;
|
||||
|
||||
if (!nmemb) {
|
||||
return 0;
|
||||
}
|
||||
while (left != right) {
|
||||
mid = (left + right + 1) / 2;
|
||||
p = base + mid * size;
|
||||
if (cmp (key, p) < 0) {
|
||||
right = mid - 1;
|
||||
} else {
|
||||
left = mid;
|
||||
}
|
||||
}
|
||||
p = base + left * size;
|
||||
if (cmp (key, p) >= 0) {
|
||||
return (void *) p;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *fbsearch_r (const void *key, const void *_base, size_t nmemb, size_t size,
|
||||
__compar_d_fn_t cmp, void *arg)
|
||||
{
|
||||
// fuzzy bsearh
|
||||
const char *base = (const char *) _base;
|
||||
unsigned left = 0;
|
||||
unsigned right = nmemb - 1;
|
||||
unsigned mid;
|
||||
const void *p = 0;
|
||||
|
||||
if (!nmemb) {
|
||||
return 0;
|
||||
}
|
||||
while (left != right) {
|
||||
mid = (left + right + 1) / 2;
|
||||
p = base + mid * size;
|
||||
if (cmp (key, p, arg) < 0) {
|
||||
right = mid - 1;
|
||||
} else {
|
||||
left = mid;
|
||||
}
|
||||
}
|
||||
p = base + left * size;
|
||||
if (cmp (key, p, arg) >= 0) {
|
||||
return (void *) p;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *
|
||||
_bsearch(const void *key, const void *_base, size_t nmemb, size_t size,
|
||||
__compar_d_fn_t cmp, void *arg)
|
||||
{
|
||||
// fuzzy bsearh
|
||||
const char *base = (const char *) _base;
|
||||
unsigned left = 0;
|
||||
unsigned right = nmemb;
|
||||
unsigned mid;
|
||||
const void *p = 0;
|
||||
int c;
|
||||
|
||||
if (!nmemb) {
|
||||
return 0;
|
||||
}
|
||||
while (left != right) {
|
||||
mid = (left + right + 1) / 2;
|
||||
p = base + mid * size;
|
||||
c = cmp (key, p, arg);
|
||||
if (c == 0) {
|
||||
return (void *) p;
|
||||
}
|
||||
if (c < 0) {
|
||||
right = mid - 1;
|
||||
} else {
|
||||
left = mid;
|
||||
}
|
||||
}
|
||||
p = base + left * size;
|
||||
if (cmp (key, p, arg) == 0) {
|
||||
return (void *) p;
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
libs_util_tests = \
|
||||
libs/util/test/test-bary \
|
||||
libs/util/test/test-baryvf \
|
||||
libs/util/test/test-bsearch \
|
||||
libs/util/test/test-cexpr \
|
||||
libs/util/test/test-cmem \
|
||||
libs/util/test/test-cs \
|
||||
|
@ -33,6 +34,10 @@ libs_util_test_test_baryvf_SOURCES=libs/util/test/test-baryvf.c
|
|||
libs_util_test_test_baryvf_LDADD=libs/util/libQFutil.la
|
||||
libs_util_test_test_baryvf_DEPENDENCIES=libs/util/libQFutil.la
|
||||
|
||||
libs_util_test_test_bsearch_SOURCES=libs/util/test/test-bsearch.c
|
||||
libs_util_test_test_bsearch_LDADD=libs/util/libQFutil.la
|
||||
libs_util_test_test_bsearch_DEPENDENCIES=libs/util/libQFutil.la
|
||||
|
||||
libs_util_test_test_cexpr_SOURCES=libs/util/test/test-cexpr.c
|
||||
libs_util_test_test_cexpr_LDADD=libs/util/libQFutil.la
|
||||
libs_util_test_test_cexpr_DEPENDENCIES=libs/util/libQFutil.la
|
||||
|
|
116
libs/util/test/test-bsearch.c
Normal file
116
libs/util/test/test-bsearch.c
Normal file
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
test-bsearch.c
|
||||
|
||||
Fuzzy and reentrant bsearch
|
||||
|
||||
Copyright (C) 2021 Bill Currie <bill@taniwha.org>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
|
||||
Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA
|
||||
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "QF/fbsearch.h"
|
||||
#include "bsearch.h"
|
||||
|
||||
// will be prefix-summed
|
||||
int data[] = {
|
||||
2, 1, 3, 1, 2, 2, 5, 4,
|
||||
2, 1, 3, 1, 2, 2, 5, 4,
|
||||
};
|
||||
#define nele ((int) (sizeof (data) / sizeof (data[0])))
|
||||
|
||||
static int
|
||||
compare (const void *a, const void *b)
|
||||
{
|
||||
return *(const int *)a - *(const int *)b;
|
||||
}
|
||||
|
||||
static int
|
||||
compared (const void *a, const void *b, void *d)
|
||||
{
|
||||
return *(const int *)a - *(const int *)b;
|
||||
}
|
||||
|
||||
static int
|
||||
fuzzy_check (const int *p, int val, const char *func)
|
||||
{
|
||||
if (val < data[0] && p) {
|
||||
printf ("%s found %d, but should not\n", func, val);
|
||||
return 0;
|
||||
}
|
||||
if (p) {
|
||||
int ok = p < data + nele - 1;
|
||||
if (p >= data + nele) {
|
||||
printf ("%s went out of bounts: %zd\n", func, p - data);
|
||||
return 0;
|
||||
}
|
||||
if (val < *p || (ok && val >= p[1])) {
|
||||
printf ("%s found %d:%d for %d\n", func, *p, ok ? p[1] : -1, val);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (!p && val >= data[0]) {
|
||||
printf ("%s did not find %d, but should have\n", func, val);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, const char **argv)
|
||||
{
|
||||
int ret = 0;
|
||||
int *p;
|
||||
|
||||
for (int i = 1; i < nele; i++) {
|
||||
data[i] += data[i - 1];
|
||||
}
|
||||
for (int i = 0; i < nele; i++) {
|
||||
printf ("%2d %d\n", i, data[i]);
|
||||
}
|
||||
for (int i = 0; i < data[nele - 1] + 2; i++) {
|
||||
p = fbsearch (&i, data, nele, sizeof (int), compare);
|
||||
if (!fuzzy_check (p, i, "fbsearch")) {
|
||||
ret |= 1;
|
||||
}
|
||||
p = fbsearch_r (&i, data, nele, sizeof (int), compared, 0);
|
||||
if (!fuzzy_check (p, i, "fbsearch_r")) {
|
||||
ret |= 1;
|
||||
}
|
||||
if (p && i == *p) {
|
||||
p = _bsearch (&i, data, nele, sizeof (int), compared, 0);
|
||||
if (!p || *p != i) {
|
||||
printf ("_bsearch did not find %d, but should have\n", i);
|
||||
ret |= 1;
|
||||
}
|
||||
} else {
|
||||
p = _bsearch (&i, data, nele, sizeof (int), compared, 0);
|
||||
if (p) {
|
||||
printf ("_bsearch found %d, but should not have\n", i);
|
||||
ret |= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
Loading…
Reference in a new issue