Written data tree leaf traversal and some test cases
This commit is contained in:
parent
9d9c090148
commit
97cf6a6e40
@ -11,6 +11,8 @@ namespace blobstore {
|
||||
namespace onblocks {
|
||||
namespace datanodestore {
|
||||
|
||||
constexpr uint32_t DataLeafNode::MAX_STORED_BYTES;
|
||||
|
||||
DataLeafNode::DataLeafNode(DataNodeView view)
|
||||
: DataNode(std::move(view)) {
|
||||
assert(*node().Depth() == 0);
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "impl/algorithms.h"
|
||||
|
||||
#include "messmer/cpp-utils/pointer.h"
|
||||
#include <cmath>
|
||||
|
||||
using blockstore::Key;
|
||||
using blobstore::onblocks::datanodestore::DataNodeStore;
|
||||
@ -102,6 +103,57 @@ unique_ptr<DataNode> DataTree::releaseRootNode() {
|
||||
return std::move(_rootNode);
|
||||
}
|
||||
|
||||
void DataTree::traverseLeaves(uint32_t beginIndex, uint32_t endIndex, function<void (DataLeafNode*, uint32_t)> func) {
|
||||
assert(beginIndex <= endIndex);
|
||||
//TODO assert(beginIndex <= numLeaves());
|
||||
//TODO assert(endIndex <= numLeaves());
|
||||
traverseLeaves(_rootNode.get(), 0, beginIndex, endIndex, func);
|
||||
}
|
||||
|
||||
//TODO Put intPow, ceilDivision, maxZeroSubtraction into utils and write test cases
|
||||
|
||||
uint32_t intPow(uint32_t base, uint32_t exponent) {
|
||||
uint32_t result = 1;
|
||||
for(int i = 0; i < exponent; ++i) {
|
||||
result *= base;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t ceilDivision(uint32_t dividend, uint32_t divisor) {
|
||||
return (dividend + divisor - 1)/divisor;
|
||||
}
|
||||
|
||||
uint32_t maxZeroSubtraction(uint32_t minuend, uint32_t subtrahend) {
|
||||
if (minuend < subtrahend) {
|
||||
return 0u;
|
||||
}
|
||||
return minuend-subtrahend;
|
||||
}
|
||||
|
||||
void DataTree::traverseLeaves(DataNode *root, uint32_t leafOffset, uint32_t beginIndex, uint32_t endIndex, function<void (DataLeafNode*, uint32_t)> func) {
|
||||
DataLeafNode *leaf = dynamic_cast<DataLeafNode*>(root);
|
||||
if (leaf != nullptr) {
|
||||
assert(beginIndex <= 1 && endIndex <= 1);
|
||||
if (beginIndex == 0 && endIndex == 1) {
|
||||
func(leaf, leafOffset);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
DataInnerNode *inner = dynamic_cast<DataInnerNode*>(root);
|
||||
uint32_t leavesPerChild = intPow(DataInnerNode::MAX_STORED_CHILDREN, root->depth()-1);
|
||||
uint32_t beginChild = beginIndex/leavesPerChild;
|
||||
uint32_t endChild = ceilDivision(endIndex, leavesPerChild);
|
||||
|
||||
for (uint32_t childIndex = beginChild; childIndex < endChild; ++childIndex) {
|
||||
uint32_t childOffset = childIndex * leavesPerChild;
|
||||
uint32_t localBeginIndex = maxZeroSubtraction(beginIndex, childOffset);
|
||||
uint32_t localEndIndex = std::min(leavesPerChild, endIndex - childOffset);
|
||||
auto child = _nodeStore->load(inner->getChild(childIndex)->key());
|
||||
traverseLeaves(child.get(), leafOffset + childOffset, localBeginIndex, localEndIndex, func);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,8 @@ public:
|
||||
|
||||
void flush() const;
|
||||
|
||||
void traverseLeaves(uint32_t beginIndex, uint32_t endIndex, std::function<void (datanodestore::DataLeafNode*, uint32_t)> func);
|
||||
|
||||
private:
|
||||
datanodestore::DataNodeStore *_nodeStore;
|
||||
std::unique_ptr<datanodestore::DataNode> _rootNode;
|
||||
@ -45,6 +47,8 @@ private:
|
||||
void deleteLastChildSubtree(datanodestore::DataInnerNode *node);
|
||||
void ifRootHasOnlyOneChildReplaceRootWithItsChild();
|
||||
|
||||
void traverseLeaves(datanodestore::DataNode *root, uint32_t leafOffset, uint32_t beginIndex, uint32_t endIndex, std::function<void (datanodestore::DataLeafNode*, uint32_t)> func);
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(DataTree);
|
||||
};
|
||||
|
||||
|
@ -0,0 +1,190 @@
|
||||
#include "../testutils/DataTreeTest.h"
|
||||
#include <google/gmock/gmock.h>
|
||||
|
||||
using ::testing::_;
|
||||
|
||||
using blobstore::onblocks::datanodestore::DataLeafNode;
|
||||
using blobstore::onblocks::datanodestore::DataInnerNode;
|
||||
using blobstore::onblocks::datanodestore::DataNode;
|
||||
using blobstore::onblocks::datatreestore::DataTree;
|
||||
using blockstore::Key;
|
||||
|
||||
class TraversorMock {
|
||||
public:
|
||||
MOCK_METHOD2(called, void(DataLeafNode*, uint32_t));
|
||||
};
|
||||
|
||||
MATCHER_P(KeyEq, expected, "node key equals") {
|
||||
return arg->key() == expected;
|
||||
}
|
||||
|
||||
class DataTreeTest_TraverseLeaves: public DataTreeTest {
|
||||
public:
|
||||
void TraverseLeaves(DataNode *root, uint32_t beginIndex, uint32_t endIndex) {
|
||||
root->flush();
|
||||
auto tree = treeStore.load(root->key());
|
||||
tree->traverseLeaves(beginIndex, endIndex, [this] (DataLeafNode *leaf, uint32_t nodeIndex) {
|
||||
traversor.called(leaf, nodeIndex);
|
||||
});
|
||||
}
|
||||
TraversorMock traversor;
|
||||
};
|
||||
|
||||
TEST_F(DataTreeTest_TraverseLeaves, TraverseSingleLeafTree) {
|
||||
auto root = CreateLeaf();
|
||||
EXPECT_CALL(traversor, called(KeyEq(root->key()), 0)).Times(1);
|
||||
|
||||
TraverseLeaves(root.get(), 0, 1);
|
||||
}
|
||||
|
||||
TEST_F(DataTreeTest_TraverseLeaves, TraverseNothingInSingleLeafTree1) {
|
||||
auto root = CreateLeaf();
|
||||
EXPECT_CALL(traversor, called(_, _)).Times(0);
|
||||
|
||||
TraverseLeaves(root.get(), 0, 0);
|
||||
}
|
||||
|
||||
TEST_F(DataTreeTest_TraverseLeaves, TraverseNothingInSingleLeafTree2) {
|
||||
auto root = CreateLeaf();
|
||||
EXPECT_CALL(traversor, called(_, _)).Times(0);
|
||||
|
||||
TraverseLeaves(root.get(), 1, 1);
|
||||
}
|
||||
|
||||
TEST_F(DataTreeTest_TraverseLeaves, TraverseFirstLeafOfFullTwolevelTree) {
|
||||
auto root = CreateFullTwoLevel();
|
||||
auto firstleafkey = root->getChild(0)->key();
|
||||
EXPECT_CALL(traversor, called(KeyEq(firstleafkey), 0)).Times(1);
|
||||
|
||||
TraverseLeaves(root.get(), 0, 1);
|
||||
}
|
||||
|
||||
TEST_F(DataTreeTest_TraverseLeaves, TraverseMiddleLeafOfFullTwolevelTree) {
|
||||
auto root = CreateFullTwoLevel();
|
||||
auto leafkey = root->getChild(5)->key();
|
||||
EXPECT_CALL(traversor, called(KeyEq(leafkey), 5)).Times(1);
|
||||
|
||||
TraverseLeaves(root.get(), 5, 6);
|
||||
}
|
||||
|
||||
TEST_F(DataTreeTest_TraverseLeaves, TraverseLastLeafOfFullTwolevelTree) {
|
||||
auto root = CreateFullTwoLevel();
|
||||
auto leafkey = root->getChild(DataInnerNode::MAX_STORED_CHILDREN-1)->key();
|
||||
EXPECT_CALL(traversor, called(KeyEq(leafkey), DataInnerNode::MAX_STORED_CHILDREN-1)).Times(1);
|
||||
|
||||
TraverseLeaves(root.get(), DataInnerNode::MAX_STORED_CHILDREN-1, DataInnerNode::MAX_STORED_CHILDREN);
|
||||
}
|
||||
|
||||
TEST_F(DataTreeTest_TraverseLeaves, TraverseNothingInFullTwolevelTree1) {
|
||||
auto root = CreateFullTwoLevel();
|
||||
EXPECT_CALL(traversor, called(_, _)).Times(0);
|
||||
|
||||
TraverseLeaves(root.get(), 0, 0);
|
||||
}
|
||||
|
||||
TEST_F(DataTreeTest_TraverseLeaves, TraverseNothingInFullTwolevelTree2) {
|
||||
auto root = CreateFullTwoLevel();
|
||||
EXPECT_CALL(traversor,called(_, _)).Times(0);
|
||||
|
||||
TraverseLeaves(root.get(), DataInnerNode::MAX_STORED_CHILDREN, DataInnerNode::MAX_STORED_CHILDREN);
|
||||
}
|
||||
|
||||
TEST_F(DataTreeTest_TraverseLeaves, TraverseFirstLeafOfThreeLevelMinDataTree) {
|
||||
auto root = CreateThreeLevelMinData();
|
||||
auto firstleafkey = LoadInnerNode(root->getChild(0)->key())->getChild(0)->key();
|
||||
EXPECT_CALL(traversor, called(KeyEq(firstleafkey), 0)).Times(1);
|
||||
|
||||
TraverseLeaves(root.get(), 0, 1);
|
||||
}
|
||||
|
||||
TEST_F(DataTreeTest_TraverseLeaves, TraverseMiddleLeafOfThreeLevelMinDataTree) {
|
||||
auto root = CreateThreeLevelMinData();
|
||||
auto leafkey = LoadInnerNode(root->getChild(0)->key())->getChild(5)->key();
|
||||
EXPECT_CALL(traversor, called(KeyEq(leafkey), 5)).Times(1);
|
||||
|
||||
TraverseLeaves(root.get(), 5, 6);
|
||||
}
|
||||
|
||||
TEST_F(DataTreeTest_TraverseLeaves, TraverseLastLeafOfThreeLevelMinDataTree) {
|
||||
auto root = CreateThreeLevelMinData();
|
||||
auto leafkey = LoadInnerNode(root->getChild(1)->key())->getChild(0)->key();
|
||||
EXPECT_CALL(traversor, called(KeyEq(leafkey), DataInnerNode::MAX_STORED_CHILDREN)).Times(1);
|
||||
|
||||
TraverseLeaves(root.get(), DataInnerNode::MAX_STORED_CHILDREN, DataInnerNode::MAX_STORED_CHILDREN+1);
|
||||
}
|
||||
|
||||
TEST_F(DataTreeTest_TraverseLeaves, TraverseAllLeavesOfFullTwolevelTree) {
|
||||
auto root = CreateFullTwoLevel();
|
||||
for (int i = 0; i < DataInnerNode::MAX_STORED_CHILDREN; ++i) {
|
||||
auto leafkey = root->getChild(i)->key();
|
||||
EXPECT_CALL(traversor, called(KeyEq(leafkey), i)).Times(1);
|
||||
}
|
||||
|
||||
TraverseLeaves(root.get(), 0, DataInnerNode::MAX_STORED_CHILDREN);
|
||||
}
|
||||
|
||||
TEST_F(DataTreeTest_TraverseLeaves, TraverseAllLeavesOfThreelevelMinDataTree) {
|
||||
auto root = CreateThreeLevelMinData();
|
||||
auto node = LoadInnerNode(root->getChild(0)->key());
|
||||
for (int i = 0; i < DataInnerNode::MAX_STORED_CHILDREN; ++i) {
|
||||
auto leafkey = node->getChild(i)->key();
|
||||
EXPECT_CALL(traversor, called(KeyEq(leafkey), i)).Times(1);
|
||||
}
|
||||
Key lastleafkey = LoadInnerNode(root->getChild(1)->key())->getChild(0)->key();
|
||||
EXPECT_CALL(traversor, called(KeyEq(lastleafkey), DataInnerNode::MAX_STORED_CHILDREN)).Times(1);
|
||||
|
||||
TraverseLeaves(root.get(), 0, DataInnerNode::MAX_STORED_CHILDREN+1);
|
||||
}
|
||||
|
||||
TEST_F(DataTreeTest_TraverseLeaves, TraverseFirstChildOfThreelevelMinDataTree) {
|
||||
auto root = CreateThreeLevelMinData();
|
||||
auto node = LoadInnerNode(root->getChild(0)->key());
|
||||
for (int i = 0; i < DataInnerNode::MAX_STORED_CHILDREN; ++i) {
|
||||
auto leafkey = node->getChild(i)->key();
|
||||
EXPECT_CALL(traversor, called(KeyEq(leafkey), i)).Times(1);
|
||||
}
|
||||
|
||||
TraverseLeaves(root.get(), 0, DataInnerNode::MAX_STORED_CHILDREN);
|
||||
}
|
||||
|
||||
TEST_F(DataTreeTest_TraverseLeaves, TraverseFirstPartOfThreelevelMinDataTree) {
|
||||
auto root = CreateThreeLevelMinData();
|
||||
auto node = LoadInnerNode(root->getChild(0)->key());
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
auto leafkey = node->getChild(i)->key();
|
||||
EXPECT_CALL(traversor, called(KeyEq(leafkey), i)).Times(1);
|
||||
}
|
||||
|
||||
TraverseLeaves(root.get(), 0, 5);
|
||||
}
|
||||
|
||||
TEST_F(DataTreeTest_TraverseLeaves, TraverseInnerPartOfThreelevelMinDataTree) {
|
||||
auto root = CreateThreeLevelMinData();
|
||||
auto node = LoadInnerNode(root->getChild(0)->key());
|
||||
for (int i = 5; i < 10; ++i) {
|
||||
auto leafkey = node->getChild(i)->key();
|
||||
EXPECT_CALL(traversor, called(KeyEq(leafkey), i)).Times(1);
|
||||
}
|
||||
|
||||
TraverseLeaves(root.get(), 5, 10);
|
||||
}
|
||||
|
||||
TEST_F(DataTreeTest_TraverseLeaves, TraverseLastPartOfThreelevelMinDataTree) {
|
||||
auto root = CreateThreeLevelMinData();
|
||||
auto node = LoadInnerNode(root->getChild(0)->key());
|
||||
for (int i = 5; i < DataInnerNode::MAX_STORED_CHILDREN; ++i) {
|
||||
auto leafkey = node->getChild(i)->key();
|
||||
EXPECT_CALL(traversor, called(KeyEq(leafkey), i)).Times(1);
|
||||
}
|
||||
Key lastleafkey = LoadInnerNode(root->getChild(1)->key())->getChild(0)->key();
|
||||
EXPECT_CALL(traversor, called(KeyEq(lastleafkey), DataInnerNode::MAX_STORED_CHILDREN)).Times(1);
|
||||
|
||||
TraverseLeaves(root.get(), 5, DataInnerNode::MAX_STORED_CHILDREN+1);
|
||||
}
|
||||
|
||||
//TODO First/Inner/LastPart of FullTwoLevelTree
|
||||
//TODO Test cases with a larger threelevel tree (say 5 children being full twolevel trees)
|
||||
//TODO Some few testcases with full threelevel tree
|
||||
//TODO Some few testcases with fourlevel mindata tree
|
||||
|
||||
//TODO ...more test cases?
|
Loading…
x
Reference in New Issue
Block a user