#ifndef STAR_B_TREE_HPP #define STAR_B_TREE_HPP #include "StarList.hpp" #include "StarMaybe.hpp" namespace Star { // Mixin class for implementing a simple B+ Tree style database. LOTS of // possibilities for improvement, especially in batch deletes / inserts. // // The Base class itself must have the following interface: // // struct Base { // typedef KeyT Key; // typedef DataT Data; // typedef PointerT Pointer; // // // Index and Leaf types may either be a literal struct, or a pointer, or a // // handle or whatever. They are meant to be opaque. // typedef IndexT Index; // typedef LeafT Leaf; // // Pointer rootPointer(); // bool rootIsLeaf(); // void setNewRoot(Pointer pointer, bool isLeaf); // // Index createIndex(Pointer beginPointer); // // // Load an existing index. // Index loadIndex(Pointer pointer); // // size_t indexPointerCount(Index const& index); // Pointer indexPointer(Index const& index, size_t i); // void indexUpdatePointer(Index& index, size_t i, Pointer p); // // Key indexKeyBefore(Index const& index, size_t i); // void indexUpdateKeyBefore(Index& index, size_t i, Key k); // // void indexRemoveBefore(Index& index, size_t i); // void indexInsertAfter(Index& index, size_t i, Key k, Pointer p); // // size_t indexLevel(Index const& index); // void setIndexLevel(Index& index, size_t indexLevel); // // // Should return true if index should try to shift elements into this index // // from sibling index. // bool indexNeedsShift(Index const& index); // // // Should return false if no shift done. If merging, always merge to the // // left. // bool indexShift(Index& left, Key const& mid, Index& right); // // // If a split has occurred, split right and return the mid-key and new // // right node. // Maybe> indexSplit(Index& index); // // // Index updated, needs storing. Return pointer to stored index (may // // change). Index will not be used after store. // Pointer storeIndex(Index index); // // // Index no longer part of BTree. Index will not be used after delete. // void deleteIndex(Index index); // // // Should create new empty leaf. // Leaf createLeaf(); // // Leaf loadLeaf(Pointer pointer); // // size_t leafElementCount(Leaf const& leaf); // Key leafKey(Leaf const& leaf, size_t i); // Data leafData(Leaf const& leaf, size_t i); // // void leafInsert(Leaf& leaf, size_t i, Key k, Data d); // void leafRemove(Leaf& leaf, size_t i); // // // Set and get next-leaf pointers. It is not required that next-leaf // // pointers be kept or that they be valid, so nextLeaf may return nothing. // void setNextLeaf(Leaf& leaf, Maybe n); // Maybe nextLeaf(Leaf const& leaf); // // // Should return true if leaf should try to shift elements into this leaf // // from sibling leaf. // bool leafNeedsShift(Leaf const& l); // // // Should return false if no change necessary. If merging, always merge to // // the left. // bool leafShift(Leaf& left, Leaf& right); // // // Always split right and return new right node if split occurs. // Maybe leafSplit(Leaf& leaf); // // // Leaf has been updated, and needs to be written to storage. Return new // // pointer (may be different). Leaf will not be used after store. // Pointer storeLeaf(Leaf leaf); // // // Leaf is no longer part of this BTree. Leaf will not be used after // // delete. // void deleteLeaf(Leaf leaf); // }; template class BTreeMixin : public Base { public: typedef typename Base::Key Key; typedef typename Base::Data Data; typedef typename Base::Pointer Pointer; typedef typename Base::Index Index; typedef typename Base::Leaf Leaf; bool contains(Key const& k); Maybe find(Key const& k); // Range is inclusve on lower bound and exclusive on upper bound. List> find(Key const& lower, Key const& upper); // Visitor is called as visitor(key, data). template void forEach(Key const& lower, Key const& upper, Visitor&& visitor); // Visitor is called as visitor(key, data). template void forAll(Visitor&& visitor); // Recover all key value pairs possible, catching exceptions during scan and // reading as much data as possible. Visitor is called as visitor(key, data), // ErrorHandler is called as error(char const*, std::exception const&) template void recoverAll(Visitor&& visitor, ErrorHandler&& error); // Visitor is called either as visitor(Index const&) or visitor(Leaf const&). // Return false to halt traversal, true to continue. template void forAllNodes(Visitor&& visitor); // returns true if old value overwritten. bool insert(Key k, Data data); // returns true if key was found. bool remove(Key k); // Removes list of keys in the given range, returns count removed. // TODO: SLOW, right now does lots of different removes separately. Need to // implement batch inserts and deletes. List> remove(Key const& lower, Key const& upper); uint64_t indexCount(); uint64_t leafCount(); uint64_t recordCount(); uint32_t indexLevels(); void createNewRoot(); private: struct DataElement { Key key; Data data; }; typedef List DataList; struct DataCollector { void operator()(Key const& k, Data const& d); List> list; }; struct RecordCounter { bool operator()(Index const& index); bool operator()(Leaf const& leaf); BTreeMixin* parent; uint64_t count; }; struct IndexCounter { bool operator()(Index const& index); bool operator()(Leaf const&); BTreeMixin* parent; uint64_t count; }; struct LeafCounter { bool operator()(Index const& index); bool operator()(Leaf const&); BTreeMixin* parent; uint64_t count; }; enum ModifyAction { InsertAction, RemoveAction }; enum ModifyState { LeafNeedsJoin, IndexNeedsJoin, LeafSplit, IndexSplit, LeafNeedsUpdate, IndexNeedsUpdate, Done }; struct ModifyInfo { ModifyInfo(ModifyAction a, DataElement e); DataElement targetElement; ModifyAction action; bool found; ModifyState state; Key newKey; Pointer newPointer; }; bool contains(Index const& index, Key const& k); bool contains(Leaf const& leaf, Key const& k); Maybe find(Index const& index, Key const& k); Maybe find(Leaf const& leaf, Key const& k); // Returns the highest key for the last leaf we have searched template Key forEach(Index const& index, Key const& lower, Key const& upper, Visitor&& o); template Key forEach(Leaf const& leaf, Key const& lower, Key const& upper, Visitor&& o); // Returns the highest key for the last leaf we have searched template Key forAll(Index const& index, Visitor&& o); template Key forAll(Leaf const& leaf, Visitor&& o); template void recoverAll(Index const& index, Visitor&& o, ErrorHandler&& error); template void recoverAll(Leaf const& leaf, Visitor&& o, ErrorHandler&& error); // Variable size values mean that merges can happen on inserts, so can't // split up into insert / remove methods void modify(Leaf& leafNode, ModifyInfo& info); void modify(Index& indexNode, ModifyInfo& info); bool modify(DataElement e, ModifyAction action); // Traverses Indexes down the tree on the left side to get the least valued // key that is pointed to by any leaf under this index. Needed when joining. Key getLeftKey(Index const& index); template void forAllNodes(Index const& index, Visitor&& visitor); pair leafFind(Leaf const& leaf, Key const& key); size_t indexFind(Index const& index, Key const& key); }; template bool BTreeMixin::contains(Key const& k) { if (Base::rootIsLeaf()) return contains(Base::loadLeaf(Base::rootPointer()), k); else return contains(Base::loadIndex(Base::rootPointer()), k); } template auto BTreeMixin::find(Key const& k) -> Maybe { if (Base::rootIsLeaf()) return find(Base::loadLeaf(Base::rootPointer()), k); else return find(Base::loadIndex(Base::rootPointer()), k); } template auto BTreeMixin::find(Key const& lower, Key const& upper) -> List> { DataCollector collector; forEach(lower, upper, collector); return collector.list; } template template void BTreeMixin::forEach(Key const& lower, Key const& upper, Visitor&& visitor) { if (Base::rootIsLeaf()) forEach(Base::loadLeaf(Base::rootPointer()), lower, upper, std::forward(visitor)); else forEach(Base::loadIndex(Base::rootPointer()), lower, upper, std::forward(visitor)); } template template void BTreeMixin::forAll(Visitor&& visitor) { if (Base::rootIsLeaf()) forAll(Base::loadLeaf(Base::rootPointer()), std::forward(visitor)); else forAll(Base::loadIndex(Base::rootPointer()), std::forward(visitor)); } template template void BTreeMixin::recoverAll(Visitor&& visitor, ErrorHandler&& error) { try { if (Base::rootIsLeaf()) recoverAll(Base::loadLeaf(Base::rootPointer()), std::forward(visitor), std::forward(error)); else recoverAll(Base::loadIndex(Base::rootPointer()), std::forward(visitor), std::forward(error)); } catch (std::exception const& e) { error("Error loading root index or leaf node", e); } } template template void BTreeMixin::forAllNodes(Visitor&& visitor) { if (Base::rootIsLeaf()) visitor(Base::loadLeaf(Base::rootPointer())); else forAllNodes(Base::loadIndex(Base::rootPointer()), std::forward(visitor)); } template bool BTreeMixin::insert(Key k, Data data) { return modify(DataElement{std::move(k), std::move(data)}, InsertAction); } template bool BTreeMixin::remove(Key k) { return modify(DataElement{std::move(k), Data()}, RemoveAction); } template auto BTreeMixin::remove(Key const& lower, Key const& upper) -> List> { DataCollector collector; forEach(lower, upper, collector); for (auto const& elem : collector.list) remove(elem.first); return collector.list; } template uint64_t BTreeMixin::indexCount() { IndexCounter counter = {this, 0}; forAllNodes(counter); return counter.count; } template uint64_t BTreeMixin::leafCount() { LeafCounter counter = {this, 0}; forAllNodes(counter); return counter.count; } template uint64_t BTreeMixin::recordCount() { RecordCounter counter = {this, 0}; forAllNodes(counter); return counter.count; } template uint32_t BTreeMixin::indexLevels() { if (Base::rootIsLeaf()) return 0; else return Base::indexLevel(Base::loadIndex(Base::rootPointer())) + 1; } template void BTreeMixin::createNewRoot() { Base::setNewRoot(Base::storeLeaf(Base::createLeaf()), true); } template void BTreeMixin::DataCollector::operator()(Key const& k, Data const& d) { list.push_back({k, d}); } template bool BTreeMixin::RecordCounter::operator()(Index const&) { return true; } template bool BTreeMixin::RecordCounter::operator()(Leaf const& leaf) { count += parent->leafElementCount(leaf); return true; } template bool BTreeMixin::IndexCounter::operator()(Index const& index) { ++count; if (parent->indexLevel(index) == 0) return false; else return true; } template bool BTreeMixin::IndexCounter::operator()(Leaf const&) { return false; } template bool BTreeMixin::LeafCounter::operator()(Index const& index) { if (parent->indexLevel(index) == 0) { count += parent->indexPointerCount(index); return false; } else { return true; } } template bool BTreeMixin::LeafCounter::operator()(Leaf const&) { return false; } template BTreeMixin::ModifyInfo::ModifyInfo(ModifyAction a, DataElement e) : targetElement(std::move(e)), action(a) { found = false; state = Done; } template bool BTreeMixin::contains(Index const& index, Key const& k) { size_t i = indexFind(index, k); if (Base::indexLevel(index) == 0) return contains(Base::loadLeaf(Base::indexPointer(index, i)), k); else return contains(Base::loadIndex(Base::indexPointer(index, i)), k); } template bool BTreeMixin::contains(Leaf const& leaf, Key const& k) { return leafFind(leaf, k).second; } template auto BTreeMixin::find(Index const& index, Key const& k) -> Maybe { size_t i = indexFind(index, k); if (Base::indexLevel(index) == 0) return find(Base::loadLeaf(Base::indexPointer(index, i)), k); else return find(Base::loadIndex(Base::indexPointer(index, i)), k); } template auto BTreeMixin::find(Leaf const& leaf, Key const& k) -> Maybe { pair res = leafFind(leaf, k); if (res.second) return Base::leafData(leaf, res.first); else return {}; } template template auto BTreeMixin::forEach(Index const& index, Key const& lower, Key const& upper, Visitor&& o) -> Key { size_t i = indexFind(index, lower); Key lastKey; if (Base::indexLevel(index) == 0) lastKey = forEach(Base::loadLeaf(Base::indexPointer(index, i)), lower, upper, std::forward(o)); else lastKey = forEach(Base::loadIndex(Base::indexPointer(index, i)), lower, upper, std::forward(o)); if (!(lastKey < upper)) return lastKey; while (i < Base::indexPointerCount(index) - 1) { ++i; // We're visiting the right side of the key, so if lastKey >= // indexKeyBefore(index, i), we have already visited this node via nextLeaf // pointers, so skip it. if (!(lastKey < Base::indexKeyBefore(index, i))) continue; if (Base::indexLevel(index) == 0) lastKey = forEach(Base::loadLeaf(Base::indexPointer(index, i)), lower, upper, std::forward(o)); else lastKey = forEach(Base::loadIndex(Base::indexPointer(index, i)), lower, upper, std::forward(o)); if (!(lastKey < upper)) break; } return lastKey; } template template auto BTreeMixin::forEach(Leaf const& leaf, Key const& lower, Key const& upper, Visitor&& o) -> Key { if (Base::leafElementCount(leaf) == 0) return Key(); size_t lowerIndex = leafFind(leaf, lower).first; for (size_t i = lowerIndex; i != Base::leafElementCount(leaf); ++i) { Key currentKey = Base::leafKey(leaf, i); if (!(currentKey < lower)) { if (currentKey < upper) o(currentKey, Base::leafData(leaf, i)); else return currentKey; } } if (auto nextLeafPointer = Base::nextLeaf(leaf)) return forEach(Base::loadLeaf(*nextLeafPointer), lower, upper, o); else return Base::leafKey(leaf, Base::leafElementCount(leaf) - 1); } template template auto BTreeMixin::forAll(Index const& index, Visitor&& o) -> Key { Key lastKey; for (size_t i = 0; i < Base::indexPointerCount(index); ++i) { // If we're to the right of a given key, but lastKey >= this key, then we // must have already visited this node via nextLeaf pointers, so we can // skip it. if (i > 0 && !(lastKey < Base::indexKeyBefore(index, i))) continue; if (Base::indexLevel(index) == 0) lastKey = forAll(Base::loadLeaf(Base::indexPointer(index, i)), std::forward(o)); else lastKey = forAll(Base::loadIndex(Base::indexPointer(index, i)), std::forward(o)); } return lastKey; } template template auto BTreeMixin::forAll(Leaf const& leaf, Visitor&& o) -> Key { if (Base::leafElementCount(leaf) == 0) return Key(); for (size_t i = 0; i != Base::leafElementCount(leaf); ++i) { Key currentKey = Base::leafKey(leaf, i); o(Base::leafKey(leaf, i), Base::leafData(leaf, i)); } if (auto nextLeafPointer = Base::nextLeaf(leaf)) return forAll(Base::loadLeaf(*nextLeafPointer), std::forward(o)); else return Base::leafKey(leaf, Base::leafElementCount(leaf) - 1); } template template void BTreeMixin::recoverAll(Index const& index, Visitor&& visitor, ErrorHandler&& error) { try { for (size_t i = 0; i < Base::indexPointerCount(index); ++i) { if (Base::indexLevel(index) == 0) { try { recoverAll(Base::loadLeaf(Base::indexPointer(index, i)), std::forward(visitor), std::forward(error)); } catch (std::exception const& e) { error("Error loading leaf node", e); } } else { try { recoverAll(Base::loadIndex(Base::indexPointer(index, i)), std::forward(visitor), std::forward(error)); } catch (std::exception const& e) { error("Error loading index node", e); } } } } catch (std::exception const& e) { error("Error reading index node", e); } } template template void BTreeMixin::recoverAll(Leaf const& leaf, Visitor&& visitor, ErrorHandler&& error) { try { for (size_t i = 0; i != Base::leafElementCount(leaf); ++i) { Key currentKey = Base::leafKey(leaf, i); visitor(Base::leafKey(leaf, i), Base::leafData(leaf, i)); } } catch (std::exception const& e) { error("Error reading leaf node", e); } } template void BTreeMixin::modify(Leaf& leafNode, ModifyInfo& info) { info.state = Done; pair res = leafFind(leafNode, info.targetElement.key); size_t i = res.first; if (res.second) { info.found = true; Base::leafRemove(leafNode, i); } // No change necessary. if (info.action == RemoveAction && !info.found) return; if (info.action == InsertAction) Base::leafInsert(leafNode, i, info.targetElement.key, std::move(info.targetElement.data)); auto splitResult = Base::leafSplit(leafNode); if (splitResult) { Base::setNextLeaf(*splitResult, Base::nextLeaf(leafNode)); info.newKey = Base::leafKey(*splitResult, 0); info.newPointer = Base::storeLeaf(splitResult.take()); Base::setNextLeaf(leafNode, info.newPointer); info.state = LeafSplit; } else if (Base::leafNeedsShift(leafNode)) { info.state = LeafNeedsJoin; } else { info.state = LeafNeedsUpdate; } } template void BTreeMixin::modify(Index& indexNode, ModifyInfo& info) { size_t i = indexFind(indexNode, info.targetElement.key); Pointer nextPointer = Base::indexPointer(indexNode, i); Leaf lowerLeaf; Index lowerIndex; if (Base::indexLevel(indexNode) == 0) { lowerLeaf = Base::loadLeaf(nextPointer); modify(lowerLeaf, info); } else { lowerIndex = Base::loadIndex(nextPointer); modify(lowerIndex, info); } if (info.state == Done) return; bool selfUpdated = false; size_t left = 0; size_t right = 0; if (i != 0 && i == Base::indexPointerCount(indexNode) - 1) { left = i - 1; right = i; } else { left = i; right = i + 1; } if (info.state == LeafNeedsJoin) { if (Base::indexPointerCount(indexNode) < 2) { // Don't have enough leaves to join, just do the pending update. info.state = LeafNeedsUpdate; } else { Leaf leftLeaf; Leaf rightLeaf; if (left == i) { leftLeaf = lowerLeaf; rightLeaf = Base::loadLeaf(Base::indexPointer(indexNode, right)); } else { leftLeaf = Base::loadLeaf(Base::indexPointer(indexNode, left)); rightLeaf = lowerLeaf; } if (!Base::leafShift(leftLeaf, rightLeaf)) { // Leaves not modified, just do the pending update. info.state = LeafNeedsUpdate; } else if (Base::leafElementCount(rightLeaf) == 0) { // Leaves merged. Base::setNextLeaf(leftLeaf, Base::nextLeaf(rightLeaf)); Base::deleteLeaf(std::move(rightLeaf)); // Replace two sibling pointer elements with one pointing to merged // leaf. if (left != 0) Base::indexUpdateKeyBefore(indexNode, left, Base::leafKey(leftLeaf, 0)); Base::indexUpdatePointer(indexNode, left, Base::storeLeaf(std::move(leftLeaf))); Base::indexRemoveBefore(indexNode, right); selfUpdated = true; } else { // Leaves shifted. Base::indexUpdatePointer(indexNode, left, Base::storeLeaf(std::move(leftLeaf))); // Right leaf first key changes on shift, so always need to update // left index node. Base::indexUpdateKeyBefore(indexNode, right, Base::leafKey(rightLeaf, 0)); Base::indexUpdatePointer(indexNode, right, Base::storeLeaf(std::move(rightLeaf))); selfUpdated = true; } } } if (info.state == IndexNeedsJoin) { if (Base::indexPointerCount(indexNode) < 2) { // Don't have enough indexes to join, just do the pending update. info.state = IndexNeedsUpdate; } else { Index leftIndex; Index rightIndex; if (left == i) { leftIndex = lowerIndex; rightIndex = Base::loadIndex(Base::indexPointer(indexNode, right)); } else { leftIndex = Base::loadIndex(Base::indexPointer(indexNode, left)); rightIndex = lowerIndex; } if (!Base::indexShift(leftIndex, getLeftKey(rightIndex), rightIndex)) { // Indexes not modified, just do the pending update. info.state = IndexNeedsUpdate; } else if (Base::indexPointerCount(rightIndex) == 0) { // Indexes merged. Base::deleteIndex(std::move(rightIndex)); // Replace two sibling pointer elements with one pointing to merged // index. if (left != 0) Base::indexUpdateKeyBefore(indexNode, left, getLeftKey(leftIndex)); Base::indexUpdatePointer(indexNode, left, Base::storeIndex(std::move(leftIndex))); Base::indexRemoveBefore(indexNode, right); selfUpdated = true; } else { // Indexes shifted. Base::indexUpdatePointer(indexNode, left, Base::storeIndex(std::move(leftIndex))); // Right index first key changes on shift, so always need to update // right index node. Key keyForRight = getLeftKey(rightIndex); Base::indexUpdatePointer(indexNode, right, Base::storeIndex(std::move(rightIndex))); Base::indexUpdateKeyBefore(indexNode, right, keyForRight); selfUpdated = true; } } } if (info.state == LeafSplit) { Base::indexUpdatePointer(indexNode, i, Base::storeLeaf(std::move(lowerLeaf))); Base::indexInsertAfter(indexNode, i, info.newKey, info.newPointer); selfUpdated = true; } if (info.state == IndexSplit) { Base::indexUpdatePointer(indexNode, i, Base::storeIndex(std::move(lowerIndex))); Base::indexInsertAfter(indexNode, i, info.newKey, info.newPointer); selfUpdated = true; } if (info.state == LeafNeedsUpdate) { Pointer lowerLeafPointer = Base::storeLeaf(std::move(lowerLeaf)); if (lowerLeafPointer != Base::indexPointer(indexNode, i)) { Base::indexUpdatePointer(indexNode, i, lowerLeafPointer); selfUpdated = true; } } if (info.state == IndexNeedsUpdate) { Pointer lowerIndexPointer = Base::storeIndex(std::move(lowerIndex)); if (lowerIndexPointer != Base::indexPointer(indexNode, i)) { Base::indexUpdatePointer(indexNode, i, lowerIndexPointer); selfUpdated = true; } } auto splitResult = Base::indexSplit(indexNode); if (splitResult) { info.newKey = splitResult->first; info.newPointer = Base::storeIndex(splitResult.take().second); info.state = IndexSplit; selfUpdated = true; } else if (Base::indexNeedsShift(indexNode)) { info.state = IndexNeedsJoin; } else if (selfUpdated) { info.state = IndexNeedsUpdate; } else { info.state = Done; } } template bool BTreeMixin::modify(DataElement e, ModifyAction action) { ModifyInfo info(action, std::move(e)); Leaf lowerLeaf; Index lowerIndex; if (Base::rootIsLeaf()) { lowerLeaf = Base::loadLeaf(Base::rootPointer()); modify(lowerLeaf, info); } else { lowerIndex = Base::loadIndex(Base::rootPointer()); modify(lowerIndex, info); } if (info.state == IndexNeedsJoin) { if (Base::indexPointerCount(lowerIndex) == 1) { // If root index has single pointer, then make that the new root. // release index first (to support the common use case of delaying // removes until setNewRoot) Pointer pointer = Base::indexPointer(lowerIndex, 0); size_t level = Base::indexLevel(lowerIndex); Base::deleteIndex(std::move(lowerIndex)); Base::setNewRoot(pointer, level == 0); } else { // Else just update. info.state = IndexNeedsUpdate; } } if (info.state == LeafNeedsJoin) { // Ignore NeedsJoin on LeafNode root, just update. info.state = LeafNeedsUpdate; } if (info.state == LeafSplit || info.state == IndexSplit) { Index newRoot; if (info.state == IndexSplit) { auto rootIndexLevel = Base::indexLevel(lowerIndex) + 1; newRoot = Base::createIndex(Base::storeIndex(std::move(lowerIndex))); Base::setIndexLevel(newRoot, rootIndexLevel); } else { newRoot = Base::createIndex(Base::storeLeaf(std::move(lowerLeaf))); Base::setIndexLevel(newRoot, 0); } Base::indexInsertAfter(newRoot, 0, info.newKey, info.newPointer); Base::setNewRoot(Base::storeIndex(std::move(newRoot)), false); } if (info.state == IndexNeedsUpdate) { Pointer newRootPointer = Base::storeIndex(std::move(lowerIndex)); if (newRootPointer != Base::rootPointer()) Base::setNewRoot(newRootPointer, false); } if (info.state == LeafNeedsUpdate) { Pointer newRootPointer = Base::storeLeaf(std::move(lowerLeaf)); if (newRootPointer != Base::rootPointer()) Base::setNewRoot(newRootPointer, true); } return info.found; } template auto BTreeMixin::getLeftKey(Index const& index) -> Key { if (Base::indexLevel(index) == 0) { Leaf leaf = Base::loadLeaf(Base::indexPointer(index, 0)); return Base::leafKey(leaf, 0); } else { return getLeftKey(Base::loadIndex(Base::indexPointer(index, 0))); } } template template void BTreeMixin::forAllNodes(Index const& index, Visitor&& visitor) { if (!visitor(index)) return; for (size_t i = 0; i < Base::indexPointerCount(index); ++i) { if (Base::indexLevel(index) != 0) { forAllNodes(Base::loadIndex(Base::indexPointer(index, i)), std::forward(visitor)); } else { if (!visitor(Base::loadLeaf(Base::indexPointer(index, i)))) return; } } } template pair BTreeMixin::leafFind(Leaf const& leaf, Key const& key) { // Return lower bound binary search result. size_t size = Base::leafElementCount(leaf); if (size == 0) return {0, false}; size_t len = size; size_t first = 0; size_t middle = 0; size_t half; while (len > 0) { half = len / 2; middle = first + half; if (Base::leafKey(leaf, middle) < key) { first = middle + 1; len = len - half - 1; } else { len = half; } } return make_pair(first, first < size && !(key < Base::leafKey(leaf, first))); } template size_t BTreeMixin::indexFind(Index const& index, Key const& key) { // Return upper bound binary search result of range [1, size]; size_t size = Base::indexPointerCount(index); if (size == 0) return 0; size_t len = size - 1; size_t first = 1; size_t middle = 1; size_t half; while (len > 0) { half = len / 2; middle = first + half; if (key < Base::indexKeyBefore(index, middle)) { len = half; } else { first = middle + 1; len = len - half - 1; } } return first - 1; } } #endif