diff --git a/CMakeLists.txt b/CMakeLists.txt index b345e659..b0034179 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,7 @@ ADD_BOOST(filesystem system thread) TARGET_LINK_LIBRARIES(${BII_BLOCK_TARGET} INTERFACE rt) ACTIVATE_CPP14() +ENABLE_STYLE_WARNINGS() # You can safely delete lines from here... diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000..cff5981a --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,37 @@ +version: 0.9.{build} +os: Visual Studio 2015 RC + +install: + - ps: wget http://www.cmake.org/files/v3.2/cmake-3.2.2-win32-x86.zip -OutFile cmake.zip + - cmd: echo "Unzipping cmake..." + - cmd: 7z x cmake.zip -o"C:\Program Files (x86)\" -y > nul + - cmd: set PATH=%PATH:CMake 2.8\bin=%;C:\Program Files (x86)\cmake-3.0.2-win32-x86\bin + - cmd: cmake --version + - cmd: echo "Downloading biicode..." + - ps: wget http://www.biicode.com/downloads/latest/win -OutFile bii-win.exe + - cmd: bii-win.exe /VERYSILENT + - cmd: set PATH=%PATH%;C:\Program Files (x86)\BiiCode\bii + - cmd: bii -v + - cmd: del bii-win.exe + - cmd: del cmake.zip + +before_build: + - cmd: bii init -L + # copy files and folders + - cmd: dir +# - cmd: echo xcopy "%APPVEYOR_BUILD_FOLDER%" blocks\%block_user%\%block_name%\ /e + - cmd: bii configure -G "Visual Studio 14 2015" + +build_script: + - cmd: bii build + +test_script: + - cmd: cd bin + - cmd: amalulla_cpp-expression-parser_test-shunting-yard.exe + +environment: + block_user: + "messmer" + block_name: + "blockstore" + diff --git a/biicode.conf b/biicode.conf index 89337881..815f499f 100644 --- a/biicode.conf +++ b/biicode.conf @@ -1,13 +1,12 @@ # Biicode configuration file [requirements] - cryptopp/cryptopp: 8 + cryptopp/cryptopp: 9 google/gmock: 2 - google/gtest: 10 + google/gtest: 11 messmer/cmake: 3 messmer/cpp-utils: 2 messmer/parallelaccessstore: 0 - messmer/tempfile: 4 [parent] messmer/blockstore: 1 diff --git a/implementations/caching/Cache.cpp b/implementations/caching/Cache.cpp deleted file mode 100644 index d3a9dba1..00000000 --- a/implementations/caching/Cache.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "Cache.h" -#include "PeriodicTask.h" - -using std::unique_ptr; -using std::make_unique; -using std::mutex; -using std::lock_guard; -using std::pair; - -namespace blockstore { -namespace caching { - -constexpr uint32_t Cache::MAX_ENTRIES; -constexpr double Cache::PURGE_LIFETIME_SEC; -constexpr double Cache::PURGE_INTERVAL; -constexpr double Cache::MAX_LIFETIME_SEC; - -Cache::Cache(): _cachedBlocks(), _timeoutFlusher(nullptr) { - //Don't initialize timeoutFlusher in the initializer list, - //because it then might already call Cache::popOldEntries() before Cache is done constructing - _timeoutFlusher = make_unique(std::bind(&Cache::_popOldEntries, this), PURGE_INTERVAL); -} - -Cache::~Cache() { -} - -unique_ptr Cache::pop(const Key &key) { - lock_guard lock(_mutex); - auto found = _cachedBlocks.pop(key); - if (found.get() == nullptr) { - return nullptr; - } - auto block = found->releaseBlock(); - return block; -} - -void Cache::push(unique_ptr block) { - lock_guard lock(_mutex); - assert(_cachedBlocks.size() <= MAX_ENTRIES); - if (_cachedBlocks.size() == MAX_ENTRIES) { - _cachedBlocks.pop(); - assert(_cachedBlocks.size() == MAX_ENTRIES-1); - } - Key key = block->key(); - _cachedBlocks.push(key, make_unique(std::move(block))); -} - -void Cache::_popOldEntries() { - lock_guard lock(_mutex); - while(_cachedBlocks.size() > 0 && _cachedBlocks.peek().ageSeconds() > PURGE_LIFETIME_SEC) { - _cachedBlocks.pop(); - } -} - -} -} diff --git a/implementations/caching/Cache.h b/implementations/caching/Cache.h deleted file mode 100644 index 7839b086..00000000 --- a/implementations/caching/Cache.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once -#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_CACHING_CACHE_H_ -#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_CACHING_CACHE_H_ - -#include "CacheEntry.h" -#include "QueueMap.h" -#include "../../interface/Block.h" -#include -#include - -namespace blockstore { -namespace caching { -class PeriodicTask; - -//TODO Test -//TODO Also throw blocks out after a timeout - -class Cache { -public: - static constexpr uint32_t MAX_ENTRIES = 1000; - //TODO Experiment with good values - static constexpr double PURGE_LIFETIME_SEC = 0.5; //When an entry has this age, it will be purged from the cache - static constexpr double PURGE_INTERVAL = 0.5; // With this interval, we check for entries to purge - static constexpr double MAX_LIFETIME_SEC = PURGE_LIFETIME_SEC + PURGE_INTERVAL; // This is the oldest age an entry can reach (given purging works in an ideal world, i.e. with the ideal interval and in zero time) - - Cache(); - virtual ~Cache(); - - void push(std::unique_ptr block); - std::unique_ptr pop(const Key &key); - -private: - void _popOldEntries(); - - - mutable std::mutex _mutex; - QueueMap _cachedBlocks; - std::unique_ptr _timeoutFlusher; -}; - -} -} - -#endif diff --git a/implementations/caching/CacheEntry.cpp b/implementations/caching/CacheEntry.cpp deleted file mode 100644 index 0ca41a63..00000000 --- a/implementations/caching/CacheEntry.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#include "CacheEntry.h" -#include "../../interface/Block.h" diff --git a/implementations/caching/CachingBlockStore.cpp b/implementations/caching/CachingBlockStore.cpp index 0e152f37..67d841ea 100644 --- a/implementations/caching/CachingBlockStore.cpp +++ b/implementations/caching/CachingBlockStore.cpp @@ -9,6 +9,7 @@ using std::unique_ptr; using std::make_unique; using cpputils::dynamic_pointer_move; +using cpputils::Data; namespace blockstore { namespace caching { @@ -27,13 +28,16 @@ unique_ptr CachingBlockStore::tryCreate(const Key &key, Data data) { } unique_ptr CachingBlockStore::load(const Key &key) { - auto block = _cache.pop(key); - if (block.get() != nullptr) { - return make_unique(std::move(block), this); - } - block = _baseBlockStore->load(key); - if (block.get() == nullptr) { - return nullptr; + boost::optional> optBlock = _cache.pop(key); + unique_ptr block; + //TODO an optional<> class with .getOrElse() would make this code simpler. boost::optional<>::value_or_eval didn't seem to work with unique_ptr members. + if (optBlock) { + block = std::move(*optBlock); + } else { + block = _baseBlockStore->load(key); + if (block.get() == nullptr) { + return nullptr; + } } return make_unique(std::move(block), this); } @@ -52,12 +56,12 @@ void CachingBlockStore::remove(std::unique_ptr block) { } uint64_t CachingBlockStore::numBlocks() const { - //TODO Add number of NewBlock instances return _baseBlockStore->numBlocks() + _numNewBlocks; } void CachingBlockStore::release(unique_ptr block) { - _cache.push(std::move(block)); + Key key = block->key(); + _cache.push(key, std::move(block)); } std::unique_ptr CachingBlockStore::tryCreateInBaseStore(const Key &key, Data data) { diff --git a/implementations/caching/CachingBlockStore.h b/implementations/caching/CachingBlockStore.h index bf9adbc5..073ecd97 100644 --- a/implementations/caching/CachingBlockStore.h +++ b/implementations/caching/CachingBlockStore.h @@ -2,32 +2,31 @@ #ifndef BLOCKSTORE_IMPLEMENTATIONS_CACHING_CACHINGBLOCKSTORE_H_ #define BLOCKSTORE_IMPLEMENTATIONS_CACHING_CACHINGBLOCKSTORE_H_ -#include "Cache.h" +#include "cache/Cache.h" #include "../../interface/BlockStore.h" namespace blockstore { namespace caching { -//TODO Rename this to CachingBlockStore and the other one to something else //TODO Check that this blockstore allows parallel destructing of blocks (otherwise we won't encrypt blocks in parallel) class CachingBlockStore: public BlockStore { public: - CachingBlockStore(std::unique_ptr baseBlockStore); + explicit CachingBlockStore(std::unique_ptr baseBlockStore); Key createKey() override; - std::unique_ptr tryCreate(const Key &key, Data data) override; + std::unique_ptr tryCreate(const Key &key, cpputils::Data data) override; std::unique_ptr load(const Key &key) override; void remove(std::unique_ptr block) override; uint64_t numBlocks() const override; void release(std::unique_ptr block); - std::unique_ptr tryCreateInBaseStore(const Key &key, Data data); + std::unique_ptr tryCreateInBaseStore(const Key &key, cpputils::Data data); void removeFromBaseStore(std::unique_ptr block); private: std::unique_ptr _baseBlockStore; - Cache _cache; + Cache> _cache; uint32_t _numNewBlocks; DISALLOW_COPY_AND_ASSIGN(CachingBlockStore); diff --git a/implementations/caching/NewBlock.cpp b/implementations/caching/NewBlock.cpp index edbfeb61..ee8837d3 100644 --- a/implementations/caching/NewBlock.cpp +++ b/implementations/caching/NewBlock.cpp @@ -3,6 +3,7 @@ using std::unique_ptr; using std::make_unique; +using cpputils::Data; namespace blockstore { namespace caching { diff --git a/implementations/caching/NewBlock.h b/implementations/caching/NewBlock.h index d71dbf69..5b2b69ac 100644 --- a/implementations/caching/NewBlock.h +++ b/implementations/caching/NewBlock.h @@ -3,7 +3,7 @@ #define BLOCKSTORE_IMPLEMENTATIONS_CACHING_NEWBLOCK_H_ #include "../../interface/BlockStore.h" -#include "../../utils/Data.h" +#include #include "messmer/cpp-utils/macros.h" #include @@ -20,7 +20,7 @@ class CachingBlockStore; // 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); + NewBlock(const Key &key, cpputils::Data data, CachingBlockStore *blockStore); virtual ~NewBlock(); const void *data() const override; @@ -35,7 +35,7 @@ public: private: CachingBlockStore *_blockStore; - Data _data; + cpputils::Data _data; std::unique_ptr _baseBlock; bool _dataChanged; diff --git a/implementations/caching/QueueMap.h b/implementations/caching/QueueMap.h deleted file mode 100644 index 758b2bd4..00000000 --- a/implementations/caching/QueueMap.h +++ /dev/null @@ -1,78 +0,0 @@ -#pragma once -#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_CACHING2_MAP_H_ -#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_CACHING2_MAP_H_ - -#include -#include -#include - -namespace blockstore { -namespace caching { - -//TODO Test -//TODO Move to utils -template -class QueueMap { -public: - QueueMap(): _entries(), _sentinel(nullptr, nullptr, &_sentinel, &_sentinel) { - } - virtual ~QueueMap() {} - - void push(const Key &key, std::unique_ptr value) { - auto newEntry = std::make_unique(&key, std::move(value), _sentinel.prev, &_sentinel); - _sentinel.prev->next = newEntry.get(); - _sentinel.prev = newEntry.get(); - auto insertResult = _entries.emplace(key, std::move(newEntry)); - assert(insertResult.second == true); - } - - std::unique_ptr pop(const Key &key) { - auto found = _entries.find(key); - if (found == _entries.end()) { - return nullptr; - } - _removeFromQueue(found->second.get()); - auto value = std::move(found->second->value); - _entries.erase(found); - return value; - } - - std::unique_ptr pop() { - return pop(*_sentinel.next->key); - } - - const Value &peek() { - return *_sentinel.next->value; - } - - uint32_t size() { - return _entries.size(); - } - -private: - struct Entry { - Entry(const Key *key_, std::unique_ptr value_, Entry *prev_, Entry *next_): key(nullptr), value(std::move(value_)), prev(prev_), next(next_) { - if (key_ != nullptr) { - key = std::make_unique(*key_); - } - } - std::unique_ptr key; - std::unique_ptr value; - Entry *prev; - Entry *next; - }; - - void _removeFromQueue(Entry *entry) { - entry->prev->next = entry->next; - entry->next->prev = entry->prev; - } - - //TODO Double indirection unique_ptr and Entry has unique_ptr. Necessary? - std::unordered_map> _entries; - Entry _sentinel; -}; - -} -} - -#endif diff --git a/implementations/caching/cache/Cache.cpp b/implementations/caching/cache/Cache.cpp new file mode 100644 index 00000000..2d464949 --- /dev/null +++ b/implementations/caching/cache/Cache.cpp @@ -0,0 +1 @@ +#include "Cache.h" diff --git a/implementations/caching/cache/Cache.h b/implementations/caching/cache/Cache.h new file mode 100644 index 00000000..951c8f66 --- /dev/null +++ b/implementations/caching/cache/Cache.h @@ -0,0 +1,86 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_CACHING_CACHE_H_ +#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_CACHING_CACHE_H_ + +#include "CacheEntry.h" +#include "QueueMap.h" +#include "PeriodicTask.h" +#include +#include +#include + +namespace blockstore { +namespace caching { + +template +class Cache { +public: + static constexpr uint32_t MAX_ENTRIES = 1000; + //TODO Experiment with good values + static constexpr double PURGE_LIFETIME_SEC = 0.5; //When an entry has this age, it will be purged from the cache + static constexpr double PURGE_INTERVAL = 0.5; // With this interval, we check for entries to purge + static constexpr double MAX_LIFETIME_SEC = PURGE_LIFETIME_SEC + PURGE_INTERVAL; // This is the oldest age an entry can reach (given purging works in an ideal world, i.e. with the ideal interval and in zero time) + + Cache(); + virtual ~Cache(); + + void push(const Key &key, Value value); + boost::optional pop(const Key &key); + +private: + void _popOldEntries(); + + mutable std::mutex _mutex; + QueueMap> _cachedBlocks; + std::unique_ptr _timeoutFlusher; +}; + +template constexpr uint32_t Cache::MAX_ENTRIES; +template constexpr double Cache::PURGE_LIFETIME_SEC; +template constexpr double Cache::PURGE_INTERVAL; +template constexpr double Cache::MAX_LIFETIME_SEC; + +template +Cache::Cache(): _cachedBlocks(), _timeoutFlusher(nullptr) { + //Don't initialize timeoutFlusher in the initializer list, + //because it then might already call Cache::popOldEntries() before Cache is done constructing + _timeoutFlusher = std::make_unique(std::bind(&Cache::_popOldEntries, this), PURGE_INTERVAL); +} + +template +Cache::~Cache() { +} + +template +boost::optional Cache::pop(const Key &key) { + std::lock_guard lock(_mutex); + auto found = _cachedBlocks.pop(key); + if (!found) { + return boost::none; + } + return found->releaseValue(); +} + +template +void Cache::push(const Key &key, Value value) { + std::lock_guard lock(_mutex); + assert(_cachedBlocks.size() <= MAX_ENTRIES); + if (_cachedBlocks.size() == MAX_ENTRIES) { + _cachedBlocks.pop(); + assert(_cachedBlocks.size() == MAX_ENTRIES-1); + } + _cachedBlocks.push(key, CacheEntry(std::move(value))); +} + +template +void Cache::_popOldEntries() { + std::lock_guard lock(_mutex); + while(_cachedBlocks.size() > 0 && _cachedBlocks.peek()->ageSeconds() > PURGE_LIFETIME_SEC) { + _cachedBlocks.pop(); + } +} + +} +} + +#endif diff --git a/implementations/caching/cache/CacheEntry.cpp b/implementations/caching/cache/CacheEntry.cpp new file mode 100644 index 00000000..a3420ac8 --- /dev/null +++ b/implementations/caching/cache/CacheEntry.cpp @@ -0,0 +1 @@ +#include "CacheEntry.h" diff --git a/implementations/caching/CacheEntry.h b/implementations/caching/cache/CacheEntry.h similarity index 68% rename from implementations/caching/CacheEntry.h rename to implementations/caching/cache/CacheEntry.h index 7c11f0c0..b49ba970 100644 --- a/implementations/caching/CacheEntry.h +++ b/implementations/caching/cache/CacheEntry.h @@ -8,12 +8,12 @@ #include namespace blockstore { -class Block; namespace caching { +template class CacheEntry { public: - CacheEntry(std::unique_ptr block): _lastAccess(currentTime()), _block(std::move(block)) { + explicit CacheEntry(Value value): _lastAccess(currentTime()), _value(std::move(value)) { } CacheEntry(CacheEntry &&) = default; @@ -22,22 +22,13 @@ public: return ((double)(currentTime() - _lastAccess).total_nanoseconds()) / ((double)1000000000); } - std::unique_ptr releaseBlock() { - return std::move(_block); - } - - void setNextEntry(const CacheEntry *entry) { - _nextEntry = entry; - } - - const CacheEntry *nextEntry() { - return _nextEntry; + Value releaseValue() { + return std::move(_value); } private: boost::posix_time::ptime _lastAccess; - std::unique_ptr _block; - const CacheEntry *_nextEntry; + Value _value; static boost::posix_time::ptime currentTime() { return boost::posix_time::microsec_clock::local_time(); diff --git a/implementations/caching/PeriodicTask.cpp b/implementations/caching/cache/PeriodicTask.cpp similarity index 62% rename from implementations/caching/PeriodicTask.cpp rename to implementations/caching/cache/PeriodicTask.cpp index 1b260530..273bb15e 100644 --- a/implementations/caching/PeriodicTask.cpp +++ b/implementations/caching/cache/PeriodicTask.cpp @@ -9,17 +9,18 @@ namespace caching { PeriodicTask::PeriodicTask(function task, double intervalSec) : _thread(), _task(task), _intervalSec(intervalSec) { _thread = boost::thread([this]() { - boost::chrono::nanoseconds interval((uint64_t)(UINT64_C(1000000000) * _intervalSec)); - try { + boost::chrono::nanoseconds interval((uint64_t)(UINT64_C(1000000000) * _intervalSec)); + try { while(true) { boost::this_thread::sleep_for(interval); _task(); } - } catch (const boost::thread_interrupted &e) { - //Do nothing, exit thread. - } catch (...) { - cerr << "PeriodicTask crashed" << endl; - } + } catch (const boost::thread_interrupted &e) { + //Do nothing, exit thread. + } catch (...) { + //TODO Think about logging + cerr << "PeriodicTask crashed" << endl; + } }); } diff --git a/implementations/caching/PeriodicTask.h b/implementations/caching/cache/PeriodicTask.h similarity index 96% rename from implementations/caching/PeriodicTask.h rename to implementations/caching/cache/PeriodicTask.h index 4ad221e2..6a0955e6 100644 --- a/implementations/caching/PeriodicTask.h +++ b/implementations/caching/cache/PeriodicTask.h @@ -7,7 +7,6 @@ namespace blockstore { namespace caching { -//TODO Test cases class PeriodicTask { public: PeriodicTask(std::function task, double intervalSec); diff --git a/implementations/caching/QueueMap.cpp b/implementations/caching/cache/QueueMap.cpp similarity index 100% rename from implementations/caching/QueueMap.cpp rename to implementations/caching/cache/QueueMap.cpp diff --git a/implementations/caching/cache/QueueMap.h b/implementations/caching/cache/QueueMap.h new file mode 100644 index 00000000..f412432b --- /dev/null +++ b/implementations/caching/cache/QueueMap.h @@ -0,0 +1,105 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_CACHING2_MAP_H_ +#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_CACHING2_MAP_H_ + +#include +#include +#include +#include +#include + +namespace blockstore { +namespace caching { + +// A class that is a queue and a map at the same time. We could also see it as an addressable queue. +template +class QueueMap { +public: + QueueMap(): _entries(), _sentinel(&_sentinel, &_sentinel) { + } + virtual ~QueueMap() { + for (auto &entry : _entries) { + entry.second.release(); + } + } + + void push(const Key &key, Value value) { + auto newEntry = _entries.emplace(std::piecewise_construct, std::forward_as_tuple(key), std::forward_as_tuple(_sentinel.prev, &_sentinel)); + if(newEntry.second != true) { + throw std::logic_error("There is already an element with this key"); + } + newEntry.first->second.init(&newEntry.first->first, std::move(value)); + //The following is ok, because std::unordered_map never invalidates pointers to its entries + _sentinel.prev->next = &newEntry.first->second; + _sentinel.prev = &newEntry.first->second; + } + + boost::optional pop(const Key &key) { + auto found = _entries.find(key); + if (found == _entries.end()) { + return boost::none; + } + _removeFromQueue(found->second); + auto value = found->second.release(); + _entries.erase(found); + return std::move(value); + } + + boost::optional pop() { + if(_sentinel.next == &_sentinel) { + return boost::none; + } + return pop(*_sentinel.next->key); + } + + boost::optional peek() { + if(_sentinel.next == &_sentinel) { + return boost::none; + } + return _sentinel.next->value(); + } + + uint32_t size() { + return _entries.size(); + } + +private: + class Entry { + public: + Entry(Entry *prev_, Entry *next_): prev(prev_), next(next_), key(nullptr), _value() { + } + void init(const Key *key_, Value value_) { + key = key_; + new(_value) Value(std::move(value_)); + } + Value release() { + Value value = std::move(*reinterpret_cast(_value)); + reinterpret_cast(_value)->~Value(); + return value; + } + const Value &value() { + return *reinterpret_cast(_value); + } + Entry *prev; + Entry *next; + const Key *key; + private: + alignas(Value) char _value[sizeof(Value)]; + DISALLOW_COPY_AND_ASSIGN(Entry); + }; + + void _removeFromQueue(const Entry &entry) { + entry.prev->next = entry.next; + entry.next->prev = entry.prev; + } + + std::unordered_map _entries; + Entry _sentinel; + + DISALLOW_COPY_AND_ASSIGN(QueueMap); +}; + +} +} + +#endif diff --git a/implementations/encrypted/EncryptedBlock.cpp b/implementations/encrypted/EncryptedBlock.cpp index 59da307d..5f66d9c9 100644 --- a/implementations/encrypted/EncryptedBlock.cpp +++ b/implementations/encrypted/EncryptedBlock.cpp @@ -1,91 +1 @@ #include "EncryptedBlock.h" -#include - -#include "../../utils/BlockStoreUtils.h" - -using CryptoPP::CFB_Mode; -using CryptoPP::AES; - -using std::make_unique; - -//TODO not only encryption, but also hmac - -namespace blockstore { -namespace encrypted { - -constexpr unsigned int EncryptedBlock::IV_SIZE; - -std::unique_ptr 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(std::move(baseBlock), encKey, std::move(data)); -} - -EncryptedBlock::EncryptedBlock(std::unique_ptr baseBlock, const EncryptionKey &encKey) - :EncryptedBlock(std::move(baseBlock), encKey, Data(USEABLE_BLOCK_SIZE(baseBlock->size()))) { - _decryptFromBaseBlock(); -} - -EncryptedBlock::EncryptedBlock(std::unique_ptr baseBlock, const EncryptionKey &encKey, Data plaintextData) - :Block(baseBlock->key()), - _baseBlock(std::move(baseBlock)), - _plaintextData(std::move(plaintextData)), - _encKey(encKey), - _dataChanged(false) { -} - -EncryptedBlock::~EncryptedBlock() { - _encryptToBaseBlock(); -} - -const void *EncryptedBlock::data() const { - return _plaintextData.data(); -} - -void EncryptedBlock::write(const void *source, uint64_t offset, uint64_t size) { - assert(offset <= _plaintextData.size() && offset + size <= _plaintextData.size()); //Also check offset < _data->size() because of possible overflow in the addition - std::memcpy((uint8_t*)_plaintextData.data()+offset, source, size); - _dataChanged = true; -} - -void EncryptedBlock::flush() { - _encryptToBaseBlock(); - return _baseBlock->flush(); -} - -size_t EncryptedBlock::size() const { - return _plaintextData.size(); -} - -void EncryptedBlock::_decryptFromBaseBlock() { - const byte *iv = (byte*)_baseBlock->data(); - const byte *data = (byte*)_baseBlock->data() + IV_SIZE; - auto decryption = CFB_Mode::Decryption((byte*)_encKey.data(), EncryptionKey::BINARY_LENGTH, iv); - decryption.ProcessData((byte*)_plaintextData.data(), data, _plaintextData.size()); -} - -void EncryptedBlock::_encryptToBaseBlock() { - if (_dataChanged) { - 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 = FixedSizeData::CreateRandom(); - auto encryption = CFB_Mode::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; -} - -} -} diff --git a/implementations/encrypted/EncryptedBlock.h b/implementations/encrypted/EncryptedBlock.h index 84b66019..f7b96f5b 100644 --- a/implementations/encrypted/EncryptedBlock.h +++ b/implementations/encrypted/EncryptedBlock.h @@ -3,59 +3,156 @@ #define BLOCKSTORE_IMPLEMENTATIONS_ENCRYPTED_ENCRYPTEDBLOCK_H_ #include "../../interface/Block.h" -#include "EncryptionKey.h" -#include "../../utils/Data.h" +#include #include "../../interface/BlockStore.h" #include "messmer/cpp-utils/macros.h" #include +#include +#include +#include "ciphers/Cipher.h" namespace blockstore { namespace encrypted { -class EncryptedBlockStore; +template class EncryptedBlockStore; +//TODO Test EncryptedBlock + +template class EncryptedBlock: public Block { public: + BOOST_CONCEPT_ASSERT((CipherConcept)); + static std::unique_ptr TryCreateNew(BlockStore *baseBlockStore, const Key &key, cpputils::Data data, const typename Cipher::EncryptionKey &encKey); + static std::unique_ptr TryDecrypt(std::unique_ptr baseBlock, const typename Cipher::EncryptionKey &key); + //TODO Storing key twice (in parent class and in object pointed to). Once would be enough. - EncryptedBlock(std::unique_ptr baseBlock, const EncryptionKey &encKey); - EncryptedBlock(std::unique_ptr baseBlock, const EncryptionKey &encKey, Data plaintextData); + EncryptedBlock(std::unique_ptr baseBlock, const typename Cipher::EncryptionKey &key, cpputils::Data plaintextWithHeader); virtual ~EncryptedBlock(); - static std::unique_ptr 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; + void write(const void *source, uint64_t offset, uint64_t count) override; void flush() override; size_t size() const override; - static constexpr unsigned int BASE_BLOCK_SIZE(unsigned int useableBlockSize) { - return useableBlockSize + IV_SIZE; - } - - static constexpr unsigned int USEABLE_BLOCK_SIZE(unsigned int baseBlockSize) { - return baseBlockSize - IV_SIZE; - } + std::unique_ptr releaseBlock(); private: std::unique_ptr _baseBlock; - Data _plaintextData; - EncryptionKey _encKey; + cpputils::Data _plaintextWithHeader; + typename Cipher::EncryptionKey _encKey; bool _dataChanged; - static constexpr unsigned int IV_SIZE = CryptoPP::AES::BLOCKSIZE; - - byte *baseBlockIV(); - byte *baseBlockData(); + static constexpr unsigned int HEADER_LENGTH = Key::BINARY_LENGTH; void _encryptToBaseBlock(); - void _decryptFromBaseBlock(); - - static Data _encrypt(const Data &plaintext, const EncryptionKey &encKey); + static cpputils::Data _prependKeyHeaderToData(const Key &key, cpputils::Data data); + static bool _keyHeaderIsCorrect(const Key &key, const cpputils::Data &data); DISALLOW_COPY_AND_ASSIGN(EncryptedBlock); }; +template +constexpr unsigned int EncryptedBlock::HEADER_LENGTH; + + +template +std::unique_ptr> EncryptedBlock::TryCreateNew(BlockStore *baseBlockStore, const Key &key, cpputils::Data data, const typename Cipher::EncryptionKey &encKey) { + cpputils::Data plaintextWithHeader = _prependKeyHeaderToData(key, std::move(data)); + cpputils::Data encrypted = Cipher::encrypt((byte*)plaintextWithHeader.data(), plaintextWithHeader.size(), encKey); + auto baseBlock = baseBlockStore->tryCreate(key, std::move(encrypted)); + if (baseBlock.get() == nullptr) { + //TODO Test this code branch + return nullptr; + } + + return std::make_unique(std::move(baseBlock), encKey, std::move(plaintextWithHeader)); +} + +template +std::unique_ptr> EncryptedBlock::TryDecrypt(std::unique_ptr baseBlock, const typename Cipher::EncryptionKey &encKey) { + //TODO Change BlockStore so we can read their "class Data" objects instead of "void *data()", and then we can change the Cipher interface to take Data objects instead of "byte *" + size + boost::optional plaintextWithHeader = Cipher::decrypt((byte*)baseBlock->data(), baseBlock->size(), encKey); + if(!plaintextWithHeader) { + //Decryption failed (e.g. an authenticated cipher detected modifications to the ciphertext) + //TODO Think about logging + std::cerr << "Decrypting block " << baseBlock->key().ToString() << " failed. Was the block modified by an attacker?" << std::endl; + return nullptr; + } + if(!_keyHeaderIsCorrect(baseBlock->key(), *plaintextWithHeader)) { + //The stored key in the block data is incorrect - an attacker might have exchanged the contents with the encrypted data from a different block + //TODO Think about logging + std::cerr << "Decrypting block " << baseBlock->key().ToString() << " failed due to invalid block key. Was the block modified by an attacker?" << std::endl; + return nullptr; + } + return std::make_unique>(std::move(baseBlock), encKey, std::move(*plaintextWithHeader)); +} + +template +cpputils::Data EncryptedBlock::_prependKeyHeaderToData(const Key &key, cpputils::Data data) { + static_assert(HEADER_LENGTH >= Key::BINARY_LENGTH, "Key doesn't fit into the header"); + cpputils::Data result(data.size() + HEADER_LENGTH); + std::memcpy(result.data(), key.data(), Key::BINARY_LENGTH); + std::memcpy((uint8_t*)result.data() + Key::BINARY_LENGTH, data.data(), data.size()); + return result; +} + +template +bool EncryptedBlock::_keyHeaderIsCorrect(const Key &key, const cpputils::Data &data) { + return 0 == std::memcmp(key.data(), data.data(), Key::BINARY_LENGTH); +} + +template +EncryptedBlock::EncryptedBlock(std::unique_ptr baseBlock, const typename Cipher::EncryptionKey &encKey, cpputils::Data plaintextWithHeader) + :Block(baseBlock->key()), + _baseBlock(std::move(baseBlock)), + _plaintextWithHeader(std::move(plaintextWithHeader)), + _encKey(encKey), + _dataChanged(false) { +} + +template +EncryptedBlock::~EncryptedBlock() { + _encryptToBaseBlock(); +} + +template +const void *EncryptedBlock::data() const { + return (uint8_t*)_plaintextWithHeader.data() + HEADER_LENGTH; +} + +template +void EncryptedBlock::write(const void *source, uint64_t offset, uint64_t count) { + assert(offset <= size() && offset + count <= size()); //Also check offset < size() because of possible overflow in the addition + std::memcpy((uint8_t*)_plaintextWithHeader.data()+HEADER_LENGTH+offset, source, count); + _dataChanged = true; +} + +template +void EncryptedBlock::flush() { + _encryptToBaseBlock(); + return _baseBlock->flush(); +} + +template +size_t EncryptedBlock::size() const { + return _plaintextWithHeader.size() - HEADER_LENGTH; +} + +template +void EncryptedBlock::_encryptToBaseBlock() { + if (_dataChanged) { + cpputils::Data encrypted = Cipher::encrypt((byte*)_plaintextWithHeader.data(), _plaintextWithHeader.size(), _encKey); + _baseBlock->write(encrypted.data(), 0, encrypted.size()); + _dataChanged = false; + } +} + +template +std::unique_ptr EncryptedBlock::releaseBlock() { + return std::move(_baseBlock); +} + } } diff --git a/implementations/encrypted/EncryptedBlockStore.cpp b/implementations/encrypted/EncryptedBlockStore.cpp index 1eb88f42..5ab7ca32 100644 --- a/implementations/encrypted/EncryptedBlockStore.cpp +++ b/implementations/encrypted/EncryptedBlockStore.cpp @@ -1,42 +1 @@ #include "EncryptedBlockStore.h" -#include "EncryptedBlock.h" -#include -#include "../../utils/BlockStoreUtils.h" - -using std::unique_ptr; -using std::make_unique; - -namespace blockstore { -namespace encrypted { - -EncryptedBlockStore::EncryptedBlockStore(unique_ptr baseBlockStore, const EncryptionKey &encKey) - : _baseBlockStore(std::move(baseBlockStore)), _encKey(encKey) { -} - -Key EncryptedBlockStore::createKey() { - return _baseBlockStore->createKey(); -} - -unique_ptr EncryptedBlockStore::tryCreate(const Key &key, Data data) { - return EncryptedBlock::TryCreateNew(_baseBlockStore.get(), key, std::move(data), _encKey); -} - -unique_ptr EncryptedBlockStore::load(const Key &key) { - auto block = _baseBlockStore->load(key); - if (block.get() == nullptr) { - return nullptr; - } - return make_unique(std::move(block), _encKey); -} - -void EncryptedBlockStore::remove(unique_ptr block) { - return _baseBlockStore->remove(std::move(block)); -} - -uint64_t EncryptedBlockStore::numBlocks() const { - return _baseBlockStore->numBlocks(); -} - -} -} - diff --git a/implementations/encrypted/EncryptedBlockStore.h b/implementations/encrypted/EncryptedBlockStore.h index 2e897d1d..ad7acd31 100644 --- a/implementations/encrypted/EncryptedBlockStore.h +++ b/implementations/encrypted/EncryptedBlockStore.h @@ -4,28 +4,79 @@ #include "../../interface/BlockStore.h" #include -#include "EncryptionKey.h" +#include +#include "EncryptedBlock.h" +#include namespace blockstore { namespace encrypted { +template class EncryptedBlockStore: public BlockStore { public: - EncryptedBlockStore(std::unique_ptr baseBlockStore, const EncryptionKey &encKey); + EncryptedBlockStore(std::unique_ptr baseBlockStore, const typename Cipher::EncryptionKey &encKey); + //TODO Are createKey() tests included in generic BlockStoreTest? If not, add it! Key createKey() override; - std::unique_ptr tryCreate(const Key &key, Data data) override; + std::unique_ptr tryCreate(const Key &key, cpputils::Data data) override; std::unique_ptr load(const Key &key) override; void remove(std::unique_ptr block) override; uint64_t numBlocks() const override; + //This function should only be used by test cases + void __setKey(const typename Cipher::EncryptionKey &encKey); + private: std::unique_ptr _baseBlockStore; - EncryptionKey _encKey; + typename Cipher::EncryptionKey _encKey; DISALLOW_COPY_AND_ASSIGN(EncryptedBlockStore); }; + + +template +EncryptedBlockStore::EncryptedBlockStore(std::unique_ptr baseBlockStore, const typename Cipher::EncryptionKey &encKey) + : _baseBlockStore(std::move(baseBlockStore)), _encKey(encKey) { +} + +template +Key EncryptedBlockStore::createKey() { + return _baseBlockStore->createKey(); +} + +template +std::unique_ptr EncryptedBlockStore::tryCreate(const Key &key, cpputils::Data data) { + //TODO Test that this returns nullptr when base blockstore returns nullptr (for all pass-through-blockstores) + return EncryptedBlock::TryCreateNew(_baseBlockStore.get(), key, std::move(data), _encKey); +} + +template +std::unique_ptr EncryptedBlockStore::load(const Key &key) { + auto block = _baseBlockStore->load(key); + if (block.get() == nullptr) { + //TODO Test this path (for all pass-through-blockstores) + return nullptr; + } + return EncryptedBlock::TryDecrypt(std::move(block), _encKey); +} + +template +void EncryptedBlockStore::remove(std::unique_ptr block) { + auto baseBlock = cpputils::dynamic_pointer_move>(block)->releaseBlock(); + return _baseBlockStore->remove(std::move(baseBlock)); +} + +template +uint64_t EncryptedBlockStore::numBlocks() const { + return _baseBlockStore->numBlocks(); +} + +template +void EncryptedBlockStore::__setKey(const typename Cipher::EncryptionKey &encKey) { + _encKey = encKey; +} + } } diff --git a/implementations/encrypted/EncryptionKey.cpp b/implementations/encrypted/EncryptionKey.cpp deleted file mode 100644 index 18a97b45..00000000 --- a/implementations/encrypted/EncryptionKey.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "EncryptionKey.h" diff --git a/implementations/encrypted/EncryptionKey.h b/implementations/encrypted/EncryptionKey.h deleted file mode 100644 index 408d30b8..00000000 --- a/implementations/encrypted/EncryptionKey.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once -#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_ENCRYPTED_ENCRYPTIONKEY_H_ -#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_ENCRYPTED_ENCRYPTIONKEY_H_ - -#include "../../utils/FixedSizeData.h" - -namespace blockstore { -namespace encrypted { - -//TODO Does EncryptionKey::GenerateRandom() use a PseudoRandomGenerator? Would be better to use real randomness. -using EncryptionKey = FixedSizeData; - -} -} - -#endif diff --git a/implementations/encrypted/ciphers/AES256_CFB.cpp b/implementations/encrypted/ciphers/AES256_CFB.cpp new file mode 100644 index 00000000..c7737b1b --- /dev/null +++ b/implementations/encrypted/ciphers/AES256_CFB.cpp @@ -0,0 +1,37 @@ +#include +#include "AES256_CFB.h" + +using CryptoPP::CFB_Mode; +using CryptoPP::AES; +using cpputils::Data; +using cpputils::FixedSizeData; + +namespace blockstore { +namespace encrypted { + +constexpr unsigned int AES256_CFB::IV_SIZE; + +Data AES256_CFB::encrypt(const byte *plaintext, unsigned int plaintextSize, const EncryptionKey &encKey) { + FixedSizeData iv = FixedSizeData::CreateRandom(); + auto encryption = CFB_Mode::Encryption(encKey.data(), encKey.BINARY_LENGTH, iv.data()); + Data ciphertext(ciphertextSize(plaintextSize)); + std::memcpy(ciphertext.data(), iv.data(), IV_SIZE); + encryption.ProcessData((byte*)ciphertext.data() + IV_SIZE, plaintext, plaintextSize); + return ciphertext; +} + +boost::optional AES256_CFB::decrypt(const byte *ciphertext, unsigned int ciphertextSize, const EncryptionKey &encKey) { + if (ciphertextSize < IV_SIZE) { + return boost::none; + } + + const byte *ciphertextIV = ciphertext; + const byte *ciphertextData = ciphertext + IV_SIZE; + auto decryption = CFB_Mode::Decryption((byte*)encKey.data(), encKey.BINARY_LENGTH, ciphertextIV); + Data plaintext(plaintextSize(ciphertextSize)); + decryption.ProcessData((byte*)plaintext.data(), ciphertextData, plaintext.size()); + return std::move(plaintext); +} + +} +} diff --git a/implementations/encrypted/ciphers/AES256_CFB.h b/implementations/encrypted/ciphers/AES256_CFB.h new file mode 100644 index 00000000..313842d6 --- /dev/null +++ b/implementations/encrypted/ciphers/AES256_CFB.h @@ -0,0 +1,39 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_ENCRYPTED_CIPHERS_AES256_CFB_H_ +#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_ENCRYPTED_CIPHERS_AES256_CFB_H_ + +#include +#include +#include +#include +#include "Cipher.h" + +namespace blockstore { +namespace encrypted { + +class AES256_CFB { +public: + BOOST_CONCEPT_ASSERT((CipherConcept)); + + using EncryptionKey = cpputils::FixedSizeData<32>; + static_assert(32 == CryptoPP::AES::MAX_KEYLENGTH, "If AES offered larger keys, we should offer a variant with it"); + + static constexpr unsigned int ciphertextSize(unsigned int plaintextBlockSize) { + return plaintextBlockSize + IV_SIZE; + } + + static constexpr unsigned int plaintextSize(unsigned int ciphertextBlockSize) { + return ciphertextBlockSize - IV_SIZE; + } + + static cpputils::Data encrypt(const byte *plaintext, unsigned int plaintextSize, const EncryptionKey &encKey); + static boost::optional decrypt(const byte *ciphertext, unsigned int ciphertextSize, const EncryptionKey &encKey); + +private: + static constexpr unsigned int IV_SIZE = CryptoPP::AES::BLOCKSIZE; +}; + +} +} + +#endif diff --git a/implementations/encrypted/ciphers/AES256_GCM.cpp b/implementations/encrypted/ciphers/AES256_GCM.cpp new file mode 100644 index 00000000..4ccf400a --- /dev/null +++ b/implementations/encrypted/ciphers/AES256_GCM.cpp @@ -0,0 +1,61 @@ +#include +#include "AES256_GCM.h" + +using CryptoPP::GCM; +using CryptoPP::AES; +using CryptoPP::AuthenticatedEncryptionFilter; +using CryptoPP::AuthenticatedDecryptionFilter; +using CryptoPP::ArraySource; +using CryptoPP::ArraySink; +using CryptoPP::GCM_64K_Tables; +using CryptoPP::HashVerificationFilter; +using cpputils::Data; +using cpputils::FixedSizeData; + +namespace blockstore { +namespace encrypted { + +constexpr unsigned int AES256_GCM::IV_SIZE; + +Data AES256_GCM::encrypt(const byte *plaintext, unsigned int plaintextSize, const EncryptionKey &encKey) { + FixedSizeData iv = FixedSizeData::CreateRandom(); + GCM::Encryption encryption; + encryption.SetKeyWithIV(encKey.data(), encKey.BINARY_LENGTH, iv.data(), IV_SIZE); + Data ciphertext(ciphertextSize(plaintextSize)); + + std::memcpy(ciphertext.data(), iv.data(), IV_SIZE); + ArraySource(plaintext, plaintextSize, true, + new AuthenticatedEncryptionFilter(encryption, + new ArraySink((byte*)ciphertext.data() + IV_SIZE, ciphertext.size() - IV_SIZE), + false, TAG_SIZE + ) + ); + return ciphertext; +} + +boost::optional AES256_GCM::decrypt(const byte *ciphertext, unsigned int ciphertextSize, const EncryptionKey &encKey) { + if (ciphertextSize < IV_SIZE + TAG_SIZE) { + return boost::none; + } + + const byte *ciphertextIV = ciphertext; + const byte *ciphertextData = ciphertext + IV_SIZE; + GCM::Decryption decryption; + decryption.SetKeyWithIV((byte*)encKey.data(), encKey.BINARY_LENGTH, ciphertextIV, IV_SIZE); + Data plaintext(plaintextSize(ciphertextSize)); + + try { + ArraySource((byte*)ciphertextData, ciphertextSize - IV_SIZE, true, + new AuthenticatedDecryptionFilter(decryption, + new ArraySink((byte*)plaintext.data(), plaintext.size()), + AuthenticatedDecryptionFilter::DEFAULT_FLAGS, TAG_SIZE + ) + ); + return std::move(plaintext); + } catch (const HashVerificationFilter::HashVerificationFailed &e) { + return boost::none; + } +} + +} +} diff --git a/implementations/encrypted/ciphers/AES256_GCM.h b/implementations/encrypted/ciphers/AES256_GCM.h new file mode 100644 index 00000000..014ef1f0 --- /dev/null +++ b/implementations/encrypted/ciphers/AES256_GCM.h @@ -0,0 +1,39 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_ENCRYPTED_CIPHERS_AES256_GCM_H_ +#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_ENCRYPTED_CIPHERS_AES256_GCM_H_ + +#include +#include +#include +#include "Cipher.h" + +namespace blockstore { +namespace encrypted { + +class AES256_GCM { +public: + BOOST_CONCEPT_ASSERT((CipherConcept)); + + using EncryptionKey = cpputils::FixedSizeData<32>; + static_assert(32 == CryptoPP::AES::MAX_KEYLENGTH, "If AES offered larger keys, we should offer a variant with it"); + + static constexpr unsigned int ciphertextSize(unsigned int plaintextBlockSize) { + return plaintextBlockSize + IV_SIZE + TAG_SIZE; + } + + static constexpr unsigned int plaintextSize(unsigned int ciphertextBlockSize) { + return ciphertextBlockSize - IV_SIZE - TAG_SIZE; + } + + static cpputils::Data encrypt(const byte *plaintext, unsigned int plaintextSize, const EncryptionKey &encKey); + static boost::optional decrypt(const byte *ciphertext, unsigned int ciphertextSize, const EncryptionKey &encKey); + +private: + static constexpr unsigned int IV_SIZE = CryptoPP::AES::BLOCKSIZE; + static constexpr unsigned int TAG_SIZE = 16; +}; + +} +} + +#endif diff --git a/implementations/encrypted/ciphers/Cipher.h b/implementations/encrypted/ciphers/Cipher.h new file mode 100644 index 00000000..5195bcb4 --- /dev/null +++ b/implementations/encrypted/ciphers/Cipher.h @@ -0,0 +1,33 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_ENCRYPTED_CIPHERS_CIPHER_H_ +#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_ENCRYPTED_CIPHERS_CIPHER_H_ + +#include +#include +#include + +namespace blockstore { +namespace encrypted { + +template +struct CipherConcept { +public: + BOOST_CONCEPT_USAGE(CipherConcept) { + same_type(UINT32_C(0), X::ciphertextSize(UINT32_C(5))); + same_type(UINT32_C(0), X::plaintextSize(UINT32_C(5))); + typename X::EncryptionKey key = X::EncryptionKey::CreateRandom(); + same_type(cpputils::Data(0), X::encrypt((uint8_t*)nullptr, UINT32_C(0), key)); + same_type(boost::optional(cpputils::Data(0)), X::decrypt((uint8_t*)nullptr, UINT32_C(0), key)); + } + +private: + // Type deduction will fail unless the arguments have the same type. + template void same_type(T const&, T const&); +}; + +} +} + + + +#endif diff --git a/implementations/inmemory/InMemoryBlock.cpp b/implementations/inmemory/InMemoryBlock.cpp index 69d8a142..14d27df9 100644 --- a/implementations/inmemory/InMemoryBlock.cpp +++ b/implementations/inmemory/InMemoryBlock.cpp @@ -9,6 +9,7 @@ using std::ostream; using std::ifstream; using std::ofstream; using std::ios; +using cpputils::Data; namespace blockstore { namespace inmemory { diff --git a/implementations/inmemory/InMemoryBlock.h b/implementations/inmemory/InMemoryBlock.h index 4efb1553..bab8427c 100644 --- a/implementations/inmemory/InMemoryBlock.h +++ b/implementations/inmemory/InMemoryBlock.h @@ -3,7 +3,7 @@ #define BLOCKSTORE_IMPLEMENTATIONS_INMEMORY_INMEMORYBLOCK_H_ #include "../../interface/Block.h" -#include "../../utils/Data.h" +#include namespace blockstore { namespace inmemory { @@ -11,7 +11,7 @@ class InMemoryBlockStore; class InMemoryBlock: public Block { public: - InMemoryBlock(const Key &key, Data size); + InMemoryBlock(const Key &key, cpputils::Data size); InMemoryBlock(const InMemoryBlock &rhs); virtual ~InMemoryBlock(); @@ -23,7 +23,7 @@ public: size_t size() const override; private: - std::shared_ptr _data; + std::shared_ptr _data; }; } diff --git a/implementations/inmemory/InMemoryBlockStore.cpp b/implementations/inmemory/InMemoryBlockStore.cpp index 3692b965..b4446349 100644 --- a/implementations/inmemory/InMemoryBlockStore.cpp +++ b/implementations/inmemory/InMemoryBlockStore.cpp @@ -9,6 +9,7 @@ using std::mutex; using std::lock_guard; using std::piecewise_construct; using std::make_tuple; +using cpputils::Data; namespace blockstore { namespace inmemory { diff --git a/implementations/inmemory/InMemoryBlockStore.h b/implementations/inmemory/InMemoryBlockStore.h index 3faea85f..b9ec5d25 100644 --- a/implementations/inmemory/InMemoryBlockStore.h +++ b/implementations/inmemory/InMemoryBlockStore.h @@ -16,7 +16,7 @@ class InMemoryBlockStore: public BlockStoreWithRandomKeys { public: InMemoryBlockStore(); - std::unique_ptr tryCreate(const Key &key, Data data) override; + std::unique_ptr tryCreate(const Key &key, cpputils::Data data) override; std::unique_ptr load(const Key &key) override; void remove(std::unique_ptr block) override; uint64_t numBlocks() const override; diff --git a/implementations/ondisk/FileAlreadyExistsException.cpp b/implementations/ondisk/FileAlreadyExistsException.cpp deleted file mode 100644 index 904b138c..00000000 --- a/implementations/ondisk/FileAlreadyExistsException.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "FileAlreadyExistsException.h" - -namespace bf = boost::filesystem; - -using std::runtime_error; -using std::string; - -namespace blockstore { -namespace ondisk { - -FileAlreadyExistsException::FileAlreadyExistsException(const bf::path &filepath) -: runtime_error(string("The file ")+filepath.c_str()+" already exists") { -} - -FileAlreadyExistsException::~FileAlreadyExistsException() { -} - -} -} diff --git a/implementations/ondisk/FileAlreadyExistsException.h b/implementations/ondisk/FileAlreadyExistsException.h deleted file mode 100644 index 2a23fdd1..00000000 --- a/implementations/ondisk/FileAlreadyExistsException.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#ifndef BLOCKSTORE_IMPLEMENTATIONS_ONDISK_FILEALREADYEXISTSEXCEPTION_H_ -#define BLOCKSTORE_IMPLEMENTATIONS_ONDISK_FILEALREADYEXISTSEXCEPTION_H_ - -#include - -#include - -namespace blockstore { -namespace ondisk { - -class FileAlreadyExistsException: public std::runtime_error { -public: - FileAlreadyExistsException(const boost::filesystem::path &filepath); - virtual ~FileAlreadyExistsException(); -}; - -} -} - -#endif diff --git a/implementations/ondisk/OnDiskBlock.cpp b/implementations/ondisk/OnDiskBlock.cpp index 4c0f25ef..4139a999 100644 --- a/implementations/ondisk/OnDiskBlock.cpp +++ b/implementations/ondisk/OnDiskBlock.cpp @@ -1,7 +1,6 @@ #include #include #include -#include "FileAlreadyExistsException.h" #include "OnDiskBlock.h" #include "OnDiskBlockStore.h" #include "../../utils/FileDoesntExistException.h" @@ -13,6 +12,7 @@ using std::ostream; using std::ifstream; using std::ofstream; using std::ios; +using cpputils::Data; namespace bf = boost::filesystem; @@ -47,11 +47,15 @@ unique_ptr OnDiskBlock::LoadFromDisk(const bf::path &rootdir, const //If it isn't a file, Data::LoadFromFile() would usually also crash. We still need this extra check //upfront, because Data::LoadFromFile() doesn't crash if we give it the path of a directory //instead the path of a file. + //TODO Data::LoadFromFile now returns boost::optional. Do we then still need this? if(!bf::is_regular_file(filepath)) { return nullptr; } - Data data = Data::LoadFromFile(filepath); - return unique_ptr(new OnDiskBlock(key, filepath, std::move(data))); + boost::optional data = Data::LoadFromFile(filepath); + if (!data) { + return nullptr; + } + return unique_ptr(new OnDiskBlock(key, filepath, std::move(*data))); } catch (const FileDoesntExistException &e) { return nullptr; } diff --git a/implementations/ondisk/OnDiskBlock.h b/implementations/ondisk/OnDiskBlock.h index a139249a..3acea481 100644 --- a/implementations/ondisk/OnDiskBlock.h +++ b/implementations/ondisk/OnDiskBlock.h @@ -4,7 +4,7 @@ #include #include "../../interface/Block.h" -#include "../../utils/Data.h" +#include #include #include "messmer/cpp-utils/macros.h" @@ -18,7 +18,7 @@ public: virtual ~OnDiskBlock(); static std::unique_ptr LoadFromDisk(const boost::filesystem::path &rootdir, const Key &key); - static std::unique_ptr CreateOnDisk(const boost::filesystem::path &rootdir, const Key &key, Data data); + static std::unique_ptr CreateOnDisk(const boost::filesystem::path &rootdir, const Key &key, cpputils::Data data); static void RemoveFromDisk(const boost::filesystem::path &rootdir, const Key &key); const void *data() const override; @@ -30,10 +30,10 @@ public: private: const boost::filesystem::path _filepath; - Data _data; + cpputils::Data _data; bool _dataChanged; - OnDiskBlock(const Key &key, const boost::filesystem::path &filepath, Data data); + OnDiskBlock(const Key &key, const boost::filesystem::path &filepath, cpputils::Data data); void _fillDataWithZeroes(); void _storeToDisk() const; diff --git a/implementations/ondisk/OnDiskBlockStore.cpp b/implementations/ondisk/OnDiskBlockStore.cpp index 4757807b..3335a047 100644 --- a/implementations/ondisk/OnDiskBlockStore.cpp +++ b/implementations/ondisk/OnDiskBlockStore.cpp @@ -4,6 +4,7 @@ using std::unique_ptr; using std::make_unique; using std::string; +using cpputils::Data; namespace bf = boost::filesystem; diff --git a/implementations/ondisk/OnDiskBlockStore.h b/implementations/ondisk/OnDiskBlockStore.h index 3a668d4d..af7f72fc 100644 --- a/implementations/ondisk/OnDiskBlockStore.h +++ b/implementations/ondisk/OnDiskBlockStore.h @@ -12,9 +12,9 @@ namespace ondisk { class OnDiskBlockStore: public BlockStoreWithRandomKeys { public: - OnDiskBlockStore(const boost::filesystem::path &rootdir); + explicit OnDiskBlockStore(const boost::filesystem::path &rootdir); - std::unique_ptr tryCreate(const Key &key, Data data) override; + std::unique_ptr tryCreate(const Key &key, cpputils::Data data) override; std::unique_ptr load(const Key &key) override; void remove(std::unique_ptr block) override; uint64_t numBlocks() const override; diff --git a/implementations/parallelaccess/BlockRef.h b/implementations/parallelaccess/BlockRef.h index a379ef7f..588b3c6e 100644 --- a/implementations/parallelaccess/BlockRef.h +++ b/implementations/parallelaccess/BlockRef.h @@ -14,7 +14,7 @@ class ParallelAccessBlockStore; class BlockRef: public Block, public parallelaccessstore::ParallelAccessStore::ResourceRefBase { public: //TODO Unneccessarily storing Key twice here (in parent class and in _baseBlock). - BlockRef(Block *baseBlock): Block(baseBlock->key()), _baseBlock(baseBlock) {} + explicit BlockRef(Block *baseBlock): Block(baseBlock->key()), _baseBlock(baseBlock) {} const void *data() const override { return _baseBlock->data(); diff --git a/implementations/parallelaccess/ParallelAccessBlockStore.cpp b/implementations/parallelaccess/ParallelAccessBlockStore.cpp index 2f59bb79..19fdb343 100644 --- a/implementations/parallelaccess/ParallelAccessBlockStore.cpp +++ b/implementations/parallelaccess/ParallelAccessBlockStore.cpp @@ -24,7 +24,7 @@ Key ParallelAccessBlockStore::createKey() { return _baseBlockStore->createKey(); } -unique_ptr ParallelAccessBlockStore::tryCreate(const Key &key, Data data) { +unique_ptr ParallelAccessBlockStore::tryCreate(const Key &key, cpputils::Data data) { auto block = _baseBlockStore->tryCreate(key, std::move(data)); if (block.get() == nullptr) { //TODO Test this code branch diff --git a/implementations/parallelaccess/ParallelAccessBlockStore.h b/implementations/parallelaccess/ParallelAccessBlockStore.h index 7c7c7adf..2f896b09 100644 --- a/implementations/parallelaccess/ParallelAccessBlockStore.h +++ b/implementations/parallelaccess/ParallelAccessBlockStore.h @@ -12,10 +12,10 @@ namespace parallelaccess { //TODO Check that this blockstore allows parallel destructing of blocks (otherwise we won't encrypt blocks in parallel) class ParallelAccessBlockStore: public BlockStore { public: - ParallelAccessBlockStore(std::unique_ptr baseBlockStore); + explicit ParallelAccessBlockStore(std::unique_ptr baseBlockStore); Key createKey() override; - std::unique_ptr tryCreate(const Key &key, Data data) override; + std::unique_ptr tryCreate(const Key &key, cpputils::Data data) override; std::unique_ptr load(const Key &key) override; void remove(std::unique_ptr block) override; uint64_t numBlocks() const override; diff --git a/implementations/parallelaccess/ParallelAccessBlockStoreAdapter.h b/implementations/parallelaccess/ParallelAccessBlockStoreAdapter.h index 56c9d7de..d4784f16 100644 --- a/implementations/parallelaccess/ParallelAccessBlockStoreAdapter.h +++ b/implementations/parallelaccess/ParallelAccessBlockStoreAdapter.h @@ -10,7 +10,7 @@ namespace parallelaccess { class ParallelAccessBlockStoreAdapter: public parallelaccessstore::ParallelAccessBaseStore { public: - ParallelAccessBlockStoreAdapter(BlockStore *baseBlockStore) + explicit ParallelAccessBlockStoreAdapter(BlockStore *baseBlockStore) :_baseBlockStore(std::move(baseBlockStore)) { } diff --git a/implementations/testfake/FakeBlock.cpp b/implementations/testfake/FakeBlock.cpp index adf8bcdf..b6505aa7 100644 --- a/implementations/testfake/FakeBlock.cpp +++ b/implementations/testfake/FakeBlock.cpp @@ -10,6 +10,7 @@ using std::ifstream; using std::ofstream; using std::ios; using std::string; +using cpputils::Data; namespace blockstore { namespace testfake { diff --git a/implementations/testfake/FakeBlock.h b/implementations/testfake/FakeBlock.h index 7f49f224..a0fc5c41 100644 --- a/implementations/testfake/FakeBlock.h +++ b/implementations/testfake/FakeBlock.h @@ -3,7 +3,7 @@ #define BLOCKSTORE_IMPLEMENTATIONS_INMEMORY_INMEMORYBLOCK_H_ #include "../../interface/Block.h" -#include "../../utils/Data.h" +#include #include "messmer/cpp-utils/macros.h" @@ -13,7 +13,7 @@ class FakeBlockStore; class FakeBlock: public Block { public: - FakeBlock(FakeBlockStore *store, const Key &key, std::shared_ptr data, bool dirty); + FakeBlock(FakeBlockStore *store, const Key &key, std::shared_ptr data, bool dirty); virtual ~FakeBlock(); const void *data() const override; @@ -25,7 +25,7 @@ public: private: FakeBlockStore *_store; - std::shared_ptr _data; + std::shared_ptr _data; bool _dataChanged; DISALLOW_COPY_AND_ASSIGN(FakeBlock); diff --git a/implementations/testfake/FakeBlockStore.cpp b/implementations/testfake/FakeBlockStore.cpp index 50dce405..eb1e09f4 100644 --- a/implementations/testfake/FakeBlockStore.cpp +++ b/implementations/testfake/FakeBlockStore.cpp @@ -7,6 +7,7 @@ using std::make_shared; using std::string; using std::mutex; using std::lock_guard; +using cpputils::Data; namespace blockstore { namespace testfake { diff --git a/implementations/testfake/FakeBlockStore.h b/implementations/testfake/FakeBlockStore.h index cec32b56..9d291cec 100644 --- a/implementations/testfake/FakeBlockStore.h +++ b/implementations/testfake/FakeBlockStore.h @@ -3,7 +3,7 @@ #define BLOCKSTORE_IMPLEMENTATIONS_INMEMORY_INMEMORYBLOCKSTORE_H_ #include "../../interface/helpers/BlockStoreWithRandomKeys.h" -#include "../../utils/Data.h" +#include #include "messmer/cpp-utils/macros.h" #include @@ -31,24 +31,24 @@ class FakeBlockStore: public BlockStoreWithRandomKeys { public: FakeBlockStore(); - std::unique_ptr tryCreate(const Key &key, Data data) override; + std::unique_ptr tryCreate(const Key &key, cpputils::Data data) override; std::unique_ptr load(const Key &key) override; void remove(std::unique_ptr block) override; uint64_t numBlocks() const override; - void updateData(const Key &key, const Data &data); + void updateData(const Key &key, const cpputils::Data &data); private: - std::map _blocks; + std::map _blocks; //This vector keeps a handle of the data regions for all created FakeBlock objects. //This way, it is ensured that no two created FakeBlock objects will work on the //same data region. Without this, it could happen that a test case creates a FakeBlock, //destructs it, creates another one, and the new one gets the same memory region. //We want to avoid this for the reasons mentioned above (overflow data). - std::vector> _used_dataregions_for_blocks; + std::vector> _used_dataregions_for_blocks; - std::unique_ptr makeFakeBlockFromData(const Key &key, const Data &data, bool dirty); + std::unique_ptr makeFakeBlockFromData(const Key &key, const cpputils::Data &data, bool dirty); DISALLOW_COPY_AND_ASSIGN(FakeBlockStore); }; diff --git a/interface/BlockStore.h b/interface/BlockStore.h index 8ea14ed3..9bb9b14e 100644 --- a/interface/BlockStore.h +++ b/interface/BlockStore.h @@ -5,7 +5,7 @@ #include "Block.h" #include #include -#include "../utils/Data.h" +#include namespace blockstore { @@ -15,20 +15,20 @@ public: virtual Key createKey() = 0; //Returns nullptr if key already exists - virtual std::unique_ptr tryCreate(const Key &key, Data data) = 0; + virtual std::unique_ptr tryCreate(const Key &key, cpputils::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 load(const Key &key) = 0; virtual void remove(std::unique_ptr block) = 0; virtual uint64_t numBlocks() const = 0; - std::unique_ptr create(const Data &data) { - std::unique_ptr block(nullptr); - while(block.get() == nullptr) { - //TODO Copy necessary? - block = tryCreate(createKey(), data.copy()); - } - return block; + std::unique_ptr create(const cpputils::Data &data) { + std::unique_ptr block(nullptr); + while(block.get() == nullptr) { + //TODO Copy necessary? + block = tryCreate(createKey(), data.copy()); + } + return block; } }; diff --git a/test/implementations/caching/cache/CacheTest_MoveConstructor.cpp b/test/implementations/caching/cache/CacheTest_MoveConstructor.cpp new file mode 100644 index 00000000..5efba4a8 --- /dev/null +++ b/test/implementations/caching/cache/CacheTest_MoveConstructor.cpp @@ -0,0 +1,36 @@ +#include +#include +#include "../../../../implementations/caching/cache/Cache.h" +#include "testutils/MinimalKeyType.h" +#include "testutils/CopyableMovableValueType.h" + +using namespace blockstore::caching; + +using ::testing::Test; +using std::unique_ptr; +using std::make_unique; + +//Test that Cache uses a move constructor for Value if possible +class CacheTest_MoveConstructor: public Test { +public: + CacheTest_MoveConstructor() { + CopyableMovableValueType::numCopyConstructorCalled = 0; + cache = make_unique>(); + } + unique_ptr> cache; +}; + +TEST_F(CacheTest_MoveConstructor, MoveIntoCache) { + cache->push(MinimalKeyType::create(0), CopyableMovableValueType(2)); + CopyableMovableValueType val = cache->pop(MinimalKeyType::create(0)).value(); + val.value(); //Access it to avoid the compiler optimizing the assignment away + EXPECT_EQ(0, CopyableMovableValueType::numCopyConstructorCalled); +} + +TEST_F(CacheTest_MoveConstructor, CopyIntoCache) { + CopyableMovableValueType value(2); + cache->push(MinimalKeyType::create(0), value); + CopyableMovableValueType val = cache->pop(MinimalKeyType::create(0)).value(); + val.value(); //Access it to avoid the compiler optimizing the assignment away + EXPECT_EQ(1, CopyableMovableValueType::numCopyConstructorCalled); +} diff --git a/test/implementations/caching/cache/CacheTest_PushAndPop.cpp b/test/implementations/caching/cache/CacheTest_PushAndPop.cpp new file mode 100644 index 00000000..c64cc7b9 --- /dev/null +++ b/test/implementations/caching/cache/CacheTest_PushAndPop.cpp @@ -0,0 +1,133 @@ +#include "testutils/CacheTest.h" + +#include "../../../../implementations/caching/cache/Cache.h" +#include "testutils/MinimalKeyType.h" +#include "testutils/MinimalValueType.h" + +using ::testing::Test; + +using namespace blockstore::caching; + +class CacheTest_PushAndPop: public CacheTest {}; + +TEST_F(CacheTest_PushAndPop, PopNonExistingEntry_EmptyCache) { + EXPECT_EQ(boost::none, pop(10)); +} + +TEST_F(CacheTest_PushAndPop, PopNonExistingEntry_NonEmptyCache) { + push(9, 10); + EXPECT_EQ(boost::none, pop(10)); +} + +TEST_F(CacheTest_PushAndPop, PopNonExistingEntry_FullCache) { + //Add a lot of even numbered keys + for (unsigned int i = 0; i < Cache::MAX_ENTRIES; ++i) { + push(2*i, 2*i); + } + //Request an odd numbered key + EXPECT_EQ(boost::none, pop(9)); +} + +TEST_F(CacheTest_PushAndPop, OneEntry) { + push(10, 20); + EXPECT_EQ(20, pop(10).value()); +} + +TEST_F(CacheTest_PushAndPop, MultipleEntries) { + push(10, 20); + push(20, 30); + push(30, 40); + EXPECT_EQ(30, pop(20).value()); + EXPECT_EQ(20, pop(10).value()); + EXPECT_EQ(40, pop(30).value()); +} + +TEST_F(CacheTest_PushAndPop, FullCache) { + for(unsigned int i = 0; i < Cache::MAX_ENTRIES; ++i) { + push(i, 2*i); + } + for(unsigned int i = 0; i < Cache::MAX_ENTRIES; ++i) { + EXPECT_EQ(2*i, pop(i).value()); + } +} + +TEST_F(CacheTest_PushAndPop, FullCache_PushNonOrdered_PopOrdered) { + for(unsigned int i = 1; i < Cache::MAX_ENTRIES; i += 2) { + push(i, 2*i); + } + for(unsigned int i = 0; i < Cache::MAX_ENTRIES; i += 2) { + push(i, 2*i); + } + for(unsigned int i = 0; i < Cache::MAX_ENTRIES; ++i) { + EXPECT_EQ(2*i, pop(i).value()); + } +} + +TEST_F(CacheTest_PushAndPop, FullCache_PushOrdered_PopNonOrdered) { + for(unsigned int i = 0; i < Cache::MAX_ENTRIES; ++i) { + push(i, 2*i); + } + for(unsigned int i = 1; i < Cache::MAX_ENTRIES; i += 2) { + EXPECT_EQ(2*i, pop(i).value()); + } + for(unsigned int i = 0; i < Cache::MAX_ENTRIES; i += 2) { + EXPECT_EQ(2*i, pop(i).value()); + } +} + +int roundDownToEven(int number) { + if (number % 2 == 0) { + return number; + } else { + return number - 1; + } +} + +int roundDownToOdd(int number) { + if (number % 2 != 0) { + return number; + } else { + return number - 1; + } +} + +TEST_F(CacheTest_PushAndPop, FullCache_PushNonOrdered_PopNonOrdered) { + for(int i = roundDownToEven(Cache::MAX_ENTRIES - 1); i >= 0; i -= 2) { + push(i, 2*i); + } + for(unsigned int i = 1; i < Cache::MAX_ENTRIES; i += 2) { + push(i, 2*i); + } + for(int i = roundDownToOdd(Cache::MAX_ENTRIES-1); i >= 0; i -= 2) { + EXPECT_EQ(2*i, pop(i).value()); + } + for(unsigned int i = 0; i < Cache::MAX_ENTRIES; i += 2) { + EXPECT_EQ(2*i, pop(i).value()); + } +} + +TEST_F(CacheTest_PushAndPop, MoreThanFullCache) { + for(unsigned int i = 0; i < Cache::MAX_ENTRIES + 2; ++i) { + push(i, 2*i); + } + //Check that the oldest two elements got deleted automatically + EXPECT_EQ(boost::none, pop(0)); + EXPECT_EQ(boost::none, pop(1)); + //Check the other elements are still there + for(unsigned int i = 2; i < Cache::MAX_ENTRIES + 2; ++i) { + EXPECT_EQ(2*i, pop(i).value()); + } +} + +TEST_F(CacheTest_PushAndPop, AfterTimeout) { + constexpr double TIMEOUT1_SEC = Cache::MAX_LIFETIME_SEC * 3/4; + constexpr double TIMEOUT2_SEC = Cache::PURGE_LIFETIME_SEC * 3/4; + static_assert(TIMEOUT1_SEC + TIMEOUT2_SEC > Cache::MAX_LIFETIME_SEC, "Ensure that our chosen timeouts push the first entry out of the cache"); + + push(10, 20); + boost::this_thread::sleep_for(boost::chrono::milliseconds(static_cast(1000 * TIMEOUT1_SEC))); + push(20, 30); + boost::this_thread::sleep_for(boost::chrono::milliseconds(static_cast(1000 * TIMEOUT2_SEC))); + EXPECT_EQ(boost::none, pop(10)); + EXPECT_EQ(30, pop(20).value()); +} diff --git a/test/implementations/caching/cache/PeriodicTaskTest.cpp b/test/implementations/caching/cache/PeriodicTaskTest.cpp new file mode 100644 index 00000000..d49492ad --- /dev/null +++ b/test/implementations/caching/cache/PeriodicTaskTest.cpp @@ -0,0 +1,63 @@ +#include + +#include "../../../../implementations/caching/cache/PeriodicTask.h" + +#include +#include +#include + +using ::testing::Test; +using std::mutex; +using std::unique_lock; +using std::condition_variable; + +using namespace blockstore::caching; + +class AtomicCounter { +public: + AtomicCounter(int count): _mutex(), _cv(), _counter(count) {} + + void decrease() { + unique_lock lock(_mutex); + --_counter; + _cv.notify_all(); + } + + void waitForZero() { + unique_lock lock(_mutex); + _cv.wait(lock, [this] () {return _counter <= 0;}); + } +private: + mutex _mutex; + condition_variable _cv; + int _counter; +}; + +class PeriodicTaskTest: public Test { +}; + +TEST_F(PeriodicTaskTest, DoesntDeadlockInDestructorWhenDestructedImmediately) { + PeriodicTask task([](){}, 1); +} + +TEST_F(PeriodicTaskTest, CallsCallbackAtLeast10Times) { + AtomicCounter counter(10); + + PeriodicTask task([&counter](){ + counter.decrease(); + }, 0.001); + + counter.waitForZero(); +} + +TEST_F(PeriodicTaskTest, DoesntCallCallbackAfterDestruction) { + std::atomic callCount(0); + { + PeriodicTask task([&callCount](){ + callCount += 1; + }, 0.001); + } + int callCountDirectlyAfterDestruction = callCount; + boost::this_thread::sleep_for(boost::chrono::seconds(1)); + EXPECT_EQ(callCountDirectlyAfterDestruction, callCount); +} diff --git a/test/implementations/caching/cache/QueueMapTest_MemoryLeak.cpp b/test/implementations/caching/cache/QueueMapTest_MemoryLeak.cpp new file mode 100644 index 00000000..74012fc0 --- /dev/null +++ b/test/implementations/caching/cache/QueueMapTest_MemoryLeak.cpp @@ -0,0 +1,87 @@ +#include "testutils/QueueMapTest.h" + +// Tests that QueueMap calls destructors correctly. +// This is needed, because QueueMap does its own memory management. +class QueueMapTest_MemoryLeak: public QueueMapTest { +public: + void EXPECT_NUM_INSTANCES(int num) { + EXPECT_EQ(num, MinimalKeyType::instances); + EXPECT_EQ(num, MinimalValueType::instances); + } +}; + +TEST_F(QueueMapTest_MemoryLeak, Empty) { + EXPECT_NUM_INSTANCES(0); +} + +TEST_F(QueueMapTest_MemoryLeak, AfterPushingOne) { + push(2, 3); + EXPECT_NUM_INSTANCES(1); +} + +TEST_F(QueueMapTest_MemoryLeak, AfterPushingTwo) { + push(2, 3); + push(3, 4); + EXPECT_NUM_INSTANCES(2); +} + +TEST_F(QueueMapTest_MemoryLeak, AfterPushingTwoAndPoppingOldest) { + push(2, 3); + push(3, 4); + pop(); + EXPECT_NUM_INSTANCES(1); +} + +TEST_F(QueueMapTest_MemoryLeak, AfterPushingTwoAndPoppingFirst) { + push(2, 3); + push(3, 4); + pop(2); + EXPECT_NUM_INSTANCES(1); +} + +TEST_F(QueueMapTest_MemoryLeak, AfterPushingTwoAndPoppingLast) { + push(2, 3); + push(3, 4); + pop(3); + EXPECT_NUM_INSTANCES(1); +} + +TEST_F(QueueMapTest_MemoryLeak, AfterPushingOnePoppingOne) { + push(2, 3); + pop(); + EXPECT_NUM_INSTANCES(0); +} + +TEST_F(QueueMapTest_MemoryLeak, AfterPushingOnePoppingOnePerKey) { + push(2, 3); + pop(2); + EXPECT_NUM_INSTANCES(0); +} + +TEST_F(QueueMapTest_MemoryLeak, AfterPushingOnePoppingOnePushingOne) { + push(2, 3); + pop(); + push(3, 4); + EXPECT_NUM_INSTANCES(1); +} + +TEST_F(QueueMapTest_MemoryLeak, AfterPushingOnePoppingOnePerKeyPushingOne) { + push(2, 3); + pop(2); + push(3, 4); + EXPECT_NUM_INSTANCES(1); +} + +TEST_F(QueueMapTest_MemoryLeak, AfterPushingOnePoppingOnePushingSame) { + push(2, 3); + pop(); + push(2, 3); + EXPECT_NUM_INSTANCES(1); +} + +TEST_F(QueueMapTest_MemoryLeak, AfterPushingOnePoppingOnePerKeyPushingSame) { + push(2, 3); + pop(2); + push(2, 3); + EXPECT_NUM_INSTANCES(1); +} diff --git a/test/implementations/caching/cache/QueueMapTest_MoveConstructor.cpp b/test/implementations/caching/cache/QueueMapTest_MoveConstructor.cpp new file mode 100644 index 00000000..4a1f15db --- /dev/null +++ b/test/implementations/caching/cache/QueueMapTest_MoveConstructor.cpp @@ -0,0 +1,51 @@ +#include +#include +#include "../../../../implementations/caching/cache/QueueMap.h" +#include "testutils/MinimalKeyType.h" +#include "testutils/CopyableMovableValueType.h" + +using namespace blockstore::caching; + +using ::testing::Test; +using std::unique_ptr; +using std::make_unique; + +//Test that QueueMap uses a move constructor for Value if possible +class QueueMapTest_MoveConstructor: public Test { +public: + QueueMapTest_MoveConstructor() { + CopyableMovableValueType::numCopyConstructorCalled = 0; + map = make_unique>(); + } + unique_ptr> map; +}; + +TEST_F(QueueMapTest_MoveConstructor, PushingAndPopping_MoveIntoMap) { + map->push(MinimalKeyType::create(0), CopyableMovableValueType(2)); + CopyableMovableValueType val = map->pop().value(); + val.value(); //Access it to avoid the compiler optimizing the assignment away + EXPECT_EQ(0, CopyableMovableValueType::numCopyConstructorCalled); +} + +TEST_F(QueueMapTest_MoveConstructor, PushingAndPoppingPerKey_MoveIntoMap) { + map->push(MinimalKeyType::create(0), CopyableMovableValueType(2)); + CopyableMovableValueType val = map->pop(MinimalKeyType::create(0)).value(); + val.value(); //Access it to avoid the compiler optimizing the assignment away + EXPECT_EQ(0, CopyableMovableValueType::numCopyConstructorCalled); +} + +TEST_F(QueueMapTest_MoveConstructor, PushingAndPopping_CopyIntoMap) { + CopyableMovableValueType value(2); + map->push(MinimalKeyType::create(0), value); + CopyableMovableValueType val = map->pop().value(); + val.value(); //Access it to avoid the compiler optimizing the assignment away + EXPECT_EQ(1, CopyableMovableValueType::numCopyConstructorCalled); +} + +TEST_F(QueueMapTest_MoveConstructor, PushingAndPoppingPerKey_CopyIntoMap) { + CopyableMovableValueType value(2); + map->push(MinimalKeyType::create(0), value); + CopyableMovableValueType val = map->pop(MinimalKeyType::create(0)).value(); + val.value(); //Access it to avoid the compiler optimizing the assignment away + EXPECT_EQ(1, CopyableMovableValueType::numCopyConstructorCalled); +} diff --git a/test/implementations/caching/cache/QueueMapTest_Peek.cpp b/test/implementations/caching/cache/QueueMapTest_Peek.cpp new file mode 100644 index 00000000..ba3c7ead --- /dev/null +++ b/test/implementations/caching/cache/QueueMapTest_Peek.cpp @@ -0,0 +1,34 @@ +#include "testutils/QueueMapTest.h" +#include + +class QueueMapPeekTest: public QueueMapTest {}; + +TEST_F(QueueMapPeekTest, PoppingFromEmpty) { + EXPECT_EQ(boost::none, peek()); +} + +TEST_F(QueueMapPeekTest, PushingOne) { + push(3, 2); + EXPECT_EQ(2, peek().value()); +} + +TEST_F(QueueMapPeekTest, PushingTwo) { + push(2, 3); + push(3, 4); + EXPECT_EQ(3, peek().value()); + EXPECT_EQ(3, peek().value()); + EXPECT_EQ(3, pop().value()); + EXPECT_EQ(4, peek().value()); + EXPECT_EQ(4, peek().value()); + EXPECT_EQ(4, pop().value()); + EXPECT_EQ(boost::none, peek()); + EXPECT_EQ(boost::none, pop()); +} + +TEST_F(QueueMapPeekTest, AfterPushingTwoAndPoppingFirst) { + push(2, 3); + push(3, 4); + pop(2); + EXPECT_EQ(boost::none, pop(2)); + EXPECT_EQ(4, peek().value()); +} diff --git a/test/implementations/caching/cache/QueueMapTest_Size.cpp b/test/implementations/caching/cache/QueueMapTest_Size.cpp new file mode 100644 index 00000000..e4d72c9d --- /dev/null +++ b/test/implementations/caching/cache/QueueMapTest_Size.cpp @@ -0,0 +1,79 @@ +#include "testutils/QueueMapTest.h" + +class QueueMapTest_Size: public QueueMapTest {}; + +TEST_F(QueueMapTest_Size, Empty) { + EXPECT_EQ(0, size()); +} + +TEST_F(QueueMapTest_Size, AfterPushingOne) { + push(2, 3); + EXPECT_EQ(1, size()); +} + +TEST_F(QueueMapTest_Size, AfterPushingTwo) { + push(2, 3); + push(3, 4); + EXPECT_EQ(2, size()); +} + +TEST_F(QueueMapTest_Size, AfterPushingTwoAndPoppingOldest) { + push(2, 3); + push(3, 4); + pop(); + EXPECT_EQ(1, size()); +} + +TEST_F(QueueMapTest_Size, AfterPushingTwoAndPoppingFirst) { + push(2, 3); + push(3, 4); + pop(2); + EXPECT_EQ(1, size()); +} + +TEST_F(QueueMapTest_Size, AfterPushingTwoAndPoppingLast) { + push(2, 3); + push(3, 4); + pop(3); + EXPECT_EQ(1, size()); +} + +TEST_F(QueueMapTest_Size, AfterPushingOnePoppingOne) { + push(2, 3); + pop(); + EXPECT_EQ(0, size()); +} + +TEST_F(QueueMapTest_Size, AfterPushingOnePoppingOnePerKey) { + push(2, 3); + pop(2); + EXPECT_EQ(0, size()); +} + +TEST_F(QueueMapTest_Size, AfterPushingOnePoppingOnePushingOne) { + push(2, 3); + pop(); + push(3, 4); + EXPECT_EQ(1, size()); +} + +TEST_F(QueueMapTest_Size, AfterPushingOnePoppingOnePerKeyPushingOne) { + push(2, 3); + pop(2); + push(3, 4); + EXPECT_EQ(1, size()); +} + +TEST_F(QueueMapTest_Size, AfterPushingOnePoppingOnePushingSame) { + push(2, 3); + pop(); + push(2, 3); + EXPECT_EQ(1, size()); +} + +TEST_F(QueueMapTest_Size, AfterPushingOnePoppingOnePerKeyPushingSame) { + push(2, 3); + pop(2); + push(2, 3); + EXPECT_EQ(1, size()); +} diff --git a/test/implementations/caching/cache/QueueMapTest_Values.cpp b/test/implementations/caching/cache/QueueMapTest_Values.cpp new file mode 100644 index 00000000..1acd5069 --- /dev/null +++ b/test/implementations/caching/cache/QueueMapTest_Values.cpp @@ -0,0 +1,150 @@ +#include "testutils/QueueMapTest.h" + +class QueueMapTest_Values: public QueueMapTest {}; + +TEST_F(QueueMapTest_Values, PoppingFromEmpty) { + EXPECT_EQ(boost::none, pop()); +} + +TEST_F(QueueMapTest_Values, PoppingFromEmptyPerKey) { + EXPECT_EQ(boost::none, pop(2)); +} + +TEST_F(QueueMapTest_Values, PoppingNonexistingPerKey) { + push(3, 2); + EXPECT_EQ(boost::none, pop(2)); +} + +TEST_F(QueueMapTest_Values, PushingOne) { + push(3, 2); + EXPECT_EQ(2, pop(3).value()); + EXPECT_EQ(boost::none, pop()); +} + +TEST_F(QueueMapTest_Values, PushingTwo) { + push(2, 3); + push(3, 4); + EXPECT_EQ(3, pop().value()); + EXPECT_EQ(4, pop().value()); + EXPECT_EQ(boost::none, pop()); +} + +TEST_F(QueueMapTest_Values, AfterPushingTwoAndPoppingFirst) { + push(2, 3); + push(3, 4); + pop(2); + EXPECT_EQ(boost::none, pop(2)); + EXPECT_EQ(4, pop(3).value()); + EXPECT_EQ(boost::none, pop()); +} + +TEST_F(QueueMapTest_Values, AfterPushingTwoAndPoppingLast) { + push(2, 3); + push(3, 4); + pop(3); + EXPECT_EQ(boost::none, pop(3)); + EXPECT_EQ(3, pop(2).value()); + EXPECT_EQ(boost::none, pop()); +} + +TEST_F(QueueMapTest_Values, AfterPushingOnePoppingOne) { + push(2, 3); + pop(); + EXPECT_EQ(boost::none, pop()); + EXPECT_EQ(boost::none, pop(2)); +} + +TEST_F(QueueMapTest_Values, AfterPushingOnePoppingOnePerKey) { + push(2, 3); + pop(2); + EXPECT_EQ(boost::none, pop()); + EXPECT_EQ(boost::none, pop(2)); +} + +TEST_F(QueueMapTest_Values, AfterPushingOnePoppingOnePushingOne) { + push(2, 3); + pop(); + push(3, 4); + EXPECT_EQ(boost::none, pop(2)); + EXPECT_EQ(4, pop(3).value()); + EXPECT_EQ(boost::none, pop()); +} + +TEST_F(QueueMapTest_Values, AfterPushingOnePoppingOnePerKeyPushingOne) { + push(2, 3); + pop(2); + push(3, 4); + EXPECT_EQ(boost::none, pop(2)); + EXPECT_EQ(4, pop(3).value()); + EXPECT_EQ(boost::none, pop()); +} + +TEST_F(QueueMapTest_Values, PushingSomePoppingMiddlePerKey) { + push(1, 2); + push(2, 3); + push(3, 4); + push(4, 5); + push(5, 6); + EXPECT_EQ(3, pop(2).value()); + EXPECT_EQ(5, pop(4).value()); + EXPECT_EQ(2, pop().value()); + EXPECT_EQ(4, pop().value()); + EXPECT_EQ(6, pop().value()); + EXPECT_EQ(boost::none, pop()); +} + +TEST_F(QueueMapTest_Values, PushingSomePoppingFirstPerKey) { + push(1, 2); + push(2, 3); + push(3, 4); + push(4, 5); + push(5, 6); + EXPECT_EQ(2, pop(1).value()); + EXPECT_EQ(3, pop(2).value()); + EXPECT_EQ(4, pop().value()); + EXPECT_EQ(5, pop().value()); + EXPECT_EQ(6, pop().value()); + EXPECT_EQ(boost::none, pop()); +} + +TEST_F(QueueMapTest_Values, PushingSomePoppingLastPerKey) { + push(1, 2); + push(2, 3); + push(3, 4); + push(4, 5); + push(5, 6); + EXPECT_EQ(6, pop(5).value()); + EXPECT_EQ(5, pop(4).value()); + EXPECT_EQ(2, pop().value()); + EXPECT_EQ(3, pop().value()); + EXPECT_EQ(4, pop().value()); + EXPECT_EQ(boost::none, pop()); +} + +//This test forces the underlying datastructure (std::map or std::unordered_map) to grow and reallocate memory. +//So it tests, that QueueMap still works after reallocating memory. +TEST_F(QueueMapTest_Values, ManyValues) { + //Push 1 million entries + for (int i = 0; i < 1000000; ++i) { + push(i, 2*i); + } + //pop every other one by key + for (int i = 0; i < 1000000; i += 2) { + EXPECT_EQ(2*i, pop(i).value()); + } + //pop the rest in queue order + for (int i = 1; i < 1000000; i += 2) { + EXPECT_EQ(2*i, peek().value()); + EXPECT_EQ(2*i, pop().value()); + } + EXPECT_EQ(0, size()); + EXPECT_EQ(boost::none, pop()); + EXPECT_EQ(boost::none, peek()); +} + +TEST_F(QueueMapTest_Values, PushAlreadyExistingValue) { + push(2, 3); + EXPECT_ANY_THROW( + push(2, 4); + ); +} diff --git a/test/implementations/caching/cache/testutils/CacheTest.cpp b/test/implementations/caching/cache/testutils/CacheTest.cpp new file mode 100644 index 00000000..b93bbccb --- /dev/null +++ b/test/implementations/caching/cache/testutils/CacheTest.cpp @@ -0,0 +1,13 @@ +#include "CacheTest.h" + +void CacheTest::push(int key, int value) { + return _cache.push(MinimalKeyType::create(key), MinimalValueType::create(value)); +} + +boost::optional CacheTest::pop(int key) { + boost::optional entry = _cache.pop(MinimalKeyType::create(key)); + if (!entry) { + return boost::none; + } + return entry->value(); +} diff --git a/test/implementations/caching/cache/testutils/CacheTest.h b/test/implementations/caching/cache/testutils/CacheTest.h new file mode 100644 index 00000000..61769936 --- /dev/null +++ b/test/implementations/caching/cache/testutils/CacheTest.h @@ -0,0 +1,25 @@ +#pragma once +#ifndef BLOCKS_MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_CACHING_CACHE_TESTUTILS_QUEUEMAPTEST_H_ +#define BLOCKS_MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_CACHING_CACHE_TESTUTILS_QUEUEMAPTEST_H_ + +#include +#include "../../../../../implementations/caching/cache/Cache.h" +#include "MinimalKeyType.h" +#include "MinimalValueType.h" +#include + +// This class is a parent class for tests on QueueMap. +// It offers functions to work with a QueueMap test object which is built using types having only the minimal type requirements. +// Furthermore, the class checks that there are no memory leaks left after destructing the QueueMap (by counting leftover instances of Keys/Values). +class CacheTest: public ::testing::Test { +public: + void push(int key, int value); + boost::optional pop(int key); + + using Cache = blockstore::caching::Cache; + +private: + Cache _cache; +}; + +#endif diff --git a/test/implementations/caching/cache/testutils/CopyableMovableValueType.cpp b/test/implementations/caching/cache/testutils/CopyableMovableValueType.cpp new file mode 100644 index 00000000..cf23bd88 --- /dev/null +++ b/test/implementations/caching/cache/testutils/CopyableMovableValueType.cpp @@ -0,0 +1,3 @@ +#include "CopyableMovableValueType.h" + +int CopyableMovableValueType::numCopyConstructorCalled = 0; diff --git a/test/implementations/caching/cache/testutils/CopyableMovableValueType.h b/test/implementations/caching/cache/testutils/CopyableMovableValueType.h new file mode 100644 index 00000000..c3201423 --- /dev/null +++ b/test/implementations/caching/cache/testutils/CopyableMovableValueType.h @@ -0,0 +1,23 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_CACHING_CACHE_TESTUTILS_COPYABLEMOVABLEVALUETYPE_H_ +#define MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_CACHING_CACHE_TESTUTILS_COPYABLEMOVABLEVALUETYPE_H_ + +class CopyableMovableValueType { +public: + static int numCopyConstructorCalled; + CopyableMovableValueType(int value): _value(value) {} + CopyableMovableValueType(const CopyableMovableValueType &rhs): CopyableMovableValueType(rhs._value) { + ++numCopyConstructorCalled; + } + CopyableMovableValueType(CopyableMovableValueType &&rhs): CopyableMovableValueType(rhs._value) { + //Don't increase numCopyConstructorCalled + } + int value() const { + return _value; + } +private: + int _value; +}; + + +#endif diff --git a/test/implementations/caching/cache/testutils/MinimalKeyType.cpp b/test/implementations/caching/cache/testutils/MinimalKeyType.cpp new file mode 100644 index 00000000..f6b327ff --- /dev/null +++ b/test/implementations/caching/cache/testutils/MinimalKeyType.cpp @@ -0,0 +1,3 @@ +#include "MinimalKeyType.h" + +int MinimalKeyType::instances = 0; diff --git a/test/implementations/caching/cache/testutils/MinimalKeyType.h b/test/implementations/caching/cache/testutils/MinimalKeyType.h new file mode 100644 index 00000000..9133d1f8 --- /dev/null +++ b/test/implementations/caching/cache/testutils/MinimalKeyType.h @@ -0,0 +1,47 @@ +#pragma once +#ifndef BLOCKS_MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_CACHING_CACHE_TESTUTILS_MINIMALKEYTYPE_H_ +#define BLOCKS_MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_CACHING_CACHE_TESTUTILS_MINIMALKEYTYPE_H_ + +#include + +// This is a not-default-constructible Key type +class MinimalKeyType { +public: + static int instances; + + static MinimalKeyType create(int value) { + return MinimalKeyType(value); + } + + MinimalKeyType(const MinimalKeyType &rhs): MinimalKeyType(rhs.value()) { + } + + ~MinimalKeyType() { + --instances; + } + + int value() const { + return _value; + } + +private: + MinimalKeyType(int value): _value(value) { + ++instances; + } + + int _value; +}; + +namespace std { +template <> struct hash { + size_t operator()(const MinimalKeyType &obj) const { + return obj.value(); + } +}; +} + +inline bool operator==(const MinimalKeyType &lhs, const MinimalKeyType &rhs) { + return lhs.value() == rhs.value(); +} + +#endif diff --git a/test/implementations/caching/cache/testutils/MinimalValueType.cpp b/test/implementations/caching/cache/testutils/MinimalValueType.cpp new file mode 100644 index 00000000..f5cbdbae --- /dev/null +++ b/test/implementations/caching/cache/testutils/MinimalValueType.cpp @@ -0,0 +1,3 @@ +#include "MinimalValueType.h" + +int MinimalValueType::instances = 0; diff --git a/test/implementations/caching/cache/testutils/MinimalValueType.h b/test/implementations/caching/cache/testutils/MinimalValueType.h new file mode 100644 index 00000000..9e48e43e --- /dev/null +++ b/test/implementations/caching/cache/testutils/MinimalValueType.h @@ -0,0 +1,44 @@ +#pragma once +#ifndef BLOCKS_MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_CACHING_CACHE_TESTUTILS_MINIMALVALUETYPE_H_ +#define BLOCKS_MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_CACHING_CACHE_TESTUTILS_MINIMALVALUETYPE_H_ + +#include +#include + +// This is a not-default-constructible non-copyable but moveable Value type +class MinimalValueType { +public: + static int instances; + + static MinimalValueType create(int value) { + return MinimalValueType(value); + } + + MinimalValueType(MinimalValueType &&rhs): MinimalValueType(rhs.value()) { + rhs._isMoved = true; + } + + ~MinimalValueType() { + assert(!_isDestructed); + --instances; + _isDestructed = true; + } + + int value() const { + assert(!_isMoved && !_isDestructed); + return _value; + } + +private: + MinimalValueType(int value): _value(value), _isMoved(false), _isDestructed(false) { + ++instances; + } + + int _value; + bool _isMoved; + bool _isDestructed; + + DISALLOW_COPY_AND_ASSIGN(MinimalValueType); +}; + +#endif diff --git a/test/implementations/caching/cache/testutils/QueueMapTest.cpp b/test/implementations/caching/cache/testutils/QueueMapTest.cpp new file mode 100644 index 00000000..f61b41a3 --- /dev/null +++ b/test/implementations/caching/cache/testutils/QueueMapTest.cpp @@ -0,0 +1,45 @@ +#include "QueueMapTest.h" + +QueueMapTest::QueueMapTest() { + MinimalKeyType::instances = 0; + MinimalValueType::instances = 0; + _map = std::make_unique>(); +} + +QueueMapTest::~QueueMapTest() { + _map.reset(); + EXPECT_EQ(0, MinimalKeyType::instances); + EXPECT_EQ(0, MinimalValueType::instances); +} + +void QueueMapTest::push(int key, int value) { + _map->push(MinimalKeyType::create(key), MinimalValueType::create(value)); +} + +boost::optional QueueMapTest::pop() { + auto elem = _map->pop(); + if (!elem) { + return boost::none; + } + return elem.value().value(); +} + +boost::optional QueueMapTest::pop(int key) { + auto elem = _map->pop(MinimalKeyType::create(key)); + if (!elem) { + return boost::none; + } + return elem.value().value(); +} + +boost::optional QueueMapTest::peek() { + auto elem = _map->peek(); + if (!elem) { + return boost::none; + } + return elem.value().value(); +} + +int QueueMapTest::size() { + return _map->size(); +} diff --git a/test/implementations/caching/cache/testutils/QueueMapTest.h b/test/implementations/caching/cache/testutils/QueueMapTest.h new file mode 100644 index 00000000..e49ad93f --- /dev/null +++ b/test/implementations/caching/cache/testutils/QueueMapTest.h @@ -0,0 +1,32 @@ +#pragma once +#ifndef BLOCKS_MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_CACHING_CACHE_TESTUTILS_QUEUEMAPTEST_H_ +#define BLOCKS_MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_CACHING_CACHE_TESTUTILS_QUEUEMAPTEST_H_ + +#include +#include +#include "../../../../../implementations/caching/cache/QueueMap.h" +#include "MinimalKeyType.h" +#include "MinimalValueType.h" +#include + +// This class is a parent class for tests on QueueMap. +// It offers functions to work with a QueueMap test object which is built using types having only the minimal type requirements. +// Furthermore, the class checks that there are no memory leaks left after destructing the QueueMap (by counting leftover instances of Keys/Values). +class QueueMapTest: public ::testing::Test { +public: + QueueMapTest(); + ~QueueMapTest(); + + void push(int key, int value); + boost::optional pop(); + boost::optional pop(int key); + boost::optional peek(); + int size(); + +private: + std::unique_ptr> _map; +}; + + + +#endif diff --git a/test/implementations/encrypted/CipherTest.cpp b/test/implementations/encrypted/CipherTest.cpp new file mode 100644 index 00000000..f5581b05 --- /dev/null +++ b/test/implementations/encrypted/CipherTest.cpp @@ -0,0 +1,226 @@ +#include +#include "../../../implementations/encrypted/ciphers/Cipher.h" +#include "../../../implementations/encrypted/ciphers/AES256_CFB.h" +#include "../../../implementations/encrypted/ciphers/AES256_GCM.h" +#include "testutils/FakeAuthenticatedCipher.h" + +#include +#include + + #include + +using namespace blockstore::encrypted; +using cpputils::Data; +using cpputils::DataFixture; + +template +class CipherTest: public ::testing::Test { +public: + BOOST_CONCEPT_ASSERT((CipherConcept)); + typename Cipher::EncryptionKey encKey = createKeyFixture(); + + static typename Cipher::EncryptionKey createKeyFixture(int seed = 0) { + Data data = DataFixture::generate(Cipher::EncryptionKey::BINARY_LENGTH, seed); + return Cipher::EncryptionKey::FromBinary(data.data()); + } + + void CheckEncryptThenDecryptIsIdentity(const Data &plaintext) { + Data ciphertext = Encrypt(plaintext); + Data decrypted = Decrypt(ciphertext); + EXPECT_EQ(plaintext, decrypted); + } + + void CheckEncryptIsIndeterministic(const Data &plaintext) { + Data ciphertext = Encrypt(plaintext); + Data ciphertext2 = Encrypt(plaintext); + EXPECT_NE(ciphertext, ciphertext2); + } + + void CheckEncryptedSize(const Data &plaintext) { + Data ciphertext = Encrypt(plaintext); + EXPECT_EQ(Cipher::ciphertextSize(plaintext.size()), ciphertext.size()); + } + + void ExpectDoesntDecrypt(const Data &ciphertext) { + auto decrypted = Cipher::decrypt((byte*)ciphertext.data(), ciphertext.size(), this->encKey); + EXPECT_FALSE(decrypted); + } + + Data Encrypt(const Data &plaintext) { + return Cipher::encrypt((byte*)plaintext.data(), plaintext.size(), this->encKey); + } + + Data Decrypt(const Data &ciphertext) { + return Cipher::decrypt((byte*)ciphertext.data(), ciphertext.size(), this->encKey).value(); + } + + static Data CreateZeroes(unsigned int size) { + return std::move(Data(size).FillWithZeroes()); + } + + static Data CreateData(unsigned int size, unsigned int seed = 0) { + return DataFixture::generate(size, seed); + } +}; + +TYPED_TEST_CASE_P(CipherTest); + +constexpr std::initializer_list SIZES = {0, 1, 100, 1024, 5000, 1048576, 20971520}; + +TYPED_TEST_P(CipherTest, Size) { + for (auto size: SIZES) { + EXPECT_EQ(size, TypeParam::ciphertextSize(TypeParam::plaintextSize(size))); + EXPECT_EQ(size, TypeParam::plaintextSize(TypeParam::ciphertextSize(size))); + } +} + +TYPED_TEST_P(CipherTest, EncryptThenDecrypt_Zeroes) { + for (auto size: SIZES) { + Data plaintext = this->CreateZeroes(size); + this->CheckEncryptThenDecryptIsIdentity(plaintext); + } +} + +TYPED_TEST_P(CipherTest, EncryptThenDecrypt_Data) { + for (auto size: SIZES) { + Data plaintext = this->CreateData(size); + this->CheckEncryptThenDecryptIsIdentity(plaintext); + } +} + +TYPED_TEST_P(CipherTest, EncryptIsIndeterministic_Zeroes) { + for (auto size: SIZES) { + Data plaintext = this->CreateZeroes(size); + this->CheckEncryptIsIndeterministic(plaintext); + } +} + +TYPED_TEST_P(CipherTest, EncryptIsIndeterministic_Data) { + for (auto size: SIZES) { + Data plaintext = this->CreateData(size); + this->CheckEncryptIsIndeterministic(plaintext); + } +} + +TYPED_TEST_P(CipherTest, EncryptedSize) { + for (auto size: SIZES) { + Data plaintext = this->CreateData(size); + this->CheckEncryptedSize(plaintext); + } +} + +TYPED_TEST_P(CipherTest, TryDecryptDataThatIsTooSmall) { + Data tooSmallCiphertext(TypeParam::ciphertextSize(0) - 1); + this->ExpectDoesntDecrypt(tooSmallCiphertext); +} + +TYPED_TEST_P(CipherTest, TryDecryptDataThatIsMuchTooSmall_0) { + static_assert(TypeParam::ciphertextSize(0) > 0, "If this fails, the test case doesn't make sense."); + Data tooSmallCiphertext(0); + this->ExpectDoesntDecrypt(tooSmallCiphertext); +} + +TYPED_TEST_P(CipherTest, TryDecryptDataThatIsMuchTooSmall_1) { + static_assert(TypeParam::ciphertextSize(0) > 1, "If this fails, the test case doesn't make sense."); + Data tooSmallCiphertext(1); + this->ExpectDoesntDecrypt(tooSmallCiphertext); +} + +REGISTER_TYPED_TEST_CASE_P(CipherTest, + Size, + EncryptThenDecrypt_Zeroes, + EncryptThenDecrypt_Data, + EncryptIsIndeterministic_Zeroes, + EncryptIsIndeterministic_Data, + EncryptedSize, + TryDecryptDataThatIsTooSmall, + TryDecryptDataThatIsMuchTooSmall_0, + TryDecryptDataThatIsMuchTooSmall_1 +); + +template +class AuthenticatedCipherTest: public CipherTest { +public: + Data zeroes1 = CipherTest::CreateZeroes(1); + Data plaintext1 = CipherTest::CreateData(1); + Data zeroes2 = CipherTest::CreateZeroes(100 * 1024); + Data plaintext2 = CipherTest::CreateData(100 * 1024); +}; + +TYPED_TEST_CASE_P(AuthenticatedCipherTest); + +TYPED_TEST_P(AuthenticatedCipherTest, ModifyFirstByte_Zeroes_Size1) { + Data ciphertext = this->Encrypt(this->zeroes1); + *(byte*)ciphertext.data() = *(byte*)ciphertext.data() + 1; + this->ExpectDoesntDecrypt(ciphertext); +} + +TYPED_TEST_P(AuthenticatedCipherTest, ModifyFirstByte_Data_Size1) { + Data ciphertext = this->Encrypt(this->plaintext1); + *(byte*)ciphertext.data() = *(byte*)ciphertext.data() + 1; + this->ExpectDoesntDecrypt(ciphertext); +} + +TYPED_TEST_P(AuthenticatedCipherTest, ModifyFirstByte_Zeroes) { + Data ciphertext = this->Encrypt(this->zeroes2); + *(byte*)ciphertext.data() = *(byte*)ciphertext.data() + 1; + this->ExpectDoesntDecrypt(ciphertext); +} + +TYPED_TEST_P(AuthenticatedCipherTest, ModifyFirstByte_Data) { + Data ciphertext = this->Encrypt(this->plaintext2); + *(byte*)ciphertext.data() = *(byte*)ciphertext.data() + 1; + this->ExpectDoesntDecrypt(ciphertext); +} + +TYPED_TEST_P(AuthenticatedCipherTest, ModifyLastByte_Zeroes) { + Data ciphertext = this->Encrypt(this->zeroes2); + ((byte*)ciphertext.data())[ciphertext.size() - 1] = ((byte*)ciphertext.data())[ciphertext.size() - 1] + 1; + this->ExpectDoesntDecrypt(ciphertext); +} + +TYPED_TEST_P(AuthenticatedCipherTest, ModifyLastByte_Data) { + Data ciphertext = this->Encrypt(this->plaintext2); + ((byte*)ciphertext.data())[ciphertext.size() - 1] = ((byte*)ciphertext.data())[ciphertext.size() - 1] + 1; + this->ExpectDoesntDecrypt(ciphertext); +} + +TYPED_TEST_P(AuthenticatedCipherTest, ModifyMiddleByte_Zeroes) { + Data ciphertext = this->Encrypt(this->zeroes2); + ((byte*)ciphertext.data())[ciphertext.size()/2] = ((byte*)ciphertext.data())[ciphertext.size()/2] + 1; + this->ExpectDoesntDecrypt(ciphertext); +} + +TYPED_TEST_P(AuthenticatedCipherTest, ModifyMiddleByte_Data) { + Data ciphertext = this->Encrypt(this->plaintext2); + ((byte*)ciphertext.data())[ciphertext.size()/2] = ((byte*)ciphertext.data())[ciphertext.size()/2] + 1; + this->ExpectDoesntDecrypt(ciphertext); +} + +TYPED_TEST_P(AuthenticatedCipherTest, TryDecryptZeroesData) { + this->ExpectDoesntDecrypt(this->zeroes2); +} + +TYPED_TEST_P(AuthenticatedCipherTest, TryDecryptRandomData) { + this->ExpectDoesntDecrypt(this->plaintext2); +} + +REGISTER_TYPED_TEST_CASE_P(AuthenticatedCipherTest, + ModifyFirstByte_Zeroes_Size1, + ModifyFirstByte_Zeroes, + ModifyFirstByte_Data_Size1, + ModifyFirstByte_Data, + ModifyLastByte_Zeroes, + ModifyLastByte_Data, + ModifyMiddleByte_Zeroes, + ModifyMiddleByte_Data, + TryDecryptZeroesData, + TryDecryptRandomData +); + + +INSTANTIATE_TYPED_TEST_CASE_P(Fake, CipherTest, FakeAuthenticatedCipher); +INSTANTIATE_TYPED_TEST_CASE_P(Fake, AuthenticatedCipherTest, FakeAuthenticatedCipher); +INSTANTIATE_TYPED_TEST_CASE_P(AES256_CFB, CipherTest, AES256_CFB); //CFB mode is not authenticated +INSTANTIATE_TYPED_TEST_CASE_P(AES256_GCM, CipherTest, AES256_GCM); +INSTANTIATE_TYPED_TEST_CASE_P(AES256_GCM, AuthenticatedCipherTest, AES256_GCM); diff --git a/test/implementations/encrypted/EncryptedBlockStoreTest.cpp b/test/implementations/encrypted/EncryptedBlockStoreTest.cpp deleted file mode 100644 index b5c6eb29..00000000 --- a/test/implementations/encrypted/EncryptedBlockStoreTest.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "../../../implementations/encrypted/EncryptedBlockStore.h" -#include "../../../implementations/testfake/FakeBlockStore.h" -#include "../../testutils/BlockStoreTest.h" -#include "google/gtest/gtest.h" - - -using blockstore::BlockStore; -using blockstore::encrypted::EncryptedBlockStore; -using blockstore::encrypted::EncryptionKey; -using blockstore::testfake::FakeBlockStore; - -using std::unique_ptr; -using std::make_unique; - -class EncryptedBlockStoreTestFixture: public BlockStoreTestFixture { -public: - unique_ptr createBlockStore() override { - return make_unique(make_unique(), EncryptionKey::FromString("1491BB4932A389EE14BC7090A272EE5517627CFA147A971A8E6E747E0C772972")); - } -}; - -INSTANTIATE_TYPED_TEST_CASE_P(Encrypted, BlockStoreTest, EncryptedBlockStoreTestFixture); - -//TODO Add specific tests, for example loading it with a different key doesn't work diff --git a/test/implementations/encrypted/EncryptedBlockStoreTest_Generic.cpp b/test/implementations/encrypted/EncryptedBlockStoreTest_Generic.cpp new file mode 100644 index 00000000..5507151d --- /dev/null +++ b/test/implementations/encrypted/EncryptedBlockStoreTest_Generic.cpp @@ -0,0 +1,40 @@ +#include "../../../implementations/encrypted/ciphers/AES256_GCM.h" +#include "../../../implementations/encrypted/ciphers/AES256_CFB.h" +#include "../../../implementations/encrypted/ciphers/Cipher.h" +#include "../../../implementations/encrypted/EncryptedBlockStore.h" +#include "../../../implementations/testfake/FakeBlockStore.h" +#include "../../testutils/BlockStoreTest.h" +#include "testutils/FakeAuthenticatedCipher.h" +#include "google/gtest/gtest.h" + +using ::testing::Test; + +using blockstore::BlockStore; +using blockstore::encrypted::EncryptedBlockStore; +using blockstore::testfake::FakeBlockStore; +using blockstore::encrypted::AES256_GCM; +using blockstore::encrypted::AES256_CFB; + +using std::unique_ptr; +using std::make_unique; + +using cpputils::Data; +using cpputils::DataFixture; + +template +class EncryptedBlockStoreTestFixture: public BlockStoreTestFixture { +public: + unique_ptr createBlockStore() override { + return make_unique>(make_unique(), createKeyFixture()); + } + +private: + static typename Cipher::EncryptionKey createKeyFixture(int seed = 0) { + Data data = DataFixture::generate(Cipher::EncryptionKey::BINARY_LENGTH, seed); + return Cipher::EncryptionKey::FromBinary(data.data()); + } +}; + +INSTANTIATE_TYPED_TEST_CASE_P(Encrypted_FakeCipher, BlockStoreTest, EncryptedBlockStoreTestFixture); +INSTANTIATE_TYPED_TEST_CASE_P(Encrypted_AES256_GCM, BlockStoreTest, EncryptedBlockStoreTestFixture); +INSTANTIATE_TYPED_TEST_CASE_P(Encrypted_AES256_CFB, BlockStoreTest, EncryptedBlockStoreTestFixture); diff --git a/test/implementations/encrypted/EncryptedBlockStoreTest_Specific.cpp b/test/implementations/encrypted/EncryptedBlockStoreTest_Specific.cpp new file mode 100644 index 00000000..0a6ca336 --- /dev/null +++ b/test/implementations/encrypted/EncryptedBlockStoreTest_Specific.cpp @@ -0,0 +1,113 @@ +#include +#include "testutils/FakeAuthenticatedCipher.h" +#include "../../../implementations/encrypted/EncryptedBlockStore.h" +#include "../../../implementations/testfake/FakeBlockStore.h" +#include "../../../utils/BlockStoreUtils.h" +#include + +using ::testing::Test; + +using std::unique_ptr; +using std::make_unique; + +using cpputils::DataFixture; +using cpputils::Data; + +using blockstore::testfake::FakeBlockStore; + +using namespace blockstore::encrypted; + +class EncryptedBlockStoreTest: public Test { +public: + static constexpr unsigned int BLOCKSIZE = 1024; + EncryptedBlockStoreTest(): + _baseBlockStore(make_unique()), + baseBlockStore(_baseBlockStore.get()), + blockStore(make_unique>(std::move(_baseBlockStore), FakeAuthenticatedCipher::Key1())), + data(DataFixture::generate(BLOCKSIZE)) { + } + unique_ptr _baseBlockStore; + FakeBlockStore *baseBlockStore; + unique_ptr> blockStore; + Data data; + + blockstore::Key CreateBlockDirectlyWithFixtureAndReturnKey() { + return blockStore->create(data)->key(); + } + + blockstore::Key CreateBlockWriteFixtureToItAndReturnKey() { + auto block = blockStore->create(Data(data.size())); + block->write(data.data(), 0, data.size()); + return block->key(); + } + + void ModifyBaseBlock(const blockstore::Key &key) { + auto block = baseBlockStore->load(key); + uint8_t middle_byte = ((byte*)block->data())[10]; + uint8_t new_middle_byte = middle_byte + 1; + block->write(&new_middle_byte, 10, 1); + } + + blockstore::Key CopyBaseBlock(const blockstore::Key &key) { + auto source = baseBlockStore->load(key); + return blockstore::utils::copyToNewBlock(baseBlockStore, *source)->key(); + } +}; + +TEST_F(EncryptedBlockStoreTest, LoadingWithSameKeyWorks_WriteOnCreate) { + auto key = CreateBlockDirectlyWithFixtureAndReturnKey(); + auto loaded = blockStore->load(key); + EXPECT_NE(nullptr, loaded.get()); + EXPECT_EQ(data.size(), loaded->size()); + EXPECT_EQ(0, std::memcmp(data.data(), loaded->data(), data.size())); +} + +TEST_F(EncryptedBlockStoreTest, LoadingWithSameKeyWorks_WriteSeparately) { + auto key = CreateBlockWriteFixtureToItAndReturnKey(); + auto loaded = blockStore->load(key); + EXPECT_NE(nullptr, loaded.get()); + EXPECT_EQ(data.size(), loaded->size()); + EXPECT_EQ(0, std::memcmp(data.data(), loaded->data(), data.size())); +} + +TEST_F(EncryptedBlockStoreTest, LoadingWithDifferentKeyDoesntWork_WriteOnCreate) { + auto key = CreateBlockDirectlyWithFixtureAndReturnKey(); + blockStore->__setKey(FakeAuthenticatedCipher::Key2()); + auto loaded = blockStore->load(key); + EXPECT_EQ(nullptr, loaded.get()); +} + +TEST_F(EncryptedBlockStoreTest, LoadingWithDifferentKeyDoesntWork_WriteSeparately) { + auto key = CreateBlockWriteFixtureToItAndReturnKey(); + blockStore->__setKey(FakeAuthenticatedCipher::Key2()); + auto loaded = blockStore->load(key); + EXPECT_EQ(nullptr, loaded.get()); +} + +TEST_F(EncryptedBlockStoreTest, LoadingModifiedBlockFails_WriteOnCreate) { + auto key = CreateBlockDirectlyWithFixtureAndReturnKey(); + ModifyBaseBlock(key); + auto loaded = blockStore->load(key); + EXPECT_EQ(nullptr, loaded.get()); +} + +TEST_F(EncryptedBlockStoreTest, LoadingModifiedBlockFails_WriteSeparately) { + auto key = CreateBlockWriteFixtureToItAndReturnKey(); + ModifyBaseBlock(key); + auto loaded = blockStore->load(key); + EXPECT_EQ(nullptr, loaded.get()); +} + +TEST_F(EncryptedBlockStoreTest, LoadingWithDifferentBlockIdFails_WriteOnCreate) { + auto key = CreateBlockDirectlyWithFixtureAndReturnKey(); + auto key2 = CopyBaseBlock(key); + auto loaded = blockStore->load(key2); + EXPECT_EQ(nullptr, loaded.get()); +} + +TEST_F(EncryptedBlockStoreTest, LoadingWithDifferentBlockIdFails_WriteSeparately) { + auto key = CreateBlockWriteFixtureToItAndReturnKey(); + auto key2 = CopyBaseBlock(key); + auto loaded = blockStore->load(key2); + EXPECT_EQ(nullptr, loaded.get()); +} diff --git a/test/implementations/encrypted/testutils/FakeAuthenticatedCipher.cpp b/test/implementations/encrypted/testutils/FakeAuthenticatedCipher.cpp new file mode 100644 index 00000000..719ae3f0 --- /dev/null +++ b/test/implementations/encrypted/testutils/FakeAuthenticatedCipher.cpp @@ -0,0 +1,3 @@ +#include "FakeAuthenticatedCipher.h" + +constexpr unsigned int FakeKey::BINARY_LENGTH; diff --git a/test/implementations/encrypted/testutils/FakeAuthenticatedCipher.h b/test/implementations/encrypted/testutils/FakeAuthenticatedCipher.h new file mode 100644 index 00000000..d99ff86d --- /dev/null +++ b/test/implementations/encrypted/testutils/FakeAuthenticatedCipher.h @@ -0,0 +1,102 @@ +#pragma once +#ifndef MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_ENCRYPTED_TESTUTILS_FAKEAUTHENTICATEDCIPHER_H_ +#define MESSMER_BLOCKSTORE_TEST_IMPLEMENTATIONS_ENCRYPTED_TESTUTILS_FAKEAUTHENTICATEDCIPHER_H_ + +#include "../../../../implementations/encrypted/ciphers/Cipher.h" +#include + +struct FakeKey { + FakeKey(uint8_t value_):value(value_) {} + static FakeKey CreateRandom() { + return FakeKey(rand()); + } + static FakeKey FromBinary(const void *data) { + return FakeKey(*(uint8_t*)data); + } + static constexpr unsigned int BINARY_LENGTH = 1; + + uint8_t value; +}; + +// This is a fake cipher that uses an indeterministic caesar chiffre and a 4-byte parity for a simple authentication mechanism +class FakeAuthenticatedCipher { +public: + BOOST_CONCEPT_ASSERT((blockstore::encrypted::CipherConcept)); + + using EncryptionKey = FakeKey; + + static EncryptionKey Key1() { + return FakeKey(5); + } + static EncryptionKey Key2() { + return FakeKey(63); + } + + static constexpr unsigned int ciphertextSize(unsigned int plaintextBlockSize) { + return plaintextBlockSize + 5; + } + + static constexpr unsigned int plaintextSize(unsigned int ciphertextBlockSize) { + return ciphertextBlockSize - 5; + } + + static cpputils::Data encrypt(const byte *plaintext, unsigned int plaintextSize, const EncryptionKey &encKey) { + cpputils::Data result(ciphertextSize(plaintextSize)); + + //Add a random IV + uint8_t iv = rand(); + std::memcpy(result.data(), &iv, 1); + + //Use caesar chiffre on plaintext + _caesar((byte*)result.data() + 1, plaintext, plaintextSize, encKey.value + iv); + + //Add parity information + int32_t parity = _parity((byte*)result.data(), plaintextSize + 1); + std::memcpy((byte*)result.data() + plaintextSize + 1, &parity, 4); + + return result; + } + + static boost::optional decrypt(const byte *ciphertext, unsigned int ciphertextSize, const EncryptionKey &encKey) { + //We need at least 5 bytes (iv + parity) + if (ciphertextSize < 5) { + return boost::none; + } + + //Check parity + int32_t expectedParity = _parity(ciphertext, plaintextSize(ciphertextSize) + 1); + int32_t actualParity = *(int32_t*)(ciphertext + plaintextSize(ciphertextSize) + 1); + if (expectedParity != actualParity) { + return boost::none; + } + + //Decrypt caesar chiffre from ciphertext + int32_t iv = *(int32_t*)ciphertext; + cpputils::Data result(plaintextSize(ciphertextSize)); + _caesar((byte*)result.data(), ciphertext + 1, plaintextSize(ciphertextSize), -(encKey.value+iv)); + return std::move(result); + } + +private: + static int32_t _parity(const byte *data, unsigned int size) { + int32_t parity = 34343435; // some init value + int32_t *intData = (int32_t*)data; + unsigned int intSize = size / sizeof(int32_t); + for (unsigned int i = 0; i < intSize; ++i) { + parity += intData[i]; + } + unsigned int remainingBytes = size - 4 * intSize; + for (unsigned int i = 0; i < remainingBytes; ++i) { + parity += (data[4*intSize + i] << (24 - 8*i)); + } + return parity; + } + + static void _caesar(byte *dst, const byte *src, unsigned int size, uint8_t key) { + for (unsigned int i = 0; i < size; ++i) { + dst[i] = src[i] + key; + } + } +}; + +#endif diff --git a/test/implementations/ondisk/OnDiskBlockStoreTest.cpp b/test/implementations/ondisk/OnDiskBlockStoreTest.cpp index 1ee64b0e..884e5516 100644 --- a/test/implementations/ondisk/OnDiskBlockStoreTest.cpp +++ b/test/implementations/ondisk/OnDiskBlockStoreTest.cpp @@ -4,7 +4,7 @@ #include "../../testutils/BlockStoreWithRandomKeysTest.h" #include "google/gtest/gtest.h" -#include "messmer/tempfile/src/TempDir.h" +#include "messmer/cpp-utils/tempfile/TempDir.h" using blockstore::BlockStore; @@ -14,7 +14,7 @@ using blockstore::ondisk::OnDiskBlockStore; using std::unique_ptr; using std::make_unique; -using tempfile::TempDir; +using cpputils::TempDir; class OnDiskBlockStoreTestFixture: public BlockStoreTestFixture { public: diff --git a/test/implementations/ondisk/OnDiskBlockTest/OnDiskBlockCreateTest.cpp b/test/implementations/ondisk/OnDiskBlockTest/OnDiskBlockCreateTest.cpp index 42663fde..89fe0bfa 100644 --- a/test/implementations/ondisk/OnDiskBlockTest/OnDiskBlockCreateTest.cpp +++ b/test/implementations/ondisk/OnDiskBlockTest/OnDiskBlockCreateTest.cpp @@ -1,18 +1,17 @@ -#include "../../../../implementations/ondisk/FileAlreadyExistsException.h" #include "../../../../implementations/ondisk/OnDiskBlock.h" #include "google/gtest/gtest.h" -#include "messmer/tempfile/src/TempFile.h" -#include "messmer/tempfile/src/TempDir.h" +#include +#include using ::testing::Test; using ::testing::WithParamInterface; using ::testing::Values; -using tempfile::TempFile; -using tempfile::TempDir; - using std::unique_ptr; +using cpputils::Data; +using cpputils::TempFile; +using cpputils::TempDir; using namespace blockstore; using namespace blockstore::ondisk; @@ -63,13 +62,13 @@ public: INSTANTIATE_TEST_CASE_P(OnDiskBlockCreateSizeTest, OnDiskBlockCreateSizeTest, Values(0, 1, 5, 1024, 10*1024*1024)); TEST_P(OnDiskBlockCreateSizeTest, OnDiskSizeIsCorrect) { - Data fileContent = Data::LoadFromFile(file.path()); + Data fileContent = Data::LoadFromFile(file.path()).value(); EXPECT_EQ(GetParam(), fileContent.size()); } TEST_P(OnDiskBlockCreateSizeTest, OnDiskBlockIsZeroedOut) { - Data fileContent = Data::LoadFromFile(file.path()); - EXPECT_EQ(0, std::memcmp(ZEROES.data(), fileContent.data(), fileContent.size())); + Data fileContent = Data::LoadFromFile(file.path()).value(); + EXPECT_EQ(ZEROES, fileContent); } // This test is also tested by OnDiskBlockStoreTest, but there the block is created using the BlockStore interface. diff --git a/test/implementations/ondisk/OnDiskBlockTest/OnDiskBlockFlushTest.cpp b/test/implementations/ondisk/OnDiskBlockTest/OnDiskBlockFlushTest.cpp index d2877f0c..8d723b92 100644 --- a/test/implementations/ondisk/OnDiskBlockTest/OnDiskBlockFlushTest.cpp +++ b/test/implementations/ondisk/OnDiskBlockTest/OnDiskBlockFlushTest.cpp @@ -1,19 +1,19 @@ -#include "../../../../implementations/ondisk/FileAlreadyExistsException.h" #include "../../../../implementations/ondisk/OnDiskBlock.h" -#include "../../../testutils/DataBlockFixture.h" +#include #include "google/gtest/gtest.h" -#include "messmer/tempfile/src/TempFile.h" -#include "messmer/tempfile/src/TempDir.h" +#include +#include using ::testing::Test; using ::testing::WithParamInterface; using ::testing::Values; -using tempfile::TempFile; -using tempfile::TempDir; - using std::unique_ptr; +using cpputils::Data; +using cpputils::DataFixture; +using cpputils::TempFile; +using cpputils::TempDir; using namespace blockstore; using namespace blockstore::ondisk; @@ -27,27 +27,23 @@ public: : dir(), key(Key::FromString("1491BB4932A389EE14BC7090AC772972")), file(dir.path() / key.ToString(), false), - randomData(GetParam()) { + randomData(DataFixture::generate(GetParam())) { } TempDir dir; Key key; TempFile file; - DataBlockFixture randomData; + Data randomData; unique_ptr CreateBlockAndLoadItFromDisk() { { - Data data(randomData.size()); - std::memcpy(data.data(), randomData.data(), randomData.size()); - auto block = OnDiskBlock::CreateOnDisk(dir.path(), key, std::move(data)); + OnDiskBlock::CreateOnDisk(dir.path(), key, randomData.copy()); } return OnDiskBlock::LoadFromDisk(dir.path(), key); } unique_ptr CreateBlock() { - Data data(randomData.size()); - std::memcpy(data.data(), randomData.data(), randomData.size()); - return OnDiskBlock::CreateOnDisk(dir.path(), key, std::move(data)); + return OnDiskBlock::CreateOnDisk(dir.path(), key, randomData.copy()); } void WriteDataToBlock(const unique_ptr &block) { @@ -60,9 +56,8 @@ public: } void EXPECT_STORED_FILE_DATA_CORRECT() { - Data actual = Data::LoadFromFile(file.path()); - EXPECT_EQ(randomData.size(), actual.size()); - EXPECT_EQ(0, std::memcmp(randomData.data(), actual.data(), randomData.size())); + Data actual = Data::LoadFromFile(file.path()).value(); + EXPECT_EQ(randomData, actual); } }; INSTANTIATE_TEST_CASE_P(OnDiskBlockFlushTest, OnDiskBlockFlushTest, Values((size_t)0, (size_t)1, (size_t)1024, (size_t)4096, (size_t)10*1024*1024)); diff --git a/test/implementations/ondisk/OnDiskBlockTest/OnDiskBlockLoadTest.cpp b/test/implementations/ondisk/OnDiskBlockTest/OnDiskBlockLoadTest.cpp index 8a32794b..8fdf8ff6 100644 --- a/test/implementations/ondisk/OnDiskBlockTest/OnDiskBlockLoadTest.cpp +++ b/test/implementations/ondisk/OnDiskBlockTest/OnDiskBlockLoadTest.cpp @@ -1,23 +1,24 @@ #include "../../../../implementations/ondisk/OnDiskBlock.h" -#include "../../../testutils/DataBlockFixture.h" +#include #include "../../../../utils/FileDoesntExistException.h" #include "google/gtest/gtest.h" -#include "../../../../utils/Data.h" -#include "messmer/tempfile/src/TempFile.h" -#include "messmer/tempfile/src/TempDir.h" +#include +#include +#include #include using ::testing::Test; using ::testing::WithParamInterface; using ::testing::Values; -using tempfile::TempFile; -using tempfile::TempDir; - using std::ofstream; using std::unique_ptr; using std::ios; +using cpputils::Data; +using cpputils::DataFixture; +using cpputils::TempFile; +using cpputils::TempDir; using namespace blockstore; using namespace blockstore::ondisk; @@ -40,18 +41,15 @@ public: data.StoreToFile(file.path()); } - void StoreData(const DataBlockFixture &data) { - //TODO Implement data.StoreToFile(filepath) instead - Data dataobj(data.size()); - std::memcpy(dataobj.data(), data.data(), data.size()); - dataobj.StoreToFile(file.path()); + void StoreData(const Data &data) { + data.StoreToFile(file.path()); } unique_ptr LoadBlock() { return OnDiskBlock::LoadFromDisk(dir.path(), key); } - void EXPECT_BLOCK_DATA_EQ(const DataBlockFixture &expected, const OnDiskBlock &actual) { + void EXPECT_BLOCK_DATA_EQ(const Data &expected, const OnDiskBlock &actual) { EXPECT_EQ(expected.size(), actual.size()); EXPECT_EQ(0, std::memcmp(expected.data(), actual.data(), expected.size())); } @@ -67,7 +65,7 @@ TEST_P(OnDiskBlockLoadTest, FileSizeIsCorrect) { } TEST_P(OnDiskBlockLoadTest, LoadedDataIsCorrect) { - DataBlockFixture randomData(GetParam()); + Data randomData = DataFixture::generate(GetParam()); StoreData(randomData); auto block = LoadBlock(); diff --git a/test/interface/helpers/BlockStoreWithRandomKeysTest.cpp b/test/interface/helpers/BlockStoreWithRandomKeysTest.cpp index fa0109a6..fdcfada0 100644 --- a/test/interface/helpers/BlockStoreWithRandomKeysTest.cpp +++ b/test/interface/helpers/BlockStoreWithRandomKeysTest.cpp @@ -1,7 +1,7 @@ #include "../../../interface/helpers/BlockStoreWithRandomKeys.h" #include "google/gtest/gtest.h" #include "google/gmock/gmock.h" -#include "../../testutils/DataBlockFixture.h" +#include using ::testing::Test; using ::testing::_; @@ -13,6 +13,8 @@ using ::testing::ByRef; using std::string; using std::unique_ptr; using std::make_unique; +using cpputils::Data; +using cpputils::DataFixture; using namespace blockstore; @@ -26,7 +28,7 @@ public: return unique_ptr(do_load(key)); } MOCK_METHOD1(do_load, Block*(const Key &)); - void remove(unique_ptr block) {} + void remove(unique_ptr block) {UNUSED(block);} MOCK_CONST_METHOD0(numBlocks, uint64_t()); }; @@ -47,7 +49,7 @@ public: const blockstore::Key key = Key::FromString("1491BB4932A389EE14BC7090AC772972"); Data createDataWithSize(size_t size) { - DataBlockFixture fixture(size); + Data fixture(DataFixture::generate(size)); Data data(size); std::memcpy(data.data(), fixture.data(), size); return data; diff --git a/test/testutils/BlockStoreTest.h b/test/testutils/BlockStoreTest.h index ad7d940c..f97eb4d5 100644 --- a/test/testutils/BlockStoreTest.h +++ b/test/testutils/BlockStoreTest.h @@ -4,8 +4,6 @@ #include "google/gtest/gtest.h" -#include "DataBlockFixture.h" - #include "../../interface/BlockStore.h" class BlockStoreTestFixture { @@ -28,14 +26,14 @@ TYPED_TEST_CASE_P(BlockStoreTest); TYPED_TEST_P(BlockStoreTest, TwoCreatedBlocksHaveDifferentKeys) { auto blockStore = this->fixture.createBlockStore(); - auto block1 = blockStore->create(blockstore::Data(1024)); - auto block2 = blockStore->create(blockstore::Data(1024)); + auto block1 = blockStore->create(cpputils::Data(1024)); + auto block2 = blockStore->create(cpputils::Data(1024)); EXPECT_NE(block1->key(), block2->key()); } TYPED_TEST_P(BlockStoreTest, BlockIsNotLoadableAfterDeleting) { auto blockStore = this->fixture.createBlockStore(); - auto blockkey = blockStore->create(blockstore::Data(1024))->key(); + auto blockkey = blockStore->create(cpputils::Data(1024))->key(); auto block = blockStore->load(blockkey); EXPECT_NE(nullptr, block.get()); blockStore->remove(std::move(block)); @@ -49,55 +47,55 @@ TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectOnEmptyBlockstore) { TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingOneBlock) { auto blockStore = this->fixture.createBlockStore(); - auto block = blockStore->create(blockstore::Data(1)); + auto block = blockStore->create(cpputils::Data(1)); EXPECT_EQ(1, blockStore->numBlocks()); } TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingOneBlock_AfterClosingBlock) { auto blockStore = this->fixture.createBlockStore(); - blockStore->create(blockstore::Data(1)); + blockStore->create(cpputils::Data(1)); EXPECT_EQ(1, blockStore->numBlocks()); } TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterRemovingTheLastBlock) { auto blockStore = this->fixture.createBlockStore(); - auto block = blockStore->create(blockstore::Data(1)); + auto block = blockStore->create(cpputils::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(blockstore::Data(1)); - auto block2 = blockStore->create(blockstore::Data(0)); + auto block1 = blockStore->create(cpputils::Data(1)); + auto block2 = blockStore->create(cpputils::Data(0)); EXPECT_EQ(2, blockStore->numBlocks()); } TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingTwoBlocks_AfterClosingFirstBlock) { auto blockStore = this->fixture.createBlockStore(); - blockStore->create(blockstore::Data(1)); - auto block2 = blockStore->create(blockstore::Data(0)); + blockStore->create(cpputils::Data(1)); + auto block2 = blockStore->create(cpputils::Data(0)); EXPECT_EQ(2, blockStore->numBlocks()); } TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingTwoBlocks_AfterClosingSecondBlock) { auto blockStore = this->fixture.createBlockStore(); - auto block1 = blockStore->create(blockstore::Data(1)); - blockStore->create(blockstore::Data(0)); + auto block1 = blockStore->create(cpputils::Data(1)); + blockStore->create(cpputils::Data(0)); EXPECT_EQ(2, blockStore->numBlocks()); } TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterAddingTwoBlocks_AfterClosingBothBlocks) { auto blockStore = this->fixture.createBlockStore(); - blockStore->create(blockstore::Data(1)); - blockStore->create(blockstore::Data(0)); + blockStore->create(cpputils::Data(1)); + blockStore->create(cpputils::Data(0)); EXPECT_EQ(2, blockStore->numBlocks()); } TYPED_TEST_P(BlockStoreTest, NumBlocksIsCorrectAfterRemovingABlock) { auto blockStore = this->fixture.createBlockStore(); - auto block = blockStore->create(blockstore::Data(1)); - blockStore->create(blockstore::Data(1)); + auto block = blockStore->create(cpputils::Data(1)); + blockStore->create(cpputils::Data(1)); blockStore->remove(std::move(block)); EXPECT_EQ(1, blockStore->numBlocks()); } diff --git a/test/testutils/BlockStoreTest_Data.h b/test/testutils/BlockStoreTest_Data.h index 11dfa2ac..202c8b32 100644 --- a/test/testutils/BlockStoreTest_Data.h +++ b/test/testutils/BlockStoreTest_Data.h @@ -12,15 +12,12 @@ public: BlockStoreDataParametrizedTest(std::unique_ptr blockStore_, const DataRange &testData_) : blockStore(std::move(blockStore_)), testData(testData_), - foregroundData(testData.count), backgroundData(testData.blocksize) { - DataBlockFixture _foregroundData(testData.count); - DataBlockFixture _backgroundData(testData.blocksize); - std::memcpy(foregroundData.data(), _foregroundData.data(), foregroundData.size()); - std::memcpy(backgroundData.data(), _backgroundData.data(), backgroundData.size()); + foregroundData(cpputils::DataFixture::generate(testData.count, 0)), + backgroundData(cpputils::DataFixture::generate(testData.blocksize, 1)) { } void TestWriteAndReadImmediately() { - auto block = blockStore->create(blockstore::Data(testData.blocksize).FillWithZeroes()); + auto block = blockStore->create(cpputils::Data(testData.blocksize).FillWithZeroes()); block->write(foregroundData.data(), testData.offset, testData.count); EXPECT_DATA_READS_AS(foregroundData, *block, testData.offset, testData.count); @@ -36,7 +33,7 @@ public: } void TestOverwriteAndRead() { - auto block = blockStore->create(blockstore::Data(testData.blocksize)); + auto block = blockStore->create(cpputils::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); @@ -46,30 +43,25 @@ public: private: std::unique_ptr blockStore; DataRange testData; - blockstore::Data foregroundData; - blockstore::Data backgroundData; + cpputils::Data foregroundData; + cpputils::Data backgroundData; - void EXPECT_DATA_EQ(const blockstore::Data &expected, const blockstore::Data &actual) { - EXPECT_EQ(expected.size(), actual.size()); - EXPECT_EQ(0, std::memcmp(expected.data(), actual.data(), expected.size())); - } - - blockstore::Key CreateBlockWriteToItAndReturnKey(const blockstore::Data &to_write) { - auto newblock = blockStore->create(blockstore::Data(testData.blocksize).FillWithZeroes()); + blockstore::Key CreateBlockWriteToItAndReturnKey(const cpputils::Data &to_write) { + auto newblock = blockStore->create(cpputils::Data(testData.blocksize).FillWithZeroes()); newblock->write(to_write.data(), testData.offset, testData.count); return newblock->key(); } - void EXPECT_DATA_READS_AS(const blockstore::Data &expected, const blockstore::Block &block, off_t offset, size_t count) { - blockstore::Data read(count); + void EXPECT_DATA_READS_AS(const cpputils::Data &expected, const blockstore::Block &block, off_t offset, size_t count) { + cpputils::Data read(count); std::memcpy(read.data(), (uint8_t*)block.data() + offset, count); - EXPECT_DATA_EQ(expected, read); + EXPECT_EQ(expected, read); } - void EXPECT_DATA_READS_AS_OUTSIDE_OF(const blockstore::Data &expected, const blockstore::Block &block, off_t start, size_t count) { - blockstore::Data begin(start); - blockstore::Data end(testData.blocksize - count - start); + void EXPECT_DATA_READS_AS_OUTSIDE_OF(const cpputils::Data &expected, const blockstore::Block &block, off_t start, size_t count) { + cpputils::Data begin(start); + cpputils::Data end(testData.blocksize - count - start); std::memcpy(begin.data(), expected.data(), start); std::memcpy(end.data(), (uint8_t*)expected.data()+start+count, end.size()); @@ -79,7 +71,7 @@ private: } void EXPECT_DATA_IS_ZEROES_OUTSIDE_OF(const blockstore::Block &block, off_t start, size_t count) { - blockstore::Data ZEROES(testData.blocksize); + cpputils::Data ZEROES(testData.blocksize); ZEROES.FillWithZeroes(); EXPECT_DATA_READS_AS_OUTSIDE_OF(ZEROES, block, start, count); } diff --git a/test/testutils/BlockStoreTest_Size.h b/test/testutils/BlockStoreTest_Size.h index 5948d836..20ee3fbb 100644 --- a/test/testutils/BlockStoreTest_Size.h +++ b/test/testutils/BlockStoreTest_Size.h @@ -1,6 +1,7 @@ // This file is meant to be included by BlockStoreTest.h only -#include "../../utils/Data.h" +#include +#include class BlockStoreSizeParameterizedTest { public: @@ -18,39 +19,34 @@ public: } 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)); - + cpputils::Data data = cpputils::DataFixture::generate(size); + auto block = blockStore->create(data); + EXPECT_EQ(0, std::memcmp(data.data(), block->data(), size)); } void TestLoadingUnchangedBlockData() { - DataBlockFixture dataFixture(size); - blockstore::Data data(size); - std::memcpy(data.data(), dataFixture.data(), size); - blockstore::Key key = blockStore->create(data)->key(); + cpputils::Data data = cpputils::DataFixture::generate(size); + blockstore::Key key = blockStore->create(data)->key(); auto loaded_block = blockStore->load(key); - EXPECT_EQ(0, std::memcmp(dataFixture.data(), loaded_block->data(), size)); + EXPECT_EQ(0, std::memcmp(data.data(), loaded_block->data(), size)); } void TestLoadedBlockIsCorrect() { - DataBlockFixture randomData(size); + cpputils::Data randomData = cpputils::DataFixture::generate(size); auto loaded_block = StoreDataToBlockAndLoadIt(randomData); EXPECT_EQ(size, loaded_block->size()); EXPECT_EQ(0, std::memcmp(randomData.data(), loaded_block->data(), size)); } void TestLoadedBlockIsCorrectWhenLoadedDirectlyAfterFlushing() { - DataBlockFixture randomData(size); + cpputils::Data randomData = cpputils::DataFixture::generate(size); auto loaded_block = StoreDataToBlockAndLoadItDirectlyAfterFlushing(randomData); EXPECT_EQ(size, loaded_block->size()); EXPECT_EQ(0, std::memcmp(randomData.data(), loaded_block->data(), size)); } void TestAfterCreate_FlushingDoesntChangeBlock() { - DataBlockFixture randomData(size); + cpputils::Data randomData = cpputils::DataFixture::generate(size); auto block = CreateBlock(); WriteDataToBlock(block.get(), randomData); block->flush(); @@ -59,7 +55,7 @@ public: } void TestAfterLoad_FlushingDoesntChangeBlock() { - DataBlockFixture randomData(size); + cpputils::Data randomData = cpputils::DataFixture::generate(size); auto block = CreateBlockAndLoadIt(); WriteDataToBlock(block.get(), randomData); block->flush(); @@ -68,10 +64,10 @@ public: } void TestAfterCreate_FlushesWhenDestructed() { - DataBlockFixture randomData(size); + cpputils::Data randomData = cpputils::DataFixture::generate(size); blockstore::Key key = key; { - auto block = blockStore->create(blockstore::Data(size)); + auto block = blockStore->create(cpputils::Data(size)); key = block->key(); WriteDataToBlock(block.get(), randomData); } @@ -80,7 +76,7 @@ public: } void TestAfterLoad_FlushesWhenDestructed() { - DataBlockFixture randomData(size); + cpputils::Data randomData = cpputils::DataFixture::generate(size); blockstore::Key key = key; { key = CreateBlock()->key(); @@ -102,26 +98,22 @@ private: std::unique_ptr blockStore; size_t size; - blockstore::Data ZEROES(size_t size) { - blockstore::Data ZEROES(size); + cpputils::Data ZEROES(size_t size) { + cpputils::Data ZEROES(size); ZEROES.FillWithZeroes(); return ZEROES; } - std::unique_ptr StoreDataToBlockAndLoadIt(const DataBlockFixture &data) { + std::unique_ptr StoreDataToBlockAndLoadIt(const cpputils::Data &data) { blockstore::Key key = StoreDataToBlockAndGetKey(data); return blockStore->load(key); } - blockstore::Key StoreDataToBlockAndGetKey(const DataBlockFixture &dataFixture) { - blockstore::Data data(dataFixture.size()); - std::memcpy(data.data(), dataFixture.data(), dataFixture.size()); + blockstore::Key StoreDataToBlockAndGetKey(const cpputils::Data &data) { return blockStore->create(data)->key(); } - std::unique_ptr StoreDataToBlockAndLoadItDirectlyAfterFlushing(const DataBlockFixture &dataFixture) { - blockstore::Data data(dataFixture.size()); - std::memcpy(data.data(), dataFixture.data(), dataFixture.size()); + std::unique_ptr StoreDataToBlockAndLoadItDirectlyAfterFlushing(const cpputils::Data &data) { auto block = blockStore->create(data); block->flush(); return blockStore->load(block->key()); @@ -133,14 +125,14 @@ private: } std::unique_ptr CreateBlock() { - return blockStore->create(blockstore::Data(size)); + return blockStore->create(cpputils::Data(size)); } - void WriteDataToBlock(blockstore::Block *block, const DataBlockFixture &randomData) { + void WriteDataToBlock(blockstore::Block *block, const cpputils::Data &randomData) { block->write(randomData.data(), 0, randomData.size()); } - void EXPECT_BLOCK_DATA_CORRECT(const blockstore::Block &block, const DataBlockFixture &randomData) { + void EXPECT_BLOCK_DATA_CORRECT(const blockstore::Block &block, const cpputils::Data &randomData) { EXPECT_EQ(randomData.size(), block.size()); EXPECT_EQ(0, std::memcmp(randomData.data(), block.data(), randomData.size())); } diff --git a/test/testutils/BlockStoreWithRandomKeysTest.h b/test/testutils/BlockStoreWithRandomKeysTest.h index 03d64cab..3f2db103 100644 --- a/test/testutils/BlockStoreWithRandomKeysTest.h +++ b/test/testutils/BlockStoreWithRandomKeysTest.h @@ -3,7 +3,6 @@ #define TEST_BLOCKSTORE_IMPLEMENTATIONS_TESTUTILS_BLOCKSTOREWITHRANDOMKEYSTEST_H_ #include -#include "DataBlockFixture.h" #include "../../interface/BlockStore.h" @@ -31,45 +30,45 @@ TYPED_TEST_CASE_P(BlockStoreWithRandomKeysTest); TYPED_TEST_P(BlockStoreWithRandomKeysTest, CreateTwoBlocksWithSameKeyAndSameSize) { auto blockStore = this->fixture.createBlockStore(); - auto block = blockStore->tryCreate(this->key, blockstore::Data(1024)); + auto block = blockStore->tryCreate(this->key, cpputils::Data(1024)); block->flush(); - auto block2 = blockStore->tryCreate(this->key, blockstore::Data(1024)); + auto block2 = blockStore->tryCreate(this->key, cpputils::Data(1024)); EXPECT_TRUE((bool)block); EXPECT_FALSE((bool)block2); } TYPED_TEST_P(BlockStoreWithRandomKeysTest, CreateTwoBlocksWithSameKeyAndDifferentSize) { auto blockStore = this->fixture.createBlockStore(); - auto block = blockStore->tryCreate(this->key, blockstore::Data(1024)); + auto block = blockStore->tryCreate(this->key, cpputils::Data(1024)); block->flush(); - auto block2 = blockStore->tryCreate(this->key, blockstore::Data(4096)); + auto block2 = blockStore->tryCreate(this->key, cpputils::Data(4096)); EXPECT_TRUE((bool)block); EXPECT_FALSE((bool)block2); } TYPED_TEST_P(BlockStoreWithRandomKeysTest, CreateTwoBlocksWithSameKeyAndFirstNullSize) { auto blockStore = this->fixture.createBlockStore(); - auto block = blockStore->tryCreate(this->key, blockstore::Data(0)); + auto block = blockStore->tryCreate(this->key, cpputils::Data(0)); block->flush(); - auto block2 = blockStore->tryCreate(this->key, blockstore::Data(1024)); + auto block2 = blockStore->tryCreate(this->key, cpputils::Data(1024)); EXPECT_TRUE((bool)block); EXPECT_FALSE((bool)block2); } TYPED_TEST_P(BlockStoreWithRandomKeysTest, CreateTwoBlocksWithSameKeyAndSecondNullSize) { auto blockStore = this->fixture.createBlockStore(); - auto block = blockStore->tryCreate(this->key, blockstore::Data(1024)); + auto block = blockStore->tryCreate(this->key, cpputils::Data(1024)); block->flush(); - auto block2 = blockStore->tryCreate(this->key, blockstore::Data(0)); + auto block2 = blockStore->tryCreate(this->key, cpputils::Data(0)); EXPECT_TRUE((bool)block); EXPECT_FALSE((bool)block2); } TYPED_TEST_P(BlockStoreWithRandomKeysTest, CreateTwoBlocksWithSameKeyAndBothNullSize) { auto blockStore = this->fixture.createBlockStore(); - auto block = blockStore->tryCreate(this->key, blockstore::Data(0)); + auto block = blockStore->tryCreate(this->key, cpputils::Data(0)); block->flush(); - auto block2 = blockStore->tryCreate(this->key, blockstore::Data(0)); + auto block2 = blockStore->tryCreate(this->key, cpputils::Data(0)); EXPECT_TRUE((bool)block); EXPECT_FALSE((bool)block2); } diff --git a/test/testutils/DataBlockFixture.cpp b/test/testutils/DataBlockFixture.cpp deleted file mode 100644 index 252015b2..00000000 --- a/test/testutils/DataBlockFixture.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include "DataBlockFixture.h" -#include -#include - -using std::min; - -DataBlockFixture::DataBlockFixture(size_t size, long long int IV): _fileData(new char[size]), _size(size) { - fillFileWithRandomData(IV); -} - -DataBlockFixture::~DataBlockFixture() { - delete[] _fileData; -} - -void DataBlockFixture::fillFileWithRandomData(long long int IV) { - long long int val = IV; - for(size_t i=0; i<_size/sizeof(long long int); ++i) { - //MMIX linear congruential generator - val *= 6364136223846793005L; - val += 1442695040888963407; - reinterpret_cast(_fileData)[i] = val; - } - uint64_t alreadyWritten = (_size/sizeof(long long int))*sizeof(long long int); - val *= 6364136223846793005L; - val += 1442695040888963407; - char *remainingBytes = reinterpret_cast(&val); - //Fill remaining bytes - for(size_t i=0; i<_size-alreadyWritten; ++i) { - reinterpret_cast(_fileData)[alreadyWritten + i] = remainingBytes[i]; - } -} - -const char *DataBlockFixture::data() const { - return _fileData; -} - -int DataBlockFixture::read(void *buf, size_t count, off_t offset) { - size_t realCount = min(count, _size - offset); - memcpy(buf, _fileData+offset, realCount); - return realCount; -} - -size_t DataBlockFixture::size() const { - return _size; -} - -bool DataBlockFixture::fileContentEqual(const char *content, size_t count, off_t offset) { - return 0 == memcmp(content, _fileData + offset, count); -} - -DataBlockFixtureWriteable::DataBlockFixtureWriteable(size_t size, long long int IV) - :DataBlockFixture(size, IV), _originalSize(size) { - _originalFileData = new char[size]; - memcpy(_originalFileData, _fileData, size); -} - -DataBlockFixtureWriteable::~DataBlockFixtureWriteable() { - delete[] _originalFileData; -} - -void DataBlockFixtureWriteable::write(const void *buf, size_t count, off_t offset) { - extendFileSizeIfNecessary(count + offset); - - memcpy(_fileData+offset, buf, count); -} - -void DataBlockFixtureWriteable::extendFileSizeIfNecessary(size_t size) { - if (size > _size) { - extendFileSize(size); - } -} - -void DataBlockFixtureWriteable::extendFileSize(size_t size) { - char *newfile = new char[size]; - memcpy(newfile, _fileData, _size); - delete[] _fileData; - _fileData = newfile; - _size = size; -} - -bool DataBlockFixtureWriteable::sizeUnchanged() { - return _size == _originalSize; -} - -bool DataBlockFixtureWriteable::regionUnchanged(off_t offset, size_t count) { - return 0 == memcmp(_fileData+offset, _originalFileData+offset, count); -} diff --git a/test/testutils/DataBlockFixture.h b/test/testutils/DataBlockFixture.h deleted file mode 100644 index 73d763d9..00000000 --- a/test/testutils/DataBlockFixture.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once -#ifndef TEST_TESTUTILS_DATABLOCKFIXTURE_H_ -#define TEST_TESTUTILS_DATABLOCKFIXTURE_H_ - -#include - -class DataBlockFixture { -public: - DataBlockFixture(size_t size, long long int IV = 1); - virtual ~DataBlockFixture(); - - int read(void *buf, size_t count, off_t offset); - - // Return true, iff the given data is equal to the data of the file at the given offset. - bool fileContentEqual(const char *content, size_t count, off_t offset); - - const char *data() const; - - size_t size() const; - -protected: - char *_fileData; - size_t _size; - -private: - void fillFileWithRandomData(long long int IV); -}; - -class DataBlockFixtureWriteable: public DataBlockFixture { -public: - DataBlockFixtureWriteable(size_t size, long long int IV = 1); - virtual ~DataBlockFixtureWriteable(); - - void write(const void *buf, size_t count, off_t offset); - - bool sizeUnchanged(); - bool regionUnchanged(off_t offset, size_t count); - -private: - void extendFileSizeIfNecessary(size_t size); - void extendFileSize(size_t size); - - char *_originalFileData; - size_t _originalSize; -}; - -#endif diff --git a/test/utils/BlockStoreUtilsTest.cpp b/test/utils/BlockStoreUtilsTest.cpp index 93515636..adc72ad8 100644 --- a/test/utils/BlockStoreUtilsTest.cpp +++ b/test/utils/BlockStoreUtilsTest.cpp @@ -1,5 +1,5 @@ #include "../../implementations/testfake/FakeBlockStore.h" -#include "../testutils/DataBlockFixture.h" +#include #include "../../utils/BlockStoreUtils.h" #include "google/gtest/gtest.h" @@ -11,6 +11,8 @@ using ::testing::Values; using std::make_unique; using std::unique_ptr; +using cpputils::Data; +using cpputils::DataFixture; using namespace blockstore; using namespace blockstore::utils; @@ -22,13 +24,13 @@ public: unsigned int SIZE = 1024 * 1024; BlockStoreUtilsTest(): ZEROES(SIZE), - dataFixture(SIZE), + dataFixture(DataFixture::generate(SIZE)), blockStore(make_unique()) { ZEROES.FillWithZeroes(); } Data ZEROES; - DataBlockFixture dataFixture; + Data dataFixture; unique_ptr blockStore; }; diff --git a/test/utils/DataTest.cpp b/test/utils/DataTest.cpp deleted file mode 100644 index 3601745a..00000000 --- a/test/utils/DataTest.cpp +++ /dev/null @@ -1,170 +0,0 @@ -#include "../testutils/DataBlockFixture.h" -#include "../../utils/Data.h" -#include "../../utils/FileDoesntExistException.h" -#include "google/gtest/gtest.h" - -#include "messmer/tempfile/src/TempFile.h" - -#include - -using ::testing::Test; -using ::testing::WithParamInterface; -using ::testing::Values; - -using tempfile::TempFile; - -using std::ifstream; -using std::ofstream; - -namespace bf = boost::filesystem; - -using namespace blockstore; - -class DataTest: public Test { -public: - bool DataIsZeroes(const Data &data) { - for (size_t i = 0; i != data.size(); ++ i) { - if (((char*)data.data())[i] != 0) { - return false; - } - } - return true; - } - - void FillData(const DataBlockFixture &fillData, Data *data) { - ASSERT_EQ(fillData.size(), data->size()); - std::memcpy(data->data(), fillData.data(), fillData.size()); - } - - void EXPECT_DATA_CORRECT(const DataBlockFixture &expectedData, const Data &data) { - ASSERT_EQ(expectedData.size(), data.size()); - EXPECT_EQ(0, std::memcmp(expectedData.data(), data.data(), expectedData.size())); - } -}; - -class DataTestWithSizeParam: public DataTest, public WithParamInterface { -public: - DataBlockFixture randomData; - - DataTestWithSizeParam(): randomData(GetParam()) {} - - void FillData(Data *data) { - DataTest::FillData(randomData, data); - } - - void StoreData(const bf::path &filepath) { - ofstream file(filepath.c_str(), std::ios::binary | std::ios::trunc); - file.write(randomData.data(), randomData.size()); - } - - void EXPECT_STORED_FILE_DATA_CORRECT(const bf::path &filepath) { - EXPECT_EQ(randomData.size(), bf::file_size(filepath)); - - ifstream file(filepath.c_str(), std::ios::binary); - char *read_data = new char[randomData.size()]; - file.read(read_data, randomData.size()); - - EXPECT_EQ(0, std::memcmp(randomData.data(), read_data, randomData.size())); - delete[] read_data; - } - - void EXPECT_DATA_CORRECT(const Data &data) { - DataTest::EXPECT_DATA_CORRECT(randomData, data); - } -}; - -INSTANTIATE_TEST_CASE_P(DataTestWithSizeParam, DataTestWithSizeParam, Values(0, 1, 2, 1024, 4096, 10*1024*1024)); - -// Working on a large data area without a crash is a good indicator that we -// are actually working on memory that was validly allocated for us. -TEST_P(DataTestWithSizeParam, WriteAndCheck) { - Data data(GetParam()); - - FillData(&data); - EXPECT_DATA_CORRECT(data); -} - -TEST_P(DataTestWithSizeParam, Size) { - Data data(GetParam()); - EXPECT_EQ(GetParam(), data.size()); -} - -TEST_P(DataTestWithSizeParam, CheckStoredFile) { - Data data(GetParam()); - FillData(&data); - - TempFile file; - data.StoreToFile(file.path()); - - EXPECT_STORED_FILE_DATA_CORRECT(file.path()); -} - -TEST_P(DataTestWithSizeParam, CheckLoadedData) { - TempFile file; - StoreData(file.path()); - - Data data = Data::LoadFromFile(file.path()); - - EXPECT_DATA_CORRECT(data); -} - -TEST_P(DataTestWithSizeParam, StoreDoesntChangeData) { - Data data(GetParam()); - FillData(&data); - - TempFile file; - data.StoreToFile(file.path()); - - EXPECT_DATA_CORRECT(data); -} - -TEST_P(DataTestWithSizeParam, StoreAndLoad) { - Data data(GetParam()); - FillData(&data); - - TempFile file; - data.StoreToFile(file.path()); - Data loaded_data = Data::LoadFromFile(file.path()); - - EXPECT_DATA_CORRECT(loaded_data); -} - -TEST_P(DataTestWithSizeParam, Copy) { - Data data(GetParam()); - FillData(&data); - - Data copy = data.copy(); - - EXPECT_DATA_CORRECT(copy); -} - -TEST_F(DataTest, InitializeWithZeroes) { - Data data(10*1024); - data.FillWithZeroes(); - EXPECT_TRUE(DataIsZeroes(data)); -} - -TEST_F(DataTest, FillModifiedDataWithZeroes) { - Data data(10*1024); - DataBlockFixture randomData(10*1024); - FillData(randomData, &data); - EXPECT_FALSE(DataIsZeroes(data)); - - data.FillWithZeroes(); - EXPECT_TRUE(DataIsZeroes(data)); -} - -//Needs 64bit for representation. This value isn't in the size param list, because the list is also used for read/write checks. -TEST_F(DataTest, LargesizeSize) { - size_t size = 10L*1024*1024*1024; - Data data(size); - EXPECT_EQ(size, data.size()); -} - -TEST_F(DataTest, LoadingNonexistingFile) { - TempFile file(false); // Pass false to constructor, so the tempfile is not created - EXPECT_THROW( - Data::LoadFromFile(file.path()), - FileDoesntExistException - ); -} diff --git a/test/utils/KeyTest.cpp b/test/utils/KeyTest.cpp deleted file mode 100644 index aa78d1ec..00000000 --- a/test/utils/KeyTest.cpp +++ /dev/null @@ -1,144 +0,0 @@ -#include "../testutils/DataBlockFixture.h" -#include "../../utils/Data.h" -#include "../../utils/Key.h" -#include "google/gtest/gtest.h" - - -using ::testing::Test; -using ::testing::WithParamInterface; -using ::testing::Values; - -using std::string; - -using namespace blockstore; - -//TODO Test blockstore::FixedSizeData instead of blockstore::Key - -class KeyTest: public Test { -public: - const string KEY1_AS_STRING = "1491BB4932A389EE14BC7090AC772972"; - const string KEY2_AS_STRING = "272EE5517627CFA147A971A8E6E747E0"; - - const DataBlockFixture KEY3_AS_BINARY; - const DataBlockFixture KEY4_AS_BINARY; - - KeyTest() : KEY3_AS_BINARY(Key::BINARY_LENGTH, 1), KEY4_AS_BINARY(Key::BINARY_LENGTH, 2) {} - - void EXPECT_DATA_EQ(const DataBlockFixture &expected, const Data &actual) { - EXPECT_EQ(expected.size(), actual.size()); - EXPECT_EQ(0, std::memcmp(expected.data(), actual.data(), expected.size())); - } -}; - -TEST_F(KeyTest, CanGenerateRandomKeysWithoutCrashing) { - Key result = Key::CreateRandom(); -} - -TEST_F(KeyTest, CreatedRandomKeysHaveCorrectLength) { - Key key = Key::CreateRandom(); - EXPECT_EQ(Key::STRING_LENGTH, key.ToString().size()); -} - -TEST_F(KeyTest, EqualsTrue) { - Key key1_1 = Key::FromString(KEY1_AS_STRING); - Key key1_2 = Key::FromString(KEY1_AS_STRING); - - EXPECT_TRUE(key1_1 == key1_2); - EXPECT_TRUE(key1_2 == key1_1); -} - -TEST_F(KeyTest, EqualsFalse) { - Key key1_1 = Key::FromString(KEY1_AS_STRING); - Key key2_1 = Key::FromString(KEY2_AS_STRING); - - EXPECT_FALSE(key1_1 == key2_1); - EXPECT_FALSE(key2_1 == key1_1); -} - -TEST_F(KeyTest, NotEqualsFalse) { - Key key1_1 = Key::FromString(KEY1_AS_STRING); - Key key1_2 = Key::FromString(KEY1_AS_STRING); - - EXPECT_FALSE(key1_1 != key1_2); - EXPECT_FALSE(key1_2 != key1_1); -} - -TEST_F(KeyTest, NotEqualsTrue) { - Key key1_1 = Key::FromString(KEY1_AS_STRING); - Key key2_1 = Key::FromString(KEY2_AS_STRING); - - EXPECT_TRUE(key1_1 != key2_1); - EXPECT_TRUE(key2_1 != key1_1); -} - -class KeyTestWithStringKeyParam: public KeyTest, public WithParamInterface {}; -INSTANTIATE_TEST_CASE_P(KeyTestWithStringKeyParam, KeyTestWithStringKeyParam, Values("2898B4B8A13CA63CBE0F0278CCE465DB", "6FFEBAD90C0DAA2B79628F0627CE9841")); - -TEST_P(KeyTestWithStringKeyParam, FromAndToString) { - Key key = Key::FromString(GetParam()); - EXPECT_EQ(GetParam(), key.ToString()); -} - -TEST_P(KeyTestWithStringKeyParam, ToAndFromString) { - Key key = Key::FromString(GetParam()); - Key key2 = Key::FromString(key.ToString()); - EXPECT_EQ(key, key2); -} - -class KeyTestWithBinaryKeyParam: public KeyTest, public WithParamInterface { -public: - static const DataBlockFixture VALUE1; - static const DataBlockFixture VALUE2; -}; -const DataBlockFixture KeyTestWithBinaryKeyParam::VALUE1(Key::BINARY_LENGTH, 3); -const DataBlockFixture KeyTestWithBinaryKeyParam::VALUE2(Key::BINARY_LENGTH, 4); -INSTANTIATE_TEST_CASE_P(KeyTestWithBinaryKeyParam, KeyTestWithBinaryKeyParam, Values(&KeyTestWithBinaryKeyParam::VALUE1, &KeyTestWithBinaryKeyParam::VALUE2)); - -TEST_P(KeyTestWithBinaryKeyParam, FromAndToBinary) { - Key key = Key::FromBinary((uint8_t*)GetParam()->data()); - Data keydata(Key::BINARY_LENGTH); - key.ToBinary(keydata.data()); - EXPECT_DATA_EQ(*GetParam(), keydata); -} - -TEST_P(KeyTestWithBinaryKeyParam, ToAndFromBinary) { - Key key = Key::FromBinary((uint8_t*)GetParam()->data()); - Data stored(Key::BINARY_LENGTH); - key.ToBinary(stored.data()); - Key loaded = Key::FromBinary(stored.data()); - EXPECT_EQ(key, loaded); -} - -class KeyTestWithKeyParam: public KeyTest, public WithParamInterface {}; -INSTANTIATE_TEST_CASE_P(KeyTestWithKeyParam, KeyTestWithKeyParam, Values(Key::FromString("2898B4B8A13CA63CBE0F0278CCE465DB"), Key::FromString("6FFEBAD90C0DAA2B79628F0627CE9841"))); - -TEST_P(KeyTestWithKeyParam, CopyConstructor) { - Key copy(GetParam()); - EXPECT_EQ(GetParam(), copy); -} - -TEST_F(KeyTest, CopyConstructorDoesntChangeSource) { - Key key1 = Key::FromString(KEY1_AS_STRING); - Key key2(key1); - EXPECT_EQ(KEY1_AS_STRING, key1.ToString()); -} - -TEST_P(KeyTestWithKeyParam, IsEqualAfterAssignment1) { - Key key2 = Key::FromString(KEY2_AS_STRING); - EXPECT_NE(GetParam(), key2); - key2 = GetParam(); - EXPECT_EQ(GetParam(), key2); -} - -TEST_F(KeyTest, AssignmentDoesntChangeSource) { - Key key1 = Key::FromString(KEY1_AS_STRING); - Key key2 = Key::FromString(KEY2_AS_STRING); - key2 = key1; - EXPECT_EQ(KEY1_AS_STRING, key1.ToString()); -} - -// This tests that a Key object is very lightweight -// (we will often pass keys around) -TEST_F(KeyTest, KeyIsLightweightObject) { - EXPECT_EQ(Key::BINARY_LENGTH, sizeof(Key)); -} diff --git a/utils/BlockStoreUtils.cpp b/utils/BlockStoreUtils.cpp index e6c33e2c..2b20cdef 100644 --- a/utils/BlockStoreUtils.cpp +++ b/utils/BlockStoreUtils.cpp @@ -1,10 +1,11 @@ #include "../interface/BlockStore.h" #include "BlockStoreUtils.h" -#include "Data.h" +#include #include #include using std::unique_ptr; +using cpputils::Data; namespace blockstore { namespace utils { diff --git a/utils/Data.cpp b/utils/Data.cpp deleted file mode 100644 index 4895f727..00000000 --- a/utils/Data.cpp +++ /dev/null @@ -1,100 +0,0 @@ -#include "Data.h" -#include "FileDoesntExistException.h" -#include -#include - -using std::istream; -using std::ofstream; -using std::ifstream; -using std::ios; -using std::unique_ptr; -using std::make_unique; - -namespace bf = boost::filesystem; - -namespace blockstore { - -Data::Data(size_t size) -: _size(size), _data(std::malloc(size)) { - if (nullptr == _data) { - throw std::bad_alloc(); - } -} - -Data::Data(Data &&rhs) -: _size(rhs._size), _data(rhs._data) { - // Make rhs invalid, so the memory doesn't get freed in its destructor. - rhs._data = nullptr; -} - -Data::~Data() { - std::free(_data); - _data = nullptr; -} - -Data Data::copy() const { - Data copy(_size); - std::memcpy(copy._data, _data, _size); - return copy; -} - -void *Data::data() { - return const_cast(const_cast(this)->data()); -} - -const void *Data::data() const { - return _data; -} - -size_t Data::size() const { - return _size; -} - -Data &Data::FillWithZeroes() { - std::memset(_data, 0, _size); - return *this; -} - -void Data::StoreToFile(const bf::path &filepath) const { - ofstream file(filepath.c_str(), ios::binary | ios::trunc); - file.write((const char*)_data, _size); -} - -Data Data::LoadFromFile(const bf::path &filepath) { - ifstream file(filepath.c_str(), ios::binary); - _assertFileExists(file, filepath); - size_t size = _getStreamSize(file); - - Data result(size); - result._readFromStream(file); - return result; -} - -void Data::_assertFileExists(const ifstream &file, const bf::path &filepath) { - if (!file.good()) { - throw FileDoesntExistException(filepath); - } -} - -size_t Data::_getStreamSize(istream &stream) { - auto current_pos = stream.tellg(); - - //Retrieve length - stream.seekg(0, stream.end); - auto endpos = stream.tellg(); - - //Restore old position - stream.seekg(current_pos, stream.beg); - - return endpos - current_pos; -} - -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()); -} - -} diff --git a/utils/Data.h b/utils/Data.h deleted file mode 100644 index 54d5ccc7..00000000 --- a/utils/Data.h +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once -#ifndef BLOCKSTORE_IMPLEMENTATIONS_ONDISK_DATA_H_ -#define BLOCKSTORE_IMPLEMENTATIONS_ONDISK_DATA_H_ - -#include -#include -#include -#include - -namespace blockstore { - -class Data { -public: - explicit Data(size_t size); - Data(Data &&rhs); // move constructor - virtual ~Data(); - - Data copy() const; - - void *data(); - const void *data() const; - - size_t size() const; - - Data &FillWithZeroes(); - - void StoreToFile(const boost::filesystem::path &filepath) const; - static Data LoadFromFile(const boost::filesystem::path &filepath); - -private: - size_t _size; - void *_data; - - static void _assertFileExists(const std::ifstream &file, const boost::filesystem::path &filepath); - static size_t _getStreamSize(std::istream &stream); - void _readFromStream(std::istream &stream); - - DISALLOW_COPY_AND_ASSIGN(Data); -}; - -//TODO Test operator== -bool operator==(const Data &lhs, const Data &rhs); - -} - -#endif diff --git a/utils/FileDoesntExistException.h b/utils/FileDoesntExistException.h index d4c0a521..9ae623db 100644 --- a/utils/FileDoesntExistException.h +++ b/utils/FileDoesntExistException.h @@ -10,7 +10,7 @@ namespace blockstore { class FileDoesntExistException: public std::runtime_error { public: - FileDoesntExistException(const boost::filesystem::path &filepath); + explicit FileDoesntExistException(const boost::filesystem::path &filepath); virtual ~FileDoesntExistException(); }; diff --git a/utils/FixedSizeData.h b/utils/FixedSizeData.h deleted file mode 100644 index f7b70f5c..00000000 --- a/utils/FixedSizeData.h +++ /dev/null @@ -1,112 +0,0 @@ -#pragma once -#ifndef BLOCKSTORE_UTILS_data_H_ -#define BLOCKSTORE_UTILS_data_H_ - -#include -#include -#include -#include - -namespace blockstore { - -template -class FixedSizeData { -public: - //Non-virtual destructor because we want objects to be small - ~FixedSizeData() {} - - static constexpr unsigned int BINARY_LENGTH = SIZE; - static constexpr unsigned int STRING_LENGTH = 2 * BINARY_LENGTH; // Hex encoding - - static FixedSizeData CreateRandom(); - - static FixedSizeData FromString(const std::string &data); - std::string ToString() const; - - static FixedSizeData FromBinary(const void *source); - void ToBinary(void *target) const; - - const unsigned char *data() const; - -private: - FixedSizeData() {} - static CryptoPP::AutoSeededRandomPool &RandomPool(); - - unsigned char _data[BINARY_LENGTH]; -}; - -template bool operator==(const FixedSizeData &lhs, const FixedSizeData &rhs); -template bool operator!=(const FixedSizeData &lhs, const FixedSizeData &rhs); - -// ----- Implementation ----- - -template constexpr unsigned int FixedSizeData::BINARY_LENGTH; -template constexpr unsigned int FixedSizeData::STRING_LENGTH; - -template -CryptoPP::AutoSeededRandomPool &FixedSizeData::RandomPool() { - static CryptoPP::AutoSeededRandomPool singleton; - return singleton; -} - -template -FixedSizeData FixedSizeData::CreateRandom() { - FixedSizeData result; - RandomPool().GenerateBlock(result._data, BINARY_LENGTH); - return result; -} - -template -FixedSizeData FixedSizeData::FromString(const std::string &data) { - assert(data.size() == STRING_LENGTH); - FixedSizeData result; - CryptoPP::StringSource(data, true, - new CryptoPP::HexDecoder( - new CryptoPP::ArraySink(result._data, BINARY_LENGTH) - ) - ); - return result; -} - -template -std::string FixedSizeData::ToString() const { - std::string result; - CryptoPP::ArraySource(_data, BINARY_LENGTH, true, - new CryptoPP::HexEncoder( - new CryptoPP::StringSink(result) - ) - ); - assert(result.size() == STRING_LENGTH); - return result; -} - -template -const unsigned char *FixedSizeData::data() const { - return _data; -} - -template -void FixedSizeData::ToBinary(void *target) const { - std::memcpy(target, _data, BINARY_LENGTH); -} - -template -FixedSizeData FixedSizeData::FromBinary(const void *source) { - FixedSizeData result; - std::memcpy(result._data, source, BINARY_LENGTH); - return result; -} - -template -bool operator==(const FixedSizeData &lhs, const FixedSizeData &rhs) { - return 0 == std::memcmp(lhs.data(), rhs.data(), FixedSizeData::BINARY_LENGTH); -} - -template -bool operator!=(const FixedSizeData &lhs, const FixedSizeData &rhs) { - return !operator==(lhs, rhs); -} - -} - -#endif diff --git a/utils/Key.h b/utils/Key.h index cd06ecda..e0b03f31 100644 --- a/utils/Key.h +++ b/utils/Key.h @@ -3,12 +3,12 @@ #define BLOCKSTORE_UTILS_KEY_H_ #include -#include "FixedSizeData.h" +#include namespace blockstore { // A key here is NOT a key for encryption, but a key as used in key->value mappings ("access handle for a block"). -using Key = FixedSizeData<16>; +using Key = cpputils::FixedSizeData<16>; }