Commit fca2bd43 authored by Philipp Götze's avatar Philipp Götze
Browse files

Added custom Bitmap for individual handling of underlying words

parent fb6ffc73
...@@ -86,7 +86,7 @@ static void BM_TreeErase(benchmark::State &state) { ...@@ -86,7 +86,7 @@ static void BM_TreeErase(benchmark::State &state) {
pop.drain(); pop.drain();
const auto pos = treeRef.lookupPositionInLeafNode(leaf, KEYPOS); const auto pos = treeRef.lookupPositionInLeafNode(leaf, KEYPOS);
// const auto pos = dbis::BitOperations::getFreeZero(leaf->bits.get_ro()); // const auto pos = leaf->bits.get_ro().getFreeZero();
#ifdef ENABLE_PCM #ifdef ENABLE_PCM
SocketCounterState before_sstate; SocketCounterState before_sstate;
......
...@@ -93,7 +93,7 @@ static void BM_TreeInsert(benchmark::State &state) { ...@@ -93,7 +93,7 @@ static void BM_TreeInsert(benchmark::State &state) {
const auto reqTup = MyTuple(KEYPOS, KEYPOS * 100, KEYPOS * 1.0); const auto reqTup = MyTuple(KEYPOS, KEYPOS * 100, KEYPOS * 1.0);
const auto pos = treeRef.lookupPositionInLeafNode(leaf, KEYPOS); const auto pos = treeRef.lookupPositionInLeafNode(leaf, KEYPOS);
//const auto pos = dbis::BitOperations::getFreeZero(leaf->bits.get_ro()); // const auto pos = leaf->bits.get_ro().getFreeZero();
#ifdef ENABLE_PCM #ifdef ENABLE_PCM
SocketCounterState before_sstate; SocketCounterState before_sstate;
......
...@@ -18,10 +18,10 @@ ...@@ -18,10 +18,10 @@
#ifndef DBIS_BitHPBPTree_hpp_ #ifndef DBIS_BitHPBPTree_hpp_
#define DBIS_BitHPBPTree_hpp_ #define DBIS_BitHPBPTree_hpp_
#include <libpmemobj/ctl.h>
#include <array> #include <array>
#include <iostream> #include <iostream>
#include <libpmemobj/ctl.h>
#include <libpmemobj++/make_persistent.hpp> #include <libpmemobj++/make_persistent.hpp>
#include <libpmemobj++/p.hpp> #include <libpmemobj++/p.hpp>
#include <libpmemobj++/persistent_ptr.hpp> #include <libpmemobj++/persistent_ptr.hpp>
...@@ -29,7 +29,9 @@ ...@@ -29,7 +29,9 @@
#include <libpmemobj++/utils.hpp> #include <libpmemobj++/utils.hpp>
#include "config.h" #include "config.h"
#include "utils/BitOperations.hpp" #include "utils/Bitmap.hpp"
#include <bitset>
#include "utils/PersistEmulation.hpp" #include "utils/PersistEmulation.hpp"
#include "utils/SearchFunctions.hpp" #include "utils/SearchFunctions.hpp"
...@@ -41,18 +43,18 @@ using pmem::obj::make_persistent; ...@@ -41,18 +43,18 @@ using pmem::obj::make_persistent;
using pmem::obj::p; using pmem::obj::p;
using pmem::obj::persistent_ptr; using pmem::obj::persistent_ptr;
using pmem::obj::transaction; using pmem::obj::transaction;
template<typename Object> template <typename Object>
using pptr = persistent_ptr<Object>; using pptr = persistent_ptr<Object>;
/** /**
* A persistent memory implementation of a B+ tree. * A persistent memory implementation of a B+ tree.
* *
* @tparam KeyType the data type of the key * @tparam KeyType the data type of the key
* @tparam ValueType the data type of the values associated with the key * @tparam ValueType the data type of the values associated with the key
* @tparam N the maximum number of keys on a branch node * @tparam N the maximum number of keys on a branch node
* @tparam M the maximum number of keys on a leaf node * @tparam M the maximum number of keys on a leaf node
*/ */
template<typename KeyType, typename ValueType, size_t N, size_t M> template <typename KeyType, typename ValueType, size_t N, size_t M>
class BitHPBPTree { class BitHPBPTree {
/// we need at least two keys on a branch node to be able to split /// we need at least two keys on a branch node to be able to split
static_assert(N > 2, "number of branch keys has to be >2."); static_assert(N > 2, "number of branch keys has to be >2.");
...@@ -62,9 +64,9 @@ class BitHPBPTree { ...@@ -62,9 +64,9 @@ class BitHPBPTree {
static_assert(M > 0, "number of leaf keys should be >0."); static_assert(M > 0, "number of leaf keys should be >0.");
#ifndef UNIT_TESTS #ifndef UNIT_TESTS
private: private:
#else #else
public: public:
#endif #endif
/// Forward declarations /// Forward declarations
...@@ -72,36 +74,43 @@ class BitHPBPTree { ...@@ -72,36 +74,43 @@ class BitHPBPTree {
struct BranchNode; struct BranchNode;
struct Node { struct Node {
Node() : tag(BLANK) {}; Node() : tag(BLANK){};
explicit Node(const pptr<LeafNode> &leaf_) : tag(LEAF), leaf(leaf_) {}; explicit Node(const pptr<LeafNode> &leaf_) : tag(LEAF), leaf(leaf_){};
explicit Node(const BranchNode *branch_) : tag(BRANCH), branch(branch_) {}; explicit Node(const BranchNode *branch_) : tag(BRANCH), branch(branch_){};
Node(const Node &other) { copy(other); }; Node(const Node &other) { copy(other); };
void copy(const Node &other) { void copy(const Node &other) {
tag = other.tag; tag = other.tag;
switch (tag) { switch (tag) {
case LEAF: { case LEAF: {
leaf = other.leaf; break; leaf = other.leaf;
break;
} }
case BRANCH: { case BRANCH: {
branch = other.branch; break; branch = other.branch;
break;
} }
default:; default:;
} }
} }
Node &operator=(const Node &other) { copy(other); return *this; } Node &operator=(const Node &other) {
copy(other);
return *this;
}
Node &operator=(const pptr<LeafNode> &leaf_) { Node &operator=(const pptr<LeafNode> &leaf_) {
tag = LEAF; leaf = leaf_; return *this; tag = LEAF;
leaf = leaf_;
return *this;
} }
Node &operator=(BranchNode *const branch_) { Node &operator=(BranchNode *const branch_) {
tag = BRANCH; branch = branch_; return *this; tag = BRANCH;
branch = branch_;
return *this;
} }
enum NodeType { enum NodeType { BLANK, LEAF, BRANCH } tag;
BLANK, LEAF, BRANCH
} tag;
union { union {
pptr<LeafNode> leaf; pptr<LeafNode> leaf;
...@@ -115,7 +124,6 @@ class BitHPBPTree { ...@@ -115,7 +124,6 @@ class BitHPBPTree {
* A structure for representing a leaf node of a B+ tree. * A structure for representing a leaf node of a B+ tree.
*/ */
struct alignas(64) LeafNode { struct alignas(64) LeafNode {
static constexpr auto NUM_KEYS = M; static constexpr auto NUM_KEYS = M;
using KEY_TYPE = KeyType; using KEY_TYPE = KeyType;
...@@ -127,7 +135,7 @@ class BitHPBPTree { ...@@ -127,7 +135,7 @@ class BitHPBPTree {
static constexpr auto BitsetSize = ((M + 63) / 64) * 8; ///< number * size of words static constexpr auto BitsetSize = ((M + 63) / 64) * 8; ///< number * size of words
static constexpr auto PaddingSize = (64 - (BitsetSize + 32) % 64) % 64; static constexpr auto PaddingSize = (64 - (BitsetSize + 32) % 64) % 64;
p<std::bitset<M>> bits; ///< bitset for valid entries p<dbis::Bitmap<M>> bits; ///< bitmap for valid entries
pptr<LeafNode> nextLeaf; ///< pointer to the subsequent sibling pptr<LeafNode> nextLeaf; ///< pointer to the subsequent sibling
pptr<LeafNode> prevLeaf; ///< pointer to the preceeding sibling pptr<LeafNode> prevLeaf; ///< pointer to the preceeding sibling
char padding[PaddingSize]; ///< padding to align keys to 64 bytes char padding[PaddingSize]; ///< padding to align keys to 64 bytes
...@@ -140,18 +148,17 @@ class BitHPBPTree { ...@@ -140,18 +148,17 @@ class BitHPBPTree {
* The rightmost child is always at position N. * The rightmost child is always at position N.
*/ */
struct alignas(64) BranchNode { struct alignas(64) BranchNode {
static constexpr auto NUM_KEYS = N; static constexpr auto NUM_KEYS = N;
using KEY_TYPE = KeyType; using KEY_TYPE = KeyType;
/** /**
* Constructor for creating a new empty branch node. * Constructor for creating a new empty branch node.
*/ */
BranchNode(){} BranchNode() {}
std::bitset<N> bits; ///< bitset for valid entries dbis::Bitmap<N> bits; ///< bitmap for valid entries
std::array<KeyType, N> keys; ///< the actual keys std::array<KeyType, N> keys; ///< the actual keys
std::array<Node, N + 1> children; ///< pointers to child nodes (BranchNode or LeafNode) std::array<Node, N + 1> children; ///< pointers to child nodes (BranchNode or LeafNode)
}; };
/** /**
...@@ -187,13 +194,9 @@ class BitHPBPTree { ...@@ -187,13 +194,9 @@ class BitHPBPTree {
/** /**
* Create a new empty branch node. * Create a new empty branch node.
*/ */
BranchNode *newBranchNode() { BranchNode *newBranchNode() { return new BranchNode(); }
return new BranchNode();
}
BranchNode *newBranchNode(const BranchNode *other) { BranchNode *newBranchNode(const BranchNode *other) { return new BranchNode(*other); }
return new BranchNode(*other);
}
/** /**
* Remove/delete an existing branch node. * Remove/delete an existing branch node.
...@@ -209,9 +212,9 @@ class BitHPBPTree { ...@@ -209,9 +212,9 @@ class BitHPBPTree {
* A structure for passing information about a node split to the caller. * A structure for passing information about a node split to the caller.
*/ */
struct SplitInfo { struct SplitInfo {
KeyType key; ///< the key at which the node was split KeyType key; ///< the key at which the node was split
Node leftChild; ///< the resulting lhs child node Node leftChild; ///< the resulting lhs child node
Node rightChild; ///< the resulting rhs child node Node rightChild; ///< the resulting rhs child node
}; };
static constexpr pobj_alloc_class_desc AllocClass{256, 64, 1, POBJ_HEADER_COMPACT}; static constexpr pobj_alloc_class_desc AllocClass{256, 64, 1, POBJ_HEADER_COMPACT};
...@@ -231,7 +234,7 @@ class BitHPBPTree { ...@@ -231,7 +234,7 @@ class BitHPBPTree {
pptr<LeafNode> currentNode; pptr<LeafNode> currentNode;
size_t currentPosition; size_t currentPosition;
public: public:
iterator() : currentNode(nullptr), currentPosition(0) {} iterator() : currentNode(nullptr), currentPosition(0) {}
iterator(const Node &root, size_t d) { iterator(const Node &root, size_t d) {
...@@ -241,16 +244,16 @@ class BitHPBPTree { ...@@ -241,16 +244,16 @@ class BitHPBPTree {
currentNode = node.leaf; currentNode = node.leaf;
currentPosition = 0; currentPosition = 0;
const auto &nodeBits = currentNode->bits.get_ro(); const auto &nodeBits = currentNode->bits.get_ro();
while(!nodeBits.test(currentPosition)) ++currentPosition; while (!nodeBits.test(currentPosition)) ++currentPosition;
} }
iterator &operator++() { iterator &operator++() {
if (currentPosition >= M-1) { if (currentPosition >= M - 1) {
currentNode = currentNode->nextLeaf; currentNode = currentNode->nextLeaf;
currentPosition = 0; currentPosition = 0;
if (currentNode == nullptr) return *this; if (currentNode == nullptr) return *this;
const auto &nodeBits = currentNode->bits.get_ro(); const auto &nodeBits = currentNode->bits.get_ro();
while(!nodeBits.test(currentPosition)) ++currentPosition; while (!nodeBits.test(currentPosition)) ++currentPosition;
} else { } else {
if (!currentNode->bits.get_ro().test(++currentPosition)) ++(*this); if (!currentNode->bits.get_ro().test(++currentPosition)) ++(*this);
} }
...@@ -287,7 +290,6 @@ class BitHPBPTree { ...@@ -287,7 +290,6 @@ class BitHPBPTree {
iterator end() { return iterator(); } iterator end() { return iterator(); }
/** /**
* Alias for a function passed to the scan method. * Alias for a function passed to the scan method.
*/ */
...@@ -297,7 +299,7 @@ class BitHPBPTree { ...@@ -297,7 +299,7 @@ class BitHPBPTree {
* Constructor for creating a new B+ tree. * Constructor for creating a new B+ tree.
*/ */
explicit BitHPBPTree(struct pobj_alloc_class_desc _alloc) : depth(0), alloc_class(_alloc) { explicit BitHPBPTree(struct pobj_alloc_class_desc _alloc) : depth(0), alloc_class(_alloc) {
// BitHPBPTree() : depth(0) { // BitHPBPTree() : depth(0) {
rootNode = newLeafNode(); rootNode = newLeafNode();
leafList = rootNode.leaf; leafList = rootNode.leaf;
LOG("created new tree with sizeof(BranchNode) = " LOG("created new tree with sizeof(BranchNode) = "
...@@ -389,10 +391,10 @@ class BitHPBPTree { ...@@ -389,10 +391,10 @@ class BitHPBPTree {
void recover() { void recover() {
LOG("Starting RECOVERY of BitHPBPTree"); LOG("Starting RECOVERY of BitHPBPTree");
pptr<LeafNode> currentLeaf = leafList; pptr<LeafNode> currentLeaf = leafList;
if(leafList == nullptr){ if (leafList == nullptr) {
LOG("No data to recover HPBPTree"); LOG("No data to recover HPBPTree");
} }
if(leafList->nextLeaf == nullptr){ if (leafList->nextLeaf == nullptr) {
/// The index has only one node, so the leaf node becomes the root node /// The index has only one node, so the leaf node becomes the root node
rootNode = leafList; rootNode = leafList;
depth = 0; depth = 0;
...@@ -413,11 +415,12 @@ class BitHPBPTree { ...@@ -413,11 +415,12 @@ class BitHPBPTree {
* Print the structure and content of the B+ tree to stdout. * Print the structure and content of the B+ tree to stdout.
*/ */
void print() const { void print() const {
if (depth == 0) printLeafNode(0u, rootNode.leaf); if (depth == 0)
else printBranchNode(0u, rootNode.branch); printLeafNode(0u, rootNode.leaf);
else
printBranchNode(0u, rootNode.branch);
} }
/** /**
* Perform a scan over all key-value pairs stored in the B+ tree. * Perform a scan over all key-value pairs stored in the B+ tree.
* For each entry the given function @func is called. * For each entry the given function @func is called.
...@@ -471,7 +474,10 @@ class BitHPBPTree { ...@@ -471,7 +474,10 @@ class BitHPBPTree {
const auto &key = leafKeys[i]; const auto &key = leafKeys[i];
if (key < minKey) continue; if (key < minKey) continue;
if (key > maxKey) { higherThanMax = true; continue; }; if (key > maxKey) {
higherThanMax = true;
continue;
};
const auto &val = leafValues[i]; const auto &val = leafValues[i];
func(key, val); func(key, val);
...@@ -529,62 +535,65 @@ class BitHPBPTree { ...@@ -529,62 +535,65 @@ class BitHPBPTree {
* @param pos the position of the child node @leaf in the @c children array of the branch node * @param pos the position of the child node @leaf in the @c children array of the branch node
* @param leaf the node at which the underflow occured * @param leaf the node at which the underflow occured
*/ */
void underflowAtLeafLevel(BranchNode * const node, const unsigned int pos, pptr<LeafNode> &leaf) { void underflowAtLeafLevel(BranchNode *const node, const unsigned int pos, pptr<LeafNode> &leaf) {
assert(pos <= N); assert(pos <= N);
auto &nodeRef = *node; auto &nodeRef = *node;
auto &leafRef = *leaf; auto &leafRef = *leaf;
auto prevNumKeys = 0u; auto prevNumKeys = 0u;
constexpr auto middle = (M + 1) / 2; constexpr auto middle = (M + 1) / 2;
/// 1. we check whether we can rebalance with one of the siblings but only if both nodes have /// 1. we check whether we can rebalance with one of the siblings but only if both nodes have
/// the same direct parent /// the same direct parent
if (pos > 0 && (prevNumKeys = leafRef.prevLeaf->bits.get_ro().count()) > middle) { if (pos > 0 && (prevNumKeys = leafRef.prevLeaf->bits.get_ro().count()) > middle) {
/// we have a sibling at the left for rebalancing the keys /// we have a sibling at the left for rebalancing the keys
balanceLeafNodes(leafRef.prevLeaf, leaf); balanceLeafNodes(leafRef.prevLeaf, leaf);
const auto newMin = leafRef.keys.get_ro()[findMinKeyPos(leafRef.keys.get_ro(), const auto newMin =
leafRef.bits.get_ro())]; leafRef.keys.get_ro()[findMinKeyPos(leafRef.keys.get_ro(), leafRef.bits.get_ro())];
const auto prevPos = findMinKeyPosGreaterThan(leafRef.keys.get_ro(), leafRef.bits.get_ro(), const auto prevPos =
newMin); findMinKeyPosGreaterThan(leafRef.keys.get_ro(), leafRef.bits.get_ro(), newMin);
nodeRef.keys[prevPos] = newMin; nodeRef.keys[prevPos] = newMin;
} else if (pos < N && leafRef.nextLeaf->bits.get_ro().count() > middle) { } else if (pos < N && leafRef.nextLeaf->bits.get_ro().count() > middle) {
/// we have a sibling at the right for rebalancing the keys /// we have a sibling at the right for rebalancing the keys
balanceLeafNodes(leafRef.nextLeaf, leaf); balanceLeafNodes(leafRef.nextLeaf, leaf);
const auto &nextLeaf = *leafRef.nextLeaf; const auto &nextLeaf = *leafRef.nextLeaf;
nodeRef.keys[pos] = nodeRef.keys[pos] =
nextLeaf.keys.get_ro()[findMinKeyPos(nextLeaf.keys.get_ro(), nextLeaf.bits.get_ro())]; nextLeaf.keys.get_ro()[findMinKeyPos(nextLeaf.keys.get_ro(), nextLeaf.bits.get_ro())];
} else { } else {
/// 2. if this fails we have to merge two leaf nodes but only if both nodes have the same /// 2. if this fails we have to merge two leaf nodes but only if both nodes have the same
/// direct parent /// direct parent
pptr<LeafNode> survivor = nullptr; pptr<LeafNode> survivor = nullptr;
if (findMinKeyPos(nodeRef.keys, nodeRef.bits) != pos && prevNumKeys <= middle) { if (findMinKeyPos(nodeRef.keys, nodeRef.bits) != pos && prevNumKeys <= middle) {
/// merge left /// merge left
survivor = mergeLeafNodes(leafRef.prevLeaf, leaf); survivor = mergeLeafNodes(leafRef.prevLeaf, leaf);
deleteLeafNode(leaf); deleteLeafNode(leaf);
/// move to next left slot /// move to next left slot
const auto prevPos = (pos == N) ? const auto prevPos =
findMaxKeyPos(nodeRef.keys, nodeRef.bits) : ///< we need a new rightmost node (pos == N) ? findMaxKeyPos(nodeRef.keys, nodeRef.bits)
findMaxKeyPosSmallerThan(nodeRef.keys, nodeRef.bits, nodeRef.keys[pos]); : ///< we need a new rightmost node
nodeRef.children[pos] = nodeRef.children[prevPos]; findMaxKeyPosSmallerThan(nodeRef.keys, nodeRef.bits, nodeRef.keys[pos]);
nodeRef.bits.reset(prevPos); nodeRef.children[pos] = nodeRef.children[prevPos];
} else if (pos < N && leafRef.nextLeaf->bits.get_ro().count() <= middle) { nodeRef.bits.reset(prevPos);
/// merge right } else if (pos < N && leafRef.nextLeaf->bits.get_ro().count() <= middle) {
survivor = mergeLeafNodes(leaf, leafRef.nextLeaf); /// merge right
deleteLeafNode(leafRef.nextLeaf); survivor = mergeLeafNodes(leaf, leafRef.nextLeaf);
/// move to next right slot deleteLeafNode(leafRef.nextLeaf);
const auto nextPos = findMinKeyPosGreaterThan(nodeRef.keys, nodeRef.bits, nodeRef.keys[pos]); /// move to next right slot
nodeRef.children[nextPos] = nodeRef.children[pos]; const auto nextPos =
nodeRef.bits.reset(pos); findMinKeyPosGreaterThan(nodeRef.keys, nodeRef.bits, nodeRef.keys[pos]);
} else assert(false); ///< this shouldn't happen?! nodeRef.children[nextPos] = nodeRef.children[pos];
nodeRef.bits.reset(pos);
if (nodeRef.bits.count() == 0) { } else
/// This is a special case that happens only if the current node is the root node. Now, we assert(false); ///< this shouldn't happen?!
/// have to replace the branch root node by a leaf node.
rootNode = survivor; if (nodeRef.bits.count() == 0) {
--depth; /// This is a special case that happens only if the current node is the root node. Now, we
} /// have to replace the branch root node by a leaf node.
rootNode = survivor;
--depth;
} }
} }
}
/** /**
* Merge two leaf nodes by moving all elements from @c node2 to @c node1. * Merge two leaf nodes by moving all elements from @c node2 to @c node1.
...@@ -593,7 +602,7 @@ class BitHPBPTree { ...@@ -593,7 +602,7 @@ class BitHPBPTree {
* @param node2 the source node * @param node2 the source node
* @return the merged node (always @c node1) * @return the merged node (always @c node1)
*/ */
pptr<LeafNode> mergeLeafNodes(const pptr<LeafNode> &node1, const pptr<LeafNode> &node2) { pptr<LeafNode> mergeLeafNodes(const pptr<LeafNode> &node1, const pptr<LeafNode> &node2) {
assert(node1 != nullptr); assert(node1 != nullptr);
assert(node2 != nullptr); assert(node2 != nullptr);
auto &node1Ref = *node1; auto &node1Ref = *node1;
...@@ -609,7 +618,7 @@ class BitHPBPTree { ...@@ -609,7 +618,7 @@ class BitHPBPTree {
const auto &node2Values = node2Ref.values.get_ro(); const auto &node2Values = node2Ref.values.get_ro();
for (auto i = 0u; i < M; ++i) { for (auto i = 0u; i < M; ++i) {
if (node2Bits.test(i)) { if (node2Bits.test(i)) {
const auto u = BitOperations::getFreeZero(node1Bits); const auto u = node1Bits.getFreeZero();
node1Keys[u] = node2Keys[i]; node1Keys[u] = node2Keys[i];
node1Values[u] = node2Values[i]; node1Values[u] = node2Values[i];
node1Bits.set(u); node1Bits.set(u);
...@@ -650,7 +659,7 @@ class BitHPBPTree { ...@@ -650,7 +659,7 @@ class BitHPBPTree {
/// move from one node to a node with larger keys /// move from one node to a node with larger keys
for (auto i = 0u; i < toMove; ++i) { for (auto i = 0u; i < toMove; ++i) {
const auto max = findMaxKeyPos(donorKeys, donorBits); const auto max = findMaxKeyPos(donorKeys, donorBits);
const auto u = BitOperations::getFreeZero(receiverBits); const auto u = receiverBits.getFreeZero();
/// move the donor's maximum key to the receiver /// move the donor's maximum key to the receiver
receiverKeys[u] = donorKeys[max]; receiverKeys[u] = donorKeys[max];
receiverValues[u] = donorValues[max]; receiverValues[u] = donorValues[max];
...@@ -661,7 +670,7 @@ class BitHPBPTree { ...@@ -661,7 +670,7 @@ class BitHPBPTree {
/// move from one node to a node with smaller keys /// move from one node to a node with smaller keys
for (auto i = 0u; i < toMove; ++i) { for (auto i = 0u; i < toMove; ++i) {
const auto min = findMinKeyPos(donorKeys, donorBits); const auto min = findMinKeyPos(donorKeys, donorBits);
const auto u = BitOperations::getFreeZero(receiverBits); const auto u = receiverBits.getFreeZero();
/// move the donor's minimum key to the receiver /// move the donor's minimum key to the receiver
receiverKeys[u] = donorKeys[min]; receiverKeys[u] = donorKeys[min];
receiverValues[u] = donorValues[min]; receiverValues[u] = donorValues[min];
...@@ -683,7 +692,7 @@ class BitHPBPTree { ...@@ -683,7 +692,7 @@ class BitHPBPTree {
* @param key the key to be deleted * @param key the key to be deleted
* @return true if the entry was deleted * @return true if the entry was deleted
*/ */
bool eraseFromBranchNode(BranchNode * const node, const unsigned int d, const KeyType &key) { bool eraseFromBranchNode(BranchNode *const node, const unsigned int d, const KeyType &key) {
assert(d >= 1); assert(d >= 1);
auto &nodeRef = *node; auto &nodeRef = *node;
bool deleted = false; bool deleted = false;
...@@ -728,13 +737,14 @@ class BitHPBPTree { ...@@ -728,13 +737,14 @@ class BitHPBPTree {
* @param child the node at which the underflow occured * @param child the node at which the underflow occured
* @return the (possibly new) child node (in case of a merge) * @return the (possibly new) child node (in case of a merge)
*/ */
BranchNode *underflowAtBranchLevel(BranchNode * const node, const unsigned int pos, BranchNode *underflowAtBranchLevel(BranchNode *const node, const unsigned int pos,
BranchNode * child) { BranchNode *child) {
assert(node != nullptr); assert(node != nullptr);
assert(child != nullptr); assert(child != nullptr);
auto &nodeRef = *node;