diff -Naur milter-greylist.thread/Makefile milter-greylist/Makefile
--- milter-greylist.thread/Makefile	2006-08-21 12:44:59.000000000 +0000
+++ milter-greylist/Makefile	2006-08-21 12:57:16.000000000 +0000
@@ -53,9 +53,10 @@
 TRUE=		true
 
 OBJ= 		milter-greylist.o pending.o sync.o dnsrbl.o list.o \
-		conf_yacc.o dump_yacc.o conf.o autowhite.o dump.o spf.o acl.o
+		conf_yacc.o dump_yacc.o conf.o autowhite.o dump.o spf.o \
+		acl.o treeset.o
 SRC= 		milter-greylist.c pending.c sync.c conf.c \
-		autowhite.c dump.c spf.c acl.c dnsrbl.c list.c
+		autowhite.c dump.c spf.c acl.c dnsrbl.c list.c treeset.c
 GENSRC=		conf_yacc.c conf_lex.c dump_yacc.c dump_lex.c 
 
 all:		milter-greylist rc-bsd.sh rc-redhat.sh \
diff -Naur milter-greylist.thread/Makefile.in milter-greylist/Makefile.in
--- milter-greylist.thread/Makefile.in	2006-08-21 12:21:21.000000000 +0000
+++ milter-greylist/Makefile.in	2006-08-21 12:22:30.000000000 +0000
@@ -53,9 +53,10 @@
 TRUE=		@TRUE@
 
 OBJ= 		milter-greylist.o pending.o sync.o dnsrbl.o list.o \
-		conf_yacc.o dump_yacc.o conf.o autowhite.o dump.o spf.o acl.o
+		conf_yacc.o dump_yacc.o conf.o autowhite.o dump.o spf.o \
+		acl.o treeset.o
 SRC= 		milter-greylist.c pending.c sync.c conf.c \
-		autowhite.c dump.c spf.c acl.c dnsrbl.c list.c
+		autowhite.c dump.c spf.c acl.c dnsrbl.c list.c treeset.c
 GENSRC=		conf_yacc.c conf_lex.c dump_yacc.c dump_lex.c 
 
 all:		milter-greylist rc-bsd.sh rc-redhat.sh \
diff -Naur milter-greylist.thread/autowhite.c milter-greylist/autowhite.c
--- milter-greylist.thread/autowhite.c	2006-08-21 12:42:39.000000000 +0000
+++ milter-greylist/autowhite.c	2006-08-21 12:43:24.000000000 +0000
@@ -66,17 +66,17 @@
 #include "acl.h"
 #include "sync.h"
 
-static void autowhite_insert_to_queue(struct autowhite *);
+static int autowhite_earlierp(void *, const void *, const void *);
 
 pthread_mutex_t autowhite_lock = PTHREAD_MUTEX_INITIALIZER;
-struct autowhitelist autowhite_head;
+treeset_head autowhite_head = TREESET_INITIALIZER(struct autowhite, a_treeent,
+						  autowhite_earlierp, NULL);
 struct autowhite_bucket *autowhite_buckets;
 
 void
 autowhite_init(void) {
 	int i;
 
-	TAILQ_INIT(&autowhite_head);
 	if ((autowhite_buckets = calloc(AUTOWHITE_BUCKETS, 
 	    sizeof(struct autowhite_bucket))) == NULL) {
 		syslog(LOG_ERR, 
@@ -103,8 +103,9 @@
 	gettimeofday(&now, NULL);
 	
 	AUTOWHITE_LOCK;
-	for (aw = TAILQ_FIRST(&autowhite_head); aw; aw = next_aw) {
-		next_aw = TAILQ_NEXT(aw, a_list);
+	for (aw = (struct autowhite *)treeset_first(&autowhite_head);
+	     aw; aw = next_aw) {
+		next_aw = (struct autowhite *)treeset_next(&autowhite_head, aw);
 		
 		/*
 		 * Expiration
@@ -181,11 +182,9 @@
 		    ((conf.c_lazyaw == 1) ||
 		    ((strcasecmp(from, aw->a_from) == 0) &&
 		    (strcasecmp(rcpt, aw->a_rcpt) == 0)))) {
+			treeset_remove(&autowhite_head, aw);
 			aw->a_tv.tv_sec = *date;
-
-			/* Rearrange the big queue */
-			TAILQ_REMOVE(&autowhite_head, aw, a_list);
-			autowhite_insert_to_queue(aw);
+			treeset_insert(&autowhite_head, aw);
 
 			dirty++;
 
@@ -271,11 +270,9 @@
 		    ((conf.c_lazyaw == 1) ||
 		    ((strcasecmp(from, aw->a_from) == 0) &&
 		    (strcasecmp(rcpt, aw->a_rcpt) == 0)))) {
+			treeset_remove(&autowhite_head, aw);
 			timeradd(&now, &delay, &aw->a_tv);
-
-			/* Rearrange the big queue */
-			TAILQ_REMOVE(&autowhite_head, aw, a_list);
-			autowhite_insert_to_queue(aw);
+			treeset_insert(&autowhite_head, aw);
 
 			dirty++;
 
@@ -325,7 +322,7 @@
 	    "Sender e-mail", "Recipient e-mail");
 
 	AUTOWHITE_LOCK;
-	TAILQ_FOREACH(aw, &autowhite_head, a_list) {
+	TREESET_FOREACH(aw, &autowhite_head, struct autowhite) {
 		iptostring(aw->a_sa, aw->a_salen, textaddr, sizeof(textaddr));
 	
 		if (conf.c_dump_no_time_translation) {
@@ -350,33 +347,18 @@
 	return done;
 }
 
-/*
- * A new entry is inserted to the back of the queue in most cases,
- * but it is not true in these situations:
- * - The conf.c_autowhite_validity was shortened
- * - System clock was turned to the past
- *
- * To ensure that the queue is sorted by expiration times (a_tv),
- * we need to find the right position where to insert a new entry.
- */
-
 /* autowhite list must be locked */
-static void
-autowhite_insert_to_queue(newentry)
-	struct autowhite *newentry;
+static int
+autowhite_earlierp(dummy, gx, gy) /* ARGSUSED */
+	void *dummy;
+	const void *gx;
+	const void *gy;
 {
-	struct autowhite *aw;
-	
-	TAILQ_FOREACH_REVERSE(aw, &autowhite_head, autowhitelist, a_list) {
-		if (aw->a_tv.tv_sec < newentry->a_tv.tv_sec ||
-		    (aw->a_tv.tv_sec == newentry->a_tv.tv_sec &&
-		     aw->a_tv.tv_usec < newentry->a_tv.tv_usec))
-			break;
-	}
-	if (aw)
-		TAILQ_INSERT_AFTER(&autowhite_head, aw, newentry, a_list);
-	else
-		TAILQ_INSERT_HEAD(&autowhite_head, newentry, a_list);
+	const struct autowhite *x = (const struct autowhite *)gx;
+	const struct autowhite *y = (const struct autowhite *)gy;
+	return (x->a_tv.tv_sec < y->a_tv.tv_sec ||
+		(x->a_tv.tv_sec == y->a_tv.tv_sec &&
+		 x->a_tv.tv_usec < y->a_tv.tv_usec));
 }
 
 struct autowhite *
@@ -407,7 +389,7 @@
 	memcpy(aw->a_sa, sa, salen);
 	aw->a_salen = salen;
 
-	autowhite_insert_to_queue(aw);
+	treeset_insert(&autowhite_head, aw);
 	TAILQ_INSERT_TAIL(&autowhite_buckets[BUCKET_HASH(aw->a_sa, 
 	    from, rcpt, AUTOWHITE_BUCKETS)].b_autowhite_head, aw, ab_list);
 
@@ -418,7 +400,7 @@
 autowhite_put(aw)	/* autowhite list must be locked */
 	struct autowhite *aw;
 {
-	TAILQ_REMOVE(&autowhite_head, aw, a_list);
+	treeset_remove(&autowhite_head, aw);
 	TAILQ_REMOVE(&autowhite_buckets[BUCKET_HASH(aw->a_sa, 
 	    aw->a_from, aw->a_rcpt, AUTOWHITE_BUCKETS)].b_autowhite_head, 
 	    aw, ab_list);
@@ -440,8 +422,9 @@
 	int count = 0;
 	
 	AUTOWHITE_LOCK;
-	for (aw = TAILQ_FIRST(&autowhite_head); aw; aw = next_aw) {
-		next_aw = TAILQ_NEXT(aw, a_list);
+	for (aw = (struct autowhite *)treeset_first(&autowhite_head);
+	     aw; aw = next_aw) {
+		next_aw = (struct autowhite *)treeset_next(&autowhite_head, aw);
 		
 		if (memcmp(sa, aw->a_sa, salen) == 0) {
 			char buf[IPADDRSTRLEN];
diff -Naur milter-greylist.thread/autowhite.h milter-greylist/autowhite.h
--- milter-greylist.thread/autowhite.h	2006-08-21 12:27:11.000000000 +0000
+++ milter-greylist/autowhite.h	2006-08-21 12:27:37.000000000 +0000
@@ -34,6 +34,7 @@
 #define _AUTOWHITE_H_
 
 #include "milter-greylist.h"
+#include "treeset.h"
 
 #ifndef AUTOWHITE_VALIDITY
 #define AUTOWHITE_VALIDITY (24 * 3600) /* 1 day */
@@ -43,15 +44,13 @@
 #define AUTOWHITE_BUCKETS 512
 #endif
 
-TAILQ_HEAD(autowhitelist, autowhite);
-
 struct autowhite {
 	struct sockaddr *a_sa;
 	socklen_t a_salen;
 	char *a_from;
 	char *a_rcpt;
 	struct timeval a_tv;
-	TAILQ_ENTRY(autowhite) a_list;
+	treeset_entry a_treeent;
 	TAILQ_ENTRY(autowhite) ab_list;
 };
 
diff -Naur milter-greylist.thread/pending.c milter-greylist/pending.c
--- milter-greylist.thread/pending.c	2006-08-21 05:33:31.000000000 +0000
+++ milter-greylist/pending.c	2006-08-21 13:19:18.000000000 +0000
@@ -71,10 +71,11 @@
 #include "autowhite.h"
 #include "milter-greylist.h"
 
-static void pending_insert_to_queue(struct pending *);
+static int pending_earlierp(void *, const void *, const void *);
 
 struct pending_bucket *pending_buckets;
-struct pendinglist pending_head;
+treeset_head pending_head = TREESET_INITIALIZER(struct pending, p_treeent,
+						pending_earlierp, NULL);
 /* protects pending_head and pending_buckets */
 pthread_mutex_t pending_lock = PTHREAD_MUTEX_INITIALIZER;
 
@@ -90,7 +91,6 @@
 pending_init(void) {
 	int i;
 
-	TAILQ_INIT(&pending_head);
 	if ((pending_buckets = calloc(PENDING_BUCKETS, 
 	    sizeof(struct pending_bucket))) == NULL) {
 		syslog(LOG_ERR, 
@@ -106,33 +106,18 @@
 	return;
 }
 
-/*
- * A new entry is inserted to the back of the queue in most cases,
- * but it is not true in these situations:
- * - The conf.c_delay was shortened
- * - System clock was turned to the past
- *
- * To ensure that the queue is sorted by expiration times (p_tv),
- * we need to find the right position where to insert a new entry.
- */
-
 /* pending_lock must be locked */
-static void
-pending_insert_to_queue(newentry)
-	struct pending *newentry;
-{
-	struct pending *p;
-	
-	TAILQ_FOREACH_REVERSE(p, &pending_head, pendinglist, p_list) {
-		if (p->p_tv.tv_sec < newentry->p_tv.tv_sec ||
-		    (p->p_tv.tv_sec == newentry->p_tv.tv_sec &&
-		     p->p_tv.tv_usec < newentry->p_tv.tv_usec))
-			break;
-	}
-	if (p)
-		TAILQ_INSERT_AFTER(&pending_head, p, newentry, p_list);
-	else
-		TAILQ_INSERT_HEAD(&pending_head, newentry, p_list);
+static int
+pending_earlierp(dummy, gx, gy) /* ARGSUSED */
+	void *dummy;
+	const void *gx;
+	const void *gy;
+{
+	const struct pending *x = (const struct pending *)gx;
+	const struct pending *y = (const struct pending *)gy;
+	return (x->p_tv.tv_sec < y->p_tv.tv_sec ||
+		(x->p_tv.tv_sec == y->p_tv.tv_sec &&
+		 x->p_tv.tv_usec < y->p_tv.tv_usec));
 }
 
 /* pending_lock must be locked */
@@ -186,7 +171,7 @@
 
 	pending->p_refcnt = 1;
 
-	pending_insert_to_queue(pending);
+	treeset_insert(&pending_head, pending);
 	TAILQ_INSERT_TAIL(&pending_buckets[BUCKET_HASH(pending->p_sa, 
 	    from, rcpt, PENDING_BUCKETS)].b_pending_head, pending, pb_list); 
 
@@ -212,7 +197,7 @@
 		    pending->p_addr, pending->p_from, pending->p_rcpt);
 	}
 
-	TAILQ_REMOVE(&pending_head, pending, p_list);
+	treeset_remove(&pending_head, pending);
 	TAILQ_REMOVE(&pending_buckets[BUCKET_HASH(pending->p_sa, 
 	    pending->p_from, pending->p_rcpt, PENDING_BUCKETS)].b_pending_head,
 	    pending, pb_list); 
@@ -233,8 +218,9 @@
 	gettimeofday(&now, NULL);
 	
 	PENDING_LOCK;
-	for (pending = TAILQ_FIRST(&pending_head); pending; pending = next) {
-		next = TAILQ_NEXT(pending, p_list);
+	for (pending = (struct pending *)treeset_first(&pending_head);
+	     pending; pending = next) {
+		next = (struct pending *)treeset_next(&pending_head, pending);
 		/*
 		 * Check for expired entries
 		 */
@@ -421,7 +407,7 @@
 	    "Sender e-mail", "Recipient e-mail");
 
 	PENDING_LOCK;
-	TAILQ_FOREACH(pending, &pending_head, p_list) {
+	TREESET_FOREACH(pending, &pending_head, struct pending) {
 		if (conf.c_dump_no_time_translation) {
 			fprintf(stream, "%s\t%s\t%s\t%ld\n", 
 			    pending->p_addr, pending->p_from, 
@@ -668,8 +654,9 @@
 		return;
 
 	PENDING_LOCK;
-	for (pending = TAILQ_FIRST(&pending_head); pending; pending = next) {
-		next = TAILQ_NEXT(pending, p_list);
+	for (pending = (struct pending *)treeset_first(&pending_head);
+	     pending; pending = next) {
+		next = (struct pending *)treeset_next(&pending_head, pending);
 
 		if (strncmp(addr, pending->p_addr, sizeof(addr)) == 0) {
 			if (!sync_flush_done) {
diff -Naur milter-greylist.thread/pending.h milter-greylist/pending.h
--- milter-greylist.thread/pending.h	2006-08-21 12:27:17.000000000 +0000
+++ milter-greylist/pending.h	2006-08-21 12:27:29.000000000 +0000
@@ -59,8 +59,7 @@
 #endif
 
 #include "milter-greylist.h"
-
-TAILQ_HEAD(pendinglist, pending);
+#include "treeset.h"
 
 struct pending {
 	char *p_addr;
@@ -70,7 +69,7 @@
 	char *p_rcpt;
 	struct timeval p_tv;
 	int p_refcnt;
-	TAILQ_ENTRY(pending) p_list;
+	treeset_entry p_treeent;
 	TAILQ_ENTRY(pending) pb_list;
 };
 
diff -Naur milter-greylist.thread/treeset.c milter-greylist/treeset.c
--- milter-greylist.thread/treeset.c	1970-01-01 00:00:00.000000000 +0000
+++ milter-greylist/treeset.c	2006-08-21 12:47:02.000000000 +0000
@@ -0,0 +1,449 @@
+/* $Id$ */
+/* vim: set sw=8 ts=8 sts=8 noexpandtab cino=(0t0: */
+
+/*-
+ * Copyright (c) 2006 AIDA Shinra <shinra@j10n.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#include <assert.h>
+
+#include "treeset.h"
+
+#define TREESET_NODE2ENTRY(head, node) \
+	((struct treeset_entry *)((char *)(node) + (head)->tset_offset))
+#define TREESET_ENTRY2NODE(head, entry) \
+	((void *)((char *)(void *)(entry) - (head)->tset_offset))
+#define TREESET_ENTRY2NODE_SAFE(head, entry) \
+	((entry) ? TREESET_ENTRY2NODE(head, entry) : NULL)
+
+#define TREESET_LESSP(head, x, y) \
+	((*(head)->tset_lessp) ((head)->tset_aux, x, y))
+#define TREESET_LESSEQP(head, x, y) (!TREESET_LESSP(head, y, x))
+#define TREESET_REDP(e) ((e) && (e)->tset_color == TREESET_RED)
+
+#define TREESET_CHANGE_CHILD(head, oldchild, newchild) do { \
+	if (!(oldchild)->tset_parent) \
+		(head)->tset_root = (newchild); \
+	else if ((oldchild) == (oldchild)->tset_parent->tset_left) \
+		(oldchild)->tset_parent->tset_left = (newchild); \
+	else \
+		(oldchild)->tset_parent->tset_right = (newchild); \
+} while (/*CONSTCOND*/ 0)
+		
+
+static void treeset_rotate_left(treeset_head *head, treeset_entry *e);
+static void treeset_rotate_right(treeset_head *head, treeset_entry *e);
+static treeset_entry *treeset_where_to_insert(const treeset_head *head,
+					      treeset_entry **lbound_entry,
+					      const void *key);
+static void treeset_do_insert(treeset_head *head, treeset_entry *lbound_entry,
+			      treeset_entry *whereto, treeset_entry *e);
+
+/* "multiset" operations */
+
+void *
+treeset_lower_bound(const treeset_head *head, const void *key)
+{
+	treeset_entry *e = head->tset_root, *last_left = NULL;
+
+	while (e) {
+		if (TREESET_LESSEQP(head, key, TREESET_ENTRY2NODE(head, e))) {
+			last_left = e;
+			e = e->tset_left;
+		} else {
+			e = e->tset_right;
+		}
+	}
+	return TREESET_ENTRY2NODE_SAFE(head, last_left);
+}
+
+void *
+treeset_upper_bound(const treeset_head *head, const void *key)
+{
+	treeset_entry *e = head->tset_root, *last_left = NULL;
+
+	while (e) {
+		if (TREESET_LESSP(head, key, TREESET_ENTRY2NODE(head, e))) {
+			last_left = e;
+			e = e->tset_left;
+		} else {
+			e = e->tset_right;
+		}
+	}
+	return TREESET_ENTRY2NODE_SAFE(head, last_left);
+}
+
+void 
+treeset_insert(treeset_head *head, void *node)
+{
+	treeset_entry *e = TREESET_NODE2ENTRY(head, node);
+	treeset_entry *lbound_entry, *whereto;
+
+	whereto = treeset_where_to_insert(head, &lbound_entry, node);
+	treeset_do_insert(head, lbound_entry, whereto, e);
+}
+
+void 
+treeset_remove(treeset_head *head, const void *node)
+{
+	treeset_entry *e = TREESET_NODE2ENTRY(head, node), *p, *child;
+	treeset_color color;
+
+	if (e->tset_left && e->tset_right) {
+		treeset_entry *succ;
+
+		for (succ = e->tset_right; succ->tset_left;
+		     succ = succ->tset_left)
+			;
+		child = succ->tset_right;
+		p = succ->tset_parent;
+		color = succ->tset_color;
+		succ->tset_color = e->tset_color;
+		TREESET_CHANGE_CHILD(head, e, succ);
+		succ->tset_parent = e->tset_parent;
+		succ->tset_left = e->tset_left;
+		e->tset_left->tset_parent = succ;
+		if (e == p) {
+			assert(succ == e->tset_right);
+			p = succ;
+		} else {
+			assert(succ == p->tset_left);
+			succ->tset_right = e->tset_right;
+			e->tset_right->tset_parent = succ;
+			p->tset_left = child;
+			if (child)
+				child->tset_parent = p;
+		}
+	} else {
+		child = e->tset_left ? e->tset_left : e->tset_right;
+		p = e->tset_parent;
+		color = e->tset_color;
+		TREESET_CHANGE_CHILD(head, e, child);
+		if (child)
+			child->tset_parent = p;
+	}
+
+	if (color == TREESET_RED) {
+		return;
+	} else if (TREESET_REDP(child)) {
+		child->tset_color = TREESET_BLACK;
+		return;
+	}
+
+	while (p) {
+		if (child == p->tset_left) {
+			if (p->tset_right->tset_color == TREESET_RED) {
+				p->tset_color = TREESET_RED;
+				p->tset_right->tset_color = TREESET_BLACK;
+				treeset_rotate_left(head, p);
+			}
+			if (!TREESET_REDP(p->tset_right->tset_left) &&
+			    !TREESET_REDP(p->tset_right->tset_right)) {
+				p->tset_right->tset_color = TREESET_RED;
+				if (p->tset_color == TREESET_BLACK) {
+					child = p;
+					p = p->tset_parent;
+					continue;
+				} else {
+					p->tset_color = TREESET_BLACK;
+					break;
+				}
+			} else if (!TREESET_REDP(p->tset_right->tset_right)) {
+				p->tset_right->tset_color = TREESET_RED;
+				p->tset_right->tset_left->tset_color =
+					TREESET_BLACK;
+				treeset_rotate_right(head, p->tset_right);
+			}
+			p->tset_right->tset_right->tset_color = TREESET_BLACK;
+			p->tset_right->tset_color = p->tset_color;
+			p->tset_color = TREESET_BLACK;
+			treeset_rotate_left(head, p);
+			break;
+		} else {
+			if (p->tset_left->tset_color == TREESET_RED) {
+				p->tset_color = TREESET_RED;
+				p->tset_left->tset_color = TREESET_BLACK;
+				treeset_rotate_right(head, p);
+			}
+			if (!TREESET_REDP(p->tset_left->tset_right) &&
+			    !TREESET_REDP(p->tset_left->tset_left)) {
+				p->tset_left->tset_color = TREESET_RED;
+				if (p->tset_color == TREESET_BLACK) {
+					child = p;
+					p = p->tset_parent;
+					continue;
+				} else {
+					p->tset_color = TREESET_BLACK;
+					break;
+				}
+			} else if (!TREESET_REDP(p->tset_left->tset_left)) {
+				p->tset_left->tset_color = TREESET_RED;
+				p->tset_left->tset_right->tset_color =
+					TREESET_BLACK;
+				treeset_rotate_left(head, p->tset_left);
+			}
+			p->tset_left->tset_left->tset_color = TREESET_BLACK;
+			p->tset_left->tset_color = p->tset_color;
+			p->tset_color = TREESET_BLACK;
+			treeset_rotate_right(head, p);
+			break;
+		}
+	}
+}
+
+void *
+treeset_first(const treeset_head *head)
+{
+	treeset_entry *e = head->tset_root;
+
+	if (!e)
+		return NULL;
+	while (e->tset_left)
+		e = e->tset_left;
+	return TREESET_ENTRY2NODE(head, e);
+}
+
+void *
+treeset_last(const treeset_head *head)
+{
+	treeset_entry *e = head->tset_root;
+
+	if (!e)
+		return NULL;
+	while (e->tset_right)
+		e = e->tset_right;
+	return TREESET_ENTRY2NODE(head, e);
+}
+
+void *
+treeset_next(const treeset_head *head, const void *node)
+{
+	const treeset_entry *e = TREESET_NODE2ENTRY(head, node);
+
+	if (e->tset_right) {
+		for (e = e->tset_right; e->tset_left; e = e->tset_left)
+			;
+	} else {
+		while (e->tset_parent && e->tset_parent->tset_right == e)
+			e = e->tset_parent;
+		e = e->tset_parent;
+	}
+	return TREESET_ENTRY2NODE_SAFE(head, e);
+}
+
+void *
+treeset_prev(const treeset_head *head, const void *node)
+{
+	const treeset_entry *e = TREESET_NODE2ENTRY(head, node);
+
+	if (e->tset_left) {
+		for (e = e->tset_left; e->tset_right; e = e->tset_right)
+			;
+	} else {
+		while (e->tset_parent && e->tset_parent->tset_left == e)
+			e = e->tset_parent;
+		e = e->tset_parent;
+	}
+	return TREESET_ENTRY2NODE_SAFE(head, e);
+}
+
+void
+treeset_destroy_all(treeset_head *head,
+		    void (*destructor) (void *aux, void *node))
+{
+	treeset_entry *e = head->tset_root;
+	
+	while (e) {
+		if (e->tset_left) {
+			e = e->tset_left;
+		} else if (e->tset_right) {
+			e = e->tset_right;
+		} else {
+			treeset_entry *p = e->tset_parent;
+			TREESET_CHANGE_CHILD(head, e, NULL);
+			(*destructor) (head->tset_aux,
+				       TREESET_ENTRY2NODE(head, e));
+			e = p;
+		}
+	}
+	assert(head->tset_root == NULL);
+}
+
+/* "set" operations */
+
+void *
+treeset_find(const treeset_head *head, const void *key)
+{
+	void *lbound_node = treeset_lower_bound(head, key);
+
+	if (lbound_node && !TREESET_LESSP(head, key, lbound_node))
+		return lbound_node;
+	return NULL;
+}
+
+void *
+treeset_find_or_insert(treeset_head *head, void *newnode)
+{
+	treeset_entry *e = TREESET_NODE2ENTRY(head, newnode);
+	treeset_entry *lbound_entry, *whereto;
+
+	whereto = treeset_where_to_insert(head, &lbound_entry, newnode);
+	if (lbound_entry) {
+		void *lbound_node = TREESET_ENTRY2NODE(head, lbound_entry);
+		if (!TREESET_LESSP(head, newnode, lbound_node))
+			return lbound_node;
+	}
+	treeset_do_insert(head, lbound_entry, whereto, e);
+	return NULL;
+}
+
+void *
+treeset_set(treeset_head *head, void *newnode)
+{
+	treeset_entry *e = TREESET_NODE2ENTRY(head, newnode);
+	treeset_entry *lbound_entry, *whereto;
+
+	whereto = treeset_where_to_insert(head, &lbound_entry, newnode);
+	if (lbound_entry) {
+		void *lbound_node = TREESET_ENTRY2NODE(head, lbound_entry);
+		if (!TREESET_LESSP(head, newnode, lbound_node)) {
+			TREESET_CHANGE_CHILD(head, lbound_entry, e);
+			if (lbound_entry->tset_left)
+				lbound_entry->tset_left->tset_parent = e;
+			if (lbound_entry->tset_right)
+				lbound_entry->tset_right->tset_parent = e;
+			*e = *lbound_entry;
+			return lbound_node;
+		}
+	}
+	treeset_do_insert(head, lbound_entry, whereto, e);
+	return NULL;
+}
+
+void *
+treeset_unset(treeset_head *head, const void *key)
+{
+	void *lbound_node = treeset_lower_bound(head, key);
+
+	if (lbound_node && !TREESET_LESSP(head, key, lbound_node)) {
+		treeset_remove(head, lbound_node);
+		return lbound_node;
+	}
+	return NULL;
+}
+
+/* Internal functions */
+
+static void
+treeset_rotate_left(treeset_head *head, treeset_entry *x)
+{
+	treeset_entry *y = x->tset_right;
+
+	TREESET_CHANGE_CHILD(head, x, y);
+	y->tset_parent = x->tset_parent;
+	x->tset_right = y->tset_left;
+	if (y->tset_left)
+		y->tset_left->tset_parent = x;
+	y->tset_left = x;
+	x->tset_parent = y;
+}
+
+static void
+treeset_rotate_right(treeset_head *head, treeset_entry *x)
+{
+	treeset_entry *y = x->tset_left;
+
+	TREESET_CHANGE_CHILD(head, x, y);
+	y->tset_parent = x->tset_parent;
+	x->tset_left = y->tset_right;
+	if (y->tset_right)
+		y->tset_right->tset_parent = x;
+	y->tset_right = x;
+	x->tset_parent = y;
+}
+
+static treeset_entry *
+treeset_where_to_insert(const treeset_head *head,
+			treeset_entry **lbound_entry, const void *key)
+{
+	treeset_entry *e = head->tset_root, *prev = NULL;
+
+	*lbound_entry = NULL;
+	while (e) {
+		prev = e;
+		if (TREESET_LESSEQP(head, key, TREESET_ENTRY2NODE(head, e))) {
+			*lbound_entry = e;
+			e = e->tset_left;
+		} else {
+			e = e->tset_right;
+		}
+	}
+	return prev;
+}
+
+static void
+treeset_do_insert(treeset_head *head, treeset_entry *lbound_entry,
+		  treeset_entry *whereto, treeset_entry *e)
+{
+	e->tset_parent = whereto;
+	e->tset_left = NULL;
+	e->tset_right = NULL;
+	e->tset_color = TREESET_RED;
+	if (whereto == NULL)
+		head->tset_root = e;
+	else if (whereto == lbound_entry)
+		whereto->tset_left = e;
+	else
+		whereto->tset_right = e;
+
+	while (TREESET_REDP(e->tset_parent)) {
+		treeset_entry *g = e->tset_parent->tset_parent;
+
+		assert(g);
+		if (!TREESET_REDP(g->tset_right)) {
+			assert(e->tset_parent == g->tset_left);
+			if (e == g->tset_left->tset_right)
+				treeset_rotate_left(head, g->tset_left);
+			g->tset_color = TREESET_RED;
+			g->tset_left->tset_color = TREESET_BLACK;
+			treeset_rotate_right(head, g);
+			break;
+		} else if (!TREESET_REDP(g->tset_left)) {
+			assert(e->tset_parent == g->tset_right);
+			if (e == g->tset_right->tset_left)
+				treeset_rotate_right(head, g->tset_right);
+			g->tset_color = TREESET_RED;
+			g->tset_right->tset_color = TREESET_BLACK;
+			treeset_rotate_left(head, g);
+			break;
+		} else {
+			g->tset_color = TREESET_RED;
+			g->tset_left->tset_color = TREESET_BLACK;
+			g->tset_right->tset_color = TREESET_BLACK;
+			e = g;
+		}
+	}
+	head->tset_root->tset_color = TREESET_BLACK;
+}
diff -Naur milter-greylist.thread/treeset.h milter-greylist/treeset.h
--- milter-greylist.thread/treeset.h	1970-01-01 00:00:00.000000000 +0000
+++ milter-greylist/treeset.h	2006-08-21 11:59:56.000000000 +0000
@@ -0,0 +1,170 @@
+/* $Id$ */
+/* vim: set sw=8 ts=8 sts=8 noexpandtab cino=(0t0: */
+
+/*-
+ * Copyright (c) 2006 AIDA Shinra <shinra@j10n.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#ifndef TREESET_H_INCLUDED
+#define TREESET_H_INCLUDED
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Usage:
+ *
+ * typedef struct {
+ *	...
+ *	int key_field;
+ *	treeset_entry entry_field;
+ *	treeset_entry another_entry;
+ *	...
+ * } your_node_type;
+ *
+ * static treeset_head head1 =
+ *	TREESET_INITIALIZER(your_node_type, entry_field,
+ *				 &lessp, &any_object);
+ *
+ * void your_function() {
+ *	treeset_head head2;
+ *	your_node_type key;
+ *	your_node_type *newnode, *var;
+ *
+ *	TREESET_INIT(&head2, your_node_type, another_entry,
+ *			  &another_lessp, &another_object);
+ *	...
+ *	newnode = (your_node_type *)malloc(sizeof(your_node_type));
+ *	...
+ *	treeset_insert(&head1, node);
+ *	key.key_field = foobar();
+ *	var = (your_node_type *)treeset_find(&head2, &key);
+ *	...
+ *	TREESET_FOREACH(var, &head1, your_node_type) {
+ *		...
+ *	}
+ *	treeset_destroy_all(&head1, &destructor);
+ * }
+ * 
+ * int lessp(void *aux, const void *x, const void *y) {
+ *	some_type *auxobj = (some_type *)aux;
+ *	const your_node_type *xnode = (const your_node_type *)x, 
+ *	const your_node_type *ynode = (const your_node_type *)y, 
+ *
+ *	if (auxobj->reversep)
+ *		return ynode->key < xnode->key;
+ *	else
+ *		return xnode->key < ynode->key;
+ * }
+ *
+ * void destructor(void *dummy, void *node) {
+ *	free(node);
+ * }
+ *
+ */
+
+typedef enum treeset_color { TREESET_RED, TREESET_BLACK } treeset_color;
+
+/* Do not touch inside this. */
+typedef struct treeset_entry {
+	struct treeset_entry *tset_parent;
+	struct treeset_entry *tset_left;
+	struct treeset_entry *tset_right;
+	treeset_color tset_color;
+} treeset_entry;
+
+/* You may touch tset_lessp and tset_aux.
+ * Do not touch tset_root other members. */
+typedef struct treeset_head {
+	treeset_entry *tset_root;
+	size_t tset_offset;
+	int (*tset_lessp) (void *aux, const void *x, const void *y);
+	void *tset_aux;
+} treeset_head;
+
+#define TREESET_INITIALIZER(type, field, less, aux) \
+{ NULL, offsetof(type, field), (less), (aux) }
+
+#define TREESET_INIT(head, type, field, less, aux) do { \
+	(head)->tset_root = NULL; \
+	(head)->tset_offset = offsetof(type, field); \
+	(head)->tset_lessp = (less); \
+	(head)->tset_aux = (aux); \
+} while(/*CONSTCOND*/ 0)
+
+/* "multiset" operations */
+
+/* Finds the first node >= key. If not found, returns NULL. */
+void *treeset_lower_bound(const treeset_head *head, const void *key);
+/* Finds the first node > key. If not found, returns NULL. */
+void *treeset_upper_bound(const treeset_head *head, const void *key);
+/* Insert the node. */
+void treeset_insert(treeset_head *head, void *node);
+/* Remove the node. */
+void treeset_remove(treeset_head *head, const void *node);
+/* Returns the first node. If the tree is empty, returns NULL. */
+void *treeset_first(const treeset_head *head);
+/* Returns the last node. If the tree is empty, returns NULL. */
+void *treeset_last(const treeset_head *head);
+/* Returns the next node. If the last node given, returns NULL. */
+void *treeset_next(const treeset_head *head, const void *node);
+/* Returns the previous node. If the first node given, returns NULL. */
+void *treeset_prev(const treeset_head *head, const void *node);
+/* Removes all nodes and invokes something like free() for them. */
+void treeset_destroy_all(treeset_head *head,
+			 void (*destructor) (void *aux, void *node));
+
+/* "set" operations */
+
+/* Finds a node == key. If not found, returns NULL. */
+void *treeset_find(const treeset_head *head, const void *key);
+/* If a node eqivalent to newnode exists, returns it.
+ * Otherwise, inserts newnode and returns NULL. */
+void *treeset_find_or_insert(treeset_head *head, void *newnode);
+/* If a node eqivalent to newnode exists, replace it to newnode and returns
+ * the removed node. Otherwise, inserts newnode and returns NULL. */
+void *treeset_set(treeset_head *head, void *newnode);
+/* If a node == key exists, removes and returns the node.
+ * Otherwise, returns NULL. */
+void *treeset_unset(treeset_head *head, const void *key);
+
+#define TREESET_FOREACH(var, head, type) \
+	for ((var) = (type *)treeset_first(head); \
+	     (var) != (type *)NULL; \
+	     (var) = (type *)treeset_next(head, var))
+
+#define TREESET_FOREACH_REVERSE(var, head, type) \
+	for ((var) = (type *)treeset_last(head); \
+	     (var) != (type *)NULL; \
+	     (var) = (type *)treeset_prev(head, var))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TREESET_H_INCLUDED */
