xref: /freebsd/lib/libc/stdlib/tsearch.c (revision 459d04a5eecbe8ea330ef9c6cbba6392b16d317e)
1*459d04a5SEd Schouten /*-
2*459d04a5SEd Schouten  * Copyright (c) 2015 Nuxi, https://nuxi.nl/
364566a3eSAlfred Perlstein  *
4*459d04a5SEd Schouten  * Redistribution and use in source and binary forms, with or without
5*459d04a5SEd Schouten  * modification, are permitted provided that the following conditions
6*459d04a5SEd Schouten  * are met:
7*459d04a5SEd Schouten  * 1. Redistributions of source code must retain the above copyright
8*459d04a5SEd Schouten  *    notice, this list of conditions and the following disclaimer.
9*459d04a5SEd Schouten  * 2. Redistributions in binary form must reproduce the above copyright
10*459d04a5SEd Schouten  *    notice, this list of conditions and the following disclaimer in the
11*459d04a5SEd Schouten  *    documentation and/or other materials provided with the distribution.
1264566a3eSAlfred Perlstein  *
13*459d04a5SEd Schouten  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14*459d04a5SEd Schouten  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15*459d04a5SEd Schouten  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16*459d04a5SEd Schouten  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17*459d04a5SEd Schouten  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18*459d04a5SEd Schouten  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19*459d04a5SEd Schouten  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20*459d04a5SEd Schouten  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21*459d04a5SEd Schouten  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22*459d04a5SEd Schouten  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23*459d04a5SEd Schouten  * SUCH DAMAGE.
2464566a3eSAlfred Perlstein  */
2564566a3eSAlfred Perlstein 
2664566a3eSAlfred Perlstein #include <sys/cdefs.h>
27333fc21eSDavid E. O'Brien __FBSDID("$FreeBSD$");
2864566a3eSAlfred Perlstein 
2964566a3eSAlfred Perlstein #define	_SEARCH_PRIVATE
3064566a3eSAlfred Perlstein #include <search.h>
3164566a3eSAlfred Perlstein #include <stdlib.h>
3264566a3eSAlfred Perlstein 
33*459d04a5SEd Schouten #include "tsearch_path.h"
34*459d04a5SEd Schouten 
3564566a3eSAlfred Perlstein void *
36*459d04a5SEd Schouten tsearch(const void *key, void **rootp,
378a29851fSPedro F. Giffuni     int (*compar)(const void *, const void *))
3864566a3eSAlfred Perlstein {
39*459d04a5SEd Schouten 	struct path path;
40*459d04a5SEd Schouten 	node_t *root, **base, **leaf, *result, *n, *x, *y, *z;
41*459d04a5SEd Schouten 	int cmp;
4264566a3eSAlfred Perlstein 
43*459d04a5SEd Schouten 	/* POSIX requires that tsearch() returns NULL if rootp is NULL. */
4464566a3eSAlfred Perlstein 	if (rootp == NULL)
45*459d04a5SEd Schouten 	return (NULL);
46*459d04a5SEd Schouten 		root = *rootp;
4764566a3eSAlfred Perlstein 
48*459d04a5SEd Schouten 	/*
49*459d04a5SEd Schouten 	 * Find the leaf where the new key needs to be inserted. Return
50*459d04a5SEd Schouten 	 * if we've found an existing entry. Keep track of the path that
51*459d04a5SEd Schouten 	 * is taken to get to the node, as we will need it to adjust the
52*459d04a5SEd Schouten 	 * balances.
53*459d04a5SEd Schouten 	*/
54*459d04a5SEd Schouten 	path_init(&path);
55*459d04a5SEd Schouten 	base = &root;
56*459d04a5SEd Schouten 	leaf = &root;
57*459d04a5SEd Schouten 	while (*leaf != NULL) {
58*459d04a5SEd Schouten 		if ((*leaf)->balance != 0) {
59*459d04a5SEd Schouten 			/*
60*459d04a5SEd Schouten 			 * If we reach a node that has a non-zero
61*459d04a5SEd Schouten 			 * balance on the way, we know that we won't
62*459d04a5SEd Schouten 			 * need to perform any rotations above this
63*459d04a5SEd Schouten 			 * point. In this case rotations are always
64*459d04a5SEd Schouten 			 * capable of keeping the subtree in balance.
65*459d04a5SEd Schouten 			 * Make this the base node and reset the path.
66*459d04a5SEd Schouten 			 */
67*459d04a5SEd Schouten 			base = leaf;
68*459d04a5SEd Schouten 			path_init(&path);
69*459d04a5SEd Schouten 		}
70*459d04a5SEd Schouten 		cmp = compar(key, (*leaf)->key);
71*459d04a5SEd Schouten 		if (cmp < 0) {
72*459d04a5SEd Schouten 			path_taking_left(&path);
73*459d04a5SEd Schouten 			leaf = &(*leaf)->llink;
74*459d04a5SEd Schouten 		} else if (cmp > 0) {
75*459d04a5SEd Schouten 			path_taking_right(&path);
76*459d04a5SEd Schouten 			leaf = &(*leaf)->rlink;
77*459d04a5SEd Schouten 		} else {
78*459d04a5SEd Schouten 			return (&(*leaf)->key);
79*459d04a5SEd Schouten 		}
8064566a3eSAlfred Perlstein 	}
8164566a3eSAlfred Perlstein 
82*459d04a5SEd Schouten 	/* Did not find a matching key in the tree. Insert a new node. */
83*459d04a5SEd Schouten 	result = *leaf = malloc(sizeof(**leaf));
84*459d04a5SEd Schouten 	if (result == NULL)
85*459d04a5SEd Schouten 		return (NULL);
86*459d04a5SEd Schouten 	result->key = (void *)key;
87*459d04a5SEd Schouten 	result->llink = NULL;
88*459d04a5SEd Schouten 	result->rlink = NULL;
89*459d04a5SEd Schouten 	result->balance = 0;
90*459d04a5SEd Schouten 
91*459d04a5SEd Schouten 	/*
92*459d04a5SEd Schouten 	 * Walk along the same path a second time and adjust the
93*459d04a5SEd Schouten 	 * balances. Except for the first node, all of these nodes must
94*459d04a5SEd Schouten 	 * have a balance of zero, meaning that these nodes will not get
95*459d04a5SEd Schouten 	 * out of balance.
96*459d04a5SEd Schouten 	*/
97*459d04a5SEd Schouten 	for (n = *base; n != *leaf;) {
98*459d04a5SEd Schouten 		if (path_took_left(&path)) {
99*459d04a5SEd Schouten 			n->balance += 1;
100*459d04a5SEd Schouten 			n = n->llink;
101*459d04a5SEd Schouten 		} else {
102*459d04a5SEd Schouten 			n->balance -= 1;
103*459d04a5SEd Schouten 			n = n->rlink;
10464566a3eSAlfred Perlstein 		}
105*459d04a5SEd Schouten 	}
106*459d04a5SEd Schouten 
107*459d04a5SEd Schouten 	/*
108*459d04a5SEd Schouten 	 * Adjusting the balances may have pushed the balance of the
109*459d04a5SEd Schouten 	 * base node out of range. Perform a rotation to bring the
110*459d04a5SEd Schouten 	 * balance back in range.
111*459d04a5SEd Schouten 	 */
112*459d04a5SEd Schouten 	x = *base;
113*459d04a5SEd Schouten 	if (x->balance > 1) {
114*459d04a5SEd Schouten 		y = x->llink;
115*459d04a5SEd Schouten 		if (y->balance < 0) {
116*459d04a5SEd Schouten 			/*
117*459d04a5SEd Schouten 			 * Left-right case.
118*459d04a5SEd Schouten 			 *
119*459d04a5SEd Schouten 			 *         x
120*459d04a5SEd Schouten 			 *        / \            z
121*459d04a5SEd Schouten 			 *       y   D          / \
122*459d04a5SEd Schouten 			 *      / \     -->    y   x
123*459d04a5SEd Schouten 			 *     A   z          /|   |\
124*459d04a5SEd Schouten 			 *        / \        A B   C D
125*459d04a5SEd Schouten 			 *       B   C
126*459d04a5SEd Schouten 			 */
127*459d04a5SEd Schouten 			z = y->rlink;
128*459d04a5SEd Schouten 			y->rlink = z->llink;
129*459d04a5SEd Schouten 			z->llink = y;
130*459d04a5SEd Schouten 			x->llink = z->rlink;
131*459d04a5SEd Schouten 			z->rlink = x;
132*459d04a5SEd Schouten 			*base = z;
133*459d04a5SEd Schouten 
134*459d04a5SEd Schouten 			x->balance = z->balance > 0 ? -1 : 0;
135*459d04a5SEd Schouten 			y->balance = z->balance < 0 ? 1 : 0;
136*459d04a5SEd Schouten 			z->balance = 0;
137*459d04a5SEd Schouten 		} else {
138*459d04a5SEd Schouten 			/*
139*459d04a5SEd Schouten 			 * Left-left case.
140*459d04a5SEd Schouten 			 *
141*459d04a5SEd Schouten 			 *        x           y
142*459d04a5SEd Schouten 			 *       / \         / \
143*459d04a5SEd Schouten 			 *      y   C  -->  A   x
144*459d04a5SEd Schouten 			 *     / \             / \
145*459d04a5SEd Schouten 			 *    A   B           B   C
146*459d04a5SEd Schouten 			 */
147*459d04a5SEd Schouten 			x->llink = y->rlink;
148*459d04a5SEd Schouten 			y->rlink = x;
149*459d04a5SEd Schouten 			*base = y;
150*459d04a5SEd Schouten 
151*459d04a5SEd Schouten 			x->balance = 0;
152*459d04a5SEd Schouten 			y->balance = 0;
153*459d04a5SEd Schouten 		}
154*459d04a5SEd Schouten 	} else if (x->balance < -1) {
155*459d04a5SEd Schouten 		y = x->rlink;
156*459d04a5SEd Schouten 		if (y->balance > 0) {
157*459d04a5SEd Schouten 			/*
158*459d04a5SEd Schouten 			 * Right-left case.
159*459d04a5SEd Schouten 			 *
160*459d04a5SEd Schouten 			 *       x
161*459d04a5SEd Schouten 			 *      / \              z
162*459d04a5SEd Schouten 			 *     A   y            / \
163*459d04a5SEd Schouten 			 *        / \   -->    x   y
164*459d04a5SEd Schouten 			 *       z   D        /|   |\
165*459d04a5SEd Schouten 			 *      / \          A B   C D
166*459d04a5SEd Schouten 			 *     B   C
167*459d04a5SEd Schouten 			 */
168*459d04a5SEd Schouten 			node_t *z = y->llink;
169*459d04a5SEd Schouten 			x->rlink = z->llink;
170*459d04a5SEd Schouten 			z->llink = x;
171*459d04a5SEd Schouten 			y->llink = z->rlink;
172*459d04a5SEd Schouten 			z->rlink = y;
173*459d04a5SEd Schouten 			*base = z;
174*459d04a5SEd Schouten 
175*459d04a5SEd Schouten 			x->balance = z->balance < 0 ? 1 : 0;
176*459d04a5SEd Schouten 			y->balance = z->balance > 0 ? -1 : 0;
177*459d04a5SEd Schouten 			z->balance = 0;
178*459d04a5SEd Schouten 		} else {
179*459d04a5SEd Schouten 			/*
180*459d04a5SEd Schouten 			 * Right-right case.
181*459d04a5SEd Schouten 			 *
182*459d04a5SEd Schouten 			 *       x               y
183*459d04a5SEd Schouten 			 *      / \             / \
184*459d04a5SEd Schouten 			 *     A   y    -->    x   C
185*459d04a5SEd Schouten 			 *        / \         / \
186*459d04a5SEd Schouten 			 *       B   C       A   B
187*459d04a5SEd Schouten 			 */
188*459d04a5SEd Schouten 			x->rlink = y->llink;
189*459d04a5SEd Schouten 			y->llink = x;
190*459d04a5SEd Schouten 			*base = y;
191*459d04a5SEd Schouten 
192*459d04a5SEd Schouten 			x->balance = 0;
193*459d04a5SEd Schouten 			y->balance = 0;
194*459d04a5SEd Schouten 		}
195*459d04a5SEd Schouten 	}
196*459d04a5SEd Schouten 
197*459d04a5SEd Schouten 	/* Return the new entry. */
198*459d04a5SEd Schouten 	*rootp = root;
199*459d04a5SEd Schouten 	return (&result->key);
20064566a3eSAlfred Perlstein }
201