// This file is part of the thin-provisioning-tools source. // // thin-provisioning-tools is free software: you can redistribute it // and/or modify it under the terms of the GNU General Public License // as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // thin-provisioning-tools is distributed in the hope that it will be // useful, but WITHOUT ANY WARRANTY; without even the implied warranty // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with thin-provisioning-tools. If not, see // . namespace persistent_data { template btree_detail::shadow_child btree:: create_shadow_child(internal_node &parent, unsigned index) { block_address b = parent.value_at(index); pair p = tm_.shadow(b, validator_); write_ref &wr = p.first; btree_detail::node_type type; node_ref n = to_node(wr); if (n.get_type() == btree_detail::INTERNAL) { type = btree_detail::INTERNAL; if (p.second) n.inc_children(internal_rc_); } else { type = btree_detail::LEAF; if (p.second) { node_ref leaf = to_node(wr); leaf.inc_children(rc_); } } parent.set_value(index, wr.get_location()); return btree_detail::shadow_child(wr, type); } template void btree:: remove(key const &key) { using namespace btree_detail; block_address block = root_; unsigned index = 0; shadow_spine spine(tm_, validator_); bool need_remove = true; for (unsigned level = 0; level < Levels - 1; ++level) { need_remove = remove_location(spine, block, key[level], &index, internal_rc_); if (!need_remove) break; internal_node n = spine.get_node(); block = n.value_at(index); } if (need_remove) { need_remove = remove_location(spine, block, key[Levels - 1], &index, rc_); if (need_remove) { leaf_node leaf = spine.get_node(); leaf.delete_at(index); } } root_ = spine.get_root(); } template template bool btree:: remove_location(btree_detail::shadow_spine &spine, block_address block, uint64_t key, unsigned *index, RC &leaf_rc) { using namespace btree_detail; unsigned i = *index; for (;;) { bool inc = spine.step(block); if (inc) inc_children(spine, leaf_rc); // patch up the parent to point to the new shadow if (spine.has_parent()) { internal_node p = spine.get_parent(); p.set_value(i, spine.get_block()); } internal_node n = spine.get_node(); if (n.get_type() == btree_detail::LEAF) { node_ref leaf = spine.get_node(); boost::optional idx = leaf.exact_search(key); if (!idx) return false; *index = *idx; return true; } bool r = rebalance_children(spine, key); if (!r) return false; n = spine.get_node(); if (n.get_type() == btree_detail::LEAF) { node_ref leaf = spine.get_node(); boost::optional idx = leaf.exact_search(key); if (!idx) return false; *index = *idx; return true; } i = n.lower_bound(key); block = n.value_at(i); } return true; } template template bool btree:: rebalance_children(btree_detail::shadow_spine &spine, uint64_t key) { internal_node n = spine.get_node(); if (n.get_nr_entries() == 1) { block_address b = n.value_at(0); read_ref child = tm_.read_lock(b, validator_); // FIXME: is it safe? ::memcpy(n.raw(), child.data(), read_ref::BLOCK_SIZE); tm_.get_sm()->dec(child.get_location()); return true; } int i = n.lower_bound(key); if (i < 0) return false; bool has_left_sibling = i > 0; bool has_right_sibling = static_cast(i) < (n.get_nr_entries() - 1); if (!has_left_sibling) rebalance2(spine, i); else if (!has_right_sibling) rebalance2(spine, i - 1); else rebalance3(spine, i - 1); return true; } template template void btree:: rebalance2(btree_detail::shadow_spine &spine, unsigned left_index) { internal_node parent = spine.get_node(); shadow_child left = create_shadow_child(parent, left_index); shadow_child right = create_shadow_child(parent, left_index + 1); // FIXME: ugly if (left.get_type() == btree_detail::INTERNAL) { internal_node l = left.get_node(); internal_node r = right.get_node(); __rebalance2(parent, l, r, left_index); } else { node_ref l = left.get_node(); node_ref r = right.get_node(); __rebalance2(parent, l, r, left_index); } } template template void btree:: __rebalance2(internal_node &parent, node_ref &left, node_ref &right, unsigned left_index) { unsigned nr_left = left.get_nr_entries(); unsigned nr_right = right.get_nr_entries(); unsigned right_index = left_index + 1; unsigned threshold = 2 * (left.merge_threshold() + 1); if (nr_left + nr_right < threshold) { // Merge the right child into the left left.copy_entries_to_left(right, nr_right); left.set_nr_entries(nr_left + nr_right); parent.delete_at(right_index); tm_.get_sm()->dec(right.get_location()); } else { // Rebalance unsigned target_left = (nr_left + nr_right) / 2; left.move_entries(right, nr_left - target_left); parent.set_key(right_index, right.key_at(0)); } } template template void btree:: rebalance3(btree_detail::shadow_spine &spine, unsigned left_index) { internal_node parent = spine.get_node(); shadow_child left = create_shadow_child(parent, left_index); shadow_child center = create_shadow_child(parent, left_index + 1); shadow_child right = create_shadow_child(parent, left_index + 2); // FIXME: ugly if (left.get_type() == btree_detail::INTERNAL) { internal_node l = left.get_node(); internal_node c = center.get_node(); internal_node r = right.get_node(); __rebalance3(parent, l, c, r, left_index); } else { node_ref l = left.get_node(); node_ref c = center.get_node(); node_ref r = right.get_node(); __rebalance3(parent, l, c, r, left_index); } } template template void btree:: __rebalance3(internal_node &parent, node_ref &left, node_ref ¢er, node_ref &right, unsigned left_index) { unsigned nr_left = left.get_nr_entries(); unsigned nr_center = center.get_nr_entries(); unsigned nr_right = right.get_nr_entries(); unsigned threshold = left.merge_threshold() * 4 + 1; if ((nr_left + nr_center + nr_right) < threshold) delete_center_node(parent, left, center, right, left_index); else redistribute3(parent, left, center, right, left_index); } template template void btree:: delete_center_node(internal_node &parent, node_ref &left, node_ref ¢er, node_ref &right, unsigned left_index) { unsigned center_index = left_index + 1; unsigned right_index = left_index + 2; unsigned max_entries = left.get_max_entries(); unsigned nr_left = left.get_nr_entries(); unsigned nr_center = center.get_nr_entries(); unsigned nr_right = right.get_nr_entries(); unsigned shift = std::min(max_entries - nr_left, nr_center); if (nr_left + shift > max_entries) throw std::runtime_error("too many entries"); left.copy_entries_to_left(center, shift); left.set_nr_entries(nr_left + shift); if (shift != nr_center) { shift = nr_center - shift; if ((nr_right + shift) > max_entries) throw std::runtime_error("too many entries"); right.shift_entries_right(shift); center.copy_entries_to_right(right, shift); right.set_nr_entries(nr_right + shift); } parent.set_key(right_index, right.key_at(0)); parent.delete_at(center_index); --right_index; tm_.get_sm()->dec(center.get_location()); __rebalance2(parent, left, right, left_index); } template template void btree:: redistribute3(internal_node &parent, node_ref &left, node_ref ¢er, node_ref &right, unsigned left_index) { unsigned center_index = left_index + 1; unsigned right_index = left_index + 2; unsigned nr_left = left.get_nr_entries(); unsigned nr_center = center.get_nr_entries(); unsigned nr_right = right.get_nr_entries(); unsigned max_entries = left.get_max_entries(); unsigned total = nr_left + nr_center + nr_right; unsigned target_right = total / 3; unsigned remainder = (target_right * 3) != total; unsigned target_left = target_right + remainder; if (target_left > max_entries || target_right > max_entries) throw std::runtime_error("too many entries"); if (nr_left < nr_right) { int s = nr_left - target_left; // FIXME: signed & unsigned comparison if (s < 0 && nr_center < static_cast(-s)) { // not enough in central node left.move_entries(center, -nr_center); s += nr_center; left.move_entries(right, s); nr_right += s; } else left.move_entries(center, s); center.move_entries(right, target_right - nr_right); } else { int s = target_right - nr_right; if (s > 0 && nr_center < static_cast(s)) { // not enough in central node center.move_entries(right, nr_center); s -= nr_center; left.move_entries(right, s); nr_left -= s; } else center.move_entries(right, s); left.move_entries(center, nr_left - target_left); } parent.set_key(center_index, center.key_at(0)); parent.set_key(right_index, right.key_at(0)); } };