Restrict datanodestore abstraction to a simple node store without tree operations like resizing

This commit is contained in:
Sebastian Messmer 2015-01-22 23:37:03 +01:00
parent fbec747427
commit 99c2e353a4
11 changed files with 109 additions and 200 deletions

View File

@ -1,6 +1,6 @@
#include <blobstore/implementations/onblocks/BlobOnBlocks.h>
#include "datanodestore/DataNode.h"
#include <blobstore/implementations/onblocks/datanodestore/DataNode.h>
using std::unique_ptr;
@ -18,7 +18,8 @@ BlobOnBlocks::~BlobOnBlocks() {
}
size_t BlobOnBlocks::size() const {
return _rootnode->numBytesInThisNode();
assert(false); //TODO Implement
//return _rootnode->numBytesInThisNode();
}
}

View File

@ -1,8 +1,8 @@
#include <blobstore/implementations/onblocks/datanodestore/DataLeafNode.h>
#include <blobstore/implementations/onblocks/datanodestore/DataNodeStore.h>
#include "BlobStoreOnBlocks.h"
#include "BlobOnBlocks.h"
#include "datanodestore/DataNodeStore.h"
#include "datanodestore/DataLeafNode.h"
using std::unique_ptr;
using std::make_unique;

View File

@ -1,5 +1,6 @@
add_subdirectory(datanodestore)
add_subdirectory(datatreestore)
add_library(blobstore_onblocks BlobOnBlocks.cpp BlobStoreOnBlocks.cpp)
target_link_libraries(blobstore_onblocks blobstore_onblocks_datanodestore)
target_link_libraries(blobstore_onblocks blobstore_onblocks_datatreestore)

View File

@ -1,5 +1,5 @@
#include <blobstore/implementations/onblocks/datanodestore/DataInnerNode.h>
#include <blobstore/implementations/onblocks/datanodestore/DataNodeStore.h>
#include "DataInnerNode.h"
#include "DataNodeStore.h"
using std::unique_ptr;
@ -11,8 +11,8 @@ namespace blobstore {
namespace onblocks {
namespace datanodestore {
DataInnerNode::DataInnerNode(DataNodeView view, const Key &key, DataNodeStore *nodestorage)
: DataNode(std::move(view), key, nodestorage) {
DataInnerNode::DataInnerNode(DataNodeView view, const Key &key)
: DataNode(std::move(view), key) {
}
DataInnerNode::~DataInnerNode() {
@ -24,66 +24,10 @@ void DataInnerNode::InitializeNewNode(const DataNode &first_child) {
first_child.key().ToBinary(ChildrenBegin()->key);
}
void DataInnerNode::read(off_t offset, size_t count, Data *result) const {
assert(count <= result->size());
const uint64_t end = offset + count;
assert(end <= numBytesInThisNode());
uint8_t *target = (uint8_t*)result->data();
const ChildEntry *child = ChildContainingFirstByteAfterOffset(offset);
uint32_t child_index = child-ChildrenBegin();
uint64_t child_first_byte_index = maxNumBytesPerChild() * child_index;
uint64_t next_child_first_byte_index = child_first_byte_index + maxNumBytesPerChild();
off_t childrelative_offset = offset - child_first_byte_index;
uint64_t already_read_bytes = readFromChild(child, childrelative_offset, count, target);
while(next_child_first_byte_index < end) { //TODO Write a test case that breaks when we're having <= instead of < here
++child;
already_read_bytes += readFromChild(child, 0, count, target + already_read_bytes);
};
assert(already_read_bytes == count);
}
uint64_t DataInnerNode::readFromChild(const ChildEntry *child, off_t inner_offset, size_t count, uint8_t *target) const {
//TODO This only works for non-rightmost children
uint64_t readable_bytes = std::min(count, maxNumBytesPerChild() - inner_offset);
//TODO READ...
return readable_bytes;
}
const DataInnerNode::ChildEntry *DataInnerNode::ChildContainingFirstByteAfterOffset(off_t offset) const {
uint8_t child_index = offset/maxNumBytesPerChild();
return ChildrenBegin()+child_index;
}
uint32_t DataInnerNode::maxNumDataBlocksPerChild() const {
return std::round(std::pow(MAX_STORED_CHILDREN, *node().Depth()));
}
uint64_t DataInnerNode::numBytesInThisNode() const {
return numBytesInNonRightmostChildrenSum() + numBytesInRightmostChild();
}
uint64_t DataInnerNode::numBytesInNonRightmostChildrenSum() const {
return maxNumBytesPerChild() * (numChildren()-1);
}
uint64_t DataInnerNode::numBytesInRightmostChild() const {
Key rightmost_child_key = Key::FromBinary(RightmostChild()->key);
auto rightmost_child = storage().load(rightmost_child_key);
return rightmost_child->numBytesInThisNode();
}
uint32_t DataInnerNode::numChildren() const {
return *node().Size();
}
//TODO This only works for non-rightmost children
uint64_t DataInnerNode::maxNumBytesPerChild() const {
return maxNumDataBlocksPerChild() * DataNodeView::DATASIZE_BYTES;
}
DataInnerNode::ChildEntry *DataInnerNode::ChildrenBegin() {
return const_cast<ChildEntry*>(const_cast<const DataInnerNode*>(this)->ChildrenBegin());
}
@ -92,23 +36,18 @@ const DataInnerNode::ChildEntry *DataInnerNode::ChildrenBegin() const {
return node().DataBegin<ChildEntry>();
}
DataInnerNode::ChildEntry *DataInnerNode::ChildrenEnd() {
return const_cast<ChildEntry*>(const_cast<const DataInnerNode*>(this)->ChildrenEnd());
}
const DataInnerNode::ChildEntry *DataInnerNode::ChildrenEnd() const {
return ChildrenBegin() + *node().Size();
}
const DataInnerNode::ChildEntry *DataInnerNode::RightmostChild() const{
const DataInnerNode::ChildEntry *DataInnerNode::RightmostExistingChild() const{
return ChildrenEnd()-1;
}
void DataInnerNode::write(off_t offset, size_t count, const Data &data) {
//TODO Implement
}
void DataInnerNode::resize(uint64_t newsize_bytes) {
//TODO Implement
}
}
}
}

View File

@ -2,7 +2,7 @@
#ifndef BLOBSTORE_IMPLEMENTATIONS_ONBLOCKS_DATANODESTORE_DATAINNERNODE_H_
#define BLOBSTORE_IMPLEMENTATIONS_ONBLOCKS_DATANODESTORE_DATAINNERNODE_H_
#include <blobstore/implementations/onblocks/datanodestore/DataNode.h>
#include "DataNode.h"
namespace blobstore {
namespace onblocks {
@ -10,7 +10,7 @@ namespace datanodestore {
class DataInnerNode: public DataNode {
public:
DataInnerNode(DataNodeView block, const blockstore::Key &key, DataNodeStore *nodestorage);
DataInnerNode(DataNodeView block, const blockstore::Key &key);
virtual ~DataInnerNode();
struct ChildEntry {
@ -21,26 +21,14 @@ public:
void InitializeNewNode(const DataNode &first_child);
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;
uint64_t numBytesInThisNode() const override;
void resize(uint64_t newsize_bytes) override;
private:
ChildEntry *ChildrenBegin();
ChildEntry *ChildrenEnd();
const ChildEntry *ChildrenBegin() const;
const ChildEntry *ChildrenEnd() const;
const ChildEntry *RightmostChild() const;
uint64_t readFromChild(const ChildEntry *child, off_t inner_offset, size_t count, uint8_t *target) const;
const ChildEntry *RightmostExistingChild() const;
uint32_t numChildren() const;
uint32_t maxNumDataBlocksPerChild() const;
uint64_t maxNumBytesPerChild() const;
uint64_t numBytesInNonRightmostChildrenSum() const;
uint64_t numBytesInRightmostChild() const;
const ChildEntry *ChildContainingFirstByteAfterOffset(off_t offset) const;
};
}

View File

@ -1,4 +1,4 @@
#include <blobstore/implementations/onblocks/datanodestore/DataLeafNode.h>
#include "DataLeafNode.h"
using std::unique_ptr;
using blockstore::Block;
@ -9,53 +9,45 @@ namespace blobstore {
namespace onblocks {
namespace datanodestore {
DataLeafNode::DataLeafNode(DataNodeView view, const Key &key, DataNodeStore *nodestorage)
: DataNode(std::move(view), key, nodestorage) {
assert(numBytesInThisNode() <= MAX_STORED_BYTES);
DataLeafNode::DataLeafNode(DataNodeView view, const Key &key)
: DataNode(std::move(view), key) {
assert(numBytes() <= MAX_STORED_BYTES);
}
DataLeafNode::~DataLeafNode() {
}
void DataLeafNode::read(off_t offset, size_t count, Data *result) const {
assert(count <= result->size());
assert(offset+count <= numBytesInThisNode());
std::memcpy(result->data(), node().DataBegin<unsigned char>()+offset, count);
}
void DataLeafNode::write(off_t offset, size_t count, const Data &data) {
assert(count <= data.size());
assert(offset+count <= numBytesInThisNode());
std::memcpy(node().DataBegin<unsigned char>()+offset, data.data(), count);
}
void DataLeafNode::InitializeNewNode() {
*node().Depth() = 0;
*node().Size() = 0;
//fillDataWithZeroes(); not needed, because a newly created block will be zeroed out. DataLeafNodeTest.SpaceIsZeroFilledWhenGrowing ensures this.
}
void *DataLeafNode::data() {
return const_cast<void*>(const_cast<const DataLeafNode*>(this)->data());
}
const void *DataLeafNode::data() const {
return node().DataBegin<uint8_t>();
}
uint32_t DataLeafNode::numBytes() const {
return *node().Size();
}
void DataLeafNode::resize(uint32_t new_size) {
assert(new_size <= MAX_STORED_BYTES);
uint32_t old_size = *node().Size();
if (new_size < old_size) {
fillDataWithZeroesFromTo(new_size, old_size);
}
*node().Size() = new_size;
}
void DataLeafNode::fillDataWithZeroesFromTo(off_t begin, off_t end) {
std::memset(node().DataBegin<unsigned char>()+begin, 0, end-begin);
}
uint64_t DataLeafNode::numBytesInThisNode() const {
return *node().Size();
}
void DataLeafNode::resize(uint64_t newsize_bytes) {
assert(newsize_bytes <= MAX_STORED_BYTES);
// If we're shrinking, we want to delete the old data
// (overwrite it with zeroes).
// TODO Mention this in thesis
if (newsize_bytes < *node().Size()) {
fillDataWithZeroesFromTo(newsize_bytes, *node().Size());
}
*node().Size() = newsize_bytes;
}
}
}
}

View File

@ -2,7 +2,7 @@
#ifndef BLOBSTORE_IMPLEMENTATIONS_ONBLOCKS_DATANODESTORE_DATALEAFNODE_H_
#define BLOBSTORE_IMPLEMENTATIONS_ONBLOCKS_DATANODESTORE_DATALEAFNODE_H_
#include <blobstore/implementations/onblocks/datanodestore/DataNode.h>
#include "DataNode.h"
namespace blobstore {
namespace onblocks {
@ -10,18 +10,19 @@ namespace datanodestore {
class DataLeafNode: public DataNode {
public:
DataLeafNode(DataNodeView block, const blockstore::Key &key, DataNodeStore *nodestorage);
DataLeafNode(DataNodeView block, const blockstore::Key &key);
virtual ~DataLeafNode();
static constexpr uint32_t MAX_STORED_BYTES = DataNodeView::DATASIZE_BYTES;
void InitializeNewNode();
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 *data();
const void *data() const;
uint64_t numBytesInThisNode() const override;
void resize(uint64_t newsize_bytes) override;
uint32_t numBytes() const;
void resize(uint32_t size);
private:
void fillDataWithZeroesFromTo(off_t begin, off_t end);

View File

@ -1,8 +1,7 @@
#include "DataInnerNode.h"
#include <blobstore/implementations/onblocks/datanodestore/DataInnerNode.h>
#include <blobstore/implementations/onblocks/datanodestore/DataLeafNode.h>
#include <blobstore/implementations/onblocks/datanodestore/DataNode.h>
#include <blobstore/implementations/onblocks/datanodestore/DataNodeStore.h>
#include "DataLeafNode.h"
#include "DataNode.h"
#include "DataNodeStore.h"
using blockstore::Block;
using blockstore::Key;
@ -15,21 +14,13 @@ namespace blobstore {
namespace onblocks {
namespace datanodestore {
DataNode::DataNode(DataNodeView node, const Key &key, DataNodeStore *nodestorage)
: _key(key), _node(std::move(node)), _nodestorage(nodestorage) {
DataNode::DataNode(DataNodeView node, const Key &key)
: _key(key), _node(std::move(node)) {
}
DataNode::~DataNode() {
}
DataNodeStore &DataNode::storage() {
return const_cast<DataNodeStore&>(const_cast<const DataNode*>(this)->storage());
}
const DataNodeStore &DataNode::storage() const {
return *_nodestorage;
}
DataNodeView &DataNode::node() {
return const_cast<DataNodeView&>(const_cast<const DataNode*>(this)->node());
}
@ -43,9 +34,10 @@ const Key &DataNode::key() const {
}
uint8_t DataNode::depth() const {
return *_node.Depth();
return *node().Depth();
}
}
}
}

View File

@ -2,7 +2,7 @@
#ifndef BLOBSTORE_IMPLEMENTATIONS_ONBLOCKS_DATANODESTORE_DATANODE_H_
#define BLOBSTORE_IMPLEMENTATIONS_ONBLOCKS_DATANODESTORE_DATANODE_H_
#include <blobstore/implementations/onblocks/datanodestore/DataNodeView.h>
#include "DataNodeView.h"
#include "blockstore/utils/Data.h"
namespace blobstore {
@ -14,21 +14,12 @@ class DataNode {
public:
virtual ~DataNode();
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 resize(uint64_t newsize_bytes) = 0;
virtual uint64_t numBytesInThisNode() const = 0;
const blockstore::Key &key() const;
uint8_t depth() const;
protected:
DataNode(DataNodeView block, const blockstore::Key &key, DataNodeStore *nodestorage);
DataNodeStore &storage();
const DataNodeStore &storage() const;
DataNode(DataNodeView block, const blockstore::Key &key);
DataNodeView &node();
const DataNodeView &node() const;
@ -36,7 +27,6 @@ protected:
private:
blockstore::Key _key; //TODO Remove this and make blockstore::Block store the key
DataNodeView _node;
DataNodeStore *_nodestorage;
DISALLOW_COPY_AND_ASSIGN(DataNode);
};

View File

@ -1,6 +1,6 @@
#include <blobstore/implementations/onblocks/datanodestore/DataInnerNode.h>
#include <blobstore/implementations/onblocks/datanodestore/DataLeafNode.h>
#include <blobstore/implementations/onblocks/datanodestore/DataNodeStore.h>
#include "DataInnerNode.h"
#include "DataLeafNode.h"
#include "DataNodeStore.h"
#include "blockstore/interface/BlockStore.h"
#include "blockstore/interface/Block.h"
@ -27,9 +27,9 @@ unique_ptr<DataNode> DataNodeStore::load(unique_ptr<Block> block, const Key &key
DataNodeView node(std::move(block));
if (*node.Depth() == 0) {
return unique_ptr<DataLeafNode>(new DataLeafNode(std::move(node), key, this));
return unique_ptr<DataLeafNode>(new DataLeafNode(std::move(node), key));
} else if (*node.Depth() <= MAX_DEPTH) {
return unique_ptr<DataInnerNode>(new DataInnerNode(std::move(node), key, this));
return unique_ptr<DataInnerNode>(new DataInnerNode(std::move(node), key));
} else {
throw runtime_error("Tree is to deep. Data corruption?");
}
@ -37,14 +37,14 @@ unique_ptr<DataNode> DataNodeStore::load(unique_ptr<Block> block, const Key &key
unique_ptr<DataInnerNode> DataNodeStore::createNewInnerNode(const DataNode &first_child) {
auto block = _blockstore->create(DataNodeView::BLOCKSIZE_BYTES);
auto newNode = make_unique<DataInnerNode>(std::move(block.block), block.key, this);
auto newNode = make_unique<DataInnerNode>(std::move(block.block), block.key);
newNode->InitializeNewNode(first_child);
return std::move(newNode);
}
unique_ptr<DataLeafNode> DataNodeStore::createNewLeafNode() {
auto block = _blockstore->create(DataNodeView::BLOCKSIZE_BYTES);
auto newNode = make_unique<DataLeafNode>(std::move(block.block), block.key, this);
auto newNode = make_unique<DataLeafNode>(std::move(block.block), block.key);
newNode->InitializeNewNode();
return std::move(newNode);
}

View File

@ -2,6 +2,8 @@
#include <blobstore/implementations/onblocks/datanodestore/DataNodeStore.h>
#include <gtest/gtest.h>
#include "fspp/utils/pointer.h"
#include "blockstore/implementations/testfake/FakeBlockStore.h"
#include "blockstore/implementations/testfake/FakeBlock.h"
#include "blobstore/implementations/onblocks/BlobStoreOnBlocks.h"
@ -15,6 +17,8 @@ using std::unique_ptr;
using std::make_unique;
using std::string;
using fspp::dynamic_pointer_move;
using blockstore::BlockStore;
using blockstore::BlockWithKey;
using blockstore::Data;
@ -47,23 +51,22 @@ public:
Key WriteDataToNewLeafBlockAndReturnKey() {
auto newleaf = nodeStore->createNewLeafNode();
newleaf->resize(randomData.size());
newleaf->write(0, randomData.size(), randomData);
std::memcpy(newleaf->data(), randomData.data(), randomData.size());
return newleaf->key();
}
void FillLeafBlockWithData() {
leaf->resize(randomData.size());
leaf->write(0, randomData.size(), randomData);
std::memcpy(leaf->data(), randomData.data(), randomData.size());
}
void ReadDataFromLoadedLeafBlock(Key key, Data *data) {
unique_ptr<DataLeafNode> LoadLeafNode(const Key &key) {
auto leaf = nodeStore->load(key);
EXPECT_IS_PTR_TYPE(DataLeafNode, leaf.get());
leaf->read(0, data->size(), data);
return dynamic_pointer_move<DataLeafNode>(leaf);
}
void ResizeLeaf(const Key &key, size_t size) {
auto leaf = nodeStore->load(key);
auto leaf = LoadLeafNode(key);
EXPECT_IS_PTR_TYPE(DataLeafNode, leaf.get());
leaf->resize(size);
}
@ -78,42 +81,33 @@ public:
TEST_F(DataLeafNodeTest, InitializesCorrectly) {
leaf->InitializeNewNode();
EXPECT_EQ(0u, leaf->numBytesInThisNode());
EXPECT_EQ(0u, leaf->numBytes());
}
TEST_F(DataLeafNodeTest, ReinitializesCorrectly) {
leaf->resize(5);
leaf->InitializeNewNode();
EXPECT_EQ(0u, leaf->numBytesInThisNode());
EXPECT_EQ(0u, leaf->numBytes());
}
TEST_F(DataLeafNodeTest, ReadWrittenDataImmediately) {
leaf->resize(randomData.size());
leaf->write(0, randomData.size(), randomData);
Data read(DataLeafNode::MAX_STORED_BYTES);
leaf->read(0, read.size(), &read);
EXPECT_EQ(0, std::memcmp(randomData.data(), read.data(), randomData.size()));
}
TEST_F(DataLeafNodeTest, ReadWrittenDataAfterReloadingBLock) {
TEST_F(DataLeafNodeTest, ReadWrittenDataAfterReloadingBlock) {
Key key = WriteDataToNewLeafBlockAndReturnKey();
Data data(DataLeafNode::MAX_STORED_BYTES);
ReadDataFromLoadedLeafBlock(key, &data);
auto loaded = LoadLeafNode(key);
EXPECT_EQ(0, std::memcmp(randomData.data(), data.data(), randomData.size()));
EXPECT_EQ(randomData.size(), loaded->numBytes());
EXPECT_EQ(0, std::memcmp(randomData.data(), loaded->data(), randomData.size()));
}
TEST_F(DataLeafNodeTest, NewLeafNodeHasSizeZero) {
EXPECT_EQ(0u, leaf->numBytesInThisNode());
EXPECT_EQ(0u, leaf->numBytes());
}
TEST_F(DataLeafNodeTest, NewLeafNodeHasSizeZero_AfterLoading) {
Key key = nodeStore->createNewLeafNode()->key();
auto leaf = nodeStore->load(key);
auto leaf = LoadLeafNode(key);
EXPECT_EQ(0u, leaf->numBytesInThisNode());
EXPECT_EQ(0u, leaf->numBytes());
}
class DataLeafNodeSizeTest: public DataLeafNodeTest, public WithParamInterface<unsigned int> {
@ -128,22 +122,19 @@ INSTANTIATE_TEST_CASE_P(DataLeafNodeSizeTest, DataLeafNodeSizeTest, Values(0, 1,
TEST_P(DataLeafNodeSizeTest, ResizeNode_ReadSizeImmediately) {
leaf->resize(GetParam());
EXPECT_EQ(GetParam(), leaf->numBytesInThisNode());
EXPECT_EQ(GetParam(), leaf->numBytes());
}
TEST_P(DataLeafNodeSizeTest, ResizeNode_ReadSizeAfterLoading) {
Key key = CreateLeafResizeItAndReturnKey();
auto leaf = nodeStore->load(key);
EXPECT_EQ(GetParam(), leaf->numBytesInThisNode());
auto leaf = LoadLeafNode(key);
EXPECT_EQ(GetParam(), leaf->numBytes());
}
TEST_F(DataLeafNodeTest, SpaceIsZeroFilledWhenGrowing) {
leaf->resize(randomData.size());
Data read(randomData.size());
leaf->read(0, read.size(), &read);
EXPECT_EQ(0, std::memcmp(ZEROES.data(), read.data(), read.size()));
EXPECT_EQ(0, std::memcmp(ZEROES.data(), leaf->data(), randomData.size()));
}
TEST_F(DataLeafNodeTest, SpaceGetsZeroFilledWhenShrinkingAndRegrowing) {
@ -154,9 +145,7 @@ TEST_F(DataLeafNodeTest, SpaceGetsZeroFilledWhenShrinkingAndRegrowing) {
leaf->resize(randomData.size());
//Check that the space was filled with zeroes
Data read(100);
leaf->read(smaller_size, read.size(), &read);
EXPECT_EQ(0, std::memcmp(ZEROES.data(), read.data(), read.size()));
EXPECT_EQ(0, std::memcmp(ZEROES.data(), ((uint8_t*)leaf->data())+smaller_size, 100));
}
TEST_F(DataLeafNodeTest, DataGetsZeroFilledWhenShrinking) {
@ -171,11 +160,27 @@ TEST_F(DataLeafNodeTest, DataGetsZeroFilledWhenShrinking) {
//After shrinking, we expect there to be zeroes in the underlying data block
ResizeLeaf(key, smaller_size);
{
auto block = blockStore->load(leaf->key());
auto block = blockStore->load(key);
EXPECT_EQ(0, std::memcmp(ZEROES.data(), (uint8_t*)block->data()+DataNodeView::HEADERSIZE_BYTES+smaller_size, 100));
}
}
TEST_F(DataLeafNodeTest, ShrinkingDoesntDestroyValidDataRegion) {
FillLeafBlockWithData();
uint32_t smaller_size = randomData.size() - 100;
leaf->resize(smaller_size);
//Check that the remaining data region is unchanged
EXPECT_EQ(0, std::memcmp(randomData.data(), leaf->data(), smaller_size));
}
/*
* The following test cases test reading/writing part of a leaf. This doesn't make much sense,
* since the new leaf abstraction doesn't offer read()/write() anymore, but direct data pointer access.
* However, these test cases might make sense wherever the read()/write() for a leaf will be implemented.
* In case they're not needed then, delete them.
struct DataRange {
DataRange(size_t leafsize_, off_t offset_, size_t count_): leafsize(leafsize_), offset(offset_), count(count_) {}
size_t leafsize;
@ -262,8 +267,8 @@ TEST_P(DataLeafNodeDataTest, OverwriteAndRead) {
leaf->resize(GetParam().leafsize);
leaf->write(0, GetParam().leafsize, this->backgroundData);
leaf->write(GetParam().offset, GetParam().count, this->foregroundData);
EXPECT_DATA_READS_AS(this->foregroundData, *leaf, GetParam().offset, GetParam().count);
EXPECT_DATA_READS_AS_OUTSIDE_OF(this->backgroundData, *leaf, GetParam().offset, GetParam().count);
}
*/