xref: /csrg-svn/lib/libc/db/btree/bt_search.c (revision 46132)
1*46132Smao /*-
2*46132Smao  * Copyright (c) 1990 The Regents of the University of California.
3*46132Smao  * All rights reserved.
4*46132Smao  *
5*46132Smao  * This code is derived from software contributed to Berkeley by
6*46132Smao  * Mike Olson.
7*46132Smao  *
8*46132Smao  * %sccs.include.redist.c%
9*46132Smao  */
10*46132Smao 
11*46132Smao #if defined(LIBC_SCCS) && !defined(lint)
12*46132Smao static char sccsid[] = "@(#)bt_search.c	5.1 (Berkeley) 01/23/91";
13*46132Smao #endif /* LIBC_SCCS and not lint */
14*46132Smao 
15*46132Smao #include <sys/types.h>
16*46132Smao #include <db.h>
17*46132Smao #include "btree.h"
18*46132Smao 
19*46132Smao /*
20*46132Smao  *  _BT_FIRST -- Find the first item in the tree that matches the supplied
21*46132Smao  *		 key.
22*46132Smao  *
23*46132Smao  *	This routine supports deletion.  When the user supplies a key to
24*46132Smao  *	be deleted, we find the first one, and iteratively delete all the
25*46132Smao  *	matching ones that follow it.
26*46132Smao  *
27*46132Smao  *	Parameters:
28*46132Smao  *		t -- btree in which to find first occurrence
29*46132Smao  *		key -- key to find
30*46132Smao  *
31*46132Smao  *	Returns:
32*46132Smao  *		The BTITEM for the matching item.  If there's no match,
33*46132Smao  *		this may point to the first item > than the supplied key,
34*46132Smao  *		or off the end of the page.
35*46132Smao  *
36*46132Smao  *	Warnings:
37*46132Smao  *		The BTITEM returned is in static space and will be overwritten
38*46132Smao  *		by the next search of any kind in any btree.
39*46132Smao  */
40*46132Smao 
41*46132Smao BTITEM *
42*46132Smao _bt_first(t, key)
43*46132Smao 	BTREE_P t;
44*46132Smao 	DBT *key;
45*46132Smao {
46*46132Smao 	BTHEADER *h;
47*46132Smao 	BTITEM *item;
48*46132Smao 	index_t next;
49*46132Smao 	int r;
50*46132Smao 
51*46132Smao 	/* find any matching item */
52*46132Smao 	item = _bt_search(t, key);
53*46132Smao 	h = t->bt_curpage;
54*46132Smao 	next = NEXTINDEX(h);
55*46132Smao 
56*46132Smao 	/* if we're off the end of the page, search failed and we're done */
57*46132Smao 	if (item->bti_index >= next)
58*46132Smao 		return (item);
59*46132Smao 
60*46132Smao 	/* as long as we have an exact match, walk backwards */
61*46132Smao 	while ((r = _bt_cmp(t, key->data, item->bti_index)) == 0) {
62*46132Smao 
63*46132Smao 		/* at start of page? */
64*46132Smao 		if (item->bti_index == 0) {
65*46132Smao 
66*46132Smao 			/* if no prev page, we're done */
67*46132Smao 			if (h->h_prevpg == P_NONE)
68*46132Smao 				return (item);
69*46132Smao 
70*46132Smao 			/* walk backward, skipping empty pages */
71*46132Smao 			do {
72*46132Smao 				if (_bt_getpage(t, h->h_prevpg) == RET_ERROR)
73*46132Smao 					return ((BTITEM *) NULL);
74*46132Smao 				h = t->bt_curpage;
75*46132Smao 			} while (NEXTINDEX(h) == 0 && h->h_prevpg != P_NONE);
76*46132Smao 
77*46132Smao 			if (NEXTINDEX(h) != 0)
78*46132Smao 				item->bti_index = NEXTINDEX(h) - 1;
79*46132Smao 			else
80*46132Smao 				item->bti_index = 0;
81*46132Smao 
82*46132Smao 			item->bti_pgno = h->h_pgno;
83*46132Smao 		} else {
84*46132Smao 			item->bti_index--;
85*46132Smao 		}
86*46132Smao 	}
87*46132Smao 
88*46132Smao 	/* if we went too far backwards, step forward one entry */
89*46132Smao 	if (r > 0) {
90*46132Smao 		if (++(item->bti_index) >= NEXTINDEX(h)
91*46132Smao 		    && h->h_nextpg != P_NONE) {
92*46132Smao 
93*46132Smao 			/* walk forward, skipping empty pages */
94*46132Smao 			do {
95*46132Smao 				if (_bt_getpage(t, h->h_nextpg) == RET_ERROR)
96*46132Smao 					return ((BTITEM *) NULL);
97*46132Smao 				h = t->bt_curpage;
98*46132Smao 			} while (h->h_nextpg != P_NONE && NEXTINDEX(h) == 0);
99*46132Smao 
100*46132Smao 			item->bti_index = 0;
101*46132Smao 			item->bti_pgno = h->h_pgno;
102*46132Smao 		}
103*46132Smao 	}
104*46132Smao 
105*46132Smao 	/* got it */
106*46132Smao 	return (item);
107*46132Smao }
108*46132Smao 
109*46132Smao /*
110*46132Smao  *  _BT_SEARCH, _BT_SEARCHR -- Search for a particular key in the tree.
111*46132Smao  *
112*46132Smao  *	Parameters:
113*46132Smao  *		t -- btree in which to search
114*46132Smao  *		key -- key to find
115*46132Smao  *
116*46132Smao  *	Returns:
117*46132Smao  *		BTITEM for matching item, if any, or the BTITEM for the
118*46132Smao  *		location of the key, if it were in the tree.
119*46132Smao  *
120*46132Smao  *	Warnings:
121*46132Smao  *		The BTITEM returned is in static memory, and will be
122*46132Smao  *		overwritten by the next search of any kind in any tree.
123*46132Smao  */
124*46132Smao 
125*46132Smao BTITEM *
126*46132Smao _bt_search(t, key)
127*46132Smao 	BTREE_P t;
128*46132Smao 	DBT *key;
129*46132Smao {
130*46132Smao 	/* we want to start all of our searches at the root */
131*46132Smao 	if (_bt_getpage(t, (pgno_t) P_ROOT) == RET_ERROR)
132*46132Smao 		return ((BTITEM *) NULL);
133*46132Smao 
134*46132Smao 	return (_bt_searchr(t, key));
135*46132Smao }
136*46132Smao 
137*46132Smao BTITEM *
138*46132Smao _bt_searchr(t, key)
139*46132Smao 	BTREE_P t;
140*46132Smao 	DBT *key;
141*46132Smao {
142*46132Smao 	BTHEADER *h = t->bt_curpage;
143*46132Smao 	index_t index;
144*46132Smao 	IDATUM *id;
145*46132Smao 	DATUM *d;
146*46132Smao 	static BTITEM item;
147*46132Smao 
148*46132Smao 	/* do a binary search on the current page */
149*46132Smao 	index = _bt_binsrch(t, &(key->data[0]));
150*46132Smao 
151*46132Smao 	/*
152*46132Smao 	 *  At this point, the binary search terminated because the endpoints
153*46132Smao 	 *  got too close together, or we have a match.  Figure out which
154*46132Smao 	 *  case applies and decide what to do based on the page type.
155*46132Smao 	 */
156*46132Smao 	if (h->h_flags & F_LEAF) {
157*46132Smao 		item.bti_pgno = h->h_pgno;
158*46132Smao 		item.bti_index = index;
159*46132Smao 		if (index < NEXTINDEX(h))
160*46132Smao 			d = (DATUM *) GETDATUM(h,index);
161*46132Smao 		else
162*46132Smao 			d = (DATUM *) NULL;
163*46132Smao 
164*46132Smao 		item.bti_datum = d;
165*46132Smao 		return(&item);
166*46132Smao 	} else {
167*46132Smao 		id = (IDATUM *) GETDATUM(h, index);
168*46132Smao 		if (_bt_push(t, h->h_pgno) == RET_ERROR)
169*46132Smao 			return ((BTITEM *) NULL);
170*46132Smao 		if (_bt_getpage(t, id->i_pgno) == RET_ERROR)
171*46132Smao 			return ((BTITEM *) NULL);
172*46132Smao 		return (_bt_searchr(t, key));
173*46132Smao 	}
174*46132Smao }
175*46132Smao 
176*46132Smao /*
177*46132Smao  *  _BT_BINSRCH -- Do a binary search for a given key on the current page.
178*46132Smao  *
179*46132Smao  *	Searches on internal pages are handled slightly differently from
180*46132Smao  *	searches on leaf pages.  This is because internal page searches
181*46132Smao  *	find the largest item <= key in the tree, and leaf searches find
182*46132Smao  *	the smallest item >= key.  This guarantees that leaf page searches
183*46132Smao  *	leave us pointing at the item's correct position, and internal
184*46132Smao  *	searches descend the tree correctly.
185*46132Smao  *
186*46132Smao  *	Parameters:
187*46132Smao  *		t -- tree to search
188*46132Smao  *		key -- key we're looking for
189*46132Smao  *
190*46132Smao  *	Returns:
191*46132Smao  *		Index of the line pointer array entry for the (closest)
192*46132Smao  *		match to key on the current page, with "closest" as defined
193*46132Smao  *		above.
194*46132Smao  */
195*46132Smao 
196*46132Smao index_t
197*46132Smao _bt_binsrch(t, key)
198*46132Smao 	BTREE_P t;
199*46132Smao 	char *key;
200*46132Smao {
201*46132Smao 	index_t lbound, ubound, cur;
202*46132Smao 	BTHEADER *h = t->bt_curpage;
203*46132Smao 	int match = 0;
204*46132Smao 	int r;
205*46132Smao 
206*46132Smao 	lbound = 0;
207*46132Smao 	ubound = NEXTINDEX(h);
208*46132Smao 	if (ubound > 0)
209*46132Smao 		--ubound;
210*46132Smao 
211*46132Smao 	/* do a binary search on the current page */
212*46132Smao 	while ((ubound - lbound) > 1) {
213*46132Smao 		cur = lbound + ((ubound - lbound) / 2);
214*46132Smao 		r = _bt_cmp(t, key, cur);
215*46132Smao 
216*46132Smao 		if (r > 0)
217*46132Smao 			lbound = cur + 1;
218*46132Smao 		else if (r < 0)
219*46132Smao 			ubound = cur;
220*46132Smao 		else {
221*46132Smao 			match++;
222*46132Smao 			break;
223*46132Smao 		}
224*46132Smao 	}
225*46132Smao 
226*46132Smao 	/*
227*46132Smao 	 *  At this point, the binary search terminated because the endpoints
228*46132Smao 	 *  got too close together, or we have a match.  Figure out which
229*46132Smao 	 *  case applies, decide what to do based on the page type (leaf or
230*46132Smao 	 *  internal), and do the right thing.
231*46132Smao 	 */
232*46132Smao 	if (match) {
233*46132Smao 		return (cur);
234*46132Smao 	} else if (ubound != lbound) {
235*46132Smao 		if (h->h_flags & F_LEAF) {
236*46132Smao 			r = _bt_cmp(t, key, lbound);
237*46132Smao 			if (r <= 0) {
238*46132Smao 				return (lbound);
239*46132Smao 			}
240*46132Smao 		} else {
241*46132Smao 			r = _bt_cmp(t, key, ubound);
242*46132Smao 
243*46132Smao 			/* for internal nodes, move as far left as possible */
244*46132Smao 			if (r < 0) {
245*46132Smao 				r = _bt_cmp(t, key, lbound);
246*46132Smao 				if (r < 0 && lbound > 0)
247*46132Smao 					--lbound;
248*46132Smao 				return (lbound);
249*46132Smao 			} else {
250*46132Smao 				return (ubound);
251*46132Smao 			}
252*46132Smao 		}
253*46132Smao 	}
254*46132Smao 
255*46132Smao 	if (h->h_flags & F_LEAF) {
256*46132Smao 		if (ubound < NEXTINDEX(h)) {
257*46132Smao 			r = _bt_cmp(t, key, ubound);
258*46132Smao 			if (r > 0)
259*46132Smao 				ubound++;
260*46132Smao 		}
261*46132Smao 	} else {
262*46132Smao 		/* for internal pages, move as far left as possible */
263*46132Smao 		if (ubound == NEXTINDEX(h))
264*46132Smao 			ubound--;
265*46132Smao 
266*46132Smao 		while (_bt_cmp(t, key, ubound) < 0)
267*46132Smao 			ubound--;
268*46132Smao 	}
269*46132Smao 	return (ubound);
270*46132Smao }
271