Changes in kernel/generic/src/adt/btree.c [7a0359b:98000fb] in mainline
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
kernel/generic/src/adt/btree.c
r7a0359b r98000fb 33 33 /** 34 34 * @file 35 * @brief 35 * @brief B+tree implementation. 36 36 * 37 37 * This file implements B+tree type and operations. … … 53 53 #include <panic.h> 54 54 #include <print.h> 55 #include <trace.h> 55 56 static void btree_destroy_subtree(btree_node_t *root); 57 static void _btree_insert(btree_t *t, btree_key_t key, void *value, btree_node_t *rsubtree, btree_node_t *node); 58 static void _btree_remove(btree_t *t, btree_key_t key, btree_node_t *node); 59 static void node_initialize(btree_node_t *node); 60 static void node_insert_key_and_lsubtree(btree_node_t *node, btree_key_t key, void *value, btree_node_t *lsubtree); 61 static void node_insert_key_and_rsubtree(btree_node_t *node, btree_key_t key, void *value, btree_node_t *rsubtree); 62 static void node_remove_key_and_lsubtree(btree_node_t *node, btree_key_t key); 63 static void node_remove_key_and_rsubtree(btree_node_t *node, btree_key_t key); 64 static btree_node_t *node_split(btree_node_t *node, btree_key_t key, void *value, btree_node_t *rsubtree, btree_key_t *median); 65 static btree_node_t *node_combine(btree_node_t *node); 66 static size_t find_key_by_subtree(btree_node_t *node, btree_node_t *subtree, bool right); 67 static void rotate_from_right(btree_node_t *lnode, btree_node_t *rnode, size_t idx); 68 static void rotate_from_left(btree_node_t *lnode, btree_node_t *rnode, size_t idx); 69 static bool try_insert_by_rotation_to_left(btree_node_t *node, btree_key_t key, void *value, btree_node_t *rsubtree); 70 static bool try_insert_by_rotation_to_right(btree_node_t *node, btree_key_t key, void *value, btree_node_t *rsubtree); 71 static bool try_rotation_from_left(btree_node_t *rnode); 72 static bool try_rotation_from_right(btree_node_t *lnode); 73 74 #define ROOT_NODE(n) (!(n)->parent) 75 #define INDEX_NODE(n) ((n)->subtree[0] != NULL) 76 #define LEAF_NODE(n) ((n)->subtree[0] == NULL) 77 78 #define FILL_FACTOR ((BTREE_M-1)/2) 79 80 #define MEDIAN_LOW_INDEX(n) (((n)->keys-1)/2) 81 #define MEDIAN_HIGH_INDEX(n) ((n)->keys/2) 82 #define MEDIAN_LOW(n) ((n)->key[MEDIAN_LOW_INDEX((n))]); 83 #define MEDIAN_HIGH(n) ((n)->key[MEDIAN_HIGH_INDEX((n))]); 56 84 57 85 static slab_cache_t *btree_node_slab; 58 59 #define ROOT_NODE(n) (!(n)->parent)60 #define INDEX_NODE(n) ((n)->subtree[0] != NULL)61 #define LEAF_NODE(n) ((n)->subtree[0] == NULL)62 63 #define FILL_FACTOR ((BTREE_M - 1) / 2)64 65 #define MEDIAN_LOW_INDEX(n) (((n)->keys-1) / 2)66 #define MEDIAN_HIGH_INDEX(n) ((n)->keys / 2)67 #define MEDIAN_LOW(n) ((n)->key[MEDIAN_LOW_INDEX((n))]);68 #define MEDIAN_HIGH(n) ((n)->key[MEDIAN_HIGH_INDEX((n))]);69 86 70 87 /** Initialize B-trees. */ 71 88 void btree_init(void) 72 89 { 73 btree_node_slab = slab_cache_create("btree_node_slab", 74 sizeof(btree_node_t), 0, NULL, NULL, SLAB_CACHE_MAGDEFERRED); 75 } 76 77 /** Initialize B-tree node. 78 * 79 * @param node B-tree node. 80 * 81 */ 82 NO_TRACE static void node_initialize(btree_node_t *node) 83 { 84 unsigned int i; 85 86 node->keys = 0; 87 88 /* Clean also space for the extra key. */ 89 for (i = 0; i < BTREE_MAX_KEYS + 1; i++) { 90 node->key[i] = 0; 91 node->value[i] = NULL; 92 node->subtree[i] = NULL; 93 } 94 95 node->subtree[i] = NULL; 96 node->parent = NULL; 97 98 link_initialize(&node->leaf_link); 99 link_initialize(&node->bfs_link); 100 node->depth = 0; 90 btree_node_slab = slab_cache_create("btree_node_slab", sizeof(btree_node_t), 0, NULL, NULL, SLAB_CACHE_MAGDEFERRED); 101 91 } 102 92 … … 104 94 * 105 95 * @param t B-tree. 106 *107 96 */ 108 97 void btree_create(btree_t *t) … … 114 103 } 115 104 105 /** Destroy empty B-tree. */ 106 void btree_destroy(btree_t *t) 107 { 108 btree_destroy_subtree(t->root); 109 } 110 111 /** Insert key-value pair into B-tree. 112 * 113 * @param t B-tree. 114 * @param key Key to be inserted. 115 * @param value Value to be inserted. 116 * @param leaf_node Leaf node where the insertion should begin. 117 */ 118 void btree_insert(btree_t *t, btree_key_t key, void *value, btree_node_t *leaf_node) 119 { 120 btree_node_t *lnode; 121 122 ASSERT(value); 123 124 lnode = leaf_node; 125 if (!lnode) { 126 if (btree_search(t, key, &lnode)) { 127 panic("B-tree %p already contains key %" PRIu64 ".", t, key); 128 } 129 } 130 131 _btree_insert(t, key, value, NULL, lnode); 132 } 133 116 134 /** Destroy subtree rooted in a node. 117 135 * 118 136 * @param root Root of the subtree. 119 * 120 */ 121 NO_TRACE static void btree_destroy_subtree(btree_node_t *root) 137 */ 138 void btree_destroy_subtree(btree_node_t *root) 122 139 { 123 140 size_t i; 124 141 125 142 if (root->keys) { 126 143 for (i = 0; i < root->keys + 1; i++) { … … 129 146 } 130 147 } 131 132 148 slab_free(btree_node_slab, root); 133 149 } 134 150 135 /** Destroy empty B-tree. */136 void btree_destroy(btree_t *t)137 {138 btree_destroy_subtree(t->root);139 }140 141 /** Insert key-value-rsubtree triplet into B-tree node.142 *143 * It is actually possible to have more keys than BTREE_MAX_KEYS.144 * This feature is used during splitting the node when the145 * number of keys is BTREE_MAX_KEYS + 1. Insert by left rotation146 * also makes use of this feature.147 *148 * @param node B-tree node into wich the new key is to be inserted.149 * @param key The key to be inserted.150 * @param value Pointer to value to be inserted.151 * @param rsubtree Pointer to the right subtree.152 *153 */154 NO_TRACE static void node_insert_key_and_rsubtree(btree_node_t *node,155 btree_key_t key, void *value, btree_node_t *rsubtree)156 {157 size_t i;158 159 for (i = 0; i < node->keys; i++) {160 if (key < node->key[i]) {161 size_t j;162 163 for (j = node->keys; j > i; j--) {164 node->key[j] = node->key[j - 1];165 node->value[j] = node->value[j - 1];166 node->subtree[j + 1] = node->subtree[j];167 }168 169 break;170 }171 }172 173 node->key[i] = key;174 node->value[i] = value;175 node->subtree[i + 1] = rsubtree;176 node->keys++;177 }178 179 /** Find key by its left or right subtree.180 *181 * @param node B-tree node.182 * @param subtree Left or right subtree of a key found in node.183 * @param right If true, subtree is a right subtree. If false,184 * subtree is a left subtree.185 *186 * @return Index of the key associated with the subtree.187 *188 */189 NO_TRACE static size_t find_key_by_subtree(btree_node_t *node,190 btree_node_t *subtree, bool right)191 {192 size_t i;193 194 for (i = 0; i < node->keys + 1; i++) {195 if (subtree == node->subtree[i])196 return i - (int) (right != false);197 }198 199 panic("Node %p does not contain subtree %p.", node, subtree);200 }201 202 /** Remove key and its left subtree pointer from B-tree node.203 *204 * Remove the key and eliminate gaps in node->key array.205 * Note that the value pointer and the left subtree pointer206 * is removed from the node as well.207 *208 * @param node B-tree node.209 * @param key Key to be removed.210 *211 */212 NO_TRACE static void node_remove_key_and_lsubtree(btree_node_t *node,213 btree_key_t key)214 {215 size_t i;216 size_t j;217 218 for (i = 0; i < node->keys; i++) {219 if (key == node->key[i]) {220 for (j = i + 1; j < node->keys; j++) {221 node->key[j - 1] = node->key[j];222 node->value[j - 1] = node->value[j];223 node->subtree[j - 1] = node->subtree[j];224 }225 226 node->subtree[j - 1] = node->subtree[j];227 node->keys--;228 229 return;230 }231 }232 233 panic("Node %p does not contain key %" PRIu64 ".", node, key);234 }235 236 /** Remove key and its right subtree pointer from B-tree node.237 *238 * Remove the key and eliminate gaps in node->key array.239 * Note that the value pointer and the right subtree pointer240 * is removed from the node as well.241 *242 * @param node B-tree node.243 * @param key Key to be removed.244 *245 */246 NO_TRACE static void node_remove_key_and_rsubtree(btree_node_t *node,247 btree_key_t key)248 {249 size_t i, j;250 251 for (i = 0; i < node->keys; i++) {252 if (key == node->key[i]) {253 for (j = i + 1; j < node->keys; j++) {254 node->key[j - 1] = node->key[j];255 node->value[j - 1] = node->value[j];256 node->subtree[j] = node->subtree[j + 1];257 }258 259 node->keys--;260 return;261 }262 }263 264 panic("Node %p does not contain key %" PRIu64 ".", node, key);265 }266 267 /** Insert key-value-lsubtree triplet into B-tree node.268 *269 * It is actually possible to have more keys than BTREE_MAX_KEYS.270 * This feature is used during insert by right rotation.271 *272 * @param node B-tree node into wich the new key is to be inserted.273 * @param key The key to be inserted.274 * @param value Pointer to value to be inserted.275 * @param lsubtree Pointer to the left subtree.276 *277 */278 NO_TRACE static void node_insert_key_and_lsubtree(btree_node_t *node,279 btree_key_t key, void *value, btree_node_t *lsubtree)280 {281 size_t i;282 283 for (i = 0; i < node->keys; i++) {284 if (key < node->key[i]) {285 size_t j;286 287 for (j = node->keys; j > i; j--) {288 node->key[j] = node->key[j - 1];289 node->value[j] = node->value[j - 1];290 node->subtree[j + 1] = node->subtree[j];291 }292 293 node->subtree[j + 1] = node->subtree[j];294 break;295 }296 }297 298 node->key[i] = key;299 node->value[i] = value;300 node->subtree[i] = lsubtree;301 302 node->keys++;303 }304 305 /** Rotate one key-value-rsubtree triplet from the left sibling to the right sibling.306 *307 * The biggest key and its value and right subtree is rotated308 * from the left node to the right. If the node is an index node,309 * than the parent node key belonging to the left node takes part310 * in the rotation.311 *312 * @param lnode Left sibling.313 * @param rnode Right sibling.314 * @param idx Index of the parent node key that is taking part315 * in the rotation.316 *317 */318 NO_TRACE static void rotate_from_left(btree_node_t *lnode, btree_node_t *rnode,319 size_t idx)320 {321 btree_key_t key = lnode->key[lnode->keys - 1];322 323 if (LEAF_NODE(lnode)) {324 void *value = lnode->value[lnode->keys - 1];325 326 node_remove_key_and_rsubtree(lnode, key);327 node_insert_key_and_lsubtree(rnode, key, value, NULL);328 lnode->parent->key[idx] = key;329 } else {330 btree_node_t *rsubtree = lnode->subtree[lnode->keys];331 332 node_remove_key_and_rsubtree(lnode, key);333 node_insert_key_and_lsubtree(rnode, lnode->parent->key[idx], NULL, rsubtree);334 lnode->parent->key[idx] = key;335 336 /* Fix parent link of the reconnected right subtree. */337 rsubtree->parent = rnode;338 }339 }340 341 /** Rotate one key-value-lsubtree triplet from the right sibling to the left sibling.342 *343 * The smallest key and its value and left subtree is rotated344 * from the right node to the left. If the node is an index node,345 * than the parent node key belonging to the right node takes part346 * in the rotation.347 *348 * @param lnode Left sibling.349 * @param rnode Right sibling.350 * @param idx Index of the parent node key that is taking part351 * in the rotation.352 *353 */354 NO_TRACE static void rotate_from_right(btree_node_t *lnode, btree_node_t *rnode,355 size_t idx)356 {357 btree_key_t key = rnode->key[0];358 359 if (LEAF_NODE(rnode)) {360 void *value = rnode->value[0];361 362 node_remove_key_and_lsubtree(rnode, key);363 node_insert_key_and_rsubtree(lnode, key, value, NULL);364 rnode->parent->key[idx] = rnode->key[0];365 } else {366 btree_node_t *lsubtree = rnode->subtree[0];367 368 node_remove_key_and_lsubtree(rnode, key);369 node_insert_key_and_rsubtree(lnode, rnode->parent->key[idx], NULL, lsubtree);370 rnode->parent->key[idx] = key;371 372 /* Fix parent link of the reconnected left subtree. */373 lsubtree->parent = lnode;374 }375 }376 377 /** Insert key-value-rsubtree triplet and rotate the node to the left, if this operation can be done.378 *379 * Left sibling of the node (if it exists) is checked for free space.380 * If there is free space, the key is inserted and the smallest key of381 * the node is moved there. The index node which is the parent of both382 * nodes is fixed.383 *384 * @param node B-tree node.385 * @param inskey Key to be inserted.386 * @param insvalue Value to be inserted.387 * @param rsubtree Right subtree of inskey.388 *389 * @return True if the rotation was performed, false otherwise.390 *391 */392 NO_TRACE static bool try_insert_by_rotation_to_left(btree_node_t *node,393 btree_key_t inskey, void *insvalue, btree_node_t *rsubtree)394 {395 size_t idx;396 btree_node_t *lnode;397 398 /*399 * If this is root node, the rotation can not be done.400 */401 if (ROOT_NODE(node))402 return false;403 404 idx = find_key_by_subtree(node->parent, node, true);405 if ((int) idx == -1) {406 /*407 * If this node is the leftmost subtree of its parent,408 * the rotation can not be done.409 */410 return false;411 }412 413 lnode = node->parent->subtree[idx];414 if (lnode->keys < BTREE_MAX_KEYS) {415 /*416 * The rotaion can be done. The left sibling has free space.417 */418 node_insert_key_and_rsubtree(node, inskey, insvalue, rsubtree);419 rotate_from_right(lnode, node, idx);420 return true;421 }422 423 return false;424 }425 426 /** Insert key-value-rsubtree triplet and rotate the node to the right, if this operation can be done.427 *428 * Right sibling of the node (if it exists) is checked for free space.429 * If there is free space, the key is inserted and the biggest key of430 * the node is moved there. The index node which is the parent of both431 * nodes is fixed.432 *433 * @param node B-tree node.434 * @param inskey Key to be inserted.435 * @param insvalue Value to be inserted.436 * @param rsubtree Right subtree of inskey.437 *438 * @return True if the rotation was performed, false otherwise.439 *440 */441 NO_TRACE static bool try_insert_by_rotation_to_right(btree_node_t *node,442 btree_key_t inskey, void *insvalue, btree_node_t *rsubtree)443 {444 size_t idx;445 btree_node_t *rnode;446 447 /*448 * If this is root node, the rotation can not be done.449 */450 if (ROOT_NODE(node))451 return false;452 453 idx = find_key_by_subtree(node->parent, node, false);454 if (idx == node->parent->keys) {455 /*456 * If this node is the rightmost subtree of its parent,457 * the rotation can not be done.458 */459 return false;460 }461 462 rnode = node->parent->subtree[idx + 1];463 if (rnode->keys < BTREE_MAX_KEYS) {464 /*465 * The rotaion can be done. The right sibling has free space.466 */467 node_insert_key_and_rsubtree(node, inskey, insvalue, rsubtree);468 rotate_from_left(node, rnode, idx);469 return true;470 }471 472 return false;473 }474 475 /** Split full B-tree node and insert new key-value-right-subtree triplet.476 *477 * This function will split a node and return a pointer to a newly created478 * node containing keys greater than or equal to the greater of medians479 * (or median) of the old keys and the newly added key. It will also write480 * the median key to a memory address supplied by the caller.481 *482 * If the node being split is an index node, the median will not be483 * included in the new node. If the node is a leaf node,484 * the median will be copied there.485 *486 * @param node B-tree node wich is going to be split.487 * @param key The key to be inserted.488 * @param value Pointer to the value to be inserted.489 * @param rsubtree Pointer to the right subtree of the key being added.490 * @param median Address in memory, where the median key will be stored.491 *492 * @return Newly created right sibling of node.493 *494 */495 NO_TRACE static btree_node_t *node_split(btree_node_t *node, btree_key_t key,496 void *value, btree_node_t *rsubtree, btree_key_t *median)497 {498 btree_node_t *rnode;499 size_t i;500 size_t j;501 502 ASSERT(median);503 ASSERT(node->keys == BTREE_MAX_KEYS);504 505 /*506 * Use the extra space to store the extra node.507 */508 node_insert_key_and_rsubtree(node, key, value, rsubtree);509 510 /*511 * Compute median of keys.512 */513 *median = MEDIAN_HIGH(node);514 515 /*516 * Allocate and initialize new right sibling.517 */518 rnode = (btree_node_t *) slab_alloc(btree_node_slab, 0);519 node_initialize(rnode);520 rnode->parent = node->parent;521 rnode->depth = node->depth;522 523 /*524 * Copy big keys, values and subtree pointers to the new right sibling.525 * If this is an index node, do not copy the median.526 */527 i = (size_t) INDEX_NODE(node);528 for (i += MEDIAN_HIGH_INDEX(node), j = 0; i < node->keys; i++, j++) {529 rnode->key[j] = node->key[i];530 rnode->value[j] = node->value[i];531 rnode->subtree[j] = node->subtree[i];532 533 /*534 * Fix parent links in subtrees.535 */536 if (rnode->subtree[j])537 rnode->subtree[j]->parent = rnode;538 }539 540 rnode->subtree[j] = node->subtree[i];541 if (rnode->subtree[j])542 rnode->subtree[j]->parent = rnode;543 544 rnode->keys = j; /* Set number of keys of the new node. */545 node->keys /= 2; /* Shrink the old node. */546 547 return rnode;548 }549 550 151 /** Recursively insert into B-tree. 551 152 * 552 * @param t 553 * @param key 554 * @param value 153 * @param t B-tree. 154 * @param key Key to be inserted. 155 * @param value Value to be inserted. 555 156 * @param rsubtree Right subtree of the inserted key. 556 * @param node Start inserting into this node. 557 * 558 */ 559 NO_TRACE static void _btree_insert(btree_t *t, btree_key_t key, void *value, 560 btree_node_t *rsubtree, btree_node_t *node) 157 * @param node Start inserting into this node. 158 */ 159 void _btree_insert(btree_t *t, btree_key_t key, void *value, btree_node_t *rsubtree, btree_node_t *node) 561 160 { 562 161 if (node->keys < BTREE_MAX_KEYS) { … … 584 183 * bigger keys (i.e. the new node) into its parent. 585 184 */ 586 185 587 186 rnode = node_split(node, key, value, rsubtree, &median); 588 187 589 188 if (LEAF_NODE(node)) { 590 189 list_prepend(&rnode->leaf_link, &node->leaf_link); … … 603 202 * Left-hand side subtree will be the old root (i.e. node). 604 203 * Right-hand side subtree will be rnode. 605 */ 204 */ 606 205 t->root->subtree[0] = node; 607 206 608 207 t->root->depth = node->depth + 1; 609 208 } 610 209 _btree_insert(t, median, NULL, rnode, node->parent); 611 } 612 } 613 614 /** Insert key-value pair into B-tree. 615 * 616 * @param t B-tree. 617 * @param key Key to be inserted. 618 * @param value Value to be inserted. 619 * @param leaf_node Leaf node where the insertion should begin. 620 * 621 */ 622 void btree_insert(btree_t *t, btree_key_t key, void *value, 623 btree_node_t *leaf_node) 210 } 211 212 } 213 214 /** Remove B-tree node. 215 * 216 * @param t B-tree. 217 * @param key Key to be removed from the B-tree along with its associated value. 218 * @param leaf_node If not NULL, pointer to the leaf node where the key is found. 219 */ 220 void btree_remove(btree_t *t, btree_key_t key, btree_node_t *leaf_node) 624 221 { 625 222 btree_node_t *lnode; 626 627 ASSERT(value);628 223 629 224 lnode = leaf_node; 630 225 if (!lnode) { 631 if (btree_search(t, key, &lnode)) 632 panic("B-tree %p already contains key %" PRIu64 ".", t, key); 633 } 634 635 _btree_insert(t, key, value, NULL, lnode); 636 } 637 638 /** Rotate in a key from the left sibling or from the index node, if this operation can be done. 639 * 640 * @param rnode Node into which to add key from its left sibling 641 * or from the index node. 642 * 643 * @return True if the rotation was performed, false otherwise. 644 * 645 */ 646 NO_TRACE static bool try_rotation_from_left(btree_node_t *rnode) 647 { 648 size_t idx; 649 btree_node_t *lnode; 650 651 /* 652 * If this is root node, the rotation can not be done. 653 */ 654 if (ROOT_NODE(rnode)) 655 return false; 656 657 idx = find_key_by_subtree(rnode->parent, rnode, true); 658 if ((int) idx == -1) { 659 /* 660 * If this node is the leftmost subtree of its parent, 661 * the rotation can not be done. 662 */ 663 return false; 664 } 665 666 lnode = rnode->parent->subtree[idx]; 667 if (lnode->keys > FILL_FACTOR) { 668 rotate_from_left(lnode, rnode, idx); 669 return true; 670 } 671 672 return false; 673 } 674 675 /** Rotate in a key from the right sibling or from the index node, if this operation can be done. 676 * 677 * @param lnode Node into which to add key from its right sibling 678 * or from the index node. 679 * 680 * @return True if the rotation was performed, false otherwise. 681 * 682 */ 683 NO_TRACE static bool try_rotation_from_right(btree_node_t *lnode) 684 { 685 size_t idx; 686 btree_node_t *rnode; 687 688 /* 689 * If this is root node, the rotation can not be done. 690 */ 691 if (ROOT_NODE(lnode)) 692 return false; 693 694 idx = find_key_by_subtree(lnode->parent, lnode, false); 695 if (idx == lnode->parent->keys) { 696 /* 697 * If this node is the rightmost subtree of its parent, 698 * the rotation can not be done. 699 */ 700 return false; 701 } 702 703 rnode = lnode->parent->subtree[idx + 1]; 704 if (rnode->keys > FILL_FACTOR) { 705 rotate_from_right(lnode, rnode, idx); 706 return true; 707 } 708 709 return false; 710 } 711 712 /** Combine node with any of its siblings. 713 * 714 * The siblings are required to be below the fill factor. 715 * 716 * @param node Node to combine with one of its siblings. 717 * 718 * @return Pointer to the rightmost of the two nodes. 719 * 720 */ 721 NO_TRACE static btree_node_t *node_combine(btree_node_t *node) 722 { 723 size_t idx; 724 btree_node_t *rnode; 725 size_t i; 726 727 ASSERT(!ROOT_NODE(node)); 728 729 idx = find_key_by_subtree(node->parent, node, false); 730 if (idx == node->parent->keys) { 731 /* 732 * Rightmost subtree of its parent, combine with the left sibling. 733 */ 734 idx--; 735 rnode = node; 736 node = node->parent->subtree[idx]; 737 } else 738 rnode = node->parent->subtree[idx + 1]; 739 740 /* Index nodes need to insert parent node key in between left and right node. */ 741 if (INDEX_NODE(node)) 742 node->key[node->keys++] = node->parent->key[idx]; 743 744 /* Copy the key-value-subtree triplets from the right node. */ 745 for (i = 0; i < rnode->keys; i++) { 746 node->key[node->keys + i] = rnode->key[i]; 747 node->value[node->keys + i] = rnode->value[i]; 748 749 if (INDEX_NODE(node)) { 750 node->subtree[node->keys + i] = rnode->subtree[i]; 751 rnode->subtree[i]->parent = node; 752 } 753 } 754 755 if (INDEX_NODE(node)) { 756 node->subtree[node->keys + i] = rnode->subtree[i]; 757 rnode->subtree[i]->parent = node; 758 } 759 760 node->keys += rnode->keys; 761 return rnode; 226 if (!btree_search(t, key, &lnode)) { 227 panic("B-tree %p does not contain key %" PRIu64 ".", t, key); 228 } 229 } 230 231 _btree_remove(t, key, lnode); 762 232 } 763 233 764 234 /** Recursively remove B-tree node. 765 235 * 766 * @param t 767 * @param key 236 * @param t B-tree. 237 * @param key Key to be removed from the B-tree along with its associated value. 768 238 * @param node Node where the key being removed resides. 769 * 770 */ 771 NO_TRACE static void _btree_remove(btree_t *t, btree_key_t key, 772 btree_node_t *node) 239 */ 240 void _btree_remove(btree_t *t, btree_key_t key, btree_node_t *node) 773 241 { 774 242 if (ROOT_NODE(node)) { 775 if ( (node->keys == 1) && (node->subtree[0])) {243 if (node->keys == 1 && node->subtree[0]) { 776 244 /* 777 245 * Free the current root and set new root. … … 789 257 node_remove_key_and_rsubtree(node, key); 790 258 } 791 792 259 return; 793 260 } … … 804 271 if (node->keys > FILL_FACTOR) { 805 272 size_t i; 806 273 807 274 /* 808 275 * The key can be immediatelly removed. … … 813 280 */ 814 281 node_remove_key_and_rsubtree(node, key); 815 816 282 for (i = 0; i < node->parent->keys; i++) { 817 283 if (node->parent->key[i] == key) 818 284 node->parent->key[i] = node->key[0]; 819 285 } 286 820 287 } else { 821 288 size_t idx; 822 btree_node_t *rnode; 823 btree_node_t *parent; 824 289 btree_node_t *rnode, *parent; 290 825 291 /* 826 292 * The node is below the fill factor as well as its left and right sibling. … … 832 298 node_remove_key_and_rsubtree(node, key); 833 299 rnode = node_combine(node); 834 835 300 if (LEAF_NODE(rnode)) 836 301 list_remove(&rnode->leaf_link); 837 838 302 idx = find_key_by_subtree(parent, rnode, true); 839 303 ASSERT((int) idx != -1); … … 843 307 } 844 308 845 /** Remove B-tree node.846 *847 * @param t B-tree.848 * @param key Key to be removed from the B-tree along849 * with its associated value.850 * @param leaf_node If not NULL, pointer to the leaf node where851 * the key is found.852 *853 */854 void btree_remove(btree_t *t, btree_key_t key, btree_node_t *leaf_node)855 {856 btree_node_t *lnode;857 858 lnode = leaf_node;859 if (!lnode) {860 if (!btree_search(t, key, &lnode))861 panic("B-tree %p does not contain key %" PRIu64 ".", t, key);862 }863 864 _btree_remove(t, key, lnode);865 }866 867 309 /** Search key in a B-tree. 868 310 * 869 * @param t 870 * @param key 311 * @param t B-tree. 312 * @param key Key to be searched. 871 313 * @param leaf_node Address where to put pointer to visited leaf node. 872 314 * 873 315 * @return Pointer to value or NULL if there is no such key. 874 *875 316 */ 876 317 void *btree_search(btree_t *t, btree_key_t key, btree_node_t **leaf_node) … … 882 323 */ 883 324 for (cur = t->root; cur; cur = next) { 884 /* 885 * Last iteration will set this with proper 886 * leaf node address. 887 */ 325 326 /* Last iteration will set this with proper leaf node address. */ 888 327 *leaf_node = cur; 889 328 … … 898 337 void *val; 899 338 size_t i; 900 339 901 340 /* 902 341 * Now if the key is smaller than cur->key[i] … … 908 347 next = cur->subtree[i]; 909 348 val = cur->value[i - 1]; 910 349 911 350 if (LEAF_NODE(cur)) 912 351 return key == cur->key[i - 1] ? val : NULL; 913 352 914 353 goto descend; 915 } 354 } 916 355 } 917 356 918 357 /* 919 * Last possibility is that the key is 920 * in the rightmost subtree. 358 * Last possibility is that the key is in the rightmost subtree. 921 359 */ 922 360 next = cur->subtree[i]; 923 361 val = cur->value[i - 1]; 924 925 362 if (LEAF_NODE(cur)) 926 363 return key == cur->key[i - 1] ? val : NULL; 927 364 } 928 365 descend: 929 ;930 } 931 366 ; 367 } 368 932 369 /* 933 * The key was not found in the *leaf_node and 934 * is smaller than any of its keys. 370 * The key was not found in the *leaf_node and is smaller than any of its keys. 935 371 */ 936 372 return NULL; … … 939 375 /** Return pointer to B-tree leaf node's left neighbour. 940 376 * 941 * @param t 377 * @param t B-tree. 942 378 * @param node Node whose left neighbour will be returned. 943 379 * 944 * @return Left neighbour of the node or NULL if the node 945 * does not have the left neighbour. 946 * 380 * @return Left neighbour of the node or NULL if the node does not have the left neighbour. 947 381 */ 948 382 btree_node_t *btree_leaf_node_left_neighbour(btree_t *t, btree_node_t *node) 949 383 { 950 384 ASSERT(LEAF_NODE(node)); 951 952 385 if (node->leaf_link.prev != &t->leaf_head) 953 386 return list_get_instance(node->leaf_link.prev, btree_node_t, leaf_link); … … 958 391 /** Return pointer to B-tree leaf node's right neighbour. 959 392 * 960 * @param t 393 * @param t B-tree. 961 394 * @param node Node whose right neighbour will be returned. 962 395 * 963 * @return Right neighbour of the node or NULL if the node 964 * does not have the right neighbour. 965 * 396 * @return Right neighbour of the node or NULL if the node does not have the right neighbour. 966 397 */ 967 398 btree_node_t *btree_leaf_node_right_neighbour(btree_t *t, btree_node_t *node) 968 399 { 969 400 ASSERT(LEAF_NODE(node)); 970 971 401 if (node->leaf_link.next != &t->leaf_head) 972 402 return list_get_instance(node->leaf_link.next, btree_node_t, leaf_link); … … 975 405 } 976 406 407 /** Initialize B-tree node. 408 * 409 * @param node B-tree node. 410 */ 411 void node_initialize(btree_node_t *node) 412 { 413 int i; 414 415 node->keys = 0; 416 417 /* Clean also space for the extra key. */ 418 for (i = 0; i < BTREE_MAX_KEYS + 1; i++) { 419 node->key[i] = 0; 420 node->value[i] = NULL; 421 node->subtree[i] = NULL; 422 } 423 node->subtree[i] = NULL; 424 425 node->parent = NULL; 426 427 link_initialize(&node->leaf_link); 428 429 link_initialize(&node->bfs_link); 430 node->depth = 0; 431 } 432 433 /** Insert key-value-lsubtree triplet into B-tree node. 434 * 435 * It is actually possible to have more keys than BTREE_MAX_KEYS. 436 * This feature is used during insert by right rotation. 437 * 438 * @param node B-tree node into wich the new key is to be inserted. 439 * @param key The key to be inserted. 440 * @param value Pointer to value to be inserted. 441 * @param lsubtree Pointer to the left subtree. 442 */ 443 void node_insert_key_and_lsubtree(btree_node_t *node, btree_key_t key, void *value, btree_node_t *lsubtree) 444 { 445 size_t i; 446 447 for (i = 0; i < node->keys; i++) { 448 if (key < node->key[i]) { 449 size_t j; 450 451 for (j = node->keys; j > i; j--) { 452 node->key[j] = node->key[j - 1]; 453 node->value[j] = node->value[j - 1]; 454 node->subtree[j + 1] = node->subtree[j]; 455 } 456 node->subtree[j + 1] = node->subtree[j]; 457 break; 458 } 459 } 460 node->key[i] = key; 461 node->value[i] = value; 462 node->subtree[i] = lsubtree; 463 464 node->keys++; 465 } 466 467 /** Insert key-value-rsubtree triplet into B-tree node. 468 * 469 * It is actually possible to have more keys than BTREE_MAX_KEYS. 470 * This feature is used during splitting the node when the 471 * number of keys is BTREE_MAX_KEYS + 1. Insert by left rotation 472 * also makes use of this feature. 473 * 474 * @param node B-tree node into wich the new key is to be inserted. 475 * @param key The key to be inserted. 476 * @param value Pointer to value to be inserted. 477 * @param rsubtree Pointer to the right subtree. 478 */ 479 void node_insert_key_and_rsubtree(btree_node_t *node, btree_key_t key, void *value, btree_node_t *rsubtree) 480 { 481 size_t i; 482 483 for (i = 0; i < node->keys; i++) { 484 if (key < node->key[i]) { 485 size_t j; 486 487 for (j = node->keys; j > i; j--) { 488 node->key[j] = node->key[j - 1]; 489 node->value[j] = node->value[j - 1]; 490 node->subtree[j + 1] = node->subtree[j]; 491 } 492 break; 493 } 494 } 495 node->key[i] = key; 496 node->value[i] = value; 497 node->subtree[i + 1] = rsubtree; 498 499 node->keys++; 500 } 501 502 /** Remove key and its left subtree pointer from B-tree node. 503 * 504 * Remove the key and eliminate gaps in node->key array. 505 * Note that the value pointer and the left subtree pointer 506 * is removed from the node as well. 507 * 508 * @param node B-tree node. 509 * @param key Key to be removed. 510 */ 511 void node_remove_key_and_lsubtree(btree_node_t *node, btree_key_t key) 512 { 513 size_t i, j; 514 515 for (i = 0; i < node->keys; i++) { 516 if (key == node->key[i]) { 517 for (j = i + 1; j < node->keys; j++) { 518 node->key[j - 1] = node->key[j]; 519 node->value[j - 1] = node->value[j]; 520 node->subtree[j - 1] = node->subtree[j]; 521 } 522 node->subtree[j - 1] = node->subtree[j]; 523 node->keys--; 524 return; 525 } 526 } 527 panic("Node %p does not contain key %" PRIu64 ".", node, key); 528 } 529 530 /** Remove key and its right subtree pointer from B-tree node. 531 * 532 * Remove the key and eliminate gaps in node->key array. 533 * Note that the value pointer and the right subtree pointer 534 * is removed from the node as well. 535 * 536 * @param node B-tree node. 537 * @param key Key to be removed. 538 */ 539 void node_remove_key_and_rsubtree(btree_node_t *node, btree_key_t key) 540 { 541 size_t i, j; 542 543 for (i = 0; i < node->keys; i++) { 544 if (key == node->key[i]) { 545 for (j = i + 1; j < node->keys; j++) { 546 node->key[j - 1] = node->key[j]; 547 node->value[j - 1] = node->value[j]; 548 node->subtree[j] = node->subtree[j + 1]; 549 } 550 node->keys--; 551 return; 552 } 553 } 554 panic("Node %p does not contain key %" PRIu64 ".", node, key); 555 } 556 557 /** Split full B-tree node and insert new key-value-right-subtree triplet. 558 * 559 * This function will split a node and return a pointer to a newly created 560 * node containing keys greater than or equal to the greater of medians 561 * (or median) of the old keys and the newly added key. It will also write 562 * the median key to a memory address supplied by the caller. 563 * 564 * If the node being split is an index node, the median will not be 565 * included in the new node. If the node is a leaf node, 566 * the median will be copied there. 567 * 568 * @param node B-tree node wich is going to be split. 569 * @param key The key to be inserted. 570 * @param value Pointer to the value to be inserted. 571 * @param rsubtree Pointer to the right subtree of the key being added. 572 * @param median Address in memory, where the median key will be stored. 573 * 574 * @return Newly created right sibling of node. 575 */ 576 btree_node_t *node_split(btree_node_t *node, btree_key_t key, void *value, btree_node_t *rsubtree, btree_key_t *median) 577 { 578 btree_node_t *rnode; 579 size_t i, j; 580 581 ASSERT(median); 582 ASSERT(node->keys == BTREE_MAX_KEYS); 583 584 /* 585 * Use the extra space to store the extra node. 586 */ 587 node_insert_key_and_rsubtree(node, key, value, rsubtree); 588 589 /* 590 * Compute median of keys. 591 */ 592 *median = MEDIAN_HIGH(node); 593 594 /* 595 * Allocate and initialize new right sibling. 596 */ 597 rnode = (btree_node_t *) slab_alloc(btree_node_slab, 0); 598 node_initialize(rnode); 599 rnode->parent = node->parent; 600 rnode->depth = node->depth; 601 602 /* 603 * Copy big keys, values and subtree pointers to the new right sibling. 604 * If this is an index node, do not copy the median. 605 */ 606 i = (size_t) INDEX_NODE(node); 607 for (i += MEDIAN_HIGH_INDEX(node), j = 0; i < node->keys; i++, j++) { 608 rnode->key[j] = node->key[i]; 609 rnode->value[j] = node->value[i]; 610 rnode->subtree[j] = node->subtree[i]; 611 612 /* 613 * Fix parent links in subtrees. 614 */ 615 if (rnode->subtree[j]) 616 rnode->subtree[j]->parent = rnode; 617 618 } 619 rnode->subtree[j] = node->subtree[i]; 620 if (rnode->subtree[j]) 621 rnode->subtree[j]->parent = rnode; 622 623 rnode->keys = j; /* Set number of keys of the new node. */ 624 node->keys /= 2; /* Shrink the old node. */ 625 626 return rnode; 627 } 628 629 /** Combine node with any of its siblings. 630 * 631 * The siblings are required to be below the fill factor. 632 * 633 * @param node Node to combine with one of its siblings. 634 * 635 * @return Pointer to the rightmost of the two nodes. 636 */ 637 btree_node_t *node_combine(btree_node_t *node) 638 { 639 size_t idx; 640 btree_node_t *rnode; 641 size_t i; 642 643 ASSERT(!ROOT_NODE(node)); 644 645 idx = find_key_by_subtree(node->parent, node, false); 646 if (idx == node->parent->keys) { 647 /* 648 * Rightmost subtree of its parent, combine with the left sibling. 649 */ 650 idx--; 651 rnode = node; 652 node = node->parent->subtree[idx]; 653 } else { 654 rnode = node->parent->subtree[idx + 1]; 655 } 656 657 /* Index nodes need to insert parent node key in between left and right node. */ 658 if (INDEX_NODE(node)) 659 node->key[node->keys++] = node->parent->key[idx]; 660 661 /* Copy the key-value-subtree triplets from the right node. */ 662 for (i = 0; i < rnode->keys; i++) { 663 node->key[node->keys + i] = rnode->key[i]; 664 node->value[node->keys + i] = rnode->value[i]; 665 if (INDEX_NODE(node)) { 666 node->subtree[node->keys + i] = rnode->subtree[i]; 667 rnode->subtree[i]->parent = node; 668 } 669 } 670 if (INDEX_NODE(node)) { 671 node->subtree[node->keys + i] = rnode->subtree[i]; 672 rnode->subtree[i]->parent = node; 673 } 674 675 node->keys += rnode->keys; 676 677 return rnode; 678 } 679 680 /** Find key by its left or right subtree. 681 * 682 * @param node B-tree node. 683 * @param subtree Left or right subtree of a key found in node. 684 * @param right If true, subtree is a right subtree. If false, subtree is a left subtree. 685 * 686 * @return Index of the key associated with the subtree. 687 */ 688 size_t find_key_by_subtree(btree_node_t *node, btree_node_t *subtree, bool right) 689 { 690 size_t i; 691 692 for (i = 0; i < node->keys + 1; i++) { 693 if (subtree == node->subtree[i]) 694 return i - (int) (right != false); 695 } 696 panic("Node %p does not contain subtree %p.", node, subtree); 697 } 698 699 /** Rotate one key-value-rsubtree triplet from the left sibling to the right sibling. 700 * 701 * The biggest key and its value and right subtree is rotated from the left node 702 * to the right. If the node is an index node, than the parent node key belonging to 703 * the left node takes part in the rotation. 704 * 705 * @param lnode Left sibling. 706 * @param rnode Right sibling. 707 * @param idx Index of the parent node key that is taking part in the rotation. 708 */ 709 void rotate_from_left(btree_node_t *lnode, btree_node_t *rnode, size_t idx) 710 { 711 btree_key_t key; 712 713 key = lnode->key[lnode->keys - 1]; 714 715 if (LEAF_NODE(lnode)) { 716 void *value; 717 718 value = lnode->value[lnode->keys - 1]; 719 node_remove_key_and_rsubtree(lnode, key); 720 node_insert_key_and_lsubtree(rnode, key, value, NULL); 721 lnode->parent->key[idx] = key; 722 } else { 723 btree_node_t *rsubtree; 724 725 rsubtree = lnode->subtree[lnode->keys]; 726 node_remove_key_and_rsubtree(lnode, key); 727 node_insert_key_and_lsubtree(rnode, lnode->parent->key[idx], NULL, rsubtree); 728 lnode->parent->key[idx] = key; 729 730 /* Fix parent link of the reconnected right subtree. */ 731 rsubtree->parent = rnode; 732 } 733 734 } 735 736 /** Rotate one key-value-lsubtree triplet from the right sibling to the left sibling. 737 * 738 * The smallest key and its value and left subtree is rotated from the right node 739 * to the left. If the node is an index node, than the parent node key belonging to 740 * the right node takes part in the rotation. 741 * 742 * @param lnode Left sibling. 743 * @param rnode Right sibling. 744 * @param idx Index of the parent node key that is taking part in the rotation. 745 */ 746 void rotate_from_right(btree_node_t *lnode, btree_node_t *rnode, size_t idx) 747 { 748 btree_key_t key; 749 750 key = rnode->key[0]; 751 752 if (LEAF_NODE(rnode)) { 753 void *value; 754 755 value = rnode->value[0]; 756 node_remove_key_and_lsubtree(rnode, key); 757 node_insert_key_and_rsubtree(lnode, key, value, NULL); 758 rnode->parent->key[idx] = rnode->key[0]; 759 } else { 760 btree_node_t *lsubtree; 761 762 lsubtree = rnode->subtree[0]; 763 node_remove_key_and_lsubtree(rnode, key); 764 node_insert_key_and_rsubtree(lnode, rnode->parent->key[idx], NULL, lsubtree); 765 rnode->parent->key[idx] = key; 766 767 /* Fix parent link of the reconnected left subtree. */ 768 lsubtree->parent = lnode; 769 } 770 771 } 772 773 /** Insert key-value-rsubtree triplet and rotate the node to the left, if this operation can be done. 774 * 775 * Left sibling of the node (if it exists) is checked for free space. 776 * If there is free space, the key is inserted and the smallest key of 777 * the node is moved there. The index node which is the parent of both 778 * nodes is fixed. 779 * 780 * @param node B-tree node. 781 * @param inskey Key to be inserted. 782 * @param insvalue Value to be inserted. 783 * @param rsubtree Right subtree of inskey. 784 * 785 * @return True if the rotation was performed, false otherwise. 786 */ 787 bool try_insert_by_rotation_to_left(btree_node_t *node, btree_key_t inskey, void *insvalue, btree_node_t *rsubtree) 788 { 789 size_t idx; 790 btree_node_t *lnode; 791 792 /* 793 * If this is root node, the rotation can not be done. 794 */ 795 if (ROOT_NODE(node)) 796 return false; 797 798 idx = find_key_by_subtree(node->parent, node, true); 799 if ((int) idx == -1) { 800 /* 801 * If this node is the leftmost subtree of its parent, 802 * the rotation can not be done. 803 */ 804 return false; 805 } 806 807 lnode = node->parent->subtree[idx]; 808 if (lnode->keys < BTREE_MAX_KEYS) { 809 /* 810 * The rotaion can be done. The left sibling has free space. 811 */ 812 node_insert_key_and_rsubtree(node, inskey, insvalue, rsubtree); 813 rotate_from_right(lnode, node, idx); 814 return true; 815 } 816 817 return false; 818 } 819 820 /** Insert key-value-rsubtree triplet and rotate the node to the right, if this operation can be done. 821 * 822 * Right sibling of the node (if it exists) is checked for free space. 823 * If there is free space, the key is inserted and the biggest key of 824 * the node is moved there. The index node which is the parent of both 825 * nodes is fixed. 826 * 827 * @param node B-tree node. 828 * @param inskey Key to be inserted. 829 * @param insvalue Value to be inserted. 830 * @param rsubtree Right subtree of inskey. 831 * 832 * @return True if the rotation was performed, false otherwise. 833 */ 834 bool try_insert_by_rotation_to_right(btree_node_t *node, btree_key_t inskey, void *insvalue, btree_node_t *rsubtree) 835 { 836 size_t idx; 837 btree_node_t *rnode; 838 839 /* 840 * If this is root node, the rotation can not be done. 841 */ 842 if (ROOT_NODE(node)) 843 return false; 844 845 idx = find_key_by_subtree(node->parent, node, false); 846 if (idx == node->parent->keys) { 847 /* 848 * If this node is the rightmost subtree of its parent, 849 * the rotation can not be done. 850 */ 851 return false; 852 } 853 854 rnode = node->parent->subtree[idx + 1]; 855 if (rnode->keys < BTREE_MAX_KEYS) { 856 /* 857 * The rotaion can be done. The right sibling has free space. 858 */ 859 node_insert_key_and_rsubtree(node, inskey, insvalue, rsubtree); 860 rotate_from_left(node, rnode, idx); 861 return true; 862 } 863 864 return false; 865 } 866 867 /** Rotate in a key from the left sibling or from the index node, if this operation can be done. 868 * 869 * @param rnode Node into which to add key from its left sibling or from the index node. 870 * 871 * @return True if the rotation was performed, false otherwise. 872 */ 873 bool try_rotation_from_left(btree_node_t *rnode) 874 { 875 size_t idx; 876 btree_node_t *lnode; 877 878 /* 879 * If this is root node, the rotation can not be done. 880 */ 881 if (ROOT_NODE(rnode)) 882 return false; 883 884 idx = find_key_by_subtree(rnode->parent, rnode, true); 885 if ((int) idx == -1) { 886 /* 887 * If this node is the leftmost subtree of its parent, 888 * the rotation can not be done. 889 */ 890 return false; 891 } 892 893 lnode = rnode->parent->subtree[idx]; 894 if (lnode->keys > FILL_FACTOR) { 895 rotate_from_left(lnode, rnode, idx); 896 return true; 897 } 898 899 return false; 900 } 901 902 /** Rotate in a key from the right sibling or from the index node, if this operation can be done. 903 * 904 * @param lnode Node into which to add key from its right sibling or from the index node. 905 * 906 * @return True if the rotation was performed, false otherwise. 907 */ 908 bool try_rotation_from_right(btree_node_t *lnode) 909 { 910 size_t idx; 911 btree_node_t *rnode; 912 913 /* 914 * If this is root node, the rotation can not be done. 915 */ 916 if (ROOT_NODE(lnode)) 917 return false; 918 919 idx = find_key_by_subtree(lnode->parent, lnode, false); 920 if (idx == lnode->parent->keys) { 921 /* 922 * If this node is the rightmost subtree of its parent, 923 * the rotation can not be done. 924 */ 925 return false; 926 } 927 928 rnode = lnode->parent->subtree[idx + 1]; 929 if (rnode->keys > FILL_FACTOR) { 930 rotate_from_right(lnode, rnode, idx); 931 return true; 932 } 933 934 return false; 935 } 936 977 937 /** Print B-tree. 978 938 * 979 939 * @param t Print out B-tree. 980 *981 940 */ 982 941 void btree_print(btree_t *t) … … 985 944 int depth = t->root->depth; 986 945 link_t head, *cur; 987 946 988 947 printf("Printing B-tree:\n"); 989 948 list_initialize(&head); 990 949 list_append(&t->root->bfs_link, &head); 991 950 992 951 /* 993 952 * Use BFS search to print out the tree. 994 953 * Levels are distinguished from one another by node->depth. 995 */ 954 */ 996 955 while (!list_empty(&head)) { 997 956 link_t *hlp; … … 1009 968 depth = node->depth; 1010 969 } 1011 970 1012 971 printf("("); 1013 1014 972 for (i = 0; i < node->keys; i++) { 1015 973 printf("%" PRIu64 "%s", node->key[i], i < node->keys - 1 ? "," : ""); … … 1018 976 } 1019 977 } 1020 1021 if (node->depth && node->subtree[i]) 978 if (node->depth && node->subtree[i]) { 1022 979 list_append(&node->subtree[i]->bfs_link, &head); 1023 980 } 1024 981 printf(")"); 1025 982 } 1026 1027 983 printf("\n"); 1028 984 … … 1034 990 1035 991 ASSERT(node); 1036 992 1037 993 printf("("); 1038 1039 994 for (i = 0; i < node->keys; i++) 1040 995 printf("%" PRIu64 "%s", node->key[i], i < node->keys - 1 ? "," : ""); 1041 1042 996 printf(")"); 1043 997 } 1044 1045 998 printf("\n"); 1046 999 }
Note:
See TracChangeset
for help on using the changeset viewer.