Import mini_event & rbtree from unbound

This commit is contained in:
Willem Toorop 2014-09-24 13:50:28 +02:00
parent 4daa944e9e
commit 17e5262acc
11 changed files with 1629 additions and 4 deletions

76
configure vendored
View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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>

42
src/util/fptr_wlist.h Normal file
View File

@ -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 */

13
src/util/import.sh Executable file
View File

@ -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

40
src/util/log.h Normal file
View File

@ -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 */

394
src/util/mini_event.c Normal file
View File

@ -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 */

177
src/util/mini_event.h Normal file
View File

@ -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 */

620
src/util/rbtree.c Normal file
View File

@ -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);
}

192
src/util/rbtree.h Normal file
View File

@ -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_ */