- BlockStore::create() gets the data of the new block as a parameter

- Fixed numBlocks() in OnDiskBlockStore, FakeBlockStore, CachingBlockStore, ...
- CachingBlockStore caches created blocks and doesn't directly create them in the underlying blockstore
This commit is contained in:
Sebastian Meßmer 2015-04-18 14:47:12 +02:00
parent 18e7d68f15
commit 417a701636
34 changed files with 392 additions and 193 deletions

View File

@ -1,4 +1,5 @@
#include "CachedBlock.h" #include "CachedBlock.h"
#include "NewBlock.h"
#include "CachingBlockStore.h" #include "CachingBlockStore.h"
#include "../../interface/Block.h" #include "../../interface/Block.h"
@ -13,15 +14,16 @@ namespace blockstore {
namespace caching { namespace caching {
CachingBlockStore::CachingBlockStore(std::unique_ptr<BlockStore> baseBlockStore) CachingBlockStore::CachingBlockStore(std::unique_ptr<BlockStore> baseBlockStore)
:_baseBlockStore(std::move(baseBlockStore)) { :_baseBlockStore(std::move(baseBlockStore)), _cache(), _numNewBlocks(0) {
} }
unique_ptr<Block> CachingBlockStore::create(size_t size) { Key CachingBlockStore::createKey() {
//TODO Also cache this and only write back in the destructor? return _baseBlockStore->createKey();
// When writing back is done efficiently in the base store (e.g. only one safe-to-disk, not one in the create() and then one in the save(), this is not supported by the current BlockStore interface), }
// then the base store could actually directly create a block in the create() call, OnDiskBlockStore wouldn't have to avoid file creation in the create() call for performance reasons and I could also adapt the OnDiskBlockStore test cases and remove a lot of flush() calls there because then blocks are loadable directly after the create call() without a flush.
// Currently, OnDiskBlockStore doesn't create new blocks directly but only after they're destructed (performance reasons), but this means a newly created block can't be loaded directly. unique_ptr<Block> CachingBlockStore::tryCreate(const Key &key, Data data) {
return make_unique<CachedBlock>(_baseBlockStore->create(size), this); ++_numNewBlocks;
return make_unique<CachedBlock>(make_unique<NewBlock>(key, std::move(data), this), this);
} }
unique_ptr<Block> CachingBlockStore::load(const Key &key) { unique_ptr<Block> CachingBlockStore::load(const Key &key) {
@ -37,16 +39,38 @@ unique_ptr<Block> CachingBlockStore::load(const Key &key) {
} }
void CachingBlockStore::remove(std::unique_ptr<Block> block) { void CachingBlockStore::remove(std::unique_ptr<Block> block) {
return _baseBlockStore->remove(std::move(dynamic_pointer_move<CachedBlock>(block)->releaseBlock())); auto baseBlock = dynamic_pointer_move<CachedBlock>(block)->releaseBlock();
auto baseNewBlock = dynamic_pointer_move<NewBlock>(baseBlock);
if (baseNewBlock.get() != nullptr) {
if(!baseNewBlock->alreadyExistsInBaseStore()) {
--_numNewBlocks;
}
baseNewBlock->remove();
} else {
_baseBlockStore->remove(std::move(baseBlock));
}
} }
uint64_t CachingBlockStore::numBlocks() const { uint64_t CachingBlockStore::numBlocks() const {
return _baseBlockStore->numBlocks(); //TODO Add number of NewBlock instances
return _baseBlockStore->numBlocks() + _numNewBlocks;
} }
void CachingBlockStore::release(unique_ptr<Block> block) { void CachingBlockStore::release(unique_ptr<Block> block) {
_cache.push(std::move(block)); _cache.push(std::move(block));
} }
std::unique_ptr<Block> CachingBlockStore::tryCreateInBaseStore(const Key &key, Data data) {
auto block = _baseBlockStore->tryCreate(key, std::move(data));
if (block.get() != nullptr) {
--_numNewBlocks;
}
return block;
}
void CachingBlockStore::removeFromBaseStore(std::unique_ptr<Block> block) {
_baseBlockStore->remove(std::move(block));
}
} }
} }

View File

@ -14,16 +14,21 @@ class CachingBlockStore: public BlockStore {
public: public:
CachingBlockStore(std::unique_ptr<BlockStore> baseBlockStore); CachingBlockStore(std::unique_ptr<BlockStore> baseBlockStore);
std::unique_ptr<Block> create(size_t size) override; Key createKey() override;
std::unique_ptr<Block> tryCreate(const Key &key, Data data) override;
std::unique_ptr<Block> load(const Key &key) override; std::unique_ptr<Block> load(const Key &key) override;
void remove(std::unique_ptr<Block> block) override; void remove(std::unique_ptr<Block> block) override;
uint64_t numBlocks() const override; uint64_t numBlocks() const override;
void release(std::unique_ptr<Block> block); void release(std::unique_ptr<Block> block);
std::unique_ptr<Block> tryCreateInBaseStore(const Key &key, Data data);
void removeFromBaseStore(std::unique_ptr<Block> block);
private: private:
std::unique_ptr<BlockStore> _baseBlockStore; std::unique_ptr<BlockStore> _baseBlockStore;
Cache _cache; Cache _cache;
uint32_t _numNewBlocks;
DISALLOW_COPY_AND_ASSIGN(CachingBlockStore); DISALLOW_COPY_AND_ASSIGN(CachingBlockStore);
}; };

View File

@ -0,0 +1,66 @@
#include "NewBlock.h"
#include "CachingBlockStore.h"
using std::unique_ptr;
using std::make_unique;
namespace blockstore {
namespace caching {
NewBlock::NewBlock(const Key &key, Data data, CachingBlockStore *blockStore)
:Block(key),
_blockStore(blockStore),
_data(std::move(data)),
_baseBlock(nullptr),
_dataChanged(true) {
}
NewBlock::~NewBlock() {
writeToBaseBlockIfChanged();
}
const void *NewBlock::data() const {
return _data.data();
}
void NewBlock::write(const void *source, uint64_t offset, uint64_t size) {
assert(offset <= _data.size() && offset + size <= _data.size());
std::memcpy((uint8_t*)_data.data()+offset, source, size);
_dataChanged = true;
}
void NewBlock::writeToBaseBlockIfChanged() {
if (_dataChanged) {
if (_baseBlock.get() == nullptr) {
//TODO What if tryCreate fails due to a duplicate key? We should ensure we don't use duplicate keys.
//TODO _data.copy() necessary?
_baseBlock = _blockStore->tryCreateInBaseStore(key(), _data.copy());
} else {
_baseBlock->write(_data.data(), 0, _data.size());
}
_dataChanged = false;
}
}
void NewBlock::remove() {
if (_baseBlock.get() != nullptr) {
_blockStore->removeFromBaseStore(std::move(_baseBlock));
}
_dataChanged = false;
}
void NewBlock::flush() {
writeToBaseBlockIfChanged();
_baseBlock->flush();
}
size_t NewBlock::size() const {
return _data.size();
}
bool NewBlock::alreadyExistsInBaseStore() const {
return _baseBlock.get() != nullptr;
}
}
}

View File

@ -0,0 +1,50 @@
#pragma once
#ifndef BLOCKSTORE_IMPLEMENTATIONS_CACHING_NEWBLOCK_H_
#define BLOCKSTORE_IMPLEMENTATIONS_CACHING_NEWBLOCK_H_
#include "../../interface/BlockStore.h"
#include "../../utils/Data.h"
#include "messmer/cpp-utils/macros.h"
#include <memory>
namespace blockstore {
namespace caching {
class CachingBlockStore;
//TODO Does it make sense to write a general DataBackedBlock that just stores a Data object and maps the block operations to it?
// Can we reuse that object somewhere else?
// Maybe a second abstract class for BlockRefBackedBlock?
// This is a block that was created in CachingBlockStore, but doesn't exist in the base block store yet.
// It only exists in the cache and it is created in the base block store when destructed.
class NewBlock: public Block {
public:
NewBlock(const Key &key, Data data, CachingBlockStore *blockStore);
virtual ~NewBlock();
const void *data() const override;
void write(const void *source, uint64_t offset, uint64_t size) override;
void flush() override;
size_t size() const override;
void remove();
bool alreadyExistsInBaseStore() const;
private:
CachingBlockStore *_blockStore;
Data _data;
std::unique_ptr<Block> _baseBlock;
bool _dataChanged;
void writeToBaseBlockIfChanged();
DISALLOW_COPY_AND_ASSIGN(NewBlock);
};
}
}
#endif

View File

@ -15,11 +15,15 @@ namespace encrypted {
constexpr unsigned int EncryptedBlock::IV_SIZE; constexpr unsigned int EncryptedBlock::IV_SIZE;
std::unique_ptr<EncryptedBlock> EncryptedBlock::CreateNew(std::unique_ptr<Block> baseBlock, const EncryptionKey &encKey) { std::unique_ptr<EncryptedBlock> EncryptedBlock::TryCreateNew(BlockStore *baseBlockStore, const Key &key, Data data, const EncryptionKey &encKey) {
auto block = make_unique<EncryptedBlock>(std::move(baseBlock), encKey); Data encrypted = _encrypt(data, encKey);
//We have to explicitly fill the block with zeroes, because otherwise the encrypted version is filled with zeroes and not the plaintext version auto baseBlock = baseBlockStore->tryCreate(key, std::move(encrypted));
utils::fillWithZeroes(block.get()); if (baseBlock.get() == nullptr) {
return block; //TODO Test this code branch
return nullptr;
}
return make_unique<EncryptedBlock>(std::move(baseBlock), encKey);
} }
EncryptedBlock::EncryptedBlock(std::unique_ptr<Block> baseBlock, const EncryptionKey &encKey) EncryptedBlock::EncryptedBlock(std::unique_ptr<Block> baseBlock, const EncryptionKey &encKey)
@ -32,7 +36,7 @@ EncryptedBlock::EncryptedBlock(std::unique_ptr<Block> baseBlock, const Encryptio
} }
EncryptedBlock::~EncryptedBlock() { EncryptedBlock::~EncryptedBlock() {
flush(); _encryptToBaseBlock();
} }
const void *EncryptedBlock::data() const { const void *EncryptedBlock::data() const {
@ -63,16 +67,21 @@ void EncryptedBlock::_decryptFromBaseBlock() {
void EncryptedBlock::_encryptToBaseBlock() { void EncryptedBlock::_encryptToBaseBlock() {
if (_dataChanged) { if (_dataChanged) {
FixedSizeData<IV_SIZE> iv = FixedSizeData<IV_SIZE>::CreateRandom(); Data encrypted = _encrypt(_plaintextData, _encKey);
auto encryption = CFB_Mode<AES>::Encryption(_encKey.data(), EncryptionKey::BINARY_LENGTH, iv.data()); _baseBlock->write(encrypted.data(), 0, encrypted.size());
//TODO More performance when not using "Data encrypted" object, but specialized CryptoPP sink
Data encrypted(_plaintextData.size());
encryption.ProcessData((byte*)encrypted.data(), (byte*)_plaintextData.data(), _plaintextData.size());
_baseBlock->write(iv.data(), 0, IV_SIZE);
_baseBlock->write(encrypted.data(), IV_SIZE, encrypted.size());
_dataChanged = false; _dataChanged = false;
} }
} }
Data EncryptedBlock::_encrypt(const Data &plaintext, const EncryptionKey &encKey) {
FixedSizeData<IV_SIZE> iv = FixedSizeData<IV_SIZE>::CreateRandom();
auto encryption = CFB_Mode<AES>::Encryption(encKey.data(), EncryptionKey::BINARY_LENGTH, iv.data());
//TODO More performance when not using "Data encrypted" object, but encrypting directly to a target that was specified via a parameter using a specialized CryptoPP sink
Data encrypted(IV_SIZE + plaintext.size());
std::memcpy(encrypted.data(), iv.data(), IV_SIZE);
encryption.ProcessData((byte*)encrypted.data() + IV_SIZE, (byte*)plaintext.data(), plaintext.size());
return encrypted;
}
} }
} }

View File

@ -5,6 +5,7 @@
#include "../../interface/Block.h" #include "../../interface/Block.h"
#include "EncryptionKey.h" #include "EncryptionKey.h"
#include "../../utils/Data.h" #include "../../utils/Data.h"
#include "../../interface/BlockStore.h"
#include "messmer/cpp-utils/macros.h" #include "messmer/cpp-utils/macros.h"
#include <memory> #include <memory>
@ -19,7 +20,7 @@ public:
EncryptedBlock(std::unique_ptr<Block> baseBlock, const EncryptionKey &encKey); EncryptedBlock(std::unique_ptr<Block> baseBlock, const EncryptionKey &encKey);
virtual ~EncryptedBlock(); virtual ~EncryptedBlock();
static std::unique_ptr<EncryptedBlock> CreateNew(std::unique_ptr<Block>, const EncryptionKey &encKey); static std::unique_ptr<EncryptedBlock> TryCreateNew(BlockStore *baseBlockStore, const Key &key, Data data, const EncryptionKey &encKey);
const void *data() const override; const void *data() const override;
void write(const void *source, uint64_t offset, uint64_t size) override; void write(const void *source, uint64_t offset, uint64_t size) override;
@ -49,6 +50,8 @@ private:
void _encryptToBaseBlock(); void _encryptToBaseBlock();
void _decryptFromBaseBlock(); void _decryptFromBaseBlock();
static Data _encrypt(const Data &plaintext, const EncryptionKey &encKey);
DISALLOW_COPY_AND_ASSIGN(EncryptedBlock); DISALLOW_COPY_AND_ASSIGN(EncryptedBlock);
}; };

View File

@ -13,8 +13,12 @@ EncryptedBlockStore::EncryptedBlockStore(unique_ptr<BlockStore> baseBlockStore,
: _baseBlockStore(std::move(baseBlockStore)), _encKey(encKey) { : _baseBlockStore(std::move(baseBlockStore)), _encKey(encKey) {
} }
unique_ptr<Block> EncryptedBlockStore::create(size_t size) { Key EncryptedBlockStore::createKey() {
return EncryptedBlock::CreateNew(_baseBlockStore->create(EncryptedBlock::BASE_BLOCK_SIZE(size)), _encKey); return _baseBlockStore->createKey();
}
unique_ptr<Block> EncryptedBlockStore::tryCreate(const Key &key, Data data) {
return EncryptedBlock::TryCreateNew(_baseBlockStore.get(), key, std::move(data), _encKey);
} }
unique_ptr<Block> EncryptedBlockStore::load(const Key &key) { unique_ptr<Block> EncryptedBlockStore::load(const Key &key) {
@ -33,5 +37,10 @@ uint64_t EncryptedBlockStore::numBlocks() const {
return _baseBlockStore->numBlocks(); return _baseBlockStore->numBlocks();
} }
unique_ptr<Block> EncryptedBlockStore::tryCreateInBaseStore(const Key &key, Data encryptedData) {
return _baseBlockStore->tryCreate(key, std::move(encryptedData));
}
} }
} }

View File

@ -13,11 +13,14 @@ class EncryptedBlockStore: public BlockStore {
public: public:
EncryptedBlockStore(std::unique_ptr<BlockStore> baseBlockStore, const EncryptionKey &encKey); EncryptedBlockStore(std::unique_ptr<BlockStore> baseBlockStore, const EncryptionKey &encKey);
std::unique_ptr<Block> create(size_t size) override; Key createKey() override;
std::unique_ptr<Block> tryCreate(const Key &key, Data data) override;
std::unique_ptr<Block> load(const Key &key) override; std::unique_ptr<Block> load(const Key &key) override;
void remove(std::unique_ptr<Block> block) override; void remove(std::unique_ptr<Block> block) override;
uint64_t numBlocks() const override; uint64_t numBlocks() const override;
std::unique_ptr<Block> tryCreateInBaseStore(const Key &key, Data encryptedData);
private: private:
std::unique_ptr<BlockStore> _baseBlockStore; std::unique_ptr<BlockStore> _baseBlockStore;
EncryptionKey _encKey; EncryptionKey _encKey;

View File

@ -13,9 +13,8 @@ using std::ios;
namespace blockstore { namespace blockstore {
namespace inmemory { namespace inmemory {
InMemoryBlock::InMemoryBlock(const Key &key, size_t size) InMemoryBlock::InMemoryBlock(const Key &key, Data data)
: Block(key), _data(make_shared<Data>(size)) { : Block(key), _data(make_shared<Data>(std::move(data))) {
_data->FillWithZeroes();
} }
InMemoryBlock::InMemoryBlock(const InMemoryBlock &rhs) InMemoryBlock::InMemoryBlock(const InMemoryBlock &rhs)

View File

@ -11,7 +11,7 @@ class InMemoryBlockStore;
class InMemoryBlock: public Block { class InMemoryBlock: public Block {
public: public:
InMemoryBlock(const Key &key, size_t size); InMemoryBlock(const Key &key, Data size);
InMemoryBlock(const InMemoryBlock &rhs); InMemoryBlock(const InMemoryBlock &rhs);
virtual ~InMemoryBlock(); virtual ~InMemoryBlock();

View File

@ -16,8 +16,8 @@ namespace inmemory {
InMemoryBlockStore::InMemoryBlockStore() InMemoryBlockStore::InMemoryBlockStore()
: _blocks() {} : _blocks() {}
unique_ptr<Block> InMemoryBlockStore::create(const Key &key, size_t size) { unique_ptr<Block> InMemoryBlockStore::tryCreate(const Key &key, Data data) {
auto insert_result = _blocks.emplace(piecewise_construct, make_tuple(key.ToString()), make_tuple(key, size)); auto insert_result = _blocks.emplace(piecewise_construct, make_tuple(key.ToString()), make_tuple(key, std::move(data)));
if (!insert_result.second) { if (!insert_result.second) {
return nullptr; return nullptr;

View File

@ -16,7 +16,7 @@ class InMemoryBlockStore: public BlockStoreWithRandomKeys {
public: public:
InMemoryBlockStore(); InMemoryBlockStore();
std::unique_ptr<Block> create(const Key &key, size_t size) override; std::unique_ptr<Block> tryCreate(const Key &key, Data data) override;
std::unique_ptr<Block> load(const Key &key) override; std::unique_ptr<Block> load(const Key &key) override;
void remove(std::unique_ptr<Block> block) override; void remove(std::unique_ptr<Block> block) override;
uint64_t numBlocks() const override; uint64_t numBlocks() const override;

View File

@ -19,11 +19,7 @@ namespace bf = boost::filesystem;
namespace blockstore { namespace blockstore {
namespace ondisk { namespace ondisk {
OnDiskBlock::OnDiskBlock(const Key &key, const bf::path &filepath, size_t size) OnDiskBlock::OnDiskBlock(const Key &key, const bf::path &filepath, Data data)
: Block(key), _filepath(filepath), _data(size), _dataChanged(false) {
}
OnDiskBlock::OnDiskBlock(const Key &key, const bf::path &filepath, Data &&data)
: Block(key), _filepath(filepath), _data(std::move(data)), _dataChanged(false) { : Block(key), _filepath(filepath), _data(std::move(data)), _dataChanged(false) {
} }
@ -61,14 +57,14 @@ unique_ptr<OnDiskBlock> OnDiskBlock::LoadFromDisk(const bf::path &rootdir, const
} }
} }
unique_ptr<OnDiskBlock> OnDiskBlock::CreateOnDisk(const bf::path &rootdir, const Key &key, size_t size) { unique_ptr<OnDiskBlock> OnDiskBlock::CreateOnDisk(const bf::path &rootdir, const Key &key, Data data) {
auto filepath = rootdir / key.ToString(); auto filepath = rootdir / key.ToString();
if (bf::exists(filepath)) { if (bf::exists(filepath)) {
return nullptr; return nullptr;
} }
auto block = unique_ptr<OnDiskBlock>(new OnDiskBlock(key, filepath, size)); auto block = unique_ptr<OnDiskBlock>(new OnDiskBlock(key, filepath, std::move(data)));
block->_fillDataWithZeroes(); block->_storeToDisk();
return block; return block;
} }

View File

@ -18,7 +18,7 @@ public:
virtual ~OnDiskBlock(); virtual ~OnDiskBlock();
static std::unique_ptr<OnDiskBlock> LoadFromDisk(const boost::filesystem::path &rootdir, const Key &key); static std::unique_ptr<OnDiskBlock> LoadFromDisk(const boost::filesystem::path &rootdir, const Key &key);
static std::unique_ptr<OnDiskBlock> CreateOnDisk(const boost::filesystem::path &rootdir, const Key &key, size_t size); static std::unique_ptr<OnDiskBlock> CreateOnDisk(const boost::filesystem::path &rootdir, const Key &key, Data data);
static void RemoveFromDisk(const boost::filesystem::path &rootdir, const Key &key); static void RemoveFromDisk(const boost::filesystem::path &rootdir, const Key &key);
const void *data() const override; const void *data() const override;
@ -33,8 +33,7 @@ private:
Data _data; Data _data;
bool _dataChanged; bool _dataChanged;
OnDiskBlock(const Key &key, const boost::filesystem::path &filepath, size_t size); OnDiskBlock(const Key &key, const boost::filesystem::path &filepath, Data data);
OnDiskBlock(const Key &key, const boost::filesystem::path &filepath, Data &&data);
void _fillDataWithZeroes(); void _fillDataWithZeroes();
void _storeToDisk() const; void _storeToDisk() const;

View File

@ -13,8 +13,8 @@ namespace ondisk {
OnDiskBlockStore::OnDiskBlockStore(const boost::filesystem::path &rootdir) OnDiskBlockStore::OnDiskBlockStore(const boost::filesystem::path &rootdir)
: _rootdir(rootdir) {} : _rootdir(rootdir) {}
unique_ptr<Block> OnDiskBlockStore::create(const Key &key, size_t size) { unique_ptr<Block> OnDiskBlockStore::tryCreate(const Key &key, Data data) {
auto block = OnDiskBlock::CreateOnDisk(_rootdir, key, size); auto block = OnDiskBlock::CreateOnDisk(_rootdir, key, std::move(data));
if (!block) { if (!block) {
return nullptr; return nullptr;

View File

@ -14,7 +14,7 @@ class OnDiskBlockStore: public BlockStoreWithRandomKeys {
public: public:
OnDiskBlockStore(const boost::filesystem::path &rootdir); OnDiskBlockStore(const boost::filesystem::path &rootdir);
std::unique_ptr<Block> create(const Key &key, size_t size) override; std::unique_ptr<Block> tryCreate(const Key &key, Data data) override;
std::unique_ptr<Block> load(const Key &key) override; std::unique_ptr<Block> load(const Key &key) override;
void remove(std::unique_ptr<Block> block) override; void remove(std::unique_ptr<Block> block) override;
uint64_t numBlocks() const override; uint64_t numBlocks() const override;

View File

@ -20,9 +20,16 @@ ParallelAccessBlockStore::ParallelAccessBlockStore(unique_ptr<BlockStore> baseBl
: _baseBlockStore(std::move(baseBlockStore)), _parallelAccessStore(make_unique<ParallelAccessBlockStoreAdapter>(_baseBlockStore.get())) { : _baseBlockStore(std::move(baseBlockStore)), _parallelAccessStore(make_unique<ParallelAccessBlockStoreAdapter>(_baseBlockStore.get())) {
} }
unique_ptr<Block> ParallelAccessBlockStore::create(size_t size) { Key ParallelAccessBlockStore::createKey() {
auto block = _baseBlockStore->create(size); return _baseBlockStore->createKey();
Key key = block->key(); }
unique_ptr<Block> ParallelAccessBlockStore::tryCreate(const Key &key, Data data) {
auto block = _baseBlockStore->tryCreate(key, std::move(data));
if (block.get() == nullptr) {
//TODO Test this code branch
return nullptr;
}
return _parallelAccessStore.add(key, std::move(block)); return _parallelAccessStore.add(key, std::move(block));
} }

View File

@ -14,7 +14,8 @@ class ParallelAccessBlockStore: public BlockStore {
public: public:
ParallelAccessBlockStore(std::unique_ptr<BlockStore> baseBlockStore); ParallelAccessBlockStore(std::unique_ptr<BlockStore> baseBlockStore);
std::unique_ptr<Block> create(size_t size) override; Key createKey() override;
std::unique_ptr<Block> tryCreate(const Key &key, Data data) override;
std::unique_ptr<Block> load(const Key &key) override; std::unique_ptr<Block> load(const Key &key) override;
void remove(std::unique_ptr<Block> block) override; void remove(std::unique_ptr<Block> block) override;
uint64_t numBlocks() const override; uint64_t numBlocks() const override;

View File

@ -14,13 +14,15 @@ namespace testfake {
FakeBlockStore::FakeBlockStore() FakeBlockStore::FakeBlockStore()
: _blocks(), _used_dataregions_for_blocks() {} : _blocks(), _used_dataregions_for_blocks() {}
unique_ptr<Block> FakeBlockStore::create(const Key &key, size_t size) { unique_ptr<Block> FakeBlockStore::tryCreate(const Key &key, Data data) {
if (_blocks.find(key.ToString()) != _blocks.end()) { auto insert_result = _blocks.emplace(key.ToString(), std::move(data));
if (!insert_result.second) {
return nullptr; return nullptr;
} }
Data data(size);
data.FillWithZeroes(); //Return a copy of the stored data
return makeFakeBlockFromData(key, data, true); return load(key);
} }
unique_ptr<Block> FakeBlockStore::load(const Key &key) { unique_ptr<Block> FakeBlockStore::load(const Key &key) {
@ -49,7 +51,7 @@ unique_ptr<Block> FakeBlockStore::makeFakeBlockFromData(const Key &key, const Da
void FakeBlockStore::updateData(const Key &key, const Data &data) { void FakeBlockStore::updateData(const Key &key, const Data &data) {
auto found = _blocks.find(key.ToString()); auto found = _blocks.find(key.ToString());
if (found == _blocks.end()) { if (found == _blocks.end()) {
auto insertResult = _blocks.emplace(key.ToString(), data.size()); auto insertResult = _blocks.emplace(key.ToString(), data.copy());
assert(true == insertResult.second); assert(true == insertResult.second);
found = insertResult.first; found = insertResult.first;
} }

View File

@ -31,7 +31,7 @@ class FakeBlockStore: public BlockStoreWithRandomKeys {
public: public:
FakeBlockStore(); FakeBlockStore();
std::unique_ptr<Block> create(const Key &key, size_t size) override; std::unique_ptr<Block> tryCreate(const Key &key, Data data) override;
std::unique_ptr<Block> load(const Key &key) override; std::unique_ptr<Block> load(const Key &key) override;
void remove(std::unique_ptr<Block> block) override; void remove(std::unique_ptr<Block> block) override;
uint64_t numBlocks() const override; uint64_t numBlocks() const override;

View File

@ -5,7 +5,7 @@
#include "Block.h" #include "Block.h"
#include <string> #include <string>
#include <memory> #include <memory>
#include "../utils/Data.h"
namespace blockstore { namespace blockstore {
@ -13,12 +13,23 @@ class BlockStore {
public: public:
virtual ~BlockStore() {} virtual ~BlockStore() {}
virtual std::unique_ptr<Block> create(size_t size) = 0; virtual Key createKey() = 0;
//Returns nullptr if key already exists
virtual std::unique_ptr<Block> tryCreate(const Key &key, Data data) = 0;
//TODO Use boost::optional (if key doesn't exist) //TODO Use boost::optional (if key doesn't exist)
// Return nullptr if block with this key doesn't exists // Return nullptr if block with this key doesn't exists
virtual std::unique_ptr<Block> load(const Key &key) = 0; virtual std::unique_ptr<Block> load(const Key &key) = 0;
virtual void remove(std::unique_ptr<Block> block) = 0; virtual void remove(std::unique_ptr<Block> block) = 0;
virtual uint64_t numBlocks() const = 0; virtual uint64_t numBlocks() const = 0;
std::unique_ptr<Block> create(const Data &data) {
std::unique_ptr<Block> block(nullptr);
while(block.get() == nullptr) {
//TODO Copy necessary?
block = tryCreate(createKey(), data.copy());
}
return block;
}
}; };
} }

View File

@ -1,20 +1 @@
#include "BlockStoreWithRandomKeys.h" #include "BlockStoreWithRandomKeys.h"
using namespace blockstore;
using std::string;
using std::unique_ptr;
unique_ptr<Block> BlockStoreWithRandomKeys::create(size_t size) {
auto result = tryCreate(size);
while (!result) {
result = tryCreate(size);
}
return result;
}
unique_ptr<Block> BlockStoreWithRandomKeys::tryCreate(size_t size) {
Key key = Key::CreateRandom();
return create(key, size);
}

View File

@ -12,14 +12,9 @@ namespace blockstore {
// work with the BlockStore interface instead. // work with the BlockStore interface instead.
class BlockStoreWithRandomKeys: public BlockStore { class BlockStoreWithRandomKeys: public BlockStore {
public: public:
//TODO Use boost::optional (if key already exists) Key createKey() final {
// Return nullptr if key already exists return Key::CreateRandom();
virtual std::unique_ptr<Block> create(const Key &key, size_t size) = 0; }
std::unique_ptr<Block> create(size_t size) final;
private:
std::unique_ptr<Block> tryCreate(size_t size);
}; };
} }

View File

@ -35,7 +35,7 @@ public:
TEST_F(OnDiskBlockCreateTest, CreatingBlockCreatesFile) { TEST_F(OnDiskBlockCreateTest, CreatingBlockCreatesFile) {
EXPECT_FALSE(bf::exists(file.path())); EXPECT_FALSE(bf::exists(file.path()));
auto block = OnDiskBlock::CreateOnDisk(dir.path(), key, 0); auto block = OnDiskBlock::CreateOnDisk(dir.path(), key, Data(0));
block->flush(); block->flush();
EXPECT_TRUE(bf::exists(file.path())); EXPECT_TRUE(bf::exists(file.path()));
@ -43,9 +43,9 @@ TEST_F(OnDiskBlockCreateTest, CreatingBlockCreatesFile) {
} }
TEST_F(OnDiskBlockCreateTest, CreatingExistingBlockReturnsNull) { TEST_F(OnDiskBlockCreateTest, CreatingExistingBlockReturnsNull) {
auto block1 = OnDiskBlock::CreateOnDisk(dir.path(), key, 0); auto block1 = OnDiskBlock::CreateOnDisk(dir.path(), key, Data(0));
block1->flush(); block1->flush();
auto block2 = OnDiskBlock::CreateOnDisk(dir.path(), key, 0); auto block2 = OnDiskBlock::CreateOnDisk(dir.path(), key, Data(0));
EXPECT_TRUE((bool)block1); EXPECT_TRUE((bool)block1);
EXPECT_FALSE((bool)block2); EXPECT_FALSE((bool)block2);
} }
@ -56,7 +56,7 @@ public:
Data ZEROES; Data ZEROES;
OnDiskBlockCreateSizeTest(): OnDiskBlockCreateSizeTest():
block(OnDiskBlock::CreateOnDisk(dir.path(), key, GetParam())), block(OnDiskBlock::CreateOnDisk(dir.path(), key, std::move(Data(GetParam()).FillWithZeroes()))),
ZEROES(block->size()) ZEROES(block->size())
{ {
block->flush(); block->flush();

View File

@ -37,13 +37,17 @@ public:
unique_ptr<OnDiskBlock> CreateBlockAndLoadItFromDisk() { unique_ptr<OnDiskBlock> CreateBlockAndLoadItFromDisk() {
{ {
auto block = OnDiskBlock::CreateOnDisk(dir.path(), key, randomData.size()); Data data(randomData.size());
std::memcpy(data.data(), randomData.data(), randomData.size());
auto block = OnDiskBlock::CreateOnDisk(dir.path(), key, std::move(data));
} }
return OnDiskBlock::LoadFromDisk(dir.path(), key); return OnDiskBlock::LoadFromDisk(dir.path(), key);
} }
unique_ptr<OnDiskBlock> CreateBlock() { unique_ptr<OnDiskBlock> CreateBlock() {
return OnDiskBlock::CreateOnDisk(dir.path(), key, randomData.size()); Data data(randomData.size());
std::memcpy(data.data(), randomData.data(), randomData.size());
return OnDiskBlock::CreateOnDisk(dir.path(), key, std::move(data));
} }
void WriteDataToBlock(const unique_ptr<OnDiskBlock> &block) { void WriteDataToBlock(const unique_ptr<OnDiskBlock> &block) {

View File

@ -1,12 +1,14 @@
#include "../../../interface/helpers/BlockStoreWithRandomKeys.h" #include "../../../interface/helpers/BlockStoreWithRandomKeys.h"
#include "google/gtest/gtest.h" #include "google/gtest/gtest.h"
#include "google/gmock/gmock.h" #include "google/gmock/gmock.h"
#include "../../testutils/DataBlockFixture.h"
using ::testing::Test; using ::testing::Test;
using ::testing::_; using ::testing::_;
using ::testing::Return; using ::testing::Return;
using ::testing::Invoke; using ::testing::Invoke;
using ::testing::Eq;
using ::testing::ByRef;
using std::string; using std::string;
using std::unique_ptr; using std::unique_ptr;
@ -16,10 +18,10 @@ using namespace blockstore;
class BlockStoreWithRandomKeysMock: public BlockStoreWithRandomKeys { class BlockStoreWithRandomKeysMock: public BlockStoreWithRandomKeys {
public: public:
unique_ptr<Block> create(const Key &key, size_t size) { unique_ptr<Block> tryCreate(const Key &key, Data data) {
return unique_ptr<Block>(do_create(key, size)); return unique_ptr<Block>(do_create(key, data));
} }
MOCK_METHOD2(do_create, Block*(const Key &, size_t)); MOCK_METHOD2(do_create, Block*(const Key &, const Data &data));
unique_ptr<Block> load(const Key &key) { unique_ptr<Block> load(const Key &key) {
return unique_ptr<Block>(do_load(key)); return unique_ptr<Block>(do_load(key));
} }
@ -43,78 +45,93 @@ public:
BlockStoreWithRandomKeysMock blockStoreMock; BlockStoreWithRandomKeysMock blockStoreMock;
BlockStore &blockStore = blockStoreMock; BlockStore &blockStore = blockStoreMock;
const blockstore::Key key = Key::FromString("1491BB4932A389EE14BC7090AC772972"); const blockstore::Key key = Key::FromString("1491BB4932A389EE14BC7090AC772972");
Data createDataWithSize(size_t size) {
DataBlockFixture fixture(size);
Data data(size);
std::memcpy(data.data(), fixture.data(), size);
return data;
}
}; };
TEST_F(BlockStoreWithRandomKeysTest, SizeIsPassedThrough0) { TEST_F(BlockStoreWithRandomKeysTest, DataIsPassedThrough0) {
EXPECT_CALL(blockStoreMock, do_create(_, 0)).WillOnce(Return(new BlockMock)); Data data = createDataWithSize(0);
blockStore.create(0); EXPECT_CALL(blockStoreMock, do_create(_, Eq(ByRef(data)))).WillOnce(Return(new BlockMock));
blockStore.create(data);
} }
TEST_F(BlockStoreWithRandomKeysTest, SizeIsPassedThrough1) { TEST_F(BlockStoreWithRandomKeysTest, DataIsPassedThrough1) {
EXPECT_CALL(blockStoreMock, do_create(_, 1)).WillOnce(Return(new BlockMock)); Data data = createDataWithSize(1);
blockStore.create(1); EXPECT_CALL(blockStoreMock, do_create(_, Eq(ByRef(data)))).WillOnce(Return(new BlockMock));
blockStore.create(data);
} }
TEST_F(BlockStoreWithRandomKeysTest, SizeIsPassedThrough1024) { TEST_F(BlockStoreWithRandomKeysTest, DataIsPassedThrough1024) {
EXPECT_CALL(blockStoreMock, do_create(_, 1024)).WillOnce(Return(new BlockMock)); Data data = createDataWithSize(1024);
blockStore.create(1024); EXPECT_CALL(blockStoreMock, do_create(_, Eq(ByRef(data)))).WillOnce(Return(new BlockMock));
blockStore.create(data);
} }
TEST_F(BlockStoreWithRandomKeysTest, KeyHasCorrectSize) { TEST_F(BlockStoreWithRandomKeysTest, KeyHasCorrectSize) {
EXPECT_CALL(blockStoreMock, do_create(_, _)).WillOnce(Invoke([](const Key &key, size_t) { EXPECT_CALL(blockStoreMock, do_create(_, _)).WillOnce(Invoke([](const Key &key, const Data &) {
EXPECT_EQ(Key::STRING_LENGTH, key.ToString().size()); EXPECT_EQ(Key::STRING_LENGTH, key.ToString().size());
return new BlockMock; return new BlockMock;
})); }));
blockStore.create(1024); blockStore.create(createDataWithSize(1024));
} }
TEST_F(BlockStoreWithRandomKeysTest, TwoBlocksGetDifferentKeys) { TEST_F(BlockStoreWithRandomKeysTest, TwoBlocksGetDifferentKeys) {
Key first_key = key; Key first_key = key;
EXPECT_CALL(blockStoreMock, do_create(_, _)) EXPECT_CALL(blockStoreMock, do_create(_, _))
.WillOnce(Invoke([&first_key](const Key &key, size_t) { .WillOnce(Invoke([&first_key](const Key &key, const Data &) {
first_key = key; first_key = key;
return new BlockMock; return new BlockMock;
})) }))
.WillOnce(Invoke([&first_key](const Key &key, size_t) { .WillOnce(Invoke([&first_key](const Key &key, const Data &) {
EXPECT_NE(first_key, key); EXPECT_NE(first_key, key);
return new BlockMock; return new BlockMock;
})); }));
blockStore.create(1024); Data data = createDataWithSize(1024);
blockStore.create(1024); blockStore.create(data);
blockStore.create(data);
} }
TEST_F(BlockStoreWithRandomKeysTest, WillTryADifferentKeyIfKeyAlreadyExists) { TEST_F(BlockStoreWithRandomKeysTest, WillTryADifferentKeyIfKeyAlreadyExists) {
Key first_key = key; Key first_key = key;
EXPECT_CALL(blockStoreMock, do_create(_, _)) Data data = createDataWithSize(1024);
.WillOnce(Invoke([&first_key](const Key &key, size_t) { EXPECT_CALL(blockStoreMock, do_create(_, Eq(ByRef(data))))
.WillOnce(Invoke([&first_key](const Key &key, const Data &) {
first_key = key; first_key = key;
return nullptr; return nullptr;
})) }))
.WillOnce(Invoke([&first_key](const Key &key, size_t) { //TODO Check that this test case fails when the second do_create call gets different data
.WillOnce(Invoke([&first_key](const Key &key, const Data &) {
EXPECT_NE(first_key, key); EXPECT_NE(first_key, key);
return new BlockMock; return new BlockMock;
})); }));
blockStore.create(1024); blockStore.create(data);
} }
TEST_F(BlockStoreWithRandomKeysTest, WillTryADifferentKeyIfKeyAlreadyExistsTwoTimes) { TEST_F(BlockStoreWithRandomKeysTest, WillTryADifferentKeyIfKeyAlreadyExistsTwoTimes) {
Key first_key = key; Key first_key = key;
EXPECT_CALL(blockStoreMock, do_create(_, _)) Data data = createDataWithSize(1024);
.WillOnce(Invoke([&first_key](const Key &key, size_t) { EXPECT_CALL(blockStoreMock, do_create(_, Eq(ByRef(data))))
.WillOnce(Invoke([&first_key](const Key &key, const Data &) {
first_key = key; first_key = key;
return nullptr; return nullptr;
})) }))
.WillOnce(Invoke([&first_key](const Key &key, size_t) { //TODO Check that this test case fails when the second/third do_create calls get different data
.WillOnce(Invoke([&first_key](const Key &key, const Data &) {
first_key = key; first_key = key;
return nullptr; return nullptr;
})) }))
.WillOnce(Invoke([&first_key](const Key &key, size_t) { .WillOnce(Invoke([&first_key](const Key &key, const Data &) {
EXPECT_NE(first_key, key); EXPECT_NE(first_key, key);
return new BlockMock; return new BlockMock;
})); }));
blockStore.create(1024); blockStore.create(data);
} }

View File

@ -28,14 +28,14 @@ TYPED_TEST_CASE_P(BlockStoreTest);
TYPED_TEST_P(BlockStoreTest, TwoCreatedBlocksHaveDifferentKeys) { TYPED_TEST_P(BlockStoreTest, TwoCreatedBlocksHaveDifferentKeys) {
auto blockStore = this->fixture.createBlockStore(); auto blockStore = this->fixture.createBlockStore();
auto block1 = blockStore->create(1024); auto block1 = blockStore->create(blockstore::Data(1024));
auto block2 = blockStore->create(1024); auto block2 = blockStore->create(blockstore::Data(1024));
EXPECT_NE(block1->key(), block2->key()); EXPECT_NE(block1->key(), block2->key());
} }
TYPED_TEST_P(BlockStoreTest, BlockIsNotLoadableAfterDeleting) { TYPED_TEST_P(BlockStoreTest, BlockIsNotLoadableAfterDeleting) {
auto blockStore = this->fixture.createBlockStore(); auto blockStore = this->fixture.createBlockStore();
auto blockkey = blockStore->create(1024)->key(); auto blockkey = blockStore->create(blockstore::Data(1024))->key();
auto block = blockStore->load(blockkey); auto block = blockStore->load(blockkey);
EXPECT_NE(nullptr, block.get()); EXPECT_NE(nullptr, block.get());
blockStore->remove(std::move(block)); blockStore->remove(std::move(block));
@ -49,55 +49,55 @@ TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectOnEmptyBlockstore) {
TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingOneBlock) { TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingOneBlock) {
auto blockStore = this->fixture.createBlockStore(); auto blockStore = this->fixture.createBlockStore();
auto block = blockStore->create(1); auto block = blockStore->create(blockstore::Data(1));
EXPECT_EQ(1, blockStore->numBlocks()); EXPECT_EQ(1, blockStore->numBlocks());
} }
TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingOneBlock_AfterClosingBlock) { TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingOneBlock_AfterClosingBlock) {
auto blockStore = this->fixture.createBlockStore(); auto blockStore = this->fixture.createBlockStore();
blockStore->create(1); blockStore->create(blockstore::Data(1));
EXPECT_EQ(1, blockStore->numBlocks()); EXPECT_EQ(1, blockStore->numBlocks());
} }
TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterRemovingTheLastBlock) { TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterRemovingTheLastBlock) {
auto blockStore = this->fixture.createBlockStore(); auto blockStore = this->fixture.createBlockStore();
auto block = blockStore->create(1); auto block = blockStore->create(blockstore::Data(1));
blockStore->remove(std::move(block)); blockStore->remove(std::move(block));
EXPECT_EQ(0, blockStore->numBlocks()); EXPECT_EQ(0, blockStore->numBlocks());
} }
TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingTwoBlocks) { TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingTwoBlocks) {
auto blockStore = this->fixture.createBlockStore(); auto blockStore = this->fixture.createBlockStore();
auto block1 = blockStore->create(1); auto block1 = blockStore->create(blockstore::Data(1));
auto block2 = blockStore->create(0); auto block2 = blockStore->create(blockstore::Data(0));
EXPECT_EQ(2, blockStore->numBlocks()); EXPECT_EQ(2, blockStore->numBlocks());
} }
TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingTwoBlocks_AfterClosingFirstBlock) { TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingTwoBlocks_AfterClosingFirstBlock) {
auto blockStore = this->fixture.createBlockStore(); auto blockStore = this->fixture.createBlockStore();
blockStore->create(1); blockStore->create(blockstore::Data(1));
auto block2 = blockStore->create(0); auto block2 = blockStore->create(blockstore::Data(0));
EXPECT_EQ(2, blockStore->numBlocks()); EXPECT_EQ(2, blockStore->numBlocks());
} }
TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingTwoBlocks_AfterClosingSecondBlock) { TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingTwoBlocks_AfterClosingSecondBlock) {
auto blockStore = this->fixture.createBlockStore(); auto blockStore = this->fixture.createBlockStore();
auto block1 = blockStore->create(1); auto block1 = blockStore->create(blockstore::Data(1));
blockStore->create(0); blockStore->create(blockstore::Data(0));
EXPECT_EQ(2, blockStore->numBlocks()); EXPECT_EQ(2, blockStore->numBlocks());
} }
TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingTwoBlocks_AfterClosingBothBlocks) { TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingTwoBlocks_AfterClosingBothBlocks) {
auto blockStore = this->fixture.createBlockStore(); auto blockStore = this->fixture.createBlockStore();
blockStore->create(1); blockStore->create(blockstore::Data(1));
blockStore->create(0); blockStore->create(blockstore::Data(0));
EXPECT_EQ(2, blockStore->numBlocks()); EXPECT_EQ(2, blockStore->numBlocks());
} }
TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterRemovingABlock) { TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterRemovingABlock) {
auto blockStore = this->fixture.createBlockStore(); auto blockStore = this->fixture.createBlockStore();
auto block = blockStore->create(1); auto block = blockStore->create(blockstore::Data(1));
blockStore->create(1); blockStore->create(blockstore::Data(1));
blockStore->remove(std::move(block)); blockStore->remove(std::move(block));
EXPECT_EQ(1, blockStore->numBlocks()); EXPECT_EQ(1, blockStore->numBlocks());
} }
@ -109,8 +109,8 @@ TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterRemovingABlock) {
REGISTER_TYPED_TEST_CASE_P(BlockStoreTest, REGISTER_TYPED_TEST_CASE_P(BlockStoreTest,
CreatedBlockHasCorrectSize, CreatedBlockHasCorrectSize,
LoadingUnchangedBlockHasCorrectSize, LoadingUnchangedBlockHasCorrectSize,
CreatedBlockIsZeroedOut, CreatedBlockData,
LoadingUnchangedBlockIsZeroedOut, LoadingUnchangedBlockData,
LoadedBlockIsCorrect, LoadedBlockIsCorrect,
// LoadedBlockIsCorrectWhenLoadedDirectlyAfterFlushing, // LoadedBlockIsCorrectWhenLoadedDirectlyAfterFlushing,
AfterCreate_FlushingDoesntChangeBlock, AfterCreate_FlushingDoesntChangeBlock,

View File

@ -20,7 +20,7 @@ public:
} }
void TestWriteAndReadImmediately() { void TestWriteAndReadImmediately() {
auto block = blockStore->create(testData.blocksize); auto block = blockStore->create(blockstore::Data(testData.blocksize).FillWithZeroes());
block->write(foregroundData.data(), testData.offset, testData.count); block->write(foregroundData.data(), testData.offset, testData.count);
EXPECT_DATA_READS_AS(foregroundData, *block, testData.offset, testData.count); EXPECT_DATA_READS_AS(foregroundData, *block, testData.offset, testData.count);
@ -36,7 +36,7 @@ public:
} }
void TestOverwriteAndRead() { void TestOverwriteAndRead() {
auto block = blockStore->create(testData.blocksize); auto block = blockStore->create(blockstore::Data(testData.blocksize));
block->write(backgroundData.data(), 0, testData.blocksize); block->write(backgroundData.data(), 0, testData.blocksize);
block->write(foregroundData.data(), testData.offset, testData.count); block->write(foregroundData.data(), testData.offset, testData.count);
EXPECT_DATA_READS_AS(foregroundData, *block, testData.offset, testData.count); EXPECT_DATA_READS_AS(foregroundData, *block, testData.offset, testData.count);
@ -55,7 +55,7 @@ private:
} }
blockstore::Key CreateBlockWriteToItAndReturnKey(const blockstore::Data &to_write) { blockstore::Key CreateBlockWriteToItAndReturnKey(const blockstore::Data &to_write) {
auto newblock = blockStore->create(testData.blocksize); auto newblock = blockStore->create(blockstore::Data(testData.blocksize).FillWithZeroes());
newblock->write(to_write.data(), testData.offset, testData.count); newblock->write(to_write.data(), testData.offset, testData.count);
return newblock->key(); return newblock->key();

View File

@ -1,29 +1,38 @@
// This file is meant to be included by BlockStoreTest.h only // This file is meant to be included by BlockStoreTest.h only
#include "../../utils/Data.h"
class BlockStoreSizeParameterizedTest { class BlockStoreSizeParameterizedTest {
public: public:
BlockStoreSizeParameterizedTest(std::unique_ptr<blockstore::BlockStore> blockStore_, size_t size_): blockStore(std::move(blockStore_)), size(size_) {} BlockStoreSizeParameterizedTest(std::unique_ptr<blockstore::BlockStore> blockStore_, size_t size_): blockStore(std::move(blockStore_)), size(size_) {}
void TestCreatedBlockHasCorrectSize() { void TestCreatedBlockHasCorrectSize() {
auto block = blockStore->create(size); auto block = CreateBlock();
EXPECT_EQ(size, block->size()); EXPECT_EQ(size, block->size());
} }
void TestLoadingUnchangedBlockHasCorrectSize() { void TestLoadingUnchangedBlockHasCorrectSize() {
blockstore::Key key = blockStore->create(size)->key(); blockstore::Key key = CreateBlock()->key();
auto loaded_block = blockStore->load(key); auto loaded_block = blockStore->load(key);
EXPECT_EQ(size, loaded_block->size()); EXPECT_EQ(size, loaded_block->size());
} }
void TestCreatedBlockIsZeroedOut() { void TestCreatedBlockData() {
auto block = blockStore->create(size); DataBlockFixture dataFixture(size);
EXPECT_EQ(0, std::memcmp(ZEROES(size).data(), block->data(), size)); blockstore::Data data(size);
std::memcpy(data.data(), dataFixture.data(), size);
auto block = blockStore->create(data);
EXPECT_EQ(0, std::memcmp(dataFixture.data(), block->data(), size));
} }
void TestLoadingUnchangedBlockIsZeroedOut() { void TestLoadingUnchangedBlockData() {
blockstore::Key key = blockStore->create(size)->key(); DataBlockFixture dataFixture(size);
blockstore::Data data(size);
std::memcpy(data.data(), dataFixture.data(), size);
blockstore::Key key = blockStore->create(data)->key();
auto loaded_block = blockStore->load(key); auto loaded_block = blockStore->load(key);
EXPECT_EQ(0, std::memcmp(ZEROES(size).data(), loaded_block->data(), size)); EXPECT_EQ(0, std::memcmp(dataFixture.data(), loaded_block->data(), size));
} }
void TestLoadedBlockIsCorrect() { void TestLoadedBlockIsCorrect() {
@ -62,7 +71,7 @@ public:
DataBlockFixture randomData(size); DataBlockFixture randomData(size);
blockstore::Key key = key; blockstore::Key key = key;
{ {
auto block = blockStore->create(size); auto block = blockStore->create(blockstore::Data(size));
key = block->key(); key = block->key();
WriteDataToBlock(block.get(), randomData); WriteDataToBlock(block.get(), randomData);
} }
@ -74,7 +83,7 @@ public:
DataBlockFixture randomData(size); DataBlockFixture randomData(size);
blockstore::Key key = key; blockstore::Key key = key;
{ {
key = blockStore->create(size)->key(); key = CreateBlock()->key();
auto block = blockStore->load(key); auto block = blockStore->load(key);
WriteDataToBlock(block.get(), randomData); WriteDataToBlock(block.get(), randomData);
} }
@ -104,26 +113,27 @@ private:
return blockStore->load(key); return blockStore->load(key);
} }
blockstore::Key StoreDataToBlockAndGetKey(const DataBlockFixture &data) { blockstore::Key StoreDataToBlockAndGetKey(const DataBlockFixture &dataFixture) {
auto block = blockStore->create(data.size()); blockstore::Data data(dataFixture.size());
block->write(data.data(), 0, data.size()); std::memcpy(data.data(), dataFixture.data(), dataFixture.size());
return block->key(); return blockStore->create(data)->key();
} }
std::unique_ptr<blockstore::Block> StoreDataToBlockAndLoadItDirectlyAfterFlushing(const DataBlockFixture &data) { std::unique_ptr<blockstore::Block> StoreDataToBlockAndLoadItDirectlyAfterFlushing(const DataBlockFixture &dataFixture) {
auto block = blockStore->create(data.size()); blockstore::Data data(dataFixture.size());
block->write(data.data(), 0, data.size()); std::memcpy(data.data(), dataFixture.data(), dataFixture.size());
auto block = blockStore->create(data);
block->flush(); block->flush();
return blockStore->load(block->key()); return blockStore->load(block->key());
} }
std::unique_ptr<blockstore::Block> CreateBlockAndLoadIt() { std::unique_ptr<blockstore::Block> CreateBlockAndLoadIt() {
blockstore::Key key = blockStore->create(size)->key(); blockstore::Key key = CreateBlock()->key();
return blockStore->load(key); return blockStore->load(key);
} }
std::unique_ptr<blockstore::Block> CreateBlock() { std::unique_ptr<blockstore::Block> CreateBlock() {
return blockStore->create(size); return blockStore->create(blockstore::Data(size));
} }
void WriteDataToBlock(blockstore::Block *block, const DataBlockFixture &randomData) { void WriteDataToBlock(blockstore::Block *block, const DataBlockFixture &randomData) {
@ -147,8 +157,8 @@ constexpr std::initializer_list<size_t> SIZES = {0, 1, 1024, 4096, 10*1024*1024}
TYPED_TEST_P_FOR_ALL_SIZES(CreatedBlockHasCorrectSize); TYPED_TEST_P_FOR_ALL_SIZES(CreatedBlockHasCorrectSize);
TYPED_TEST_P_FOR_ALL_SIZES(LoadingUnchangedBlockHasCorrectSize); TYPED_TEST_P_FOR_ALL_SIZES(LoadingUnchangedBlockHasCorrectSize);
TYPED_TEST_P_FOR_ALL_SIZES(CreatedBlockIsZeroedOut); TYPED_TEST_P_FOR_ALL_SIZES(CreatedBlockData);
TYPED_TEST_P_FOR_ALL_SIZES(LoadingUnchangedBlockIsZeroedOut); TYPED_TEST_P_FOR_ALL_SIZES(LoadingUnchangedBlockData);
TYPED_TEST_P_FOR_ALL_SIZES(LoadedBlockIsCorrect); TYPED_TEST_P_FOR_ALL_SIZES(LoadedBlockIsCorrect);
//TYPED_TEST_P_FOR_ALL_SIZES(LoadedBlockIsCorrectWhenLoadedDirectlyAfterFlushing); //TYPED_TEST_P_FOR_ALL_SIZES(LoadedBlockIsCorrectWhenLoadedDirectlyAfterFlushing);
TYPED_TEST_P_FOR_ALL_SIZES(AfterCreate_FlushingDoesntChangeBlock); TYPED_TEST_P_FOR_ALL_SIZES(AfterCreate_FlushingDoesntChangeBlock);

View File

@ -31,45 +31,45 @@ TYPED_TEST_CASE_P(BlockStoreWithRandomKeysTest);
TYPED_TEST_P(BlockStoreWithRandomKeysTest, CreateTwoBlocksWithSameKeyAndSameSize) { TYPED_TEST_P(BlockStoreWithRandomKeysTest, CreateTwoBlocksWithSameKeyAndSameSize) {
auto blockStore = this->fixture.createBlockStore(); auto blockStore = this->fixture.createBlockStore();
auto block = blockStore->create(this->key, 1024); auto block = blockStore->tryCreate(this->key, blockstore::Data(1024));
block->flush(); block->flush();
auto block2 = blockStore->create(this->key, 1024); auto block2 = blockStore->tryCreate(this->key, blockstore::Data(1024));
EXPECT_TRUE((bool)block); EXPECT_TRUE((bool)block);
EXPECT_FALSE((bool)block2); EXPECT_FALSE((bool)block2);
} }
TYPED_TEST_P(BlockStoreWithRandomKeysTest, CreateTwoBlocksWithSameKeyAndDifferentSize) { TYPED_TEST_P(BlockStoreWithRandomKeysTest, CreateTwoBlocksWithSameKeyAndDifferentSize) {
auto blockStore = this->fixture.createBlockStore(); auto blockStore = this->fixture.createBlockStore();
auto block = blockStore->create(this->key, 1024); auto block = blockStore->tryCreate(this->key, blockstore::Data(1024));
block->flush(); block->flush();
auto block2 = blockStore->create(this->key, 4096); auto block2 = blockStore->tryCreate(this->key, blockstore::Data(4096));
EXPECT_TRUE((bool)block); EXPECT_TRUE((bool)block);
EXPECT_FALSE((bool)block2); EXPECT_FALSE((bool)block2);
} }
TYPED_TEST_P(BlockStoreWithRandomKeysTest, CreateTwoBlocksWithSameKeyAndFirstNullSize) { TYPED_TEST_P(BlockStoreWithRandomKeysTest, CreateTwoBlocksWithSameKeyAndFirstNullSize) {
auto blockStore = this->fixture.createBlockStore(); auto blockStore = this->fixture.createBlockStore();
auto block = blockStore->create(this->key, 0); auto block = blockStore->tryCreate(this->key, blockstore::Data(0));
block->flush(); block->flush();
auto block2 = blockStore->create(this->key, 1024); auto block2 = blockStore->tryCreate(this->key, blockstore::Data(1024));
EXPECT_TRUE((bool)block); EXPECT_TRUE((bool)block);
EXPECT_FALSE((bool)block2); EXPECT_FALSE((bool)block2);
} }
TYPED_TEST_P(BlockStoreWithRandomKeysTest, CreateTwoBlocksWithSameKeyAndSecondNullSize) { TYPED_TEST_P(BlockStoreWithRandomKeysTest, CreateTwoBlocksWithSameKeyAndSecondNullSize) {
auto blockStore = this->fixture.createBlockStore(); auto blockStore = this->fixture.createBlockStore();
auto block = blockStore->create(this->key, 1024); auto block = blockStore->tryCreate(this->key, blockstore::Data(1024));
block->flush(); block->flush();
auto block2 = blockStore->create(this->key, 0); auto block2 = blockStore->tryCreate(this->key, blockstore::Data(0));
EXPECT_TRUE((bool)block); EXPECT_TRUE((bool)block);
EXPECT_FALSE((bool)block2); EXPECT_FALSE((bool)block2);
} }
TYPED_TEST_P(BlockStoreWithRandomKeysTest, CreateTwoBlocksWithSameKeyAndBothNullSize) { TYPED_TEST_P(BlockStoreWithRandomKeysTest, CreateTwoBlocksWithSameKeyAndBothNullSize) {
auto blockStore = this->fixture.createBlockStore(); auto blockStore = this->fixture.createBlockStore();
auto block = blockStore->create(this->key, 0); auto block = blockStore->tryCreate(this->key, blockstore::Data(0));
block->flush(); block->flush();
auto block2 = blockStore->create(this->key, 0); auto block2 = blockStore->tryCreate(this->key, blockstore::Data(0));
EXPECT_TRUE((bool)block); EXPECT_TRUE((bool)block);
EXPECT_FALSE((bool)block2); EXPECT_FALSE((bool)block2);
} }

View File

@ -33,7 +33,7 @@ public:
}; };
TEST_F(BlockStoreUtilsTest, FillWithZeroes) { TEST_F(BlockStoreUtilsTest, FillWithZeroes) {
auto block = blockStore->create(SIZE); auto block = blockStore->create(Data(SIZE));
block->write(dataFixture.data(), 0, SIZE); block->write(dataFixture.data(), 0, SIZE);
EXPECT_NE(0, std::memcmp(ZEROES.data(), block->data(), SIZE)); EXPECT_NE(0, std::memcmp(ZEROES.data(), block->data(), SIZE));
fillWithZeroes(block.get()); fillWithZeroes(block.get());
@ -43,14 +43,14 @@ TEST_F(BlockStoreUtilsTest, FillWithZeroes) {
class BlockStoreUtilsTest_CopyToNewBlock: public BlockStoreUtilsTest {}; class BlockStoreUtilsTest_CopyToNewBlock: public BlockStoreUtilsTest {};
TEST_F(BlockStoreUtilsTest_CopyToNewBlock, CopyEmptyBlock) { TEST_F(BlockStoreUtilsTest_CopyToNewBlock, CopyEmptyBlock) {
auto block = blockStore->create(0); auto block = blockStore->create(Data(0));
auto block2 = copyToNewBlock(blockStore.get(), *block); auto block2 = copyToNewBlock(blockStore.get(), *block);
EXPECT_EQ(0u, block2->size()); EXPECT_EQ(0u, block2->size());
} }
TEST_F(BlockStoreUtilsTest_CopyToNewBlock, CopyZeroBlock) { TEST_F(BlockStoreUtilsTest_CopyToNewBlock, CopyZeroBlock) {
auto block = blockStore->create(SIZE); auto block = blockStore->create(ZEROES);
auto block2 = copyToNewBlock(blockStore.get(), *block); auto block2 = copyToNewBlock(blockStore.get(), *block);
EXPECT_EQ(SIZE, block2->size()); EXPECT_EQ(SIZE, block2->size());
@ -58,7 +58,7 @@ TEST_F(BlockStoreUtilsTest_CopyToNewBlock, CopyZeroBlock) {
} }
TEST_F(BlockStoreUtilsTest_CopyToNewBlock, CopyDataBlock) { TEST_F(BlockStoreUtilsTest_CopyToNewBlock, CopyDataBlock) {
auto block = blockStore->create(SIZE); auto block = blockStore->create(Data(SIZE));
block->write(dataFixture.data(), 0, SIZE); block->write(dataFixture.data(), 0, SIZE);
auto block2 = copyToNewBlock(blockStore.get(), *block); auto block2 = copyToNewBlock(blockStore.get(), *block);
@ -67,7 +67,7 @@ TEST_F(BlockStoreUtilsTest_CopyToNewBlock, CopyDataBlock) {
} }
TEST_F(BlockStoreUtilsTest_CopyToNewBlock, OriginalBlockUnchanged) { TEST_F(BlockStoreUtilsTest_CopyToNewBlock, OriginalBlockUnchanged) {
auto block = blockStore->create(SIZE); auto block = blockStore->create(Data(SIZE));
block->write(dataFixture.data(), 0, SIZE); block->write(dataFixture.data(), 0, SIZE);
auto block2 = copyToNewBlock(blockStore.get(), *block); auto block2 = copyToNewBlock(blockStore.get(), *block);
@ -78,14 +78,14 @@ TEST_F(BlockStoreUtilsTest_CopyToNewBlock, OriginalBlockUnchanged) {
class BlockStoreUtilsTest_CopyToExistingBlock: public BlockStoreUtilsTest {}; class BlockStoreUtilsTest_CopyToExistingBlock: public BlockStoreUtilsTest {};
TEST_F(BlockStoreUtilsTest_CopyToExistingBlock, CopyEmptyBlock) { TEST_F(BlockStoreUtilsTest_CopyToExistingBlock, CopyEmptyBlock) {
auto block = blockStore->create(0); auto block = blockStore->create(Data(0));
auto block2 = blockStore->create(0); auto block2 = blockStore->create(Data(0));
copyTo(block2.get(), *block); copyTo(block2.get(), *block);
} }
TEST_F(BlockStoreUtilsTest_CopyToExistingBlock, CopyZeroBlock) { TEST_F(BlockStoreUtilsTest_CopyToExistingBlock, CopyZeroBlock) {
auto block = blockStore->create(SIZE); auto block = blockStore->create(ZEROES);
auto block2 = blockStore->create(SIZE); auto block2 = blockStore->create(Data(SIZE));
block2->write(dataFixture.data(), 0, SIZE); block2->write(dataFixture.data(), 0, SIZE);
copyTo(block2.get(), *block); copyTo(block2.get(), *block);
@ -93,18 +93,18 @@ TEST_F(BlockStoreUtilsTest_CopyToExistingBlock, CopyZeroBlock) {
} }
TEST_F(BlockStoreUtilsTest_CopyToExistingBlock, CopyDataBlock) { TEST_F(BlockStoreUtilsTest_CopyToExistingBlock, CopyDataBlock) {
auto block = blockStore->create(SIZE); auto block = blockStore->create(Data(SIZE));
block->write(dataFixture.data(), 0, SIZE); block->write(dataFixture.data(), 0, SIZE);
auto block2 = blockStore->create(SIZE); auto block2 = blockStore->create(Data(SIZE));
copyTo(block2.get(), *block); copyTo(block2.get(), *block);
EXPECT_EQ(0, std::memcmp(dataFixture.data(), block2->data(), SIZE)); EXPECT_EQ(0, std::memcmp(dataFixture.data(), block2->data(), SIZE));
} }
TEST_F(BlockStoreUtilsTest_CopyToExistingBlock, OriginalBlockUnchanged) { TEST_F(BlockStoreUtilsTest_CopyToExistingBlock, OriginalBlockUnchanged) {
auto block = blockStore->create(SIZE); auto block = blockStore->create(Data(SIZE));
block->write(dataFixture.data(), 0, SIZE); block->write(dataFixture.data(), 0, SIZE);
auto block2 = blockStore->create(SIZE); auto block2 = blockStore->create(Data(SIZE));
copyTo(block2.get(), *block); copyTo(block2.get(), *block);
EXPECT_EQ(0, std::memcmp(dataFixture.data(), block->data(), SIZE)); EXPECT_EQ(0, std::memcmp(dataFixture.data(), block->data(), SIZE));

View File

@ -10,9 +10,9 @@ namespace blockstore {
namespace utils { namespace utils {
unique_ptr<Block> copyToNewBlock(BlockStore *blockStore, const Block &block) { unique_ptr<Block> copyToNewBlock(BlockStore *blockStore, const Block &block) {
auto newBlock = blockStore->create(block.size()); Data data(block.size());
copyTo(newBlock.get(), block); std::memcpy(data.data(), block.data(), block.size());
return newBlock; return blockStore->create(data);
} }
void copyTo(Block *target, const Block &source) { void copyTo(Block *target, const Block &source) {

View File

@ -50,8 +50,9 @@ size_t Data::size() const {
return _size; return _size;
} }
void Data::FillWithZeroes() { Data &Data::FillWithZeroes() {
std::memset(_data, 0, _size); std::memset(_data, 0, _size);
return *this;
} }
void Data::StoreToFile(const bf::path &filepath) const { void Data::StoreToFile(const bf::path &filepath) const {
@ -92,4 +93,8 @@ void Data::_readFromStream(istream &stream) {
stream.read((char*)_data, _size); stream.read((char*)_data, _size);
} }
bool operator==(const Data &lhs, const Data &rhs) {
return lhs.size() == rhs.size() && 0 == memcmp(lhs.data(), rhs.data(), lhs.size());
}
} }

View File

@ -11,7 +11,7 @@ namespace blockstore {
class Data { class Data {
public: public:
Data(size_t size); explicit Data(size_t size);
Data(Data &&rhs); // move constructor Data(Data &&rhs); // move constructor
virtual ~Data(); virtual ~Data();
@ -22,7 +22,7 @@ public:
size_t size() const; size_t size() const;
void FillWithZeroes(); Data &FillWithZeroes();
void StoreToFile(const boost::filesystem::path &filepath) const; void StoreToFile(const boost::filesystem::path &filepath) const;
static Data LoadFromFile(const boost::filesystem::path &filepath); static Data LoadFromFile(const boost::filesystem::path &filepath);
@ -38,6 +38,9 @@ private:
DISALLOW_COPY_AND_ASSIGN(Data); DISALLOW_COPY_AND_ASSIGN(Data);
}; };
//TODO Test operator==
bool operator==(const Data &lhs, const Data &rhs);
} }
#endif #endif