isbtree3.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. /*
  2. * CDE - Common Desktop Environment
  3. *
  4. * Copyright (c) 1993-2012, The Open Group. All rights reserved.
  5. *
  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)
  10. * any later version.
  11. *
  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
  16. * details.
  17. *
  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
  22. */
  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 $ */
  28. /*
  29. * Copyright (c) 1988 by Sun Microsystems, Inc.
  30. */
  31. /*
  32. * isbtree3.c
  33. *
  34. * Description:
  35. * B-tree operations: REMOVE
  36. *
  37. */
  38. #include "isam_impl.h"
  39. extern int _iskeycmp();
  40. static void remove_entry(Btree *, char *, int);
  41. static void move_from_right(Btree *, char *, char *, int);
  42. static void move_from_left(Btree *, char *, char *, int);
  43. /* _isbtree_remove() - Remove entry from B-tree ----------------------------*/
  44. void
  45. _isbtree_remove(Btree *btree)
  46. {
  47. struct keydesc2 *pkeydesc2 = btree->keydesc2;
  48. int nkeys; /* Number of keys in the page */
  49. int i;
  50. char *pkp, *pkp2, *pkp3;
  51. struct bufhdr *kp2bhdr;
  52. Blkno blkno2;
  53. int level;
  54. int capac, halfcapac;
  55. int move_keys;
  56. /* Set comparison function. */
  57. _iskeycmp_set(pkeydesc2, pkeydesc2->k2_nparts+1); /* +1 for recno field */
  58. /*
  59. * Main loop.
  60. * Remove he current entry from leaf.
  61. * If it was leftmost entry, propagate new leftmost to upper levels.
  62. * If block has less than capac/2 + 1 entries, either move some entries
  63. * from next left or right block, or merge with next left or right
  64. * block.
  65. */
  66. for (i = btree->depth - 1; i >= 0; i--) {
  67. /* Re-fix the current page for update. */
  68. btree->bufhdr[i] = _isdisk_refix(btree->bufhdr[i], ISFIXWRITE);
  69. pkp = btree->bufhdr[i]->isb_buffer;
  70. level = ldshort(pkp + BT_LEVEL_OFF);
  71. capac = ldshort(pkp + BT_CAPAC_OFF); /* Block capacity */
  72. nkeys = ldshort(pkp + BT_NKEYS_OFF); /* Number of keys in block */
  73. halfcapac = (capac+1) >> 1; /* same as capac/2 + 1 */
  74. assert(level + i + 1 == btree->depth);
  75. assert(i == 0 || nkeys >= halfcapac);
  76. /* Remove entry from this page. */
  77. remove_entry(btree, pkp, btree->curpos[i]);
  78. nkeys--;
  79. /* Must propagate new leftmost key up. */
  80. if (btree->curpos[i] == 0 && i > 0)
  81. leftkey_up(btree, i);
  82. if (nkeys >= halfcapac || i == 0) {
  83. /*
  84. * Page is still balanced. No changes are necessary.
  85. * Also, no chages are needed if this is root block.
  86. */
  87. break;
  88. }
  89. else {
  90. if(btree->curpos[i-1] > 0) {
  91. /*
  92. * Block pkp is not the leftmost descendent of its parent.
  93. * Either merge with left brother, or move a few entries
  94. * from it.
  95. */
  96. pkp3 = btree->bufhdr[i-1]->isb_buffer;
  97. blkno2 = ldblkno(pkp3 + ISPAGESIZE -
  98. (btree->curpos[i-1]) * BLKNOSIZE);
  99. /* Fix the left brother. */
  100. kp2bhdr = _isdisk_fix(btree->fcb, btree->fcb->indfd,
  101. blkno2, ISFIXWRITE);
  102. pkp2 = kp2bhdr->isb_buffer;
  103. if (ldshort(pkp2 + BT_NKEYS_OFF) + nkeys > capac) {
  104. /*
  105. * Current page cannot be merged with the left brother.
  106. * Move some entries from the left brother into current.
  107. */
  108. move_keys = (ldshort(pkp2 + BT_NKEYS_OFF) - halfcapac +1)/2;
  109. assert(move_keys > 0);
  110. move_from_left (btree, pkp2, pkp, move_keys);
  111. leftkey_up(btree, i); /* Propagate new leftmost key */
  112. break; /* From main loop */
  113. }
  114. else {
  115. /*
  116. * Current page and left brotehr will be merged.
  117. * Move all entries from current into left, and free
  118. * current.
  119. */
  120. /* Move all entries from current into left. */
  121. move_from_right(btree, pkp2, pkp, nkeys);
  122. /* Free current page. */
  123. _isindfreel_free(btree->fcb, btree->bufhdr[i]->isb_blkno);
  124. btree->bufhdr[i] = kp2bhdr; /* Update search path */
  125. /* The loop must iterate to delete entry in parent block. */
  126. }
  127. }
  128. else {
  129. /*
  130. * Block pkp is the leftmost descendent of its parent.
  131. * Either merge with right brother, or move a few entries
  132. * from it.
  133. */
  134. pkp3 = btree->bufhdr[i-1]->isb_buffer;
  135. blkno2 = ldblkno(pkp3 + ISPAGESIZE -
  136. (btree->curpos[i-1] + 2) * BLKNOSIZE);
  137. /* Fix the right brother. */
  138. kp2bhdr = _isdisk_fix(btree->fcb, btree->fcb->indfd,
  139. blkno2, ISFIXWRITE);
  140. pkp2 = kp2bhdr->isb_buffer;
  141. if (ldshort(pkp2 + BT_NKEYS_OFF) + nkeys > capac) {
  142. /*
  143. * Current page cannot be merged with the right brother.
  144. * Move some entries from the right brother into current.
  145. */
  146. move_keys = (ldshort(pkp2 + BT_NKEYS_OFF) - halfcapac +1)/2;
  147. assert(move_keys > 0);
  148. move_from_right(btree, pkp, pkp2, move_keys);
  149. /* Update search path to go through right brother. */
  150. btree->curpos[i-1]++;
  151. btree->bufhdr[i] = kp2bhdr;
  152. btree->curpos[i] = 0;
  153. leftkey_up(btree, i); /* Propagate new leftmost key */
  154. break; /* From main loop */
  155. }
  156. else {
  157. /*
  158. * Current page and right brother will be merged.
  159. * Move all entries from right into current, and free
  160. * right.
  161. */
  162. /* Move all entries from right brother into current. */
  163. move_from_right(btree, pkp, pkp2, ldshort(pkp2 + BT_NKEYS_OFF));
  164. /* Free right brother page. */
  165. _isindfreel_free(btree->fcb, kp2bhdr->isb_blkno);
  166. /* Update search path to point at right brother*/
  167. btree->curpos[i-1]++;
  168. /* The loop must iterate to delete entry in parent block. */
  169. }
  170. }
  171. }
  172. } /* end of main loop */
  173. if (btree->depth > 1) {
  174. pkp = btree->bufhdr[0]->isb_buffer;
  175. if (ldshort(pkp + BT_NKEYS_OFF) < 2) {
  176. /*
  177. * Root now has only 1 entry and it is not the sole block.
  178. * Replace root with its only descendant, don't change
  179. * root blkno.
  180. */
  181. pkp2 = btree->bufhdr[1]->isb_buffer;
  182. memcpy( pkp,pkp2, ISPAGESIZE);
  183. /* Free page. */
  184. _isindfreel_free(btree->fcb, btree->bufhdr[1]->isb_blkno);
  185. }
  186. }
  187. }
  188. /*--------- remove supporting local functions -------------------------------*/
  189. static void
  190. remove_entry(Btree *btree, char *pkp, int pos)
  191. {
  192. int keylength = btree->keydesc2->k2_len;
  193. int nkeys = ldshort(pkp + BT_NKEYS_OFF);
  194. int level = ldshort(pkp + BT_LEVEL_OFF);
  195. assert(nkeys > 0);
  196. assert(pos >= 0 && pos < nkeys);
  197. /* Shift nkeys - pos - 1 entries to the left. */
  198. memcpy(pkp + BT_KEYS_OFF + pos * keylength,
  199. pkp + BT_KEYS_OFF + (pos + 1) * keylength,
  200. (nkeys - pos - 1) * keylength);
  201. /* For non-leaf nodes, remove block number from table of down pointers. */
  202. if (level > 0) {
  203. memmove(pkp + ISPAGESIZE - (nkeys - 1) * BLKNOSIZE,
  204. pkp + ISPAGESIZE - nkeys * BLKNOSIZE,
  205. (nkeys - pos - 1) * BLKNOSIZE);
  206. }
  207. stshort((short) (nkeys - 1), pkp + BT_NKEYS_OFF);
  208. }
  209. static void
  210. move_from_right(Btree *btree, char *l, char *r, int move_keys)
  211. {
  212. int keylength = btree->keydesc2->k2_len;
  213. int lnkeys = ldshort(l + BT_NKEYS_OFF);
  214. int rnkeys = ldshort(r + BT_NKEYS_OFF);
  215. int level = ldshort(r + BT_LEVEL_OFF);
  216. /* Move move_keys from l into r block. */
  217. memcpy( l + BT_KEYS_OFF + lnkeys * keylength,r + BT_KEYS_OFF,
  218. move_keys * keylength);
  219. /* Move remaining entries in r to the left side. */
  220. memcpy( r + BT_KEYS_OFF,r + BT_KEYS_OFF + move_keys * keylength,
  221. (rnkeys - move_keys) * keylength);
  222. /* If non-leaf, move the pointers stored at the end of block. */
  223. if (level > 0) {
  224. memcpy(l + ISPAGESIZE - (lnkeys + move_keys) * BLKNOSIZE,
  225. r + ISPAGESIZE - move_keys * BLKNOSIZE,
  226. move_keys * BLKNOSIZE);
  227. memmove(r + ISPAGESIZE - (rnkeys - move_keys) * BLKNOSIZE,
  228. r + ISPAGESIZE - rnkeys * BLKNOSIZE,
  229. (rnkeys - move_keys) * BLKNOSIZE);
  230. }
  231. lnkeys += move_keys;
  232. rnkeys -= move_keys;
  233. stshort((short) lnkeys, l + BT_NKEYS_OFF);
  234. stshort((short) rnkeys, r + BT_NKEYS_OFF);
  235. }
  236. static void
  237. move_from_left(Btree *btree, char *l, char *r, int move_keys)
  238. {
  239. int keylength = btree->keydesc2->k2_len;
  240. int lnkeys = ldshort(l + BT_NKEYS_OFF);
  241. int rnkeys = ldshort(r + BT_NKEYS_OFF);
  242. int level = ldshort(r + BT_LEVEL_OFF);
  243. /* Move entries in r to the right side to create space for move_keys. */
  244. memmove( r + BT_KEYS_OFF + move_keys * keylength,r + BT_KEYS_OFF,
  245. rnkeys * keylength);
  246. /* Move move_keys from l into r block. */
  247. memcpy( r + BT_KEYS_OFF,l + BT_KEYS_OFF + (lnkeys - move_keys) * keylength,
  248. move_keys * keylength);
  249. /* If non-leaf, move the pointers stored at the end of block. */
  250. if (level > 0) {
  251. memcpy(r + ISPAGESIZE - (rnkeys + move_keys) * BLKNOSIZE,
  252. r + ISPAGESIZE - rnkeys * BLKNOSIZE,
  253. rnkeys * BLKNOSIZE);
  254. memcpy(r + ISPAGESIZE - move_keys * BLKNOSIZE,
  255. l + ISPAGESIZE - lnkeys * BLKNOSIZE,
  256. move_keys * BLKNOSIZE);
  257. }
  258. lnkeys -= move_keys;
  259. rnkeys += move_keys;
  260. stshort((short) lnkeys, l + BT_NKEYS_OFF);
  261. stshort((short) rnkeys, r + BT_NKEYS_OFF);
  262. }