- 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:
parent
18e7d68f15
commit
417a701636
@ -1,4 +1,5 @@
|
||||
#include "CachedBlock.h"
|
||||
#include "NewBlock.h"
|
||||
#include "CachingBlockStore.h"
|
||||
#include "../../interface/Block.h"
|
||||
|
||||
@ -13,15 +14,16 @@ namespace blockstore {
|
||||
namespace caching {
|
||||
|
||||
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) {
|
||||
//TODO Also cache this and only write back in the destructor?
|
||||
// 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.
|
||||
return make_unique<CachedBlock>(_baseBlockStore->create(size), this);
|
||||
Key CachingBlockStore::createKey() {
|
||||
return _baseBlockStore->createKey();
|
||||
}
|
||||
|
||||
unique_ptr<Block> CachingBlockStore::tryCreate(const Key &key, Data data) {
|
||||
++_numNewBlocks;
|
||||
return make_unique<CachedBlock>(make_unique<NewBlock>(key, std::move(data), this), this);
|
||||
}
|
||||
|
||||
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) {
|
||||
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 {
|
||||
return _baseBlockStore->numBlocks();
|
||||
//TODO Add number of NewBlock instances
|
||||
return _baseBlockStore->numBlocks() + _numNewBlocks;
|
||||
}
|
||||
|
||||
void CachingBlockStore::release(unique_ptr<Block> 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));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -14,16 +14,21 @@ class CachingBlockStore: public BlockStore {
|
||||
public:
|
||||
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;
|
||||
void remove(std::unique_ptr<Block> block) override;
|
||||
uint64_t numBlocks() const override;
|
||||
|
||||
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:
|
||||
std::unique_ptr<BlockStore> _baseBlockStore;
|
||||
Cache _cache;
|
||||
uint32_t _numNewBlocks;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CachingBlockStore);
|
||||
};
|
||||
|
66
implementations/caching/NewBlock.cpp
Normal file
66
implementations/caching/NewBlock.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
50
implementations/caching/NewBlock.h
Normal file
50
implementations/caching/NewBlock.h
Normal 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
|
@ -15,11 +15,15 @@ namespace encrypted {
|
||||
|
||||
constexpr unsigned int EncryptedBlock::IV_SIZE;
|
||||
|
||||
std::unique_ptr<EncryptedBlock> EncryptedBlock::CreateNew(std::unique_ptr<Block> baseBlock, const EncryptionKey &encKey) {
|
||||
auto block = make_unique<EncryptedBlock>(std::move(baseBlock), encKey);
|
||||
//We have to explicitly fill the block with zeroes, because otherwise the encrypted version is filled with zeroes and not the plaintext version
|
||||
utils::fillWithZeroes(block.get());
|
||||
return block;
|
||||
std::unique_ptr<EncryptedBlock> EncryptedBlock::TryCreateNew(BlockStore *baseBlockStore, const Key &key, Data data, const EncryptionKey &encKey) {
|
||||
Data encrypted = _encrypt(data, encKey);
|
||||
auto baseBlock = baseBlockStore->tryCreate(key, std::move(encrypted));
|
||||
if (baseBlock.get() == nullptr) {
|
||||
//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)
|
||||
@ -32,7 +36,7 @@ EncryptedBlock::EncryptedBlock(std::unique_ptr<Block> baseBlock, const Encryptio
|
||||
}
|
||||
|
||||
EncryptedBlock::~EncryptedBlock() {
|
||||
flush();
|
||||
_encryptToBaseBlock();
|
||||
}
|
||||
|
||||
const void *EncryptedBlock::data() const {
|
||||
@ -63,16 +67,21 @@ void EncryptedBlock::_decryptFromBaseBlock() {
|
||||
|
||||
void EncryptedBlock::_encryptToBaseBlock() {
|
||||
if (_dataChanged) {
|
||||
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 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());
|
||||
Data encrypted = _encrypt(_plaintextData, _encKey);
|
||||
_baseBlock->write(encrypted.data(), 0, encrypted.size());
|
||||
_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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "../../interface/Block.h"
|
||||
#include "EncryptionKey.h"
|
||||
#include "../../utils/Data.h"
|
||||
#include "../../interface/BlockStore.h"
|
||||
|
||||
#include "messmer/cpp-utils/macros.h"
|
||||
#include <memory>
|
||||
@ -19,7 +20,7 @@ public:
|
||||
EncryptedBlock(std::unique_ptr<Block> baseBlock, const EncryptionKey &encKey);
|
||||
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;
|
||||
void write(const void *source, uint64_t offset, uint64_t size) override;
|
||||
@ -49,6 +50,8 @@ private:
|
||||
void _encryptToBaseBlock();
|
||||
void _decryptFromBaseBlock();
|
||||
|
||||
static Data _encrypt(const Data &plaintext, const EncryptionKey &encKey);
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(EncryptedBlock);
|
||||
};
|
||||
|
||||
|
@ -13,8 +13,12 @@ EncryptedBlockStore::EncryptedBlockStore(unique_ptr<BlockStore> baseBlockStore,
|
||||
: _baseBlockStore(std::move(baseBlockStore)), _encKey(encKey) {
|
||||
}
|
||||
|
||||
unique_ptr<Block> EncryptedBlockStore::create(size_t size) {
|
||||
return EncryptedBlock::CreateNew(_baseBlockStore->create(EncryptedBlock::BASE_BLOCK_SIZE(size)), _encKey);
|
||||
Key EncryptedBlockStore::createKey() {
|
||||
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) {
|
||||
@ -33,5 +37,10 @@ uint64_t EncryptedBlockStore::numBlocks() const {
|
||||
return _baseBlockStore->numBlocks();
|
||||
}
|
||||
|
||||
unique_ptr<Block> EncryptedBlockStore::tryCreateInBaseStore(const Key &key, Data encryptedData) {
|
||||
return _baseBlockStore->tryCreate(key, std::move(encryptedData));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,11 +13,14 @@ class EncryptedBlockStore: public BlockStore {
|
||||
public:
|
||||
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;
|
||||
void remove(std::unique_ptr<Block> block) override;
|
||||
uint64_t numBlocks() const override;
|
||||
|
||||
std::unique_ptr<Block> tryCreateInBaseStore(const Key &key, Data encryptedData);
|
||||
|
||||
private:
|
||||
std::unique_ptr<BlockStore> _baseBlockStore;
|
||||
EncryptionKey _encKey;
|
||||
|
@ -13,9 +13,8 @@ using std::ios;
|
||||
namespace blockstore {
|
||||
namespace inmemory {
|
||||
|
||||
InMemoryBlock::InMemoryBlock(const Key &key, size_t size)
|
||||
: Block(key), _data(make_shared<Data>(size)) {
|
||||
_data->FillWithZeroes();
|
||||
InMemoryBlock::InMemoryBlock(const Key &key, Data data)
|
||||
: Block(key), _data(make_shared<Data>(std::move(data))) {
|
||||
}
|
||||
|
||||
InMemoryBlock::InMemoryBlock(const InMemoryBlock &rhs)
|
||||
|
@ -11,7 +11,7 @@ class InMemoryBlockStore;
|
||||
|
||||
class InMemoryBlock: public Block {
|
||||
public:
|
||||
InMemoryBlock(const Key &key, size_t size);
|
||||
InMemoryBlock(const Key &key, Data size);
|
||||
InMemoryBlock(const InMemoryBlock &rhs);
|
||||
virtual ~InMemoryBlock();
|
||||
|
||||
|
@ -16,8 +16,8 @@ namespace inmemory {
|
||||
InMemoryBlockStore::InMemoryBlockStore()
|
||||
: _blocks() {}
|
||||
|
||||
unique_ptr<Block> InMemoryBlockStore::create(const Key &key, size_t size) {
|
||||
auto insert_result = _blocks.emplace(piecewise_construct, make_tuple(key.ToString()), make_tuple(key, 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, std::move(data)));
|
||||
|
||||
if (!insert_result.second) {
|
||||
return nullptr;
|
||||
|
@ -16,7 +16,7 @@ class InMemoryBlockStore: public BlockStoreWithRandomKeys {
|
||||
public:
|
||||
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;
|
||||
void remove(std::unique_ptr<Block> block) override;
|
||||
uint64_t numBlocks() const override;
|
||||
|
@ -19,11 +19,7 @@ namespace bf = boost::filesystem;
|
||||
namespace blockstore {
|
||||
namespace ondisk {
|
||||
|
||||
OnDiskBlock::OnDiskBlock(const Key &key, const bf::path &filepath, size_t size)
|
||||
: Block(key), _filepath(filepath), _data(size), _dataChanged(false) {
|
||||
}
|
||||
|
||||
OnDiskBlock::OnDiskBlock(const Key &key, const bf::path &filepath, Data &&data)
|
||||
OnDiskBlock::OnDiskBlock(const Key &key, const bf::path &filepath, Data data)
|
||||
: 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();
|
||||
if (bf::exists(filepath)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto block = unique_ptr<OnDiskBlock>(new OnDiskBlock(key, filepath, size));
|
||||
block->_fillDataWithZeroes();
|
||||
auto block = unique_ptr<OnDiskBlock>(new OnDiskBlock(key, filepath, std::move(data)));
|
||||
block->_storeToDisk();
|
||||
return block;
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ public:
|
||||
virtual ~OnDiskBlock();
|
||||
|
||||
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);
|
||||
|
||||
const void *data() const override;
|
||||
@ -33,8 +33,7 @@ private:
|
||||
Data _data;
|
||||
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 _storeToDisk() const;
|
||||
|
@ -13,8 +13,8 @@ namespace ondisk {
|
||||
OnDiskBlockStore::OnDiskBlockStore(const boost::filesystem::path &rootdir)
|
||||
: _rootdir(rootdir) {}
|
||||
|
||||
unique_ptr<Block> OnDiskBlockStore::create(const Key &key, size_t size) {
|
||||
auto block = OnDiskBlock::CreateOnDisk(_rootdir, key, size);
|
||||
unique_ptr<Block> OnDiskBlockStore::tryCreate(const Key &key, Data data) {
|
||||
auto block = OnDiskBlock::CreateOnDisk(_rootdir, key, std::move(data));
|
||||
|
||||
if (!block) {
|
||||
return nullptr;
|
||||
|
@ -14,7 +14,7 @@ class OnDiskBlockStore: public BlockStoreWithRandomKeys {
|
||||
public:
|
||||
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;
|
||||
void remove(std::unique_ptr<Block> block) override;
|
||||
uint64_t numBlocks() const override;
|
||||
|
@ -20,9 +20,16 @@ ParallelAccessBlockStore::ParallelAccessBlockStore(unique_ptr<BlockStore> baseBl
|
||||
: _baseBlockStore(std::move(baseBlockStore)), _parallelAccessStore(make_unique<ParallelAccessBlockStoreAdapter>(_baseBlockStore.get())) {
|
||||
}
|
||||
|
||||
unique_ptr<Block> ParallelAccessBlockStore::create(size_t size) {
|
||||
auto block = _baseBlockStore->create(size);
|
||||
Key key = block->key();
|
||||
Key ParallelAccessBlockStore::createKey() {
|
||||
return _baseBlockStore->createKey();
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,8 @@ class ParallelAccessBlockStore: public BlockStore {
|
||||
public:
|
||||
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;
|
||||
void remove(std::unique_ptr<Block> block) override;
|
||||
uint64_t numBlocks() const override;
|
||||
|
@ -14,13 +14,15 @@ namespace testfake {
|
||||
FakeBlockStore::FakeBlockStore()
|
||||
: _blocks(), _used_dataregions_for_blocks() {}
|
||||
|
||||
unique_ptr<Block> FakeBlockStore::create(const Key &key, size_t size) {
|
||||
if (_blocks.find(key.ToString()) != _blocks.end()) {
|
||||
unique_ptr<Block> FakeBlockStore::tryCreate(const Key &key, Data data) {
|
||||
auto insert_result = _blocks.emplace(key.ToString(), std::move(data));
|
||||
|
||||
if (!insert_result.second) {
|
||||
return nullptr;
|
||||
}
|
||||
Data data(size);
|
||||
data.FillWithZeroes();
|
||||
return makeFakeBlockFromData(key, data, true);
|
||||
|
||||
//Return a copy of the stored data
|
||||
return load(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) {
|
||||
auto found = _blocks.find(key.ToString());
|
||||
if (found == _blocks.end()) {
|
||||
auto insertResult = _blocks.emplace(key.ToString(), data.size());
|
||||
auto insertResult = _blocks.emplace(key.ToString(), data.copy());
|
||||
assert(true == insertResult.second);
|
||||
found = insertResult.first;
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ class FakeBlockStore: public BlockStoreWithRandomKeys {
|
||||
public:
|
||||
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;
|
||||
void remove(std::unique_ptr<Block> block) override;
|
||||
uint64_t numBlocks() const override;
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include "Block.h"
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include "../utils/Data.h"
|
||||
|
||||
namespace blockstore {
|
||||
|
||||
@ -13,12 +13,23 @@ class BlockStore {
|
||||
public:
|
||||
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)
|
||||
// Return nullptr if block with this key doesn't exists
|
||||
virtual std::unique_ptr<Block> load(const Key &key) = 0;
|
||||
virtual void remove(std::unique_ptr<Block> block) = 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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,20 +1 @@
|
||||
#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);
|
||||
}
|
||||
|
||||
|
@ -12,14 +12,9 @@ namespace blockstore {
|
||||
// work with the BlockStore interface instead.
|
||||
class BlockStoreWithRandomKeys: public BlockStore {
|
||||
public:
|
||||
//TODO Use boost::optional (if key already exists)
|
||||
// Return nullptr if key already exists
|
||||
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);
|
||||
Key createKey() final {
|
||||
return Key::CreateRandom();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ public:
|
||||
TEST_F(OnDiskBlockCreateTest, CreatingBlockCreatesFile) {
|
||||
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();
|
||||
|
||||
EXPECT_TRUE(bf::exists(file.path()));
|
||||
@ -43,9 +43,9 @@ TEST_F(OnDiskBlockCreateTest, CreatingBlockCreatesFile) {
|
||||
}
|
||||
|
||||
TEST_F(OnDiskBlockCreateTest, CreatingExistingBlockReturnsNull) {
|
||||
auto block1 = OnDiskBlock::CreateOnDisk(dir.path(), key, 0);
|
||||
auto block1 = OnDiskBlock::CreateOnDisk(dir.path(), key, Data(0));
|
||||
block1->flush();
|
||||
auto block2 = OnDiskBlock::CreateOnDisk(dir.path(), key, 0);
|
||||
auto block2 = OnDiskBlock::CreateOnDisk(dir.path(), key, Data(0));
|
||||
EXPECT_TRUE((bool)block1);
|
||||
EXPECT_FALSE((bool)block2);
|
||||
}
|
||||
@ -56,7 +56,7 @@ public:
|
||||
Data ZEROES;
|
||||
|
||||
OnDiskBlockCreateSizeTest():
|
||||
block(OnDiskBlock::CreateOnDisk(dir.path(), key, GetParam())),
|
||||
block(OnDiskBlock::CreateOnDisk(dir.path(), key, std::move(Data(GetParam()).FillWithZeroes()))),
|
||||
ZEROES(block->size())
|
||||
{
|
||||
block->flush();
|
||||
|
@ -37,13 +37,17 @@ public:
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -1,12 +1,14 @@
|
||||
#include "../../../interface/helpers/BlockStoreWithRandomKeys.h"
|
||||
#include "google/gtest/gtest.h"
|
||||
#include "google/gmock/gmock.h"
|
||||
|
||||
#include "../../testutils/DataBlockFixture.h"
|
||||
|
||||
using ::testing::Test;
|
||||
using ::testing::_;
|
||||
using ::testing::Return;
|
||||
using ::testing::Invoke;
|
||||
using ::testing::Eq;
|
||||
using ::testing::ByRef;
|
||||
|
||||
using std::string;
|
||||
using std::unique_ptr;
|
||||
@ -16,10 +18,10 @@ using namespace blockstore;
|
||||
|
||||
class BlockStoreWithRandomKeysMock: public BlockStoreWithRandomKeys {
|
||||
public:
|
||||
unique_ptr<Block> create(const Key &key, size_t size) {
|
||||
return unique_ptr<Block>(do_create(key, size));
|
||||
unique_ptr<Block> tryCreate(const Key &key, Data data) {
|
||||
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) {
|
||||
return unique_ptr<Block>(do_load(key));
|
||||
}
|
||||
@ -43,78 +45,93 @@ public:
|
||||
BlockStoreWithRandomKeysMock blockStoreMock;
|
||||
BlockStore &blockStore = blockStoreMock;
|
||||
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) {
|
||||
EXPECT_CALL(blockStoreMock, do_create(_, 0)).WillOnce(Return(new BlockMock));
|
||||
blockStore.create(0);
|
||||
TEST_F(BlockStoreWithRandomKeysTest, DataIsPassedThrough0) {
|
||||
Data data = createDataWithSize(0);
|
||||
EXPECT_CALL(blockStoreMock, do_create(_, Eq(ByRef(data)))).WillOnce(Return(new BlockMock));
|
||||
blockStore.create(data);
|
||||
}
|
||||
|
||||
TEST_F(BlockStoreWithRandomKeysTest, SizeIsPassedThrough1) {
|
||||
EXPECT_CALL(blockStoreMock, do_create(_, 1)).WillOnce(Return(new BlockMock));
|
||||
blockStore.create(1);
|
||||
TEST_F(BlockStoreWithRandomKeysTest, DataIsPassedThrough1) {
|
||||
Data data = createDataWithSize(1);
|
||||
EXPECT_CALL(blockStoreMock, do_create(_, Eq(ByRef(data)))).WillOnce(Return(new BlockMock));
|
||||
blockStore.create(data);
|
||||
}
|
||||
|
||||
TEST_F(BlockStoreWithRandomKeysTest, SizeIsPassedThrough1024) {
|
||||
EXPECT_CALL(blockStoreMock, do_create(_, 1024)).WillOnce(Return(new BlockMock));
|
||||
blockStore.create(1024);
|
||||
TEST_F(BlockStoreWithRandomKeysTest, DataIsPassedThrough1024) {
|
||||
Data data = createDataWithSize(1024);
|
||||
EXPECT_CALL(blockStoreMock, do_create(_, Eq(ByRef(data)))).WillOnce(Return(new BlockMock));
|
||||
blockStore.create(data);
|
||||
}
|
||||
|
||||
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());
|
||||
return new BlockMock;
|
||||
}));
|
||||
|
||||
blockStore.create(1024);
|
||||
blockStore.create(createDataWithSize(1024));
|
||||
}
|
||||
|
||||
TEST_F(BlockStoreWithRandomKeysTest, TwoBlocksGetDifferentKeys) {
|
||||
Key first_key = key;
|
||||
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;
|
||||
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);
|
||||
return new BlockMock;
|
||||
}));
|
||||
|
||||
blockStore.create(1024);
|
||||
blockStore.create(1024);
|
||||
Data data = createDataWithSize(1024);
|
||||
blockStore.create(data);
|
||||
blockStore.create(data);
|
||||
}
|
||||
|
||||
TEST_F(BlockStoreWithRandomKeysTest, WillTryADifferentKeyIfKeyAlreadyExists) {
|
||||
Key first_key = key;
|
||||
EXPECT_CALL(blockStoreMock, do_create(_, _))
|
||||
.WillOnce(Invoke([&first_key](const Key &key, size_t) {
|
||||
Data data = createDataWithSize(1024);
|
||||
EXPECT_CALL(blockStoreMock, do_create(_, Eq(ByRef(data))))
|
||||
.WillOnce(Invoke([&first_key](const Key &key, const Data &) {
|
||||
first_key = key;
|
||||
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);
|
||||
return new BlockMock;
|
||||
}));
|
||||
|
||||
blockStore.create(1024);
|
||||
blockStore.create(data);
|
||||
}
|
||||
|
||||
TEST_F(BlockStoreWithRandomKeysTest, WillTryADifferentKeyIfKeyAlreadyExistsTwoTimes) {
|
||||
Key first_key = key;
|
||||
EXPECT_CALL(blockStoreMock, do_create(_, _))
|
||||
.WillOnce(Invoke([&first_key](const Key &key, size_t) {
|
||||
Data data = createDataWithSize(1024);
|
||||
EXPECT_CALL(blockStoreMock, do_create(_, Eq(ByRef(data))))
|
||||
.WillOnce(Invoke([&first_key](const Key &key, const Data &) {
|
||||
first_key = key;
|
||||
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;
|
||||
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);
|
||||
return new BlockMock;
|
||||
}));
|
||||
|
||||
blockStore.create(1024);
|
||||
blockStore.create(data);
|
||||
}
|
||||
|
@ -28,14 +28,14 @@ TYPED_TEST_CASE_P(BlockStoreTest);
|
||||
|
||||
TYPED_TEST_P(BlockStoreTest, TwoCreatedBlocksHaveDifferentKeys) {
|
||||
auto blockStore = this->fixture.createBlockStore();
|
||||
auto block1 = blockStore->create(1024);
|
||||
auto block2 = blockStore->create(1024);
|
||||
auto block1 = blockStore->create(blockstore::Data(1024));
|
||||
auto block2 = blockStore->create(blockstore::Data(1024));
|
||||
EXPECT_NE(block1->key(), block2->key());
|
||||
}
|
||||
|
||||
TYPED_TEST_P(BlockStoreTest, BlockIsNotLoadableAfterDeleting) {
|
||||
auto blockStore = this->fixture.createBlockStore();
|
||||
auto blockkey = blockStore->create(1024)->key();
|
||||
auto blockkey = blockStore->create(blockstore::Data(1024))->key();
|
||||
auto block = blockStore->load(blockkey);
|
||||
EXPECT_NE(nullptr, block.get());
|
||||
blockStore->remove(std::move(block));
|
||||
@ -49,55 +49,55 @@ TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectOnEmptyBlockstore) {
|
||||
|
||||
TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingOneBlock) {
|
||||
auto blockStore = this->fixture.createBlockStore();
|
||||
auto block = blockStore->create(1);
|
||||
auto block = blockStore->create(blockstore::Data(1));
|
||||
EXPECT_EQ(1, blockStore->numBlocks());
|
||||
}
|
||||
|
||||
TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingOneBlock_AfterClosingBlock) {
|
||||
auto blockStore = this->fixture.createBlockStore();
|
||||
blockStore->create(1);
|
||||
blockStore->create(blockstore::Data(1));
|
||||
EXPECT_EQ(1, blockStore->numBlocks());
|
||||
}
|
||||
|
||||
TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterRemovingTheLastBlock) {
|
||||
auto blockStore = this->fixture.createBlockStore();
|
||||
auto block = blockStore->create(1);
|
||||
auto block = blockStore->create(blockstore::Data(1));
|
||||
blockStore->remove(std::move(block));
|
||||
EXPECT_EQ(0, blockStore->numBlocks());
|
||||
}
|
||||
|
||||
TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingTwoBlocks) {
|
||||
auto blockStore = this->fixture.createBlockStore();
|
||||
auto block1 = blockStore->create(1);
|
||||
auto block2 = blockStore->create(0);
|
||||
auto block1 = blockStore->create(blockstore::Data(1));
|
||||
auto block2 = blockStore->create(blockstore::Data(0));
|
||||
EXPECT_EQ(2, blockStore->numBlocks());
|
||||
}
|
||||
|
||||
TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingTwoBlocks_AfterClosingFirstBlock) {
|
||||
auto blockStore = this->fixture.createBlockStore();
|
||||
blockStore->create(1);
|
||||
auto block2 = blockStore->create(0);
|
||||
blockStore->create(blockstore::Data(1));
|
||||
auto block2 = blockStore->create(blockstore::Data(0));
|
||||
EXPECT_EQ(2, blockStore->numBlocks());
|
||||
}
|
||||
|
||||
TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingTwoBlocks_AfterClosingSecondBlock) {
|
||||
auto blockStore = this->fixture.createBlockStore();
|
||||
auto block1 = blockStore->create(1);
|
||||
blockStore->create(0);
|
||||
auto block1 = blockStore->create(blockstore::Data(1));
|
||||
blockStore->create(blockstore::Data(0));
|
||||
EXPECT_EQ(2, blockStore->numBlocks());
|
||||
}
|
||||
|
||||
TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingTwoBlocks_AfterClosingBothBlocks) {
|
||||
auto blockStore = this->fixture.createBlockStore();
|
||||
blockStore->create(1);
|
||||
blockStore->create(0);
|
||||
blockStore->create(blockstore::Data(1));
|
||||
blockStore->create(blockstore::Data(0));
|
||||
EXPECT_EQ(2, blockStore->numBlocks());
|
||||
}
|
||||
|
||||
TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterRemovingABlock) {
|
||||
auto blockStore = this->fixture.createBlockStore();
|
||||
auto block = blockStore->create(1);
|
||||
blockStore->create(1);
|
||||
auto block = blockStore->create(blockstore::Data(1));
|
||||
blockStore->create(blockstore::Data(1));
|
||||
blockStore->remove(std::move(block));
|
||||
EXPECT_EQ(1, blockStore->numBlocks());
|
||||
}
|
||||
@ -109,8 +109,8 @@ TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterRemovingABlock) {
|
||||
REGISTER_TYPED_TEST_CASE_P(BlockStoreTest,
|
||||
CreatedBlockHasCorrectSize,
|
||||
LoadingUnchangedBlockHasCorrectSize,
|
||||
CreatedBlockIsZeroedOut,
|
||||
LoadingUnchangedBlockIsZeroedOut,
|
||||
CreatedBlockData,
|
||||
LoadingUnchangedBlockData,
|
||||
LoadedBlockIsCorrect,
|
||||
// LoadedBlockIsCorrectWhenLoadedDirectlyAfterFlushing,
|
||||
AfterCreate_FlushingDoesntChangeBlock,
|
||||
|
@ -20,7 +20,7 @@ public:
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
EXPECT_DATA_READS_AS(foregroundData, *block, testData.offset, testData.count);
|
||||
@ -36,7 +36,7 @@ public:
|
||||
}
|
||||
|
||||
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(foregroundData.data(), 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) {
|
||||
auto newblock = blockStore->create(testData.blocksize);
|
||||
auto newblock = blockStore->create(blockstore::Data(testData.blocksize).FillWithZeroes());
|
||||
|
||||
newblock->write(to_write.data(), testData.offset, testData.count);
|
||||
return newblock->key();
|
||||
|
@ -1,29 +1,38 @@
|
||||
// This file is meant to be included by BlockStoreTest.h only
|
||||
|
||||
#include "../../utils/Data.h"
|
||||
|
||||
class BlockStoreSizeParameterizedTest {
|
||||
public:
|
||||
BlockStoreSizeParameterizedTest(std::unique_ptr<blockstore::BlockStore> blockStore_, size_t size_): blockStore(std::move(blockStore_)), size(size_) {}
|
||||
|
||||
void TestCreatedBlockHasCorrectSize() {
|
||||
auto block = blockStore->create(size);
|
||||
auto block = CreateBlock();
|
||||
EXPECT_EQ(size, block->size());
|
||||
}
|
||||
|
||||
void TestLoadingUnchangedBlockHasCorrectSize() {
|
||||
blockstore::Key key = blockStore->create(size)->key();
|
||||
blockstore::Key key = CreateBlock()->key();
|
||||
auto loaded_block = blockStore->load(key);
|
||||
EXPECT_EQ(size, loaded_block->size());
|
||||
}
|
||||
|
||||
void TestCreatedBlockIsZeroedOut() {
|
||||
auto block = blockStore->create(size);
|
||||
EXPECT_EQ(0, std::memcmp(ZEROES(size).data(), block->data(), size));
|
||||
void TestCreatedBlockData() {
|
||||
DataBlockFixture dataFixture(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() {
|
||||
blockstore::Key key = blockStore->create(size)->key();
|
||||
void TestLoadingUnchangedBlockData() {
|
||||
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);
|
||||
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() {
|
||||
@ -62,7 +71,7 @@ public:
|
||||
DataBlockFixture randomData(size);
|
||||
blockstore::Key key = key;
|
||||
{
|
||||
auto block = blockStore->create(size);
|
||||
auto block = blockStore->create(blockstore::Data(size));
|
||||
key = block->key();
|
||||
WriteDataToBlock(block.get(), randomData);
|
||||
}
|
||||
@ -74,7 +83,7 @@ public:
|
||||
DataBlockFixture randomData(size);
|
||||
blockstore::Key key = key;
|
||||
{
|
||||
key = blockStore->create(size)->key();
|
||||
key = CreateBlock()->key();
|
||||
auto block = blockStore->load(key);
|
||||
WriteDataToBlock(block.get(), randomData);
|
||||
}
|
||||
@ -104,26 +113,27 @@ private:
|
||||
return blockStore->load(key);
|
||||
}
|
||||
|
||||
blockstore::Key StoreDataToBlockAndGetKey(const DataBlockFixture &data) {
|
||||
auto block = blockStore->create(data.size());
|
||||
block->write(data.data(), 0, data.size());
|
||||
return block->key();
|
||||
blockstore::Key StoreDataToBlockAndGetKey(const DataBlockFixture &dataFixture) {
|
||||
blockstore::Data data(dataFixture.size());
|
||||
std::memcpy(data.data(), dataFixture.data(), dataFixture.size());
|
||||
return blockStore->create(data)->key();
|
||||
}
|
||||
|
||||
std::unique_ptr<blockstore::Block> StoreDataToBlockAndLoadItDirectlyAfterFlushing(const DataBlockFixture &data) {
|
||||
auto block = blockStore->create(data.size());
|
||||
block->write(data.data(), 0, data.size());
|
||||
std::unique_ptr<blockstore::Block> StoreDataToBlockAndLoadItDirectlyAfterFlushing(const DataBlockFixture &dataFixture) {
|
||||
blockstore::Data data(dataFixture.size());
|
||||
std::memcpy(data.data(), dataFixture.data(), dataFixture.size());
|
||||
auto block = blockStore->create(data);
|
||||
block->flush();
|
||||
return blockStore->load(block->key());
|
||||
}
|
||||
|
||||
std::unique_ptr<blockstore::Block> CreateBlockAndLoadIt() {
|
||||
blockstore::Key key = blockStore->create(size)->key();
|
||||
blockstore::Key key = CreateBlock()->key();
|
||||
return blockStore->load(key);
|
||||
}
|
||||
|
||||
std::unique_ptr<blockstore::Block> CreateBlock() {
|
||||
return blockStore->create(size);
|
||||
return blockStore->create(blockstore::Data(size));
|
||||
}
|
||||
|
||||
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(LoadingUnchangedBlockHasCorrectSize);
|
||||
TYPED_TEST_P_FOR_ALL_SIZES(CreatedBlockIsZeroedOut);
|
||||
TYPED_TEST_P_FOR_ALL_SIZES(LoadingUnchangedBlockIsZeroedOut);
|
||||
TYPED_TEST_P_FOR_ALL_SIZES(CreatedBlockData);
|
||||
TYPED_TEST_P_FOR_ALL_SIZES(LoadingUnchangedBlockData);
|
||||
TYPED_TEST_P_FOR_ALL_SIZES(LoadedBlockIsCorrect);
|
||||
//TYPED_TEST_P_FOR_ALL_SIZES(LoadedBlockIsCorrectWhenLoadedDirectlyAfterFlushing);
|
||||
TYPED_TEST_P_FOR_ALL_SIZES(AfterCreate_FlushingDoesntChangeBlock);
|
||||
|
@ -31,45 +31,45 @@ TYPED_TEST_CASE_P(BlockStoreWithRandomKeysTest);
|
||||
|
||||
TYPED_TEST_P(BlockStoreWithRandomKeysTest, CreateTwoBlocksWithSameKeyAndSameSize) {
|
||||
auto blockStore = this->fixture.createBlockStore();
|
||||
auto block = blockStore->create(this->key, 1024);
|
||||
auto block = blockStore->tryCreate(this->key, blockstore::Data(1024));
|
||||
block->flush();
|
||||
auto block2 = blockStore->create(this->key, 1024);
|
||||
auto block2 = blockStore->tryCreate(this->key, blockstore::Data(1024));
|
||||
EXPECT_TRUE((bool)block);
|
||||
EXPECT_FALSE((bool)block2);
|
||||
}
|
||||
|
||||
TYPED_TEST_P(BlockStoreWithRandomKeysTest, CreateTwoBlocksWithSameKeyAndDifferentSize) {
|
||||
auto blockStore = this->fixture.createBlockStore();
|
||||
auto block = blockStore->create(this->key, 1024);
|
||||
auto block = blockStore->tryCreate(this->key, blockstore::Data(1024));
|
||||
block->flush();
|
||||
auto block2 = blockStore->create(this->key, 4096);
|
||||
auto block2 = blockStore->tryCreate(this->key, blockstore::Data(4096));
|
||||
EXPECT_TRUE((bool)block);
|
||||
EXPECT_FALSE((bool)block2);
|
||||
}
|
||||
|
||||
TYPED_TEST_P(BlockStoreWithRandomKeysTest, CreateTwoBlocksWithSameKeyAndFirstNullSize) {
|
||||
auto blockStore = this->fixture.createBlockStore();
|
||||
auto block = blockStore->create(this->key, 0);
|
||||
auto block = blockStore->tryCreate(this->key, blockstore::Data(0));
|
||||
block->flush();
|
||||
auto block2 = blockStore->create(this->key, 1024);
|
||||
auto block2 = blockStore->tryCreate(this->key, blockstore::Data(1024));
|
||||
EXPECT_TRUE((bool)block);
|
||||
EXPECT_FALSE((bool)block2);
|
||||
}
|
||||
|
||||
TYPED_TEST_P(BlockStoreWithRandomKeysTest, CreateTwoBlocksWithSameKeyAndSecondNullSize) {
|
||||
auto blockStore = this->fixture.createBlockStore();
|
||||
auto block = blockStore->create(this->key, 1024);
|
||||
auto block = blockStore->tryCreate(this->key, blockstore::Data(1024));
|
||||
block->flush();
|
||||
auto block2 = blockStore->create(this->key, 0);
|
||||
auto block2 = blockStore->tryCreate(this->key, blockstore::Data(0));
|
||||
EXPECT_TRUE((bool)block);
|
||||
EXPECT_FALSE((bool)block2);
|
||||
}
|
||||
|
||||
TYPED_TEST_P(BlockStoreWithRandomKeysTest, CreateTwoBlocksWithSameKeyAndBothNullSize) {
|
||||
auto blockStore = this->fixture.createBlockStore();
|
||||
auto block = blockStore->create(this->key, 0);
|
||||
auto block = blockStore->tryCreate(this->key, blockstore::Data(0));
|
||||
block->flush();
|
||||
auto block2 = blockStore->create(this->key, 0);
|
||||
auto block2 = blockStore->tryCreate(this->key, blockstore::Data(0));
|
||||
EXPECT_TRUE((bool)block);
|
||||
EXPECT_FALSE((bool)block2);
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ public:
|
||||
};
|
||||
|
||||
TEST_F(BlockStoreUtilsTest, FillWithZeroes) {
|
||||
auto block = blockStore->create(SIZE);
|
||||
auto block = blockStore->create(Data(SIZE));
|
||||
block->write(dataFixture.data(), 0, SIZE);
|
||||
EXPECT_NE(0, std::memcmp(ZEROES.data(), block->data(), SIZE));
|
||||
fillWithZeroes(block.get());
|
||||
@ -43,14 +43,14 @@ TEST_F(BlockStoreUtilsTest, FillWithZeroes) {
|
||||
class BlockStoreUtilsTest_CopyToNewBlock: public BlockStoreUtilsTest {};
|
||||
|
||||
TEST_F(BlockStoreUtilsTest_CopyToNewBlock, CopyEmptyBlock) {
|
||||
auto block = blockStore->create(0);
|
||||
auto block = blockStore->create(Data(0));
|
||||
auto block2 = copyToNewBlock(blockStore.get(), *block);
|
||||
|
||||
EXPECT_EQ(0u, block2->size());
|
||||
}
|
||||
|
||||
TEST_F(BlockStoreUtilsTest_CopyToNewBlock, CopyZeroBlock) {
|
||||
auto block = blockStore->create(SIZE);
|
||||
auto block = blockStore->create(ZEROES);
|
||||
auto block2 = copyToNewBlock(blockStore.get(), *block);
|
||||
|
||||
EXPECT_EQ(SIZE, block2->size());
|
||||
@ -58,7 +58,7 @@ TEST_F(BlockStoreUtilsTest_CopyToNewBlock, CopyZeroBlock) {
|
||||
}
|
||||
|
||||
TEST_F(BlockStoreUtilsTest_CopyToNewBlock, CopyDataBlock) {
|
||||
auto block = blockStore->create(SIZE);
|
||||
auto block = blockStore->create(Data(SIZE));
|
||||
block->write(dataFixture.data(), 0, SIZE);
|
||||
auto block2 = copyToNewBlock(blockStore.get(), *block);
|
||||
|
||||
@ -67,7 +67,7 @@ TEST_F(BlockStoreUtilsTest_CopyToNewBlock, CopyDataBlock) {
|
||||
}
|
||||
|
||||
TEST_F(BlockStoreUtilsTest_CopyToNewBlock, OriginalBlockUnchanged) {
|
||||
auto block = blockStore->create(SIZE);
|
||||
auto block = blockStore->create(Data(SIZE));
|
||||
block->write(dataFixture.data(), 0, SIZE);
|
||||
auto block2 = copyToNewBlock(blockStore.get(), *block);
|
||||
|
||||
@ -78,14 +78,14 @@ TEST_F(BlockStoreUtilsTest_CopyToNewBlock, OriginalBlockUnchanged) {
|
||||
class BlockStoreUtilsTest_CopyToExistingBlock: public BlockStoreUtilsTest {};
|
||||
|
||||
TEST_F(BlockStoreUtilsTest_CopyToExistingBlock, CopyEmptyBlock) {
|
||||
auto block = blockStore->create(0);
|
||||
auto block2 = blockStore->create(0);
|
||||
auto block = blockStore->create(Data(0));
|
||||
auto block2 = blockStore->create(Data(0));
|
||||
copyTo(block2.get(), *block);
|
||||
}
|
||||
|
||||
TEST_F(BlockStoreUtilsTest_CopyToExistingBlock, CopyZeroBlock) {
|
||||
auto block = blockStore->create(SIZE);
|
||||
auto block2 = blockStore->create(SIZE);
|
||||
auto block = blockStore->create(ZEROES);
|
||||
auto block2 = blockStore->create(Data(SIZE));
|
||||
block2->write(dataFixture.data(), 0, SIZE);
|
||||
copyTo(block2.get(), *block);
|
||||
|
||||
@ -93,18 +93,18 @@ TEST_F(BlockStoreUtilsTest_CopyToExistingBlock, CopyZeroBlock) {
|
||||
}
|
||||
|
||||
TEST_F(BlockStoreUtilsTest_CopyToExistingBlock, CopyDataBlock) {
|
||||
auto block = blockStore->create(SIZE);
|
||||
auto block = blockStore->create(Data(SIZE));
|
||||
block->write(dataFixture.data(), 0, SIZE);
|
||||
auto block2 = blockStore->create(SIZE);
|
||||
auto block2 = blockStore->create(Data(SIZE));
|
||||
copyTo(block2.get(), *block);
|
||||
|
||||
EXPECT_EQ(0, std::memcmp(dataFixture.data(), block2->data(), SIZE));
|
||||
}
|
||||
|
||||
TEST_F(BlockStoreUtilsTest_CopyToExistingBlock, OriginalBlockUnchanged) {
|
||||
auto block = blockStore->create(SIZE);
|
||||
auto block = blockStore->create(Data(SIZE));
|
||||
block->write(dataFixture.data(), 0, SIZE);
|
||||
auto block2 = blockStore->create(SIZE);
|
||||
auto block2 = blockStore->create(Data(SIZE));
|
||||
copyTo(block2.get(), *block);
|
||||
|
||||
EXPECT_EQ(0, std::memcmp(dataFixture.data(), block->data(), SIZE));
|
||||
|
@ -10,9 +10,9 @@ namespace blockstore {
|
||||
namespace utils {
|
||||
|
||||
unique_ptr<Block> copyToNewBlock(BlockStore *blockStore, const Block &block) {
|
||||
auto newBlock = blockStore->create(block.size());
|
||||
copyTo(newBlock.get(), block);
|
||||
return newBlock;
|
||||
Data data(block.size());
|
||||
std::memcpy(data.data(), block.data(), block.size());
|
||||
return blockStore->create(data);
|
||||
}
|
||||
|
||||
void copyTo(Block *target, const Block &source) {
|
||||
|
@ -50,8 +50,9 @@ size_t Data::size() const {
|
||||
return _size;
|
||||
}
|
||||
|
||||
void Data::FillWithZeroes() {
|
||||
Data &Data::FillWithZeroes() {
|
||||
std::memset(_data, 0, _size);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Data::StoreToFile(const bf::path &filepath) const {
|
||||
@ -92,4 +93,8 @@ void Data::_readFromStream(istream &stream) {
|
||||
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());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ namespace blockstore {
|
||||
|
||||
class Data {
|
||||
public:
|
||||
Data(size_t size);
|
||||
explicit Data(size_t size);
|
||||
Data(Data &&rhs); // move constructor
|
||||
virtual ~Data();
|
||||
|
||||
@ -22,7 +22,7 @@ public:
|
||||
|
||||
size_t size() const;
|
||||
|
||||
void FillWithZeroes();
|
||||
Data &FillWithZeroes();
|
||||
|
||||
void StoreToFile(const boost::filesystem::path &filepath) const;
|
||||
static Data LoadFromFile(const boost::filesystem::path &filepath);
|
||||
@ -38,6 +38,9 @@ private:
|
||||
DISALLOW_COPY_AND_ASSIGN(Data);
|
||||
};
|
||||
|
||||
//TODO Test operator==
|
||||
bool operator==(const Data &lhs, const Data &rhs);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user