mirror of https://github.com/getdnsapi/getdns.git
Import mini_event & rbtree from unbound
This commit is contained in:
parent
4daa944e9e
commit
17e5262acc
|
@ -11700,6 +11700,44 @@ $as_echo "#define HAVE_ATTR_FORMAT 1" >>confdefs.h
|
|||
|
||||
fi
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler (${CC-cc}) accepts the \"unused\" attribute" >&5
|
||||
$as_echo_n "checking whether the C compiler (${CC-cc}) accepts the \"unused\" attribute... " >&6; }
|
||||
if ${ac_cv_c_unused_attribute+:} false; then :
|
||||
$as_echo_n "(cached) " >&6
|
||||
else
|
||||
ac_cv_c_unused_attribute=no
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
#include <stdio.h>
|
||||
void f (char *u __attribute__((unused)));
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
|
||||
f ("x");
|
||||
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
if ac_fn_c_try_compile "$LINENO"; then :
|
||||
ac_cv_c_unused_attribute="yes"
|
||||
else
|
||||
ac_cv_c_unused_attribute="no"
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
|
||||
|
||||
fi
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_unused_attribute" >&5
|
||||
$as_echo "$ac_cv_c_unused_attribute" >&6; }
|
||||
if test $ac_cv_c_unused_attribute = yes; then
|
||||
|
||||
$as_echo "#define HAVE_ATTR_UNUSED 1" >>confdefs.h
|
||||
|
||||
fi
|
||||
|
||||
ac_fn_c_check_func "$LINENO" "strlcpy" "ac_cv_func_strlcpy"
|
||||
if test "x$ac_cv_func_strlcpy" = xyes; then :
|
||||
$as_echo "#define HAVE_STRLCPY 1" >>confdefs.h
|
||||
|
@ -11716,6 +11754,44 @@ fi
|
|||
|
||||
|
||||
|
||||
$as_echo "#define USE_MINI_EVENT 1" >>confdefs.h
|
||||
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking return type of signal handlers" >&5
|
||||
$as_echo_n "checking return type of signal handlers... " >&6; }
|
||||
if ${ac_cv_type_signal+:} false; then :
|
||||
$as_echo_n "(cached) " >&6
|
||||
else
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
#include <sys/types.h>
|
||||
#include <signal.h>
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
return *(signal (0, 0)) (0) == 1;
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
if ac_fn_c_try_compile "$LINENO"; then :
|
||||
ac_cv_type_signal=int
|
||||
else
|
||||
ac_cv_type_signal=void
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
|
||||
fi
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_signal" >&5
|
||||
$as_echo "$ac_cv_type_signal" >&6; }
|
||||
|
||||
cat >>confdefs.h <<_ACEOF
|
||||
#define RETSIGTYPE $ac_cv_type_signal
|
||||
_ACEOF
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
38
configure.ac
38
configure.ac
|
@ -404,8 +404,29 @@ if test $ac_cv_c_format_attribute = yes; then
|
|||
AC_DEFINE(HAVE_ATTR_FORMAT, 1, [Whether the C compiler accepts the "format" attribute])
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING(whether the C compiler (${CC-cc}) accepts the "unused" attribute)
|
||||
AC_CACHE_VAL(ac_cv_c_unused_attribute,
|
||||
[ac_cv_c_unused_attribute=no
|
||||
AC_TRY_COMPILE(
|
||||
[#include <stdio.h>
|
||||
void f (char *u __attribute__((unused)));
|
||||
], [
|
||||
f ("x");
|
||||
],
|
||||
[ac_cv_c_unused_attribute="yes"],
|
||||
[ac_cv_c_unused_attribute="no"])
|
||||
])
|
||||
AC_MSG_RESULT($ac_cv_c_unused_attribute)
|
||||
if test $ac_cv_c_unused_attribute = yes; then
|
||||
AC_DEFINE(HAVE_ATTR_UNUSED, 1, [Whether the C compiler accepts the "unused" attribute])
|
||||
fi
|
||||
|
||||
AC_REPLACE_FUNCS(strlcpy)
|
||||
|
||||
AC_DEFINE(USE_MINI_EVENT, 1, [Needed for sync stub resolver functions])
|
||||
|
||||
AC_TYPE_SIGNAL
|
||||
|
||||
AH_BOTTOM([
|
||||
/** Use on-board gldns */
|
||||
|
||||
|
@ -445,6 +466,23 @@ AH_BOTTOM([
|
|||
# define ATTR_FORMAT(archetype, string_index, first_to_check) /* empty */
|
||||
#endif /* !HAVE_ATTR_FORMAT */
|
||||
|
||||
#if defined(DOXYGEN)
|
||||
# define ATTR_UNUSED(x) x
|
||||
#elif defined(__cplusplus)
|
||||
# define ATTR_UNUSED(x)
|
||||
#elif defined(HAVE_ATTR_UNUSED)
|
||||
# define ATTR_UNUSED(x) x __attribute__((unused))
|
||||
#else /* !HAVE_ATTR_UNUSED */
|
||||
# define ATTR_UNUSED(x) x
|
||||
#endif /* !HAVE_ATTR_UNUSED */
|
||||
|
||||
/* detect if we need to cast to unsigned int for FD_SET to avoid warnings */
|
||||
#ifdef HAVE_WINSOCK2_H
|
||||
#define FD_SET_T (u_int)
|
||||
#else
|
||||
#define FD_SET_T
|
||||
#endif
|
||||
|
||||
#ifdef TIME_WITH_SYS_TIME
|
||||
# include <sys/time.h>
|
||||
# include <time.h>
|
||||
|
|
|
@ -87,6 +87,8 @@ LIBOBJDIR=
|
|||
LIBOBJS=@LIBOBJS@
|
||||
COMPAT_OBJ=$(LIBOBJS:.o=.lo)
|
||||
|
||||
UTIL_OBJ=mini_event.lo rbtree.lo
|
||||
|
||||
.SUFFIXES: .c .o .a .lo .h
|
||||
|
||||
.c.o:
|
||||
|
@ -95,7 +97,7 @@ COMPAT_OBJ=$(LIBOBJS:.o=.lo)
|
|||
.c.lo:
|
||||
$(LIBTOOL) --quiet --tag=CC --mode=compile $(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
$(GLDNS_OBJ) $(COMPAT_OBJ):
|
||||
$(GLDNS_OBJ) $(COMPAT_OBJ) $(UTIL_OBJ):
|
||||
@:
|
||||
$(LIBTOOL) --quiet --tag=CC --mode=compile $(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
|
@ -134,8 +136,8 @@ libgetdns_ext_ev.la: libgetdns.la extension/libev.lo
|
|||
$(LIBTOOL) --tag=CC --mode=link $(CC) $(CFLAGS) -o $@ extension/libev.lo ./.libs/libgetdns.la $(EXTENSION_LIBEV_LDFLAGS) $(EXTENSION_LIBEV_EXT_LIBS) -rpath $(libdir) -version-info $(libversion) -no-undefined -export-symbols $(srcdir)/extension/libev.symbols
|
||||
|
||||
|
||||
libgetdns.la: $(GETDNS_OBJ) $(GLDNS_OBJ) $(COMPAT_OBJ)
|
||||
$(LIBTOOL) --tag=CC --mode=link $(CC) $(CFLAGS) -o $@ $(GETDNS_OBJ) $(GLDNS_OBJ) $(COMPAT_OBJ) $(LDFLAGS) -rpath $(libdir) -version-info $(libversion) -no-undefined -export-symbols $(srcdir)/libgetdns.symbols
|
||||
libgetdns.la: $(GETDNS_OBJ) $(GLDNS_OBJ) $(COMPAT_OBJ) $(UTIL_OBJ)
|
||||
$(LIBTOOL) --tag=CC --mode=link $(CC) $(CFLAGS) -o $@ $(GETDNS_OBJ) $(GLDNS_OBJ) $(COMPAT_OBJ) $(UTIL_OBJ) $(LDFLAGS) -rpath $(libdir) -version-info $(libversion) -no-undefined -export-symbols $(srcdir)/libgetdns.symbols
|
||||
|
||||
|
||||
test: FORCE
|
||||
|
@ -191,9 +193,10 @@ configure.status: configure
|
|||
|
||||
depend:
|
||||
(cd $(srcdir) ; awk 'BEGIN{P=1}{if(P)print}/^# Dependencies/{P=0}' Makefile.in > Makefile.in.new )
|
||||
(cd $(srcdir) ; gcc -MM -I. gldns/*.c compat/*.c | \
|
||||
(cd $(srcdir) ; gcc -MM -I. gldns/*.c compat/*.c util/*.c | \
|
||||
sed -e 's?gldns/?$$(srcdir)/gldns/?g' \
|
||||
-e 's?compat/?$$(srcdir)/compat/?g' \
|
||||
-e 's?util/?$$(srcdir)/util/?g' \
|
||||
-e 's!\(.*\)\.o[ :]*!\1.lo \1.o: !g' >> Makefile.in.new )
|
||||
(cd $(srcdir) ; diff Makefile.in.new Makefile.in && rm Makefile.in.new \
|
||||
|| mv Makefile.in.new Makefile.in )
|
||||
|
@ -214,3 +217,7 @@ wire2str.lo wire2str.o: $(srcdir)/gldns/wire2str.c config.h $(srcdir)/gldns/wire
|
|||
$(srcdir)/gldns/rrdef.h $(srcdir)/gldns/pkthdr.h $(srcdir)/gldns/parseutil.h $(srcdir)/gldns/gbuffer.h \
|
||||
$(srcdir)/gldns/keyraw.h
|
||||
strlcpy.lo strlcpy.o: $(srcdir)/compat/strlcpy.c config.h
|
||||
mini_event.lo mini_event.o: $(srcdir)/util/mini_event.c config.h $(srcdir)/util/mini_event.h $(srcdir)/util/rbtree.h \
|
||||
$(srcdir)/util/fptr_wlist.h
|
||||
rbtree.lo rbtree.o: $(srcdir)/util/rbtree.c config.h $(srcdir)/util/log.h $(srcdir)/util/fptr_wlist.h \
|
||||
$(srcdir)/util/rbtree.h
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
/* Whether the C compiler accepts the "format" attribute */
|
||||
#undef HAVE_ATTR_FORMAT
|
||||
|
||||
/* Whether the C compiler accepts the "unused" attribute */
|
||||
#undef HAVE_ATTR_UNUSED
|
||||
|
||||
/* Define to 1 if you have the <bsd/string.h> header file. */
|
||||
#undef HAVE_BSD_STRING_H
|
||||
|
||||
|
@ -106,6 +109,9 @@
|
|||
/* Define to the version of this package. */
|
||||
#undef PACKAGE_VERSION
|
||||
|
||||
/* Define as the return type of signal handlers (`int' or `void'). */
|
||||
#undef RETSIGTYPE
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#undef STDC_HEADERS
|
||||
|
||||
|
@ -115,6 +121,9 @@
|
|||
/* Default trust anchor file */
|
||||
#undef TRUST_ANCHOR_FILE
|
||||
|
||||
/* Needed for sync stub resolver functions */
|
||||
#undef USE_MINI_EVENT
|
||||
|
||||
/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
|
||||
<pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
|
||||
#define below would cause a syntax error. */
|
||||
|
@ -188,6 +197,23 @@
|
|||
# define ATTR_FORMAT(archetype, string_index, first_to_check) /* empty */
|
||||
#endif /* !HAVE_ATTR_FORMAT */
|
||||
|
||||
#if defined(DOXYGEN)
|
||||
# define ATTR_UNUSED(x) x
|
||||
#elif defined(__cplusplus)
|
||||
# define ATTR_UNUSED(x)
|
||||
#elif defined(HAVE_ATTR_UNUSED)
|
||||
# define ATTR_UNUSED(x) x __attribute__((unused))
|
||||
#else /* !HAVE_ATTR_UNUSED */
|
||||
# define ATTR_UNUSED(x) x
|
||||
#endif /* !HAVE_ATTR_UNUSED */
|
||||
|
||||
/* detect if we need to cast to unsigned int for FD_SET to avoid warnings */
|
||||
#ifdef HAVE_WINSOCK2_H
|
||||
#define FD_SET_T (u_int)
|
||||
#else
|
||||
#define FD_SET_T
|
||||
#endif
|
||||
|
||||
#ifdef TIME_WITH_SYS_TIME
|
||||
# include <sys/time.h>
|
||||
# include <time.h>
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
/**
|
||||
*
|
||||
* /brief dummy prototypes for function pointer whitelisting
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2013, NLnet Labs, Verisign, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the names of the copyright holders nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL Verisign, Inc. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef UTIL_FPTR_WLIST_H
|
||||
#define UTIL_FPTR_WLIST_H
|
||||
|
||||
#define fptr_ok(x)
|
||||
#define fptr_whitelist_event(x)
|
||||
#define fptr_whitelist_rbtree_cmp(x)
|
||||
|
||||
#endif /* UTIL_FPTR_WLIST_H */
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Meant to be run from this directory
|
||||
|
||||
mkdir ub || true
|
||||
cd ub
|
||||
for f in mini_event.c mini_event.h rbtree.c rbtree.h
|
||||
do
|
||||
wget http://unbound.net/svn/trunk/util/$f
|
||||
sed -e 's/event_/getdns_event_/g' -e 's/signal_add/getdns_signal_add/g' -e 's/signal_del/getdns_signal_del/g' -e 's/struct event/struct getdns_event/g' -e 's/mini_ev_cmp/getdns_mini_ev_cmp/g' -e 's/#include "rbtree\.h"/#include "util\/rbtree.h"/g' -e 's/rbnode_/getdns_rbnode_/g' -e 's/rbtree_/getdns_rbtree_/g' -e 's/#include "fptr_wlist\.h"/#include "util\/fptr_wlist.h"/g' -e 's/#include "log\.h"/#include "util\/log.h"/g' $f > ../$f
|
||||
done
|
||||
cd ..
|
||||
rm -r ub
|
|
@ -0,0 +1,40 @@
|
|||
/**
|
||||
*
|
||||
* /brief dummy prototypes for logging a la unbound
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2013, NLnet Labs, Verisign, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the names of the copyright holders nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL Verisign, Inc. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef UTIL_LOG_H
|
||||
#define UTIL_LOG_H
|
||||
|
||||
#define log_assert(x)
|
||||
|
||||
#endif /* UTIL_LOG_H */
|
||||
|
|
@ -0,0 +1,394 @@
|
|||
/*
|
||||
* mini_event.c - implementation of part of libevent api, portably.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* fake libevent implementation. Less broad in functionality, and only
|
||||
* supports select(2).
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#ifdef HAVE_TIME_H
|
||||
#include <time.h>
|
||||
#endif
|
||||
#include <sys/time.h>
|
||||
|
||||
#if defined(USE_MINI_EVENT) && !defined(USE_WINSOCK)
|
||||
#include <signal.h>
|
||||
#include "util/mini_event.h"
|
||||
#include "util/fptr_wlist.h"
|
||||
|
||||
/** compare events in tree, based on timevalue, ptr for uniqueness */
|
||||
int getdns_mini_ev_cmp(const void* a, const void* b)
|
||||
{
|
||||
const struct getdns_event *e = (const struct getdns_event*)a;
|
||||
const struct getdns_event *f = (const struct getdns_event*)b;
|
||||
if(e->ev_timeout.tv_sec < f->ev_timeout.tv_sec)
|
||||
return -1;
|
||||
if(e->ev_timeout.tv_sec > f->ev_timeout.tv_sec)
|
||||
return 1;
|
||||
if(e->ev_timeout.tv_usec < f->ev_timeout.tv_usec)
|
||||
return -1;
|
||||
if(e->ev_timeout.tv_usec > f->ev_timeout.tv_usec)
|
||||
return 1;
|
||||
if(e < f)
|
||||
return -1;
|
||||
if(e > f)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** set time */
|
||||
static int
|
||||
settime(struct getdns_event_base* base)
|
||||
{
|
||||
if(gettimeofday(base->time_tv, NULL) < 0) {
|
||||
return -1;
|
||||
}
|
||||
#ifndef S_SPLINT_S
|
||||
*base->time_secs = (time_t)base->time_tv->tv_sec;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** create event base */
|
||||
void *getdns_event_init(time_t* time_secs, struct timeval* time_tv)
|
||||
{
|
||||
struct getdns_event_base* base = (struct getdns_event_base*)malloc(
|
||||
sizeof(struct getdns_event_base));
|
||||
if(!base)
|
||||
return NULL;
|
||||
memset(base, 0, sizeof(*base));
|
||||
base->time_secs = time_secs;
|
||||
base->time_tv = time_tv;
|
||||
if(settime(base) < 0) {
|
||||
getdns_event_base_free(base);
|
||||
return NULL;
|
||||
}
|
||||
base->times = getdns_rbtree_create(getdns_mini_ev_cmp);
|
||||
if(!base->times) {
|
||||
getdns_event_base_free(base);
|
||||
return NULL;
|
||||
}
|
||||
base->capfd = MAX_FDS;
|
||||
#ifdef FD_SETSIZE
|
||||
if((int)FD_SETSIZE < base->capfd)
|
||||
base->capfd = (int)FD_SETSIZE;
|
||||
#endif
|
||||
base->fds = (struct getdns_event**)calloc((size_t)base->capfd,
|
||||
sizeof(struct getdns_event*));
|
||||
if(!base->fds) {
|
||||
getdns_event_base_free(base);
|
||||
return NULL;
|
||||
}
|
||||
base->signals = (struct getdns_event**)calloc(MAX_SIG, sizeof(struct getdns_event*));
|
||||
if(!base->signals) {
|
||||
getdns_event_base_free(base);
|
||||
return NULL;
|
||||
}
|
||||
#ifndef S_SPLINT_S
|
||||
FD_ZERO(&base->reads);
|
||||
FD_ZERO(&base->writes);
|
||||
#endif
|
||||
return base;
|
||||
}
|
||||
|
||||
/** get version */
|
||||
const char *getdns_event_get_version(void)
|
||||
{
|
||||
return "mini-event-"PACKAGE_VERSION;
|
||||
}
|
||||
|
||||
/** get polling method, select */
|
||||
const char *getdns_event_get_method(void)
|
||||
{
|
||||
return "select";
|
||||
}
|
||||
|
||||
/** call timeouts handlers, and return how long to wait for next one or -1 */
|
||||
static void handle_timeouts(struct getdns_event_base* base, struct timeval* now,
|
||||
struct timeval* wait)
|
||||
{
|
||||
struct getdns_event* p;
|
||||
#ifndef S_SPLINT_S
|
||||
wait->tv_sec = (time_t)-1;
|
||||
#endif
|
||||
|
||||
while((getdns_rbnode_t*)(p = (struct getdns_event*)getdns_rbtree_first(base->times))
|
||||
!=RBTREE_NULL) {
|
||||
#ifndef S_SPLINT_S
|
||||
if(p->ev_timeout.tv_sec > now->tv_sec ||
|
||||
(p->ev_timeout.tv_sec==now->tv_sec &&
|
||||
p->ev_timeout.tv_usec > now->tv_usec)) {
|
||||
/* there is a next larger timeout. wait for it */
|
||||
wait->tv_sec = p->ev_timeout.tv_sec - now->tv_sec;
|
||||
if(now->tv_usec > p->ev_timeout.tv_usec) {
|
||||
wait->tv_sec--;
|
||||
wait->tv_usec = 1000000 - (now->tv_usec -
|
||||
p->ev_timeout.tv_usec);
|
||||
} else {
|
||||
wait->tv_usec = p->ev_timeout.tv_usec
|
||||
- now->tv_usec;
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
/* event times out, remove it */
|
||||
(void)getdns_rbtree_delete(base->times, p);
|
||||
p->ev_events &= ~EV_TIMEOUT;
|
||||
fptr_ok(fptr_whitelist_event(p->ev_callback));
|
||||
(*p->ev_callback)(p->ev_fd, EV_TIMEOUT, p->ev_arg);
|
||||
}
|
||||
}
|
||||
|
||||
/** call select and callbacks for that */
|
||||
static int handle_select(struct getdns_event_base* base, struct timeval* wait)
|
||||
{
|
||||
fd_set r, w;
|
||||
int ret, i;
|
||||
|
||||
#ifndef S_SPLINT_S
|
||||
if(wait->tv_sec==(time_t)-1)
|
||||
wait = NULL;
|
||||
#endif
|
||||
memmove(&r, &base->reads, sizeof(fd_set));
|
||||
memmove(&w, &base->writes, sizeof(fd_set));
|
||||
memmove(&base->ready, &base->content, sizeof(fd_set));
|
||||
|
||||
if((ret = select(base->maxfd+1, &r, &w, NULL, wait)) == -1) {
|
||||
ret = errno;
|
||||
if(settime(base) < 0)
|
||||
return -1;
|
||||
errno = ret;
|
||||
if(ret == EAGAIN || ret == EINTR)
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
if(settime(base) < 0)
|
||||
return -1;
|
||||
|
||||
for(i=0; i<base->maxfd+1; i++) {
|
||||
short bits = 0;
|
||||
if(!base->fds[i] || !(FD_ISSET(i, &base->ready))) {
|
||||
continue;
|
||||
}
|
||||
if(FD_ISSET(i, &r)) {
|
||||
bits |= EV_READ;
|
||||
ret--;
|
||||
}
|
||||
if(FD_ISSET(i, &w)) {
|
||||
bits |= EV_WRITE;
|
||||
ret--;
|
||||
}
|
||||
bits &= base->fds[i]->ev_events;
|
||||
if(bits) {
|
||||
fptr_ok(fptr_whitelist_event(
|
||||
base->fds[i]->ev_callback));
|
||||
(*base->fds[i]->ev_callback)(base->fds[i]->ev_fd,
|
||||
bits, base->fds[i]->ev_arg);
|
||||
if(ret==0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** run select in a loop */
|
||||
int getdns_event_base_dispatch(struct getdns_event_base* base)
|
||||
{
|
||||
struct timeval wait;
|
||||
if(settime(base) < 0)
|
||||
return -1;
|
||||
while(!base->need_to_exit)
|
||||
{
|
||||
/* see if timeouts need handling */
|
||||
handle_timeouts(base, base->time_tv, &wait);
|
||||
if(base->need_to_exit)
|
||||
return 0;
|
||||
/* do select */
|
||||
if(handle_select(base, &wait) < 0) {
|
||||
if(base->need_to_exit)
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** exit that loop */
|
||||
int getdns_event_base_loopexit(struct getdns_event_base* base,
|
||||
struct timeval* ATTR_UNUSED(tv))
|
||||
{
|
||||
base->need_to_exit = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* free event base, free events yourself */
|
||||
void getdns_event_base_free(struct getdns_event_base* base)
|
||||
{
|
||||
if(!base)
|
||||
return;
|
||||
if(base->times)
|
||||
free(base->times);
|
||||
if(base->fds)
|
||||
free(base->fds);
|
||||
if(base->signals)
|
||||
free(base->signals);
|
||||
free(base);
|
||||
}
|
||||
|
||||
/** set content of event */
|
||||
void getdns_event_set(struct getdns_event* ev, int fd, short bits,
|
||||
void (*cb)(int, short, void *), void* arg)
|
||||
{
|
||||
ev->node.key = ev;
|
||||
ev->ev_fd = fd;
|
||||
ev->ev_events = bits;
|
||||
ev->ev_callback = cb;
|
||||
fptr_ok(fptr_whitelist_event(ev->ev_callback));
|
||||
ev->ev_arg = arg;
|
||||
ev->added = 0;
|
||||
}
|
||||
|
||||
/* add event to a base */
|
||||
int getdns_event_base_set(struct getdns_event_base* base, struct getdns_event* ev)
|
||||
{
|
||||
ev->ev_base = base;
|
||||
ev->added = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* add event to make it active, you may not change it with getdns_event_set anymore */
|
||||
int getdns_event_add(struct getdns_event* ev, struct timeval* tv)
|
||||
{
|
||||
if(ev->added)
|
||||
getdns_event_del(ev);
|
||||
if(ev->ev_fd != -1 && ev->ev_fd >= ev->ev_base->capfd)
|
||||
return -1;
|
||||
if( (ev->ev_events&(EV_READ|EV_WRITE)) && ev->ev_fd != -1) {
|
||||
ev->ev_base->fds[ev->ev_fd] = ev;
|
||||
if(ev->ev_events&EV_READ) {
|
||||
FD_SET(FD_SET_T ev->ev_fd, &ev->ev_base->reads);
|
||||
}
|
||||
if(ev->ev_events&EV_WRITE) {
|
||||
FD_SET(FD_SET_T ev->ev_fd, &ev->ev_base->writes);
|
||||
}
|
||||
FD_SET(FD_SET_T ev->ev_fd, &ev->ev_base->content);
|
||||
FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->ready);
|
||||
if(ev->ev_fd > ev->ev_base->maxfd)
|
||||
ev->ev_base->maxfd = ev->ev_fd;
|
||||
}
|
||||
if(tv && (ev->ev_events&EV_TIMEOUT)) {
|
||||
#ifndef S_SPLINT_S
|
||||
struct timeval *now = ev->ev_base->time_tv;
|
||||
ev->ev_timeout.tv_sec = tv->tv_sec + now->tv_sec;
|
||||
ev->ev_timeout.tv_usec = tv->tv_usec + now->tv_usec;
|
||||
while(ev->ev_timeout.tv_usec > 1000000) {
|
||||
ev->ev_timeout.tv_usec -= 1000000;
|
||||
ev->ev_timeout.tv_sec++;
|
||||
}
|
||||
#endif
|
||||
(void)getdns_rbtree_insert(ev->ev_base->times, &ev->node);
|
||||
}
|
||||
ev->added = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* remove event, you may change it again */
|
||||
int getdns_event_del(struct getdns_event* ev)
|
||||
{
|
||||
if(ev->ev_fd != -1 && ev->ev_fd >= ev->ev_base->capfd)
|
||||
return -1;
|
||||
if((ev->ev_events&EV_TIMEOUT))
|
||||
(void)getdns_rbtree_delete(ev->ev_base->times, &ev->node);
|
||||
if((ev->ev_events&(EV_READ|EV_WRITE)) && ev->ev_fd != -1) {
|
||||
ev->ev_base->fds[ev->ev_fd] = NULL;
|
||||
FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->reads);
|
||||
FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->writes);
|
||||
FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->ready);
|
||||
FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->content);
|
||||
}
|
||||
ev->added = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** which base gets to handle signals */
|
||||
static struct getdns_event_base* signal_base = NULL;
|
||||
/** signal handler */
|
||||
static RETSIGTYPE sigh(int sig)
|
||||
{
|
||||
struct getdns_event* ev;
|
||||
if(!signal_base || sig < 0 || sig >= MAX_SIG)
|
||||
return;
|
||||
ev = signal_base->signals[sig];
|
||||
if(!ev)
|
||||
return;
|
||||
fptr_ok(fptr_whitelist_event(ev->ev_callback));
|
||||
(*ev->ev_callback)(sig, EV_SIGNAL, ev->ev_arg);
|
||||
}
|
||||
|
||||
/** install signal handler */
|
||||
int getdns_signal_add(struct getdns_event* ev, struct timeval* ATTR_UNUSED(tv))
|
||||
{
|
||||
if(ev->ev_fd == -1 || ev->ev_fd >= MAX_SIG)
|
||||
return -1;
|
||||
signal_base = ev->ev_base;
|
||||
ev->ev_base->signals[ev->ev_fd] = ev;
|
||||
ev->added = 1;
|
||||
if(signal(ev->ev_fd, sigh) == SIG_ERR) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** remove signal handler */
|
||||
int getdns_signal_del(struct getdns_event* ev)
|
||||
{
|
||||
if(ev->ev_fd == -1 || ev->ev_fd >= MAX_SIG)
|
||||
return -1;
|
||||
ev->ev_base->signals[ev->ev_fd] = NULL;
|
||||
ev->added = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else /* USE_MINI_EVENT */
|
||||
#ifndef USE_WINSOCK
|
||||
int getdns_mini_ev_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* not USE_WINSOCK */
|
||||
#endif /* USE_MINI_EVENT */
|
|
@ -0,0 +1,177 @@
|
|||
/*
|
||||
* mini-event.h - micro implementation of libevent api, using select() only.
|
||||
*
|
||||
* Copyright (c) 2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* This file implements part of the event(3) libevent api.
|
||||
* The back end is only select. Max number of fds is limited.
|
||||
* Max number of signals is limited, one handler per signal only.
|
||||
* And one handler per fd.
|
||||
*
|
||||
* Although limited to select() and a max (1024) open fds, it
|
||||
* is efficient:
|
||||
* o dispatch call caches fd_sets to use.
|
||||
* o handler calling takes time ~ to the number of fds.
|
||||
* o timeouts are stored in a redblack tree, sorted, so take log(n).
|
||||
* Timeouts are only accurate to the second (no subsecond accuracy).
|
||||
* To avoid cpu hogging, fractional timeouts are rounded up to a whole second.
|
||||
*/
|
||||
|
||||
#ifndef MINI_EVENT_H
|
||||
#define MINI_EVENT_H
|
||||
|
||||
#if defined(USE_MINI_EVENT) && !defined(USE_WINSOCK)
|
||||
|
||||
#ifndef HAVE_EVENT_BASE_FREE
|
||||
#define HAVE_EVENT_BASE_FREE
|
||||
#endif
|
||||
|
||||
/** event timeout */
|
||||
#define EV_TIMEOUT 0x01
|
||||
/** event fd readable */
|
||||
#define EV_READ 0x02
|
||||
/** event fd writable */
|
||||
#define EV_WRITE 0x04
|
||||
/** event signal */
|
||||
#define EV_SIGNAL 0x08
|
||||
/** event must persist */
|
||||
#define EV_PERSIST 0x10
|
||||
|
||||
/* needs our redblack tree */
|
||||
#include "util/rbtree.h"
|
||||
|
||||
/** max number of file descriptors to support */
|
||||
#define MAX_FDS 1024
|
||||
/** max number of signals to support */
|
||||
#define MAX_SIG 32
|
||||
|
||||
/** event base */
|
||||
struct getdns_event_base
|
||||
{
|
||||
/** sorted by timeout (absolute), ptr */
|
||||
getdns_rbtree_t* times;
|
||||
/** array of 0 - maxfd of ptr to event for it */
|
||||
struct getdns_event** fds;
|
||||
/** max fd in use */
|
||||
int maxfd;
|
||||
/** capacity - size of the fds array */
|
||||
int capfd;
|
||||
/* fdset for read write, for fds ready, and added */
|
||||
fd_set
|
||||
/** fds for reading */
|
||||
reads,
|
||||
/** fds for writing */
|
||||
writes,
|
||||
/** fds determined ready for use */
|
||||
ready,
|
||||
/** ready plus newly added events. */
|
||||
content;
|
||||
/** array of 0 - maxsig of ptr to event for it */
|
||||
struct getdns_event** signals;
|
||||
/** if we need to exit */
|
||||
int need_to_exit;
|
||||
/** where to store time in seconds */
|
||||
time_t* time_secs;
|
||||
/** where to store time in microseconds */
|
||||
struct timeval* time_tv;
|
||||
};
|
||||
|
||||
/**
|
||||
* Event structure. Has some of the event elements.
|
||||
*/
|
||||
struct getdns_event {
|
||||
/** node in timeout rbtree */
|
||||
getdns_rbnode_t node;
|
||||
/** is event already added */
|
||||
int added;
|
||||
|
||||
/** event base it belongs to */
|
||||
struct getdns_event_base *ev_base;
|
||||
/** fd to poll or -1 for timeouts. signal number for sigs. */
|
||||
int ev_fd;
|
||||
/** what events this event is interested in, see EV_.. above. */
|
||||
short ev_events;
|
||||
/** timeout value */
|
||||
struct timeval ev_timeout;
|
||||
|
||||
/** callback to call: fd, eventbits, userarg */
|
||||
void (*ev_callback)(int, short, void *arg);
|
||||
/** callback user arg */
|
||||
void *ev_arg;
|
||||
};
|
||||
|
||||
/* function prototypes (some are as they appear in event.h) */
|
||||
/** create event base */
|
||||
void *getdns_event_init(time_t* time_secs, struct timeval* time_tv);
|
||||
/** get version */
|
||||
const char *getdns_event_get_version(void);
|
||||
/** get polling method, select */
|
||||
const char *getdns_event_get_method(void);
|
||||
/** run select in a loop */
|
||||
int getdns_event_base_dispatch(struct getdns_event_base *);
|
||||
/** exit that loop */
|
||||
int getdns_event_base_loopexit(struct getdns_event_base *, struct timeval *);
|
||||
/** free event base. Free events yourself */
|
||||
void getdns_event_base_free(struct getdns_event_base *);
|
||||
/** set content of event */
|
||||
void getdns_event_set(struct getdns_event *, int, short, void (*)(int, short, void *), void *);
|
||||
/** add event to a base. You *must* call this for every event. */
|
||||
int getdns_event_base_set(struct getdns_event_base *, struct getdns_event *);
|
||||
/** add event to make it active. You may not change it with getdns_event_set anymore */
|
||||
int getdns_event_add(struct getdns_event *, struct timeval *);
|
||||
/** remove event. You may change it again */
|
||||
int getdns_event_del(struct getdns_event *);
|
||||
|
||||
/** add a timer */
|
||||
#define evtimer_add(ev, tv) getdns_event_add(ev, tv)
|
||||
/** remove a timer */
|
||||
#define evtimer_del(ev) getdns_event_del(ev)
|
||||
|
||||
/* uses different implementation. Cannot mix fd/timeouts and signals inside
|
||||
* the same struct getdns_event. create several event structs for that. */
|
||||
/** install signal handler */
|
||||
int getdns_signal_add(struct getdns_event *, struct timeval *);
|
||||
/** set signal event contents */
|
||||
#define signal_set(ev, x, cb, arg) \
|
||||
getdns_event_set(ev, x, EV_SIGNAL|EV_PERSIST, cb, arg)
|
||||
/** remove signal handler */
|
||||
int getdns_signal_del(struct getdns_event *);
|
||||
|
||||
#endif /* USE_MINI_EVENT and not USE_WINSOCK */
|
||||
|
||||
/** compare events in tree, based on timevalue, ptr for uniqueness */
|
||||
int getdns_mini_ev_cmp(const void* a, const void* b);
|
||||
|
||||
#endif /* MINI_EVENT_H */
|
|
@ -0,0 +1,620 @@
|
|||
/*
|
||||
* rbtree.c -- generic red black tree
|
||||
*
|
||||
* Copyright (c) 2001-2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Implementation of a redblack tree.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "util/log.h"
|
||||
#include "util/fptr_wlist.h"
|
||||
#include "util/rbtree.h"
|
||||
|
||||
/** Node colour black */
|
||||
#define BLACK 0
|
||||
/** Node colour red */
|
||||
#define RED 1
|
||||
|
||||
/** the NULL node, global alloc */
|
||||
getdns_rbnode_t getdns_rbtree_null_node = {
|
||||
RBTREE_NULL, /* Parent. */
|
||||
RBTREE_NULL, /* Left. */
|
||||
RBTREE_NULL, /* Right. */
|
||||
NULL, /* Key. */
|
||||
BLACK /* Color. */
|
||||
};
|
||||
|
||||
/** rotate subtree left (to preserve redblack property) */
|
||||
static void getdns_rbtree_rotate_left(getdns_rbtree_t *rbtree, getdns_rbnode_t *node);
|
||||
/** rotate subtree right (to preserve redblack property) */
|
||||
static void getdns_rbtree_rotate_right(getdns_rbtree_t *rbtree, getdns_rbnode_t *node);
|
||||
/** Fixup node colours when insert happened */
|
||||
static void getdns_rbtree_insert_fixup(getdns_rbtree_t *rbtree, getdns_rbnode_t *node);
|
||||
/** Fixup node colours when delete happened */
|
||||
static void getdns_rbtree_delete_fixup(getdns_rbtree_t* rbtree, getdns_rbnode_t* child, getdns_rbnode_t* child_parent);
|
||||
|
||||
/*
|
||||
* Creates a new red black tree, intializes and returns a pointer to it.
|
||||
*
|
||||
* Return NULL on failure.
|
||||
*
|
||||
*/
|
||||
getdns_rbtree_t *
|
||||
getdns_rbtree_create (int (*cmpf)(const void *, const void *))
|
||||
{
|
||||
getdns_rbtree_t *rbtree;
|
||||
|
||||
/* Allocate memory for it */
|
||||
rbtree = (getdns_rbtree_t *) malloc(sizeof(getdns_rbtree_t));
|
||||
if (!rbtree) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initialize it */
|
||||
getdns_rbtree_init(rbtree, cmpf);
|
||||
|
||||
return rbtree;
|
||||
}
|
||||
|
||||
void
|
||||
getdns_rbtree_init(getdns_rbtree_t *rbtree, int (*cmpf)(const void *, const void *))
|
||||
{
|
||||
/* Initialize it */
|
||||
rbtree->root = RBTREE_NULL;
|
||||
rbtree->count = 0;
|
||||
rbtree->cmp = cmpf;
|
||||
}
|
||||
|
||||
/*
|
||||
* Rotates the node to the left.
|
||||
*
|
||||
*/
|
||||
static void
|
||||
getdns_rbtree_rotate_left(getdns_rbtree_t *rbtree, getdns_rbnode_t *node)
|
||||
{
|
||||
getdns_rbnode_t *right = node->right;
|
||||
node->right = right->left;
|
||||
if (right->left != RBTREE_NULL)
|
||||
right->left->parent = node;
|
||||
|
||||
right->parent = node->parent;
|
||||
|
||||
if (node->parent != RBTREE_NULL) {
|
||||
if (node == node->parent->left) {
|
||||
node->parent->left = right;
|
||||
} else {
|
||||
node->parent->right = right;
|
||||
}
|
||||
} else {
|
||||
rbtree->root = right;
|
||||
}
|
||||
right->left = node;
|
||||
node->parent = right;
|
||||
}
|
||||
|
||||
/*
|
||||
* Rotates the node to the right.
|
||||
*
|
||||
*/
|
||||
static void
|
||||
getdns_rbtree_rotate_right(getdns_rbtree_t *rbtree, getdns_rbnode_t *node)
|
||||
{
|
||||
getdns_rbnode_t *left = node->left;
|
||||
node->left = left->right;
|
||||
if (left->right != RBTREE_NULL)
|
||||
left->right->parent = node;
|
||||
|
||||
left->parent = node->parent;
|
||||
|
||||
if (node->parent != RBTREE_NULL) {
|
||||
if (node == node->parent->right) {
|
||||
node->parent->right = left;
|
||||
} else {
|
||||
node->parent->left = left;
|
||||
}
|
||||
} else {
|
||||
rbtree->root = left;
|
||||
}
|
||||
left->right = node;
|
||||
node->parent = left;
|
||||
}
|
||||
|
||||
static void
|
||||
getdns_rbtree_insert_fixup(getdns_rbtree_t *rbtree, getdns_rbnode_t *node)
|
||||
{
|
||||
getdns_rbnode_t *uncle;
|
||||
|
||||
/* While not at the root and need fixing... */
|
||||
while (node != rbtree->root && node->parent->color == RED) {
|
||||
/* If our parent is left child of our grandparent... */
|
||||
if (node->parent == node->parent->parent->left) {
|
||||
uncle = node->parent->parent->right;
|
||||
|
||||
/* If our uncle is red... */
|
||||
if (uncle->color == RED) {
|
||||
/* Paint the parent and the uncle black... */
|
||||
node->parent->color = BLACK;
|
||||
uncle->color = BLACK;
|
||||
|
||||
/* And the grandparent red... */
|
||||
node->parent->parent->color = RED;
|
||||
|
||||
/* And continue fixing the grandparent */
|
||||
node = node->parent->parent;
|
||||
} else { /* Our uncle is black... */
|
||||
/* Are we the right child? */
|
||||
if (node == node->parent->right) {
|
||||
node = node->parent;
|
||||
getdns_rbtree_rotate_left(rbtree, node);
|
||||
}
|
||||
/* Now we're the left child, repaint and rotate... */
|
||||
node->parent->color = BLACK;
|
||||
node->parent->parent->color = RED;
|
||||
getdns_rbtree_rotate_right(rbtree, node->parent->parent);
|
||||
}
|
||||
} else {
|
||||
uncle = node->parent->parent->left;
|
||||
|
||||
/* If our uncle is red... */
|
||||
if (uncle->color == RED) {
|
||||
/* Paint the parent and the uncle black... */
|
||||
node->parent->color = BLACK;
|
||||
uncle->color = BLACK;
|
||||
|
||||
/* And the grandparent red... */
|
||||
node->parent->parent->color = RED;
|
||||
|
||||
/* And continue fixing the grandparent */
|
||||
node = node->parent->parent;
|
||||
} else { /* Our uncle is black... */
|
||||
/* Are we the right child? */
|
||||
if (node == node->parent->left) {
|
||||
node = node->parent;
|
||||
getdns_rbtree_rotate_right(rbtree, node);
|
||||
}
|
||||
/* Now we're the right child, repaint and rotate... */
|
||||
node->parent->color = BLACK;
|
||||
node->parent->parent->color = RED;
|
||||
getdns_rbtree_rotate_left(rbtree, node->parent->parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
rbtree->root->color = BLACK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Inserts a node into a red black tree.
|
||||
*
|
||||
* Returns NULL on failure or the pointer to the newly added node
|
||||
* otherwise.
|
||||
*/
|
||||
getdns_rbnode_t *
|
||||
getdns_rbtree_insert (getdns_rbtree_t *rbtree, getdns_rbnode_t *data)
|
||||
{
|
||||
/* XXX Not necessary, but keeps compiler quiet... */
|
||||
int r = 0;
|
||||
|
||||
/* We start at the root of the tree */
|
||||
getdns_rbnode_t *node = rbtree->root;
|
||||
getdns_rbnode_t *parent = RBTREE_NULL;
|
||||
|
||||
fptr_ok(fptr_whitelist_getdns_rbtree_cmp(rbtree->cmp));
|
||||
/* Lets find the new parent... */
|
||||
while (node != RBTREE_NULL) {
|
||||
/* Compare two keys, do we have a duplicate? */
|
||||
if ((r = rbtree->cmp(data->key, node->key)) == 0) {
|
||||
return NULL;
|
||||
}
|
||||
parent = node;
|
||||
|
||||
if (r < 0) {
|
||||
node = node->left;
|
||||
} else {
|
||||
node = node->right;
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize the new node */
|
||||
data->parent = parent;
|
||||
data->left = data->right = RBTREE_NULL;
|
||||
data->color = RED;
|
||||
rbtree->count++;
|
||||
|
||||
/* Insert it into the tree... */
|
||||
if (parent != RBTREE_NULL) {
|
||||
if (r < 0) {
|
||||
parent->left = data;
|
||||
} else {
|
||||
parent->right = data;
|
||||
}
|
||||
} else {
|
||||
rbtree->root = data;
|
||||
}
|
||||
|
||||
/* Fix up the red-black properties... */
|
||||
getdns_rbtree_insert_fixup(rbtree, data);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Searches the red black tree, returns the data if key is found or NULL otherwise.
|
||||
*
|
||||
*/
|
||||
getdns_rbnode_t *
|
||||
getdns_rbtree_search (getdns_rbtree_t *rbtree, const void *key)
|
||||
{
|
||||
getdns_rbnode_t *node;
|
||||
|
||||
if (getdns_rbtree_find_less_equal(rbtree, key, &node)) {
|
||||
return node;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/** helpers for delete: swap node colours */
|
||||
static void swap_int8(uint8_t* x, uint8_t* y)
|
||||
{
|
||||
uint8_t t = *x; *x = *y; *y = t;
|
||||
}
|
||||
|
||||
/** helpers for delete: swap node pointers */
|
||||
static void swap_np(getdns_rbnode_t** x, getdns_rbnode_t** y)
|
||||
{
|
||||
getdns_rbnode_t* t = *x; *x = *y; *y = t;
|
||||
}
|
||||
|
||||
/** Update parent pointers of child trees of 'parent' */
|
||||
static void change_parent_ptr(getdns_rbtree_t* rbtree, getdns_rbnode_t* parent, getdns_rbnode_t* old, getdns_rbnode_t* new)
|
||||
{
|
||||
if(parent == RBTREE_NULL)
|
||||
{
|
||||
log_assert(rbtree->root == old);
|
||||
if(rbtree->root == old) rbtree->root = new;
|
||||
return;
|
||||
}
|
||||
log_assert(parent->left == old || parent->right == old
|
||||
|| parent->left == new || parent->right == new);
|
||||
if(parent->left == old) parent->left = new;
|
||||
if(parent->right == old) parent->right = new;
|
||||
}
|
||||
/** Update parent pointer of a node 'child' */
|
||||
static void change_child_ptr(getdns_rbnode_t* child, getdns_rbnode_t* old, getdns_rbnode_t* new)
|
||||
{
|
||||
if(child == RBTREE_NULL) return;
|
||||
log_assert(child->parent == old || child->parent == new);
|
||||
if(child->parent == old) child->parent = new;
|
||||
}
|
||||
|
||||
getdns_rbnode_t*
|
||||
getdns_rbtree_delete(getdns_rbtree_t *rbtree, const void *key)
|
||||
{
|
||||
getdns_rbnode_t *to_delete;
|
||||
getdns_rbnode_t *child;
|
||||
if((to_delete = getdns_rbtree_search(rbtree, key)) == 0) return 0;
|
||||
rbtree->count--;
|
||||
|
||||
/* make sure we have at most one non-leaf child */
|
||||
if(to_delete->left != RBTREE_NULL && to_delete->right != RBTREE_NULL)
|
||||
{
|
||||
/* swap with smallest from right subtree (or largest from left) */
|
||||
getdns_rbnode_t *smright = to_delete->right;
|
||||
while(smright->left != RBTREE_NULL)
|
||||
smright = smright->left;
|
||||
/* swap the smright and to_delete elements in the tree,
|
||||
* but the getdns_rbnode_t is first part of user data struct
|
||||
* so cannot just swap the keys and data pointers. Instead
|
||||
* readjust the pointers left,right,parent */
|
||||
|
||||
/* swap colors - colors are tied to the position in the tree */
|
||||
swap_int8(&to_delete->color, &smright->color);
|
||||
|
||||
/* swap child pointers in parents of smright/to_delete */
|
||||
change_parent_ptr(rbtree, to_delete->parent, to_delete, smright);
|
||||
if(to_delete->right != smright)
|
||||
change_parent_ptr(rbtree, smright->parent, smright, to_delete);
|
||||
|
||||
/* swap parent pointers in children of smright/to_delete */
|
||||
change_child_ptr(smright->left, smright, to_delete);
|
||||
change_child_ptr(smright->left, smright, to_delete);
|
||||
change_child_ptr(smright->right, smright, to_delete);
|
||||
change_child_ptr(smright->right, smright, to_delete);
|
||||
change_child_ptr(to_delete->left, to_delete, smright);
|
||||
if(to_delete->right != smright)
|
||||
change_child_ptr(to_delete->right, to_delete, smright);
|
||||
if(to_delete->right == smright)
|
||||
{
|
||||
/* set up so after swap they work */
|
||||
to_delete->right = to_delete;
|
||||
smright->parent = smright;
|
||||
}
|
||||
|
||||
/* swap pointers in to_delete/smright nodes */
|
||||
swap_np(&to_delete->parent, &smright->parent);
|
||||
swap_np(&to_delete->left, &smright->left);
|
||||
swap_np(&to_delete->right, &smright->right);
|
||||
|
||||
/* now delete to_delete (which is at the location where the smright previously was) */
|
||||
}
|
||||
log_assert(to_delete->left == RBTREE_NULL || to_delete->right == RBTREE_NULL);
|
||||
|
||||
if(to_delete->left != RBTREE_NULL) child = to_delete->left;
|
||||
else child = to_delete->right;
|
||||
|
||||
/* unlink to_delete from the tree, replace to_delete with child */
|
||||
change_parent_ptr(rbtree, to_delete->parent, to_delete, child);
|
||||
change_child_ptr(child, to_delete, to_delete->parent);
|
||||
|
||||
if(to_delete->color == RED)
|
||||
{
|
||||
/* if node is red then the child (black) can be swapped in */
|
||||
}
|
||||
else if(child->color == RED)
|
||||
{
|
||||
/* change child to BLACK, removing a RED node is no problem */
|
||||
if(child!=RBTREE_NULL) child->color = BLACK;
|
||||
}
|
||||
else getdns_rbtree_delete_fixup(rbtree, child, to_delete->parent);
|
||||
|
||||
/* unlink completely */
|
||||
to_delete->parent = RBTREE_NULL;
|
||||
to_delete->left = RBTREE_NULL;
|
||||
to_delete->right = RBTREE_NULL;
|
||||
to_delete->color = BLACK;
|
||||
return to_delete;
|
||||
}
|
||||
|
||||
static void getdns_rbtree_delete_fixup(getdns_rbtree_t* rbtree, getdns_rbnode_t* child, getdns_rbnode_t* child_parent)
|
||||
{
|
||||
getdns_rbnode_t* sibling;
|
||||
int go_up = 1;
|
||||
|
||||
/* determine sibling to the node that is one-black short */
|
||||
if(child_parent->right == child) sibling = child_parent->left;
|
||||
else sibling = child_parent->right;
|
||||
|
||||
while(go_up)
|
||||
{
|
||||
if(child_parent == RBTREE_NULL)
|
||||
{
|
||||
/* removed parent==black from root, every path, so ok */
|
||||
return;
|
||||
}
|
||||
|
||||
if(sibling->color == RED)
|
||||
{ /* rotate to get a black sibling */
|
||||
child_parent->color = RED;
|
||||
sibling->color = BLACK;
|
||||
if(child_parent->right == child)
|
||||
getdns_rbtree_rotate_right(rbtree, child_parent);
|
||||
else getdns_rbtree_rotate_left(rbtree, child_parent);
|
||||
/* new sibling after rotation */
|
||||
if(child_parent->right == child) sibling = child_parent->left;
|
||||
else sibling = child_parent->right;
|
||||
}
|
||||
|
||||
if(child_parent->color == BLACK
|
||||
&& sibling->color == BLACK
|
||||
&& sibling->left->color == BLACK
|
||||
&& sibling->right->color == BLACK)
|
||||
{ /* fixup local with recolor of sibling */
|
||||
if(sibling != RBTREE_NULL)
|
||||
sibling->color = RED;
|
||||
|
||||
child = child_parent;
|
||||
child_parent = child_parent->parent;
|
||||
/* prepare to go up, new sibling */
|
||||
if(child_parent->right == child) sibling = child_parent->left;
|
||||
else sibling = child_parent->right;
|
||||
}
|
||||
else go_up = 0;
|
||||
}
|
||||
|
||||
if(child_parent->color == RED
|
||||
&& sibling->color == BLACK
|
||||
&& sibling->left->color == BLACK
|
||||
&& sibling->right->color == BLACK)
|
||||
{
|
||||
/* move red to sibling to rebalance */
|
||||
if(sibling != RBTREE_NULL)
|
||||
sibling->color = RED;
|
||||
child_parent->color = BLACK;
|
||||
return;
|
||||
}
|
||||
log_assert(sibling != RBTREE_NULL);
|
||||
|
||||
/* get a new sibling, by rotating at sibling. See which child
|
||||
of sibling is red */
|
||||
if(child_parent->right == child
|
||||
&& sibling->color == BLACK
|
||||
&& sibling->right->color == RED
|
||||
&& sibling->left->color == BLACK)
|
||||
{
|
||||
sibling->color = RED;
|
||||
sibling->right->color = BLACK;
|
||||
getdns_rbtree_rotate_left(rbtree, sibling);
|
||||
/* new sibling after rotation */
|
||||
if(child_parent->right == child) sibling = child_parent->left;
|
||||
else sibling = child_parent->right;
|
||||
}
|
||||
else if(child_parent->left == child
|
||||
&& sibling->color == BLACK
|
||||
&& sibling->left->color == RED
|
||||
&& sibling->right->color == BLACK)
|
||||
{
|
||||
sibling->color = RED;
|
||||
sibling->left->color = BLACK;
|
||||
getdns_rbtree_rotate_right(rbtree, sibling);
|
||||
/* new sibling after rotation */
|
||||
if(child_parent->right == child) sibling = child_parent->left;
|
||||
else sibling = child_parent->right;
|
||||
}
|
||||
|
||||
/* now we have a black sibling with a red child. rotate and exchange colors. */
|
||||
sibling->color = child_parent->color;
|
||||
child_parent->color = BLACK;
|
||||
if(child_parent->right == child)
|
||||
{
|
||||
log_assert(sibling->left->color == RED);
|
||||
sibling->left->color = BLACK;
|
||||
getdns_rbtree_rotate_right(rbtree, child_parent);
|
||||
}
|
||||
else
|
||||
{
|
||||
log_assert(sibling->right->color == RED);
|
||||
sibling->right->color = BLACK;
|
||||
getdns_rbtree_rotate_left(rbtree, child_parent);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
getdns_rbtree_find_less_equal(getdns_rbtree_t *rbtree, const void *key, getdns_rbnode_t **result)
|
||||
{
|
||||
int r;
|
||||
getdns_rbnode_t *node;
|
||||
|
||||
log_assert(result);
|
||||
|
||||
/* We start at root... */
|
||||
node = rbtree->root;
|
||||
|
||||
*result = NULL;
|
||||
fptr_ok(fptr_whitelist_getdns_rbtree_cmp(rbtree->cmp));
|
||||
|
||||
/* While there are children... */
|
||||
while (node != RBTREE_NULL) {
|
||||
r = rbtree->cmp(key, node->key);
|
||||
if (r == 0) {
|
||||
/* Exact match */
|
||||
*result = node;
|
||||
return 1;
|
||||
}
|
||||
if (r < 0) {
|
||||
node = node->left;
|
||||
} else {
|
||||
/* Temporary match */
|
||||
*result = node;
|
||||
node = node->right;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds the first element in the red black tree
|
||||
*
|
||||
*/
|
||||
getdns_rbnode_t *
|
||||
getdns_rbtree_first (getdns_rbtree_t *rbtree)
|
||||
{
|
||||
getdns_rbnode_t *node;
|
||||
|
||||
for (node = rbtree->root; node->left != RBTREE_NULL; node = node->left);
|
||||
return node;
|
||||
}
|
||||
|
||||
getdns_rbnode_t *
|
||||
getdns_rbtree_last (getdns_rbtree_t *rbtree)
|
||||
{
|
||||
getdns_rbnode_t *node;
|
||||
|
||||
for (node = rbtree->root; node->right != RBTREE_NULL; node = node->right);
|
||||
return node;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the next node...
|
||||
*
|
||||
*/
|
||||
getdns_rbnode_t *
|
||||
getdns_rbtree_next (getdns_rbnode_t *node)
|
||||
{
|
||||
getdns_rbnode_t *parent;
|
||||
|
||||
if (node->right != RBTREE_NULL) {
|
||||
/* One right, then keep on going left... */
|
||||
for (node = node->right; node->left != RBTREE_NULL; node = node->left);
|
||||
} else {
|
||||
parent = node->parent;
|
||||
while (parent != RBTREE_NULL && node == parent->right) {
|
||||
node = parent;
|
||||
parent = parent->parent;
|
||||
}
|
||||
node = parent;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
getdns_rbnode_t *
|
||||
getdns_rbtree_previous(getdns_rbnode_t *node)
|
||||
{
|
||||
getdns_rbnode_t *parent;
|
||||
|
||||
if (node->left != RBTREE_NULL) {
|
||||
/* One left, then keep on going right... */
|
||||
for (node = node->left; node->right != RBTREE_NULL; node = node->right);
|
||||
} else {
|
||||
parent = node->parent;
|
||||
while (parent != RBTREE_NULL && node == parent->left) {
|
||||
node = parent;
|
||||
parent = parent->parent;
|
||||
}
|
||||
node = parent;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
/** recursive descent traverse */
|
||||
static void
|
||||
traverse_post(void (*func)(getdns_rbnode_t*, void*), void* arg, getdns_rbnode_t* node)
|
||||
{
|
||||
if(!node || node == RBTREE_NULL)
|
||||
return;
|
||||
/* recurse */
|
||||
traverse_post(func, arg, node->left);
|
||||
traverse_post(func, arg, node->right);
|
||||
/* call user func */
|
||||
(*func)(node, arg);
|
||||
}
|
||||
|
||||
void
|
||||
traverse_postorder(getdns_rbtree_t* tree, void (*func)(getdns_rbnode_t*, void*), void* arg)
|
||||
{
|
||||
traverse_post(func, arg, tree->root);
|
||||
}
|
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
* rbtree.h -- generic red-black tree
|
||||
*
|
||||
* Copyright (c) 2001-2007, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Red black tree. Implementation taken from NSD 3.0.5, adjusted for use
|
||||
* in unbound (memory allocation, logging and so on).
|
||||
*/
|
||||
|
||||
#ifndef UTIL_RBTREE_H_
|
||||
#define UTIL_RBTREE_H_
|
||||
|
||||
/**
|
||||
* This structure must be the first member of the data structure in
|
||||
* the rbtree. This allows easy casting between an getdns_rbnode_t and the
|
||||
* user data (poor man's inheritance).
|
||||
*/
|
||||
typedef struct getdns_rbnode_t getdns_rbnode_t;
|
||||
/**
|
||||
* The getdns_rbnode_t struct definition.
|
||||
*/
|
||||
struct getdns_rbnode_t {
|
||||
/** parent in rbtree, RBTREE_NULL for root */
|
||||
getdns_rbnode_t *parent;
|
||||
/** left node (smaller items) */
|
||||
getdns_rbnode_t *left;
|
||||
/** right node (larger items) */
|
||||
getdns_rbnode_t *right;
|
||||
/** pointer to sorting key */
|
||||
const void *key;
|
||||
/** colour of this node */
|
||||
uint8_t color;
|
||||
};
|
||||
|
||||
/** The nullpointer, points to empty node */
|
||||
#define RBTREE_NULL &getdns_rbtree_null_node
|
||||
/** the global empty node */
|
||||
extern getdns_rbnode_t getdns_rbtree_null_node;
|
||||
|
||||
/** An entire red black tree */
|
||||
typedef struct getdns_rbtree_t getdns_rbtree_t;
|
||||
/** definition for tree struct */
|
||||
struct getdns_rbtree_t {
|
||||
/** The root of the red-black tree */
|
||||
getdns_rbnode_t *root;
|
||||
|
||||
/** The number of the nodes in the tree */
|
||||
size_t count;
|
||||
|
||||
/**
|
||||
* Key compare function. <0,0,>0 like strcmp.
|
||||
* Return 0 on two NULL ptrs.
|
||||
*/
|
||||
int (*cmp) (const void *, const void *);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create new tree (malloced) with given key compare function.
|
||||
* @param cmpf: compare function (like strcmp) takes pointers to two keys.
|
||||
* @return: new tree, empty.
|
||||
*/
|
||||
getdns_rbtree_t *getdns_rbtree_create(int (*cmpf)(const void *, const void *));
|
||||
|
||||
/**
|
||||
* Init a new tree (malloced by caller) with given key compare function.
|
||||
* @param rbtree: uninitialised memory for new tree, returned empty.
|
||||
* @param cmpf: compare function (like strcmp) takes pointers to two keys.
|
||||
*/
|
||||
void getdns_rbtree_init(getdns_rbtree_t *rbtree, int (*cmpf)(const void *, const void *));
|
||||
|
||||
/**
|
||||
* Insert data into the tree.
|
||||
* @param rbtree: tree to insert to.
|
||||
* @param data: element to insert.
|
||||
* @return: data ptr or NULL if key already present.
|
||||
*/
|
||||
getdns_rbnode_t *getdns_rbtree_insert(getdns_rbtree_t *rbtree, getdns_rbnode_t *data);
|
||||
|
||||
/**
|
||||
* Delete element from tree.
|
||||
* @param rbtree: tree to delete from.
|
||||
* @param key: key of item to delete.
|
||||
* @return: node that is now unlinked from the tree. User to delete it.
|
||||
* returns 0 if node not present
|
||||
*/
|
||||
getdns_rbnode_t *getdns_rbtree_delete(getdns_rbtree_t *rbtree, const void *key);
|
||||
|
||||
/**
|
||||
* Find key in tree. Returns NULL if not found.
|
||||
* @param rbtree: tree to find in.
|
||||
* @param key: key that must match.
|
||||
* @return: node that fits or NULL.
|
||||
*/
|
||||
getdns_rbnode_t *getdns_rbtree_search(getdns_rbtree_t *rbtree, const void *key);
|
||||
|
||||
/**
|
||||
* Find, but match does not have to be exact.
|
||||
* @param rbtree: tree to find in.
|
||||
* @param key: key to find position of.
|
||||
* @param result: set to the exact node if present, otherwise to element that
|
||||
* precedes the position of key in the tree. NULL if no smaller element.
|
||||
* @return: true if exact match in result. Else result points to <= element,
|
||||
* or NULL if key is smaller than the smallest key.
|
||||
*/
|
||||
int getdns_rbtree_find_less_equal(getdns_rbtree_t *rbtree, const void *key,
|
||||
getdns_rbnode_t **result);
|
||||
|
||||
/**
|
||||
* Returns first (smallest) node in the tree
|
||||
* @param rbtree: tree
|
||||
* @return: smallest element or NULL if tree empty.
|
||||
*/
|
||||
getdns_rbnode_t *getdns_rbtree_first(getdns_rbtree_t *rbtree);
|
||||
|
||||
/**
|
||||
* Returns last (largest) node in the tree
|
||||
* @param rbtree: tree
|
||||
* @return: largest element or NULL if tree empty.
|
||||
*/
|
||||
getdns_rbnode_t *getdns_rbtree_last(getdns_rbtree_t *rbtree);
|
||||
|
||||
/**
|
||||
* Returns next larger node in the tree
|
||||
* @param rbtree: tree
|
||||
* @return: next larger element or NULL if no larger in tree.
|
||||
*/
|
||||
getdns_rbnode_t *getdns_rbtree_next(getdns_rbnode_t *rbtree);
|
||||
|
||||
/**
|
||||
* Returns previous smaller node in the tree
|
||||
* @param rbtree: tree
|
||||
* @return: previous smaller element or NULL if no previous in tree.
|
||||
*/
|
||||
getdns_rbnode_t *getdns_rbtree_previous(getdns_rbnode_t *rbtree);
|
||||
|
||||
/**
|
||||
* Call with node=variable of struct* with getdns_rbnode_t as first element.
|
||||
* with type is the type of a pointer to that struct.
|
||||
*/
|
||||
#define RBTREE_FOR(node, type, rbtree) \
|
||||
for(node=(type)getdns_rbtree_first(rbtree); \
|
||||
(getdns_rbnode_t*)node != RBTREE_NULL; \
|
||||
node = (type)getdns_rbtree_next((getdns_rbnode_t*)node))
|
||||
|
||||
/**
|
||||
* Call function for all elements in the redblack tree, such that
|
||||
* leaf elements are called before parent elements. So that all
|
||||
* elements can be safely free()d.
|
||||
* Note that your function must not remove the nodes from the tree.
|
||||
* Since that may trigger rebalances of the rbtree.
|
||||
* @param tree: the tree
|
||||
* @param func: function called with element and user arg.
|
||||
* The function must not alter the rbtree.
|
||||
* @param arg: user argument.
|
||||
*/
|
||||
void traverse_postorder(getdns_rbtree_t* tree, void (*func)(getdns_rbnode_t*, void*),
|
||||
void* arg);
|
||||
|
||||
#endif /* UTIL_RBTREE_H_ */
|
Loading…
Reference in New Issue