2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these libraries and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
23 /*%% (c) Copyright 1993, 1994 Hewlett-Packard Company */
24 /*%% (c) Copyright 1993, 1994 International Business Machines Corp. */
25 /*%% (c) Copyright 1993, 1994 Sun Microsystems, Inc. */
26 /*%% (c) Copyright 1993, 1994 Novell, Inc. */
27 /*%% $XConsortium: isbtree3.c /main/3 1995/10/23 11:36:12 rswiston $ */
29 static char sccsid[] = "@(#)isbtree3.c 1.5 89/07/17 Copyr 1988 Sun Micro";
33 * Copyright (c) 1988 by Sun Microsystems, Inc.
40 * B-tree operations: REMOVE
44 #include "isam_impl.h"
46 extern int _iskeycmp();
48 static void remove_entry(Btree *, char *, int);
49 static void move_from_right(Btree *, char *, char *, int);
50 static void move_from_left(Btree *, char *, char *, int);
52 /* _isbtree_remove() - Remove entry from B-tree ----------------------------*/
55 _isbtree_remove(Btree *btree)
57 struct keydesc2 *pkeydesc2 = btree->keydesc2;
58 int nkeys; /* Number of keys in the page */
60 char *pkp, *pkp2, *pkp3;
61 struct bufhdr *kp2bhdr;
67 /* Set comparison function. */
68 _iskeycmp_set(pkeydesc2, pkeydesc2->k2_nparts+1); /* +1 for recno field */
72 * Remove he current entry from leaf.
73 * If it was leftmost entry, propagate new leftmost to upper levels.
74 * If block has less than capac/2 + 1 entries, either move some entries
75 * from next left or right block, or merge with next left or right
79 for (i = btree->depth - 1; i >= 0; i--) {
81 /* Re-fix the current page for update. */
82 btree->bufhdr[i] = _isdisk_refix(btree->bufhdr[i], ISFIXWRITE);
83 pkp = btree->bufhdr[i]->isb_buffer;
85 level = ldshort(pkp + BT_LEVEL_OFF);
86 capac = ldshort(pkp + BT_CAPAC_OFF); /* Block capacity */
87 nkeys = ldshort(pkp + BT_NKEYS_OFF); /* Number of keys in block */
89 halfcapac = (capac+1) >> 1; /* same as capac/2 + 1 */
91 assert(level + i + 1 == btree->depth);
92 assert(i == 0 || nkeys >= halfcapac);
94 /* Remove entry from this page. */
95 remove_entry(btree, pkp, btree->curpos[i]);
98 /* Must propagate new leftmost key up. */
99 if (btree->curpos[i] == 0 && i > 0)
100 leftkey_up(btree, i);
102 if (nkeys >= halfcapac || i == 0) {
105 * Page is still balanced. No changes are necessary.
106 * Also, no chages are needed if this is root block.
113 if(btree->curpos[i-1] > 0) {
116 * Block pkp is not the leftmost descendent of its parent.
117 * Either merge with left brother, or move a few entries
121 pkp3 = btree->bufhdr[i-1]->isb_buffer;
123 blkno2 = ldblkno(pkp3 + ISPAGESIZE -
124 (btree->curpos[i-1]) * BLKNOSIZE);
126 /* Fix the left brother. */
127 kp2bhdr = _isdisk_fix(btree->fcb, btree->fcb->indfd,
129 pkp2 = kp2bhdr->isb_buffer;
131 if (ldshort(pkp2 + BT_NKEYS_OFF) + nkeys > capac) {
134 * Current page cannot be merged with the left brother.
135 * Move some entries from the left brother into current.
138 move_keys = (ldshort(pkp2 + BT_NKEYS_OFF) - halfcapac +1)/2;
139 assert(move_keys > 0);
141 move_from_left (btree, pkp2, pkp, move_keys);
143 leftkey_up(btree, i); /* Propagate new leftmost key */
145 break; /* From main loop */
150 * Current page and left brotehr will be merged.
151 * Move all entries from current into left, and free
155 /* Move all entries from current into left. */
156 move_from_right(btree, pkp2, pkp, nkeys);
158 /* Free current page. */
159 _isindfreel_free(btree->fcb, btree->bufhdr[i]->isb_blkno);
161 btree->bufhdr[i] = kp2bhdr; /* Update search path */
163 /* The loop must iterate to delete entry in parent block. */
169 * Block pkp is the leftmost descendent of its parent.
170 * Either merge with right brother, or move a few entries
174 pkp3 = btree->bufhdr[i-1]->isb_buffer;
176 blkno2 = ldblkno(pkp3 + ISPAGESIZE -
177 (btree->curpos[i-1] + 2) * BLKNOSIZE);
179 /* Fix the right brother. */
180 kp2bhdr = _isdisk_fix(btree->fcb, btree->fcb->indfd,
182 pkp2 = kp2bhdr->isb_buffer;
184 if (ldshort(pkp2 + BT_NKEYS_OFF) + nkeys > capac) {
187 * Current page cannot be merged with the right brother.
188 * Move some entries from the right brother into current.
191 move_keys = (ldshort(pkp2 + BT_NKEYS_OFF) - halfcapac +1)/2;
192 assert(move_keys > 0);
194 move_from_right(btree, pkp, pkp2, move_keys);
196 /* Update search path to go through right brother. */
197 btree->curpos[i-1]++;
198 btree->bufhdr[i] = kp2bhdr;
199 btree->curpos[i] = 0;
200 leftkey_up(btree, i); /* Propagate new leftmost key */
202 break; /* From main loop */
207 * Current page and right brother will be merged.
208 * Move all entries from right into current, and free
212 /* Move all entries from right brother into current. */
213 move_from_right(btree, pkp, pkp2, ldshort(pkp2 + BT_NKEYS_OFF));
215 /* Free right brother page. */
216 _isindfreel_free(btree->fcb, kp2bhdr->isb_blkno);
218 /* Update search path to point at right brother*/
219 btree->curpos[i-1]++;
220 /* The loop must iterate to delete entry in parent block. */
225 } /* end of main loop */
227 if (btree->depth > 1) {
228 pkp = btree->bufhdr[0]->isb_buffer;
230 if (ldshort(pkp + BT_NKEYS_OFF) < 2) {
233 * Root now has only 1 entry and it is not the sole block.
234 * Replace root with its only descendant, don't change
237 pkp2 = btree->bufhdr[1]->isb_buffer;
238 memcpy( pkp,pkp2, ISPAGESIZE);
241 _isindfreel_free(btree->fcb, btree->bufhdr[1]->isb_blkno);
247 /*--------- remove supporting local functions -------------------------------*/
250 remove_entry(Btree *btree, char *pkp, int pos)
252 int keylength = btree->keydesc2->k2_len;
253 int nkeys = ldshort(pkp + BT_NKEYS_OFF);
254 int level = ldshort(pkp + BT_LEVEL_OFF);
257 assert(pos >= 0 && pos < nkeys);
259 /* Shift nkeys - pos - 1 entries to the left. */
260 memcpy(pkp + BT_KEYS_OFF + pos * keylength,
261 pkp + BT_KEYS_OFF + (pos + 1) * keylength,
262 (nkeys - pos - 1) * keylength);
264 /* For non-leaf nodes, remove block number from table of down pointers. */
267 memmove(pkp + ISPAGESIZE - (nkeys - 1) * BLKNOSIZE,
268 pkp + ISPAGESIZE - nkeys * BLKNOSIZE,
269 (nkeys - pos - 1) * BLKNOSIZE);
272 stshort((short) (nkeys - 1), pkp + BT_NKEYS_OFF);
276 move_from_right(Btree *btree, char *l, char *r, int move_keys)
278 int keylength = btree->keydesc2->k2_len;
279 int lnkeys = ldshort(l + BT_NKEYS_OFF);
280 int rnkeys = ldshort(r + BT_NKEYS_OFF);
281 int level = ldshort(r + BT_LEVEL_OFF);
283 /* Move move_keys from l into r block. */
284 memcpy( l + BT_KEYS_OFF + lnkeys * keylength,r + BT_KEYS_OFF,
285 move_keys * keylength);
287 /* Move remaining entries in r to the left side. */
288 memcpy( r + BT_KEYS_OFF,r + BT_KEYS_OFF + move_keys * keylength,
289 (rnkeys - move_keys) * keylength);
291 /* If non-leaf, move the pointers stored at the end of block. */
293 memcpy(l + ISPAGESIZE - (lnkeys + move_keys) * BLKNOSIZE,
294 r + ISPAGESIZE - move_keys * BLKNOSIZE,
295 move_keys * BLKNOSIZE);
297 memmove(r + ISPAGESIZE - (rnkeys - move_keys) * BLKNOSIZE,
298 r + ISPAGESIZE - rnkeys * BLKNOSIZE,
299 (rnkeys - move_keys) * BLKNOSIZE);
305 stshort((short) lnkeys, l + BT_NKEYS_OFF);
306 stshort((short) rnkeys, r + BT_NKEYS_OFF);
310 move_from_left(Btree *btree, char *l, char *r, int move_keys)
312 int keylength = btree->keydesc2->k2_len;
313 int lnkeys = ldshort(l + BT_NKEYS_OFF);
314 int rnkeys = ldshort(r + BT_NKEYS_OFF);
315 int level = ldshort(r + BT_LEVEL_OFF);
317 /* Move entries in r to the right side to create space for move_keys. */
318 memmove( r + BT_KEYS_OFF + move_keys * keylength,r + BT_KEYS_OFF,
321 /* Move move_keys from l into r block. */
322 memcpy( r + BT_KEYS_OFF,l + BT_KEYS_OFF + (lnkeys - move_keys) * keylength,
323 move_keys * keylength);
325 /* If non-leaf, move the pointers stored at the end of block. */
328 memcpy(r + ISPAGESIZE - (rnkeys + move_keys) * BLKNOSIZE,
329 r + ISPAGESIZE - rnkeys * BLKNOSIZE,
332 memcpy(r + ISPAGESIZE - move_keys * BLKNOSIZE,
333 l + ISPAGESIZE - lnkeys * BLKNOSIZE,
334 move_keys * BLKNOSIZE);
340 stshort((short) lnkeys, l + BT_NKEYS_OFF);
341 stshort((short) rnkeys, r + BT_NKEYS_OFF);