diff --git a/implementations/onblocks/datanodestore/DataLeafNode.cpp b/implementations/onblocks/datanodestore/DataLeafNode.cpp index 3df4b546..6f9b6b4e 100644 --- a/implementations/onblocks/datanodestore/DataLeafNode.cpp +++ b/implementations/onblocks/datanodestore/DataLeafNode.cpp @@ -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); diff --git a/implementations/onblocks/datatreestore/DataTree.cpp b/implementations/onblocks/datatreestore/DataTree.cpp index 621b4fea..1dc158e0 100644 --- a/implementations/onblocks/datatreestore/DataTree.cpp +++ b/implementations/onblocks/datatreestore/DataTree.cpp @@ -7,6 +7,7 @@ #include "impl/algorithms.h" #include "messmer/cpp-utils/pointer.h" +#include using blockstore::Key; using blobstore::onblocks::datanodestore::DataNodeStore; @@ -102,6 +103,57 @@ unique_ptr DataTree::releaseRootNode() { return std::move(_rootNode); } +void DataTree::traverseLeaves(uint32_t beginIndex, uint32_t endIndex, function 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 func) { + DataLeafNode *leaf = dynamic_cast(root); + if (leaf != nullptr) { + assert(beginIndex <= 1 && endIndex <= 1); + if (beginIndex == 0 && endIndex == 1) { + func(leaf, leafOffset); + } + return; + } + + DataInnerNode *inner = dynamic_cast(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); + } +} } } diff --git a/implementations/onblocks/datatreestore/DataTree.h b/implementations/onblocks/datatreestore/DataTree.h index 52245429..92a255e8 100644 --- a/implementations/onblocks/datatreestore/DataTree.h +++ b/implementations/onblocks/datatreestore/DataTree.h @@ -31,6 +31,8 @@ public: void flush() const; + void traverseLeaves(uint32_t beginIndex, uint32_t endIndex, std::function func); + private: datanodestore::DataNodeStore *_nodeStore; std::unique_ptr _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 func); + DISALLOW_COPY_AND_ASSIGN(DataTree); }; diff --git a/test/implementations/onblocks/datatreestore/traverseLeaves/DataTreeTest_TraverseLeaves.cpp b/test/implementations/onblocks/datatreestore/traverseLeaves/DataTreeTest_TraverseLeaves.cpp new file mode 100644 index 00000000..2c8cdea3 --- /dev/null +++ b/test/implementations/onblocks/datatreestore/traverseLeaves/DataTreeTest_TraverseLeaves.cpp @@ -0,0 +1,190 @@ +#include "../testutils/DataTreeTest.h" +#include + +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?