When traversing a blob out of range, automatically create the leaves up to the traversed point

This commit is contained in:
Sebastian Messmer 2015-04-10 21:52:30 +02:00
parent bb98e9b31d
commit bee68464dc
9 changed files with 347 additions and 30 deletions

View File

@ -33,13 +33,17 @@ void BlobOnBlocks::resize(uint64_t numBytes) {
void BlobOnBlocks::traverseLeaves(uint64_t beginByte, uint64_t sizeBytes, function<void (uint64_t, DataLeafNode *leaf, uint32_t, uint32_t)> func) const {
uint64_t endByte = beginByte + sizeBytes;
assert(endByte <= size());
uint32_t firstLeaf = beginByte / _datatree->maxBytesPerLeaf();
uint32_t endLeaf = utils::ceilDivision(endByte, _datatree->maxBytesPerLeaf());
_datatree->traverseLeaves(firstLeaf, endLeaf, [&func, beginByte, endByte](DataLeafNode *leaf, uint32_t leafIndex) {
bool traversingOutOfRange = _datatree->numStoredBytes() < endByte; //TODO numBytes() inefficient
_datatree->traverseLeaves(firstLeaf, endLeaf, [&func, beginByte, endByte, endLeaf, traversingOutOfRange](DataLeafNode *leaf, uint32_t leafIndex) {
uint64_t indexOfFirstLeafByte = leafIndex * leaf->maxStoreableBytes();
uint32_t dataBegin = utils::maxZeroSubtraction(beginByte, indexOfFirstLeafByte);
uint32_t dataEnd = std::min((uint64_t)leaf->maxStoreableBytes(), endByte - indexOfFirstLeafByte);
if (leafIndex == endLeaf-1 && traversingOutOfRange) {
// If we are traversing an area that didn't exist before, then the last leaf was just created with a wrong size. We have to fix it.
leaf->resize(dataEnd);
}
func(indexOfFirstLeafByte, leaf, dataBegin, dataEnd-dataBegin);
});
}
@ -61,7 +65,7 @@ uint64_t BlobOnBlocks::tryRead(void *target, uint64_t offset, uint64_t count) co
}
void BlobOnBlocks::write(const void *source, uint64_t offset, uint64_t size) {
resizeIfSmallerThan(offset + size);
//resizeIfSmallerThan(offset + size);
traverseLeaves(offset, size, [source, offset] (uint64_t indexOfFirstLeafByte, DataLeafNode *leaf, uint32_t leafDataOffset, uint32_t leafDataSize) {
//TODO Simplify formula, make it easier to understand
leaf->write((uint8_t*)source + indexOfFirstLeafByte - offset + leafDataOffset, leafDataOffset, leafDataSize);

View File

@ -21,14 +21,14 @@ public:
return _baseTree->maxBytesPerLeaf();
}
void traverseLeaves(uint32_t beginIndex, uint32_t endIndex, std::function<void (const datanodestore::DataLeafNode*, uint32_t)> func) const {
return const_cast<const datatreestore::DataTree*>(_baseTree)->traverseLeaves(beginIndex, endIndex, func);
}
void traverseLeaves(uint32_t beginIndex, uint32_t endIndex, std::function<void (datanodestore::DataLeafNode*, uint32_t)> func) {
return _baseTree->traverseLeaves(beginIndex, endIndex, func);
}
uint32_t numLeaves() const {
return _baseTree->numLeaves();
}
void resizeNumBytes(uint64_t newNumBytes) {
return _baseTree->resizeNumBytes(newNumBytes);
}

View File

@ -24,6 +24,7 @@ using std::function;
using boost::shared_mutex;
using boost::shared_lock;
using boost::unique_lock;
using std::vector;
using cpputils::dynamic_pointer_move;
using cpputils::optional_ownership_ptr;
@ -82,8 +83,9 @@ unique_ptr<DataLeafNode> DataTree::addDataLeafAt(DataInnerNode *insertPos) {
return new_leaf;
}
optional_ownership_ptr<DataNode> DataTree::createChainOfInnerNodes(unsigned int num, DataLeafNode *leaf) {
optional_ownership_ptr<DataNode> chain = cpputils::WithoutOwnership<DataNode>(leaf);
optional_ownership_ptr<DataNode> DataTree::createChainOfInnerNodes(unsigned int num, DataNode *child) {
//TODO This function is implemented twice, once with optional_ownership_ptr, once with unique_ptr. Redundancy!
optional_ownership_ptr<DataNode> chain = cpputils::WithoutOwnership<DataNode>(child);
for(unsigned int i=0; i<num; ++i) {
auto newnode = _nodeStore->createNewInnerNode(*chain);
chain = cpputils::WithOwnership<DataNode>(std::move(newnode));
@ -91,11 +93,27 @@ optional_ownership_ptr<DataNode> DataTree::createChainOfInnerNodes(unsigned int
return chain;
}
unique_ptr<DataLeafNode> DataTree::addDataLeafToFullTree() {
unique_ptr<DataNode> DataTree::createChainOfInnerNodes(unsigned int num, unique_ptr<DataNode> child) {
unique_ptr<DataNode> chain = std::move(child);
for(unsigned int i=0; i<num; ++i) {
chain = _nodeStore->createNewInnerNode(*chain);
}
return chain;
}
DataInnerNode* DataTree::increaseTreeDepth(unsigned int levels) {
assert(levels >= 1);
auto copyOfOldRoot = _nodeStore->createNewNodeAsCopyFrom(*_rootNode);
auto newRootNode = DataNode::convertToNewInnerNode(std::move(_rootNode), *copyOfOldRoot);
auto newLeaf = addDataLeafAt(newRootNode.get());
auto chain = createChainOfInnerNodes(levels-1, copyOfOldRoot.get());
auto newRootNode = DataNode::convertToNewInnerNode(std::move(_rootNode), *chain);
DataInnerNode *result = newRootNode.get();
_rootNode = std::move(newRootNode);
return result;
}
unique_ptr<DataLeafNode> DataTree::addDataLeafToFullTree() {
DataInnerNode *rootNode = increaseTreeDepth(1);
auto newLeaf = addDataLeafAt(rootNode);
return newLeaf;
}
@ -111,22 +129,46 @@ unique_ptr<DataNode> DataTree::releaseRootNode() {
return std::move(_rootNode);
}
//TODO Test numLeaves()
uint32_t DataTree::numLeaves() const {
//TODO Direct calculating the number of leaves would be faster
uint64_t currentNumBytes = _numStoredBytes();
if(currentNumBytes == 0) {
//We always have at least one leaf
currentNumBytes = 1;
}
return utils::ceilDivision(currentNumBytes, _nodeStore->layout().maxBytesPerLeaf());
}
void DataTree::traverseLeaves(uint32_t beginIndex, uint32_t endIndex, function<void (DataLeafNode*, uint32_t)> func) {
const_cast<const DataTree*>(this)->traverseLeaves(beginIndex, endIndex, [func](const DataLeafNode* leaf, uint32_t leafIndex) {
func(const_cast<DataLeafNode*>(leaf), leafIndex);
});
}
void DataTree::traverseLeaves(uint32_t beginIndex, uint32_t endIndex, function<void (const DataLeafNode*, uint32_t)> func) const {
shared_lock<shared_mutex> lock(_mutex);
unique_lock<shared_mutex> lock(_mutex); //TODO Only lock when resizing
assert(beginIndex <= endIndex);
//TODO assert(beginIndex <= numLeaves());
//TODO assert(endIndex <= numLeaves());
traverseLeaves(_rootNode.get(), 0, beginIndex, endIndex, func);
uint8_t neededTreeDepth = utils::ceilLog(_nodeStore->layout().maxChildrenPerInnerNode(), endIndex);
uint32_t numLeaves = this->numLeaves();
if (_rootNode->depth() < neededTreeDepth) {
//TODO Test cases that actually increase it here by 0 level / 1 level / more than 1 level
increaseTreeDepth(neededTreeDepth - _rootNode->depth());
}
if (numLeaves < endIndex) {
//TODO Can this case be efficiently combined with the traversing?
LastLeaf(_rootNode.get())->resize(_nodeStore->layout().maxBytesPerLeaf());
}
uint32_t lastLeafIndex = std::max(numLeaves, endIndex) - 1;
if (numLeaves < beginIndex) {
//TODO Test cases with numLeaves < / >= beginIndex
return _traverseLeaves(_rootNode.get(), 0, numLeaves, endIndex, [beginIndex, numLeaves, lastLeafIndex, &func](DataLeafNode* node, uint32_t index) {
if (index >= beginIndex) {
func(node, index);
}
});
} else {
return _traverseLeaves(_rootNode.get(), 0, beginIndex, endIndex, func);
}
}
void DataTree::traverseLeaves(const DataNode *root, uint32_t leafOffset, uint32_t beginIndex, uint32_t endIndex, function<void (const DataLeafNode*, uint32_t)> func) const {
const DataLeafNode *leaf = dynamic_cast<const DataLeafNode*>(root);
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) {
@ -135,20 +177,42 @@ void DataTree::traverseLeaves(const DataNode *root, uint32_t leafOffset, uint32_
return;
}
const DataInnerNode *inner = dynamic_cast<const DataInnerNode*>(root);
DataInnerNode *inner = dynamic_cast<DataInnerNode*>(root);
uint32_t leavesPerChild = leavesPerFullChild(*inner);
uint32_t beginChild = beginIndex/leavesPerChild;
uint32_t endChild = utils::ceilDivision(endIndex, leavesPerChild);
vector<unique_ptr<DataNode>> children = getOrCreateChildren(inner, beginChild, endChild);
for (uint32_t childIndex = beginChild; childIndex < endChild; ++childIndex) {
uint32_t childOffset = childIndex * leavesPerChild;
uint32_t localBeginIndex = utils::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);
auto child = std::move(children[childIndex-beginChild]);
_traverseLeaves(child.get(), leafOffset + childOffset, localBeginIndex, localEndIndex, func);
}
}
vector<unique_ptr<DataNode>> DataTree::getOrCreateChildren(DataInnerNode *node, uint32_t begin, uint32_t end) {
vector<unique_ptr<DataNode>> children;
children.reserve(end-begin);
for (uint32_t childIndex = begin; childIndex < std::min(node->numChildren(), end); ++childIndex) {
children.emplace_back(_nodeStore->load(node->getChild(childIndex)->key()));
}
for (uint32_t childIndex = node->numChildren(); childIndex < end; ++childIndex) {
children.emplace_back(addChildTo(node));
}
assert(children.size() == end-begin);
return children;
}
unique_ptr<DataNode> DataTree::addChildTo(DataInnerNode *node) {
auto new_leaf = _nodeStore->createNewLeafNode();
new_leaf->resize(_nodeStore->layout().maxBytesPerLeaf());
auto chain = createChainOfInnerNodes(node->depth()-1, std::move(new_leaf));
node->addChild(*chain);
return std::move(chain);
}
uint32_t DataTree::leavesPerFullChild(const DataInnerNode &root) const {
return utils::intPow(_nodeStore->layout().maxChildrenPerInnerNode(), root.depth()-1);
}

View File

@ -29,10 +29,10 @@ public:
const blockstore::Key &key() const;
uint32_t maxBytesPerLeaf() const;
void traverseLeaves(uint32_t beginIndex, uint32_t endIndex, std::function<void (const datanodestore::DataLeafNode*, uint32_t)> func) const;
void traverseLeaves(uint32_t beginIndex, uint32_t endIndex, std::function<void (datanodestore::DataLeafNode*, uint32_t)> func);
void resizeNumBytes(uint64_t newNumBytes);
uint32_t numLeaves() const;
uint64_t numStoredBytes() const;
void flush() const;
@ -49,19 +49,23 @@ private:
friend class DataTreeStore;
std::unique_ptr<datanodestore::DataLeafNode> addDataLeafAt(datanodestore::DataInnerNode *insertPos);
cpputils::optional_ownership_ptr<datanodestore::DataNode> createChainOfInnerNodes(unsigned int num, datanodestore::DataLeafNode *leaf);
cpputils::optional_ownership_ptr<datanodestore::DataNode> createChainOfInnerNodes(unsigned int num, datanodestore::DataNode *child);
std::unique_ptr<datanodestore::DataNode> createChainOfInnerNodes(unsigned int num, std::unique_ptr<datanodestore::DataNode> child);
std::unique_ptr<datanodestore::DataLeafNode> addDataLeafToFullTree();
void deleteLastChildSubtree(datanodestore::DataInnerNode *node);
void ifRootHasOnlyOneChildReplaceRootWithItsChild();
//TODO Use underscore for private methods
void traverseLeaves(const datanodestore::DataNode *root, uint32_t leafOffset, uint32_t beginIndex, uint32_t endIndex, std::function<void (const datanodestore::DataLeafNode*, uint32_t)> func) const;
void _traverseLeaves(datanodestore::DataNode *root, uint32_t leafOffset, uint32_t beginIndex, uint32_t endIndex, std::function<void (datanodestore::DataLeafNode*, uint32_t)> func);
uint32_t leavesPerFullChild(const datanodestore::DataInnerNode &root) const;
uint64_t _numStoredBytes() const;
uint64_t _numStoredBytes(const datanodestore::DataNode &root) const;
cpputils::optional_ownership_ptr<datanodestore::DataLeafNode> LastLeaf(datanodestore::DataNode *root);
std::unique_ptr<datanodestore::DataLeafNode> LastLeaf(std::unique_ptr<datanodestore::DataNode> root);
datanodestore::DataInnerNode* increaseTreeDepth(unsigned int levels);
std::vector<std::unique_ptr<datanodestore::DataNode>> getOrCreateChildren(datanodestore::DataInnerNode *node, uint32_t begin, uint32_t end);
std::unique_ptr<datanodestore::DataNode> addChildTo(datanodestore::DataInnerNode *node);
DISALLOW_COPY_AND_ASSIGN(DataTree);
};

View File

@ -1,5 +1,7 @@
#include "Math.h"
#include <cmath>
namespace blobstore {
namespace onblocks {
namespace utils {
@ -23,6 +25,10 @@ uint32_t maxZeroSubtraction(uint32_t minuend, uint32_t subtrahend) {
return minuend-subtrahend;
}
uint32_t ceilLog(uint32_t base, uint32_t value) {
return std::ceil((long double)std::log(value)/(long double)std::log(base));
}
}
}
}

View File

@ -11,6 +11,7 @@ namespace utils {
uint32_t intPow(uint32_t base, uint32_t exponent);
uint32_t ceilDivision(uint32_t dividend, uint32_t divisor);
uint32_t maxZeroSubtraction(uint32_t minuend, uint32_t subtrahend);
uint32_t ceilLog(uint32_t base, uint32_t value);
}
}

View File

@ -109,6 +109,12 @@ INSTANTIATE_TEST_CASE_P(BlobReadWriteDataTest, BlobReadWriteDataTest, Values(
DataRange(BlobReadWriteDataTest::LARGE_SIZE, BlobReadWriteDataTest::LARGE_SIZE*1/3, BlobReadWriteDataTest::LARGE_SIZE*2/3) // access middle to end
));
TEST_P(BlobReadWriteDataTest, WritingDoesntChangeSize) {
blob->resize(GetParam().blobsize);
blob->write(this->foregroundData.data(), GetParam().offset, GetParam().count);
EXPECT_EQ(GetParam().blobsize, blob->size());
}
TEST_P(BlobReadWriteDataTest, WriteAndReadImmediately) {
blob->resize(GetParam().blobsize);
blob->write(this->foregroundData.data(), GetParam().offset, GetParam().count);

View File

@ -0,0 +1,200 @@
#include "testutils/DataTreeTest.h"
#include "testutils/TwoLevelDataFixture.h"
#include "../../../../implementations/onblocks/utils/Math.h"
#include <messmer/blockstore/utils/Data.h>
#include <tuple>
using ::testing::WithParamInterface;
using ::testing::Values;
using ::testing::Combine;
using std::tuple;
using std::get;
using std::function;
using std::mem_fn;
using cpputils::dynamic_pointer_move;
using blobstore::onblocks::datanodestore::DataLeafNode;
using blobstore::onblocks::datanodestore::DataInnerNode;
using blobstore::onblocks::datanodestore::DataNode;
using blobstore::onblocks::datanodestore::DataNodeLayout;
using blobstore::onblocks::datatreestore::DataTree;
using blobstore::onblocks::utils::ceilDivision;
using blockstore::Key;
using blockstore::Data;
using std::unique_ptr;
class DataTreeTest_ResizeByTraversing: public DataTreeTest {
public:
static constexpr DataNodeLayout LAYOUT = DataNodeLayout(BLOCKSIZE_BYTES);
unique_ptr<DataTree> CreateTree(unique_ptr<DataNode> root) {
Key key = root->key();
root.reset();
return treeStore.load(key);
}
unique_ptr<DataTree> CreateLeafTreeWithSize(uint32_t size) {
return CreateTree(CreateLeafWithSize(size));
}
unique_ptr<DataTree> CreateTwoLeafTreeWithSecondLeafSize(uint32_t size) {
return CreateTree(CreateTwoLeafWithSecondLeafSize(size));
}
unique_ptr<DataTree> CreateFullTwoLevelTreeWithLastLeafSize(uint32_t size) {
return CreateTree(CreateFullTwoLevelWithLastLeafSize(size));
}
unique_ptr<DataTree> CreateThreeLevelTreeWithTwoChildrenAndLastLeafSize(uint32_t size) {
return CreateTree(CreateThreeLevelWithTwoChildrenAndLastLeafSize(size));
}
unique_ptr<DataTree> CreateThreeLevelTreeWithThreeChildrenAndLastLeafSize(uint32_t size) {
return CreateTree(CreateThreeLevelWithThreeChildrenAndLastLeafSize(size));
}
unique_ptr<DataTree> CreateFullThreeLevelTreeWithLastLeafSize(uint32_t size) {
return CreateTree(CreateFullThreeLevelWithLastLeafSize(size));
}
unique_ptr<DataTree> CreateFourLevelMinDataTreeWithLastLeafSize(uint32_t size) {
return CreateTree(CreateFourLevelMinDataWithLastLeafSize(size));
}
void EXPECT_IS_LEFTMAXDATA_TREE(const Key &key) {
auto root = nodeStore->load(key);
DataInnerNode *inner = dynamic_cast<DataInnerNode*>(root.get());
if (inner != nullptr) {
for (uint32_t i = 0; i < inner->numChildren()-1; ++i) {
EXPECT_IS_MAXDATA_TREE(inner->getChild(i)->key());
}
EXPECT_IS_LEFTMAXDATA_TREE(inner->LastChild()->key());
}
}
void EXPECT_IS_MAXDATA_TREE(const Key &key) {
auto root = nodeStore->load(key);
DataInnerNode *inner = dynamic_cast<DataInnerNode*>(root.get());
if (inner != nullptr) {
for (uint32_t i = 0; i < inner->numChildren(); ++i) {
EXPECT_IS_MAXDATA_TREE(inner->getChild(i)->key());
}
} else {
DataLeafNode *leaf = dynamic_cast<DataLeafNode*>(root.get());
EXPECT_EQ(nodeStore->layout().maxBytesPerLeaf(), leaf->numBytes());
}
}
};
constexpr DataNodeLayout DataTreeTest_ResizeByTraversing::LAYOUT;
class DataTreeTest_ResizeByTraversing_P: public DataTreeTest_ResizeByTraversing, public WithParamInterface<tuple<function<unique_ptr<DataTree>(DataTreeTest_ResizeByTraversing*, uint32_t)>, uint32_t, uint32_t>> {
public:
DataTreeTest_ResizeByTraversing_P()
: oldLastLeafSize(get<1>(GetParam())),
tree(get<0>(GetParam())(this, oldLastLeafSize)),
numberOfLeavesToAdd(get<2>(GetParam())),
newNumberOfLeaves(tree->numLeaves()+numberOfLeavesToAdd),
ZEROES(LAYOUT.maxBytesPerLeaf())
{
ZEROES.FillWithZeroes();
}
void GrowTree(const Key &key, uint32_t numLeavesToAdd) {
auto tree = treeStore.load(key);
GrowTree(tree.get(), numLeavesToAdd);
}
void GrowTree(DataTree *tree, uint32_t numLeavesToAdd) {
uint32_t oldNumLeaves = tree->numLeaves();
uint32_t newNumLeaves = oldNumLeaves + numLeavesToAdd;
//TODO Test cases where beginIndex is inside of the existing leaves
tree->traverseLeaves(newNumLeaves-1, newNumLeaves, [] (DataLeafNode*,uint32_t){});
tree->flush();
}
unique_ptr<DataLeafNode> LastLeaf(const Key &key) {
auto root = nodeStore->load(key);
auto leaf = dynamic_pointer_move<DataLeafNode>(root);
if (leaf.get() != nullptr) {
return leaf;
}
auto inner = dynamic_pointer_move<DataInnerNode>(root);
return LastLeaf(inner->LastChild()->key());
}
uint32_t oldLastLeafSize;
unique_ptr<DataTree> tree;
uint32_t numberOfLeavesToAdd;
uint32_t newNumberOfLeaves;
Data ZEROES;
};
INSTANTIATE_TEST_CASE_P(DataTreeTest_ResizeByTraversing_P, DataTreeTest_ResizeByTraversing_P,
Combine(
//Tree we're starting with
Values<function<unique_ptr<DataTree>(DataTreeTest_ResizeByTraversing*, uint32_t)>>(
mem_fn(&DataTreeTest_ResizeByTraversing::CreateLeafTreeWithSize),
mem_fn(&DataTreeTest_ResizeByTraversing::CreateTwoLeafTreeWithSecondLeafSize),
mem_fn(&DataTreeTest_ResizeByTraversing::CreateFullTwoLevelTreeWithLastLeafSize),
mem_fn(&DataTreeTest_ResizeByTraversing::CreateThreeLevelTreeWithTwoChildrenAndLastLeafSize),
mem_fn(&DataTreeTest_ResizeByTraversing::CreateThreeLevelTreeWithThreeChildrenAndLastLeafSize),
mem_fn(&DataTreeTest_ResizeByTraversing::CreateFullThreeLevelTreeWithLastLeafSize),
mem_fn(&DataTreeTest_ResizeByTraversing::CreateFourLevelMinDataTreeWithLastLeafSize)
),
//Last leaf size of the start tree
Values(
0u,
1u,
10u,
DataTreeTest_ResizeByTraversing::LAYOUT.maxBytesPerLeaf()
),
//Number of leaves we're adding
Values(
1u,
2u,
DataTreeTest_ResizeByTraversing::LAYOUT.maxChildrenPerInnerNode(), //Full two level tree
2* DataTreeTest_ResizeByTraversing::LAYOUT.maxChildrenPerInnerNode(), //Three level tree with two children
3* DataTreeTest_ResizeByTraversing::LAYOUT.maxChildrenPerInnerNode(), //Three level tree with three children
DataTreeTest_ResizeByTraversing::LAYOUT.maxChildrenPerInnerNode() * DataTreeTest_ResizeByTraversing::LAYOUT.maxChildrenPerInnerNode(), //Full three level tree
DataTreeTest_ResizeByTraversing::LAYOUT.maxChildrenPerInnerNode() * DataTreeTest_ResizeByTraversing::LAYOUT.maxChildrenPerInnerNode() + 1 //Four level mindata tree
)
)
);
TEST_P(DataTreeTest_ResizeByTraversing_P, StructureIsValid) {
GrowTree(tree.get(), numberOfLeavesToAdd);
EXPECT_IS_LEFTMAXDATA_TREE(tree->key());
}
TEST_P(DataTreeTest_ResizeByTraversing_P, NumBytesIsCorrect) {
GrowTree(tree.get(), numberOfLeavesToAdd);
// tree->numLeaves() only goes down the right border nodes and expects the tree to be a left max data tree.
// This is what the StructureIsValid test case is for.
EXPECT_EQ(newNumberOfLeaves, tree->numLeaves());
}
TEST_P(DataTreeTest_ResizeByTraversing_P, DepthFlagsAreCorrect) {
GrowTree(tree.get(), numberOfLeavesToAdd);
uint32_t depth = ceil(log(newNumberOfLeaves)/log(DataTreeTest_ResizeByTraversing::LAYOUT.maxChildrenPerInnerNode()));
CHECK_DEPTH(depth, tree->key());
}
TEST_P(DataTreeTest_ResizeByTraversing_P, KeyDoesntChange) {
Key key = tree->key();
tree->flush();
GrowTree(tree.get(), numberOfLeavesToAdd);
EXPECT_EQ(key, tree->key());
}
TEST_P(DataTreeTest_ResizeByTraversing_P, DataStaysIntact) {
uint32_t oldNumberOfLeaves = std::max(1u, ceilDivision(tree->numStoredBytes(), nodeStore->layout().maxBytesPerLeaf()));
TwoLevelDataFixture data(nodeStore, TwoLevelDataFixture::SizePolicy::Unchanged);
Key key = tree->key();
tree.reset();
data.FillInto(nodeStore->load(key).get());
GrowTree(key, newNumberOfLeaves);
data.EXPECT_DATA_CORRECT(nodeStore->load(key).get(), oldNumberOfLeaves, oldLastLeafSize);
}

View File

@ -0,0 +1,32 @@
#include <google/gtest/gtest.h>
#include "../../../../implementations/onblocks/utils/Math.h"
#include <limits>
using namespace blobstore::onblocks::utils;
using ::testing::Test;
using std::numeric_limits;
class CeilLogTest: public Test {};
TEST_F(CeilLogTest, Log3_0) {
EXPECT_EQ(0, ceilLog(3, 0));
}
TEST_F(CeilLogTest, Log3_1) {
EXPECT_EQ(0, ceilLog(3, 1));
}
TEST_F(CeilLogTest, Log3_2) {
EXPECT_EQ(1, ceilLog(3, 2));
}
TEST_F(CeilLogTest, Log3_3) {
EXPECT_EQ(1, ceilLog(3, 3));
}
TEST_F(CeilLogTest, Log3_4) {
EXPECT_EQ(2, ceilLog(3, 4));
}
//TODO ...