libcryfs/implementations/caching/cache/QueueMap.h

122 lines
3.3 KiB
C
Raw Normal View History

2015-04-15 20:39:58 +02:00
#pragma once
2015-10-15 13:09:21 +02:00
#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_CACHING_CACHE_QUEUEMAP_H_
#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_CACHING_CACHE_QUEUEMAP_H_
2015-04-15 20:39:58 +02:00
#include <memory>
#include <unordered_map>
#include <cassert>
#include <boost/optional.hpp>
#include <messmer/cpp-utils/macros.h>
2015-10-08 18:05:09 +02:00
#include <messmer/cpp-utils/assert/assert.h>
2015-04-15 20:39:58 +02:00
namespace blockstore {
namespace caching {
2015-04-15 20:39:58 +02:00
//TODO FreeList for performance
//TODO Single linked list with pointer to last element (for insertion) should be enough for a queue. No double linked list needed.
// But then, popping arbitrary elements needs to be rewritten so that _removeFromQueue() is _removeSuccessorFromQueue()
// and the map doesn't store the element itself, but its predecessor. That is, popping might be a bit slower. Test with experiments!
2015-04-27 10:47:33 +02:00
// A class that is a queue and a map at the same time. We could also see it as an addressable queue.
2015-04-15 20:39:58 +02:00
template<class Key, class Value>
2015-11-26 19:29:16 +01:00
class QueueMap final {
2015-04-15 20:39:58 +02:00
public:
QueueMap(): _entries(), _sentinel(&_sentinel, &_sentinel) {
}
~QueueMap() {
for (auto &entry : _entries) {
entry.second.release();
}
2015-04-15 20:39:58 +02:00
}
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));
2015-10-15 04:49:31 +02:00
if (!newEntry.second) {
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;
2015-04-15 20:39:58 +02:00
}
boost::optional<Value> pop(const Key &key) {
2015-04-15 20:39:58 +02:00
auto found = _entries.find(key);
if (found == _entries.end()) {
return boost::none;
2015-04-15 20:39:58 +02:00
}
_removeFromQueue(found->second);
auto value = found->second.release();
2015-04-15 20:39:58 +02:00
_entries.erase(found);
return std::move(value);
2015-04-15 20:39:58 +02:00
}
boost::optional<Value> pop() {
if(_sentinel.next == &_sentinel) {
return boost::none;
}
return pop(*_sentinel.next->key);
2015-04-15 20:39:58 +02:00
}
boost::optional<const Key &> peekKey() {
if(_sentinel.next == &_sentinel) {
return boost::none;
}
return *_sentinel.next->key;
}
boost::optional<const Value &> peek() {
if(_sentinel.next == &_sentinel) {
return boost::none;
}
return _sentinel.next->value();
}
2015-10-05 16:51:36 +02:00
uint32_t size() const {
2015-04-15 20:39:58 +02:00
return _entries.size();
}
private:
2015-11-26 19:29:16 +01:00
class Entry final {
public:
2015-11-26 19:29:16 +01:00
Entry(Entry *prev_, Entry *next_): prev(prev_), next(next_), key(nullptr), __value() {
}
void init(const Key *key_, Value value_) {
key = key_;
2015-11-26 19:29:16 +01:00
new(__value) Value(std::move(value_));
}
Value release() {
2015-11-26 19:29:16 +01:00
Value value = std::move(*_value());
_value()->~Value();
return value;
}
const Value &value() {
2015-11-26 19:29:16 +01:00
return *_value();
}
2015-04-15 20:39:58 +02:00
Entry *prev;
Entry *next;
const Key *key;
private:
2015-11-26 19:29:16 +01:00
Value *_value() {
return reinterpret_cast<Value*>(__value);
}
alignas(Value) char __value[sizeof(Value)];
DISALLOW_COPY_AND_ASSIGN(Entry);
2015-04-15 20:39:58 +02:00
};
void _removeFromQueue(const Entry &entry) {
entry.prev->next = entry.next;
entry.next->prev = entry.prev;
2015-04-15 20:39:58 +02:00
}
std::unordered_map<Key, Entry> _entries;
2015-04-15 20:39:58 +02:00
Entry _sentinel;
DISALLOW_COPY_AND_ASSIGN(QueueMap);
2015-04-15 20:39:58 +02:00
};
}
}
#endif