Replace magic number in DataNodeView with a depth field

This commit is contained in:
Sebastian Messmer 2014-12-13 12:00:19 +01:00
parent fb2b511d06
commit a97eb08224
8 changed files with 65 additions and 38 deletions

View File

@ -14,9 +14,10 @@ DataInnerNode::DataInnerNode(DataNodeView view)
DataInnerNode::~DataInnerNode() { DataInnerNode::~DataInnerNode() {
} }
void DataInnerNode::InitializeNewNode() { void DataInnerNode::InitializeNewNode(const Key &first_child_key, const DataNodeView &first_child) {
*_node.MagicNumber() = _node.magicNumberNodeWithChildren; *_node.Depth() = *first_child.Depth() + 1;
*_node.Size() = 0; *_node.Size() = 1;
first_child_key.ToBinary(ChildrenBegin()->key);
} }
void DataInnerNode::read(off_t offset, size_t count, Data *result) const { void DataInnerNode::read(off_t offset, size_t count, Data *result) const {
@ -47,9 +48,10 @@ uint64_t DataInnerNode::readFromChild(const ChildEntry *child, off_t inner_offse
const DataInnerNode::ChildEntry *DataInnerNode::ChildContainingFirstByteAfterOffset(off_t offset) const { const DataInnerNode::ChildEntry *DataInnerNode::ChildContainingFirstByteAfterOffset(off_t offset) const {
uint32_t offset_blocks = offset / _node.BLOCKSIZE_BYTES; uint32_t offset_blocks = offset / _node.BLOCKSIZE_BYTES;
//TODO no binary search anymore
return return
std::upper_bound(ChildrenBegin(), ChildrenEnd(), offset_blocks, [](uint32_t offset_blocks, const ChildEntry &child) { std::upper_bound(ChildrenBegin(), ChildrenEnd(), offset_blocks, [](uint32_t offset_blocks, const ChildEntry &child) {
return offset_blocks < child.numBlocksInThisAndLeftwardNodes; return false;//return offset_blocks < child.numBlocksInThisAndLeftwardNodes;
}); });
} }
@ -69,7 +71,12 @@ uint64_t DataInnerNode::numBytesInLeftwardSiblings(const ChildEntry *child) cons
} }
uint64_t DataInnerNode::numBytesInChildAndLeftwardSiblings(const ChildEntry *child) const { uint64_t DataInnerNode::numBytesInChildAndLeftwardSiblings(const ChildEntry *child) const {
return (uint64_t)child->numBlocksInThisAndLeftwardNodes * _node.BLOCKSIZE_BYTES; //TODO Rewrite
//return (uint64_t)child->numBlocksInThisAndLeftwardNodes * _node.BLOCKSIZE_BYTES;
}
DataInnerNode::ChildEntry *DataInnerNode::ChildrenBegin() {
return const_cast<ChildEntry*>(const_cast<const DataInnerNode*>(this)->ChildrenBegin());
} }
const DataInnerNode::ChildEntry *DataInnerNode::ChildrenBegin() const { const DataInnerNode::ChildEntry *DataInnerNode::ChildrenBegin() const {

View File

@ -13,13 +13,12 @@ public:
virtual ~DataInnerNode(); virtual ~DataInnerNode();
struct ChildEntry { struct ChildEntry {
uint32_t numBlocksInThisAndLeftwardNodes;
uint8_t key[Key::KEYLENGTH_BINARY]; uint8_t key[Key::KEYLENGTH_BINARY];
}; };
static constexpr uint32_t MAX_STORED_CHILDREN = DataNodeView::DATASIZE_BYTES / sizeof(ChildEntry); static constexpr uint32_t MAX_STORED_CHILDREN = DataNodeView::DATASIZE_BYTES / sizeof(ChildEntry);
void InitializeNewNode(); void InitializeNewNode(const Key &first_child_key, const DataNodeView &first_child);
void read(off_t offset, size_t count, blockstore::Data *result) const override; void read(off_t offset, size_t count, blockstore::Data *result) const override;
void write(off_t offset, size_t count, const blockstore::Data &data) override; void write(off_t offset, size_t count, const blockstore::Data &data) override;
@ -28,7 +27,7 @@ public:
void resize(uint64_t newsize_bytes) override; void resize(uint64_t newsize_bytes) override;
private: private:
ChildEntry *ChildrenBegin();
const ChildEntry *ChildrenBegin() const; const ChildEntry *ChildrenBegin() const;
const ChildEntry *ChildrenEnd() const; const ChildEntry *ChildrenEnd() const;
const ChildEntry *ChildrenLast() const; const ChildEntry *ChildrenLast() const;

View File

@ -28,7 +28,7 @@ void DataLeafNode::write(off_t offset, size_t count, const Data &data) {
} }
void DataLeafNode::InitializeNewNode() { void DataLeafNode::InitializeNewNode() {
*_node.MagicNumber() = _node.magicNumberLeaf; *_node.Depth() = 0;
*_node.Size() = 0; *_node.Size() = 0;
//fillDataWithZeroes(); not needed, because a newly created block will be zeroed out. DataLeafNodeTest.SpaceIsZeroFilledWhenGrowing ensures this. //fillDataWithZeroes(); not needed, because a newly created block will be zeroed out. DataLeafNodeTest.SpaceIsZeroFilledWhenGrowing ensures this.
} }

View File

@ -22,19 +22,18 @@ DataNode::~DataNode() {
unique_ptr<DataNode> DataNode::load(unique_ptr<Block> block) { unique_ptr<DataNode> DataNode::load(unique_ptr<Block> block) {
DataNodeView node(std::move(block)); DataNodeView node(std::move(block));
if (*node.MagicNumber() == node.magicNumberNodeWithChildren) { if (*node.Depth() == 0) {
return unique_ptr<DataInnerNode>(new DataInnerNode(std::move(node)));
} else if (*node.MagicNumber() == node.magicNumberLeaf) {
return unique_ptr<DataLeafNode>(new DataLeafNode(std::move(node))); return unique_ptr<DataLeafNode>(new DataLeafNode(std::move(node)));
} else if (*node.Depth() < MAX_DEPTH) {
return unique_ptr<DataInnerNode>(new DataInnerNode(std::move(node)));
} else { } else {
//TODO Better exception throw runtime_error("Tree is to deep. Data corruption?");
throw runtime_error("Invalid node magic number");
} }
} }
unique_ptr<DataNode> DataNode::createNewInnerNode(unique_ptr<Block> block) { unique_ptr<DataNode> DataNode::createNewInnerNode(unique_ptr<Block> block, const Key &first_child_key, const DataNode &first_child) {
auto newNode = unique_ptr<DataInnerNode>(new DataInnerNode(std::move(block))); auto newNode = unique_ptr<DataInnerNode>(new DataInnerNode(std::move(block)));
newNode->InitializeNewNode(); newNode->InitializeNewNode(first_child_key, first_child._node);
return std::move(newNode); return std::move(newNode);
} }

View File

@ -13,6 +13,8 @@ class DataNode {
public: public:
virtual ~DataNode(); virtual ~DataNode();
static constexpr uint8_t MAX_DEPTH = 10;
virtual void read(off_t offset, size_t count, blockstore::Data *result) const = 0; virtual void read(off_t offset, size_t count, blockstore::Data *result) const = 0;
virtual void write(off_t offset, size_t count, const blockstore::Data &data) = 0; virtual void write(off_t offset, size_t count, const blockstore::Data &data) = 0;
@ -21,7 +23,7 @@ public:
static std::unique_ptr<DataNode> load(std::unique_ptr<blockstore::Block> block); static std::unique_ptr<DataNode> load(std::unique_ptr<blockstore::Block> block);
static std::unique_ptr<DataNode> createNewLeafNode(std::unique_ptr<blockstore::Block> block); static std::unique_ptr<DataNode> createNewLeafNode(std::unique_ptr<blockstore::Block> block);
static std::unique_ptr<DataNode> createNewInnerNode(std::unique_ptr<blockstore::Block> block); static std::unique_ptr<DataNode> createNewInnerNode(std::unique_ptr<blockstore::Block> block, const Key &first_child_key, const DataNode &first_child);
protected: protected:
DataNode(DataNodeView block); DataNode(DataNodeView block);

View File

@ -24,8 +24,8 @@ public:
//Total size of the header //Total size of the header
static constexpr unsigned int HEADERSIZE_BYTES = 8; static constexpr unsigned int HEADERSIZE_BYTES = 8;
//Where in the header is the magic number //Where in the header is the depth field
static constexpr unsigned int MAGICNUMBER_OFFSET_BYTES = 0; static constexpr unsigned int DEPTH_OFFSET_BYTES = 0;
//Where in the header is the size field (for inner nodes: number of children, for leafs: content data size) //Where in the header is the size field (for inner nodes: number of children, for leafs: content data size)
static constexpr unsigned int SIZE_OFFSET_BYTES = 4; static constexpr unsigned int SIZE_OFFSET_BYTES = 4;
@ -34,17 +34,12 @@ public:
//How much space is there for data //How much space is there for data
static constexpr unsigned int DATASIZE_BYTES = BLOCKSIZE_BYTES - HEADERSIZE_BYTES; static constexpr unsigned int DATASIZE_BYTES = BLOCKSIZE_BYTES - HEADERSIZE_BYTES;
static constexpr unsigned char magicNumberNodeWithChildren = 0x01; const uint8_t *Depth() const {
static constexpr unsigned char magicNumberLeaf = 0x02; return GetOffset<DEPTH_OFFSET_BYTES, uint8_t>();
static constexpr unsigned char magicNumberRootWithChildren = 0x03;
static constexpr unsigned char magicNumberRootLeaf = 0x04;
const uint8_t *MagicNumber() const {
return GetOffset<MAGICNUMBER_OFFSET_BYTES, uint8_t>();
} }
uint8_t *MagicNumber() { uint8_t *Depth() {
return const_cast<uint8_t*>(const_cast<const DataNodeView*>(this)->MagicNumber()); return const_cast<uint8_t*>(const_cast<const DataNodeView*>(this)->Depth());
} }
const uint32_t *Size() const { const uint32_t *Size() const {

View File

@ -31,8 +31,11 @@ TEST_F(DataNodeTest, CreateLeafNodeCreatesLeafNode) {
} }
TEST_F(DataNodeTest, CreateInnerNodeCreatesInnerNode) { TEST_F(DataNodeTest, CreateInnerNodeCreatesInnerNode) {
auto leafblock = blockStore->create(BlobStoreOnBlocks::BLOCKSIZE);
auto leaf = DataNode::createNewLeafNode(std::move(leafblock.block));
auto block = blockStore->create(BlobStoreOnBlocks::BLOCKSIZE); auto block = blockStore->create(BlobStoreOnBlocks::BLOCKSIZE);
auto node = DataNode::createNewInnerNode(std::move(block.block)); auto node = DataNode::createNewInnerNode(std::move(block.block), leafblock.key, *leaf);
EXPECT_IS_PTR_TYPE(DataInnerNode, node.get()); EXPECT_IS_PTR_TYPE(DataInnerNode, node.get());
} }
@ -48,11 +51,13 @@ TEST_F(DataNodeTest, LeafNodeIsRecognizedAfterStoreAndLoad) {
EXPECT_IS_PTR_TYPE(DataLeafNode, loaded_node.get()); EXPECT_IS_PTR_TYPE(DataLeafNode, loaded_node.get());
} }
TEST_F(DataNodeTest, InnerNodeIsRecognizedAfterStoreAndLoad) { TEST_F(DataNodeTest, InnerNodeWithDepth1IsRecognizedAfterStoreAndLoad) {
auto block = blockStore->create(BlobStoreOnBlocks::BLOCKSIZE); auto block = blockStore->create(BlobStoreOnBlocks::BLOCKSIZE);
Key key = block.key; Key key = block.key;
{ {
DataNode::createNewInnerNode(std::move(block.block)); auto leafblock = blockStore->create(BlobStoreOnBlocks::BLOCKSIZE);
auto leaf = DataNode::createNewLeafNode(std::move(leafblock.block));
DataNode::createNewInnerNode(std::move(block.block), leafblock.key, *leaf);
} }
auto loaded_node = DataNode::load(blockStore->load(key)); auto loaded_node = DataNode::load(blockStore->load(key));
@ -60,12 +65,28 @@ TEST_F(DataNodeTest, InnerNodeIsRecognizedAfterStoreAndLoad) {
EXPECT_IS_PTR_TYPE(DataInnerNode, loaded_node.get()); EXPECT_IS_PTR_TYPE(DataInnerNode, loaded_node.get());
} }
TEST_F(DataNodeTest, DataNodeCrashesOnLoadIfMagicNumberIsWrong) { TEST_F(DataNodeTest, InnerNodeWithDepth2IsRecognizedAfterStoreAndLoad) {
auto block = blockStore->create(BlobStoreOnBlocks::BLOCKSIZE);
Key key = block.key;
{
auto leafblock = blockStore->create(BlobStoreOnBlocks::BLOCKSIZE);
auto leaf = DataNode::createNewLeafNode(std::move(leafblock.block));
auto inner1block = blockStore->create(BlobStoreOnBlocks::BLOCKSIZE);
auto inner1 = DataNode::createNewInnerNode(std::move(inner1block.block), leafblock.key, *leaf);
DataNode::createNewInnerNode(std::move(block.block), inner1block.key, *inner1);
}
auto loaded_node = DataNode::load(blockStore->load(key));
EXPECT_IS_PTR_TYPE(DataInnerNode, loaded_node.get());
}
TEST_F(DataNodeTest, DataNodeCrashesOnLoadIfDepthIsTooHigh) {
auto block = blockStore->create(BlobStoreOnBlocks::BLOCKSIZE); auto block = blockStore->create(BlobStoreOnBlocks::BLOCKSIZE);
Key key = block.key; Key key = block.key;
{ {
DataNodeView view(std::move(block.block)); DataNodeView view(std::move(block.block));
*view.MagicNumber() = 0xFF; // this is an invalid magic number *view.Depth() = 200u; // this is an invalid depth
} }
auto loaded_block = blockStore->load(key); auto loaded_block = blockStore->load(key);

View File

@ -28,14 +28,18 @@ public:
unique_ptr<BlockStore> blockStore = make_unique<FakeBlockStore>(); unique_ptr<BlockStore> blockStore = make_unique<FakeBlockStore>();
}; };
TEST_F(DataNodeViewTest, MagicNumberIsStored) { class DataNodeViewDepthTest: public DataNodeViewTest, public WithParamInterface<uint8_t> {
};
INSTANTIATE_TEST_CASE_P(DataNodeViewDepthTest, DataNodeViewDepthTest, Values(0, 1, 3, 10, 100));
TEST_P(DataNodeViewDepthTest, DepthIsStored) {
auto block = blockStore->create(BlobStoreOnBlocks::BLOCKSIZE); auto block = blockStore->create(BlobStoreOnBlocks::BLOCKSIZE);
{ {
DataNodeView view(std::move(block.block)); DataNodeView view(std::move(block.block));
*view.MagicNumber() = 0x3F; *view.Depth() = GetParam();
} }
DataNodeView view(blockStore->load(block.key)); DataNodeView view(blockStore->load(block.key));
EXPECT_EQ(0x3F, *view.MagicNumber()); EXPECT_EQ(GetParam(), *view.Depth());
} }
class DataNodeViewSizeTest: public DataNodeViewTest, public WithParamInterface<uint32_t> { class DataNodeViewSizeTest: public DataNodeViewTest, public WithParamInterface<uint32_t> {
@ -68,12 +72,12 @@ TEST_F(DataNodeViewTest, HeaderAndBodyDontOverlap) {
auto block = blockStore->create(BlobStoreOnBlocks::BLOCKSIZE); auto block = blockStore->create(BlobStoreOnBlocks::BLOCKSIZE);
{ {
DataNodeView view(std::move(block.block)); DataNodeView view(std::move(block.block));
*view.MagicNumber() = 0xAA; *view.Depth() = 3;
*view.Size() = 1000000000u; *view.Size() = 1000000000u;
std::memcpy(view.DataBegin<uint8_t>(), randomData.data(), DataNodeView::DATASIZE_BYTES); std::memcpy(view.DataBegin<uint8_t>(), randomData.data(), DataNodeView::DATASIZE_BYTES);
} }
DataNodeView view(blockStore->load(block.key)); DataNodeView view(blockStore->load(block.key));
EXPECT_EQ(0xAA, *view.MagicNumber()); EXPECT_EQ(3, *view.Depth());
EXPECT_EQ(1000000000u, *view.Size()); EXPECT_EQ(1000000000u, *view.Size());
EXPECT_EQ(0, std::memcmp(view.DataBegin<uint8_t>(), randomData.data(), DataNodeView::DATASIZE_BYTES)); EXPECT_EQ(0, std::memcmp(view.DataBegin<uint8_t>(), randomData.data(), DataNodeView::DATASIZE_BYTES));
} }