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>
|
2015-04-17 12:56:21 +02:00
|
|
|
#include <unordered_map>
|
2015-04-16 14:10:44 +02:00
|
|
|
#include <cassert>
|
2015-04-27 01:22:39 +02:00
|
|
|
#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 {
|
2015-04-16 14:10:44 +02:00
|
|
|
namespace caching {
|
2015-04-15 20:39:58 +02:00
|
|
|
|
2015-10-22 17:41:43 +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:
|
2015-04-27 01:22:39 +02:00
|
|
|
QueueMap(): _entries(), _sentinel(&_sentinel, &_sentinel) {
|
|
|
|
}
|
2015-11-27 14:06:40 +01:00
|
|
|
~QueueMap() {
|
2015-04-27 01:22:39 +02:00
|
|
|
for (auto &entry : _entries) {
|
|
|
|
entry.second.release();
|
|
|
|
}
|
2015-04-15 20:39:58 +02:00
|
|
|
}
|
|
|
|
|
2015-04-27 01:22:39 +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");
|
|
|
|
}
|
2015-04-27 01:22:39 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2015-04-27 01:22:39 +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()) {
|
2015-04-27 01:22:39 +02:00
|
|
|
return boost::none;
|
2015-04-15 20:39:58 +02:00
|
|
|
}
|
2015-04-27 01:22:39 +02:00
|
|
|
_removeFromQueue(found->second);
|
|
|
|
auto value = found->second.release();
|
2015-04-15 20:39:58 +02:00
|
|
|
_entries.erase(found);
|
2015-04-27 01:22:39 +02:00
|
|
|
return std::move(value);
|
2015-04-15 20:39:58 +02:00
|
|
|
}
|
|
|
|
|
2015-04-27 01:22:39 +02:00
|
|
|
boost::optional<Value> pop() {
|
|
|
|
if(_sentinel.next == &_sentinel) {
|
|
|
|
return boost::none;
|
|
|
|
}
|
2015-04-17 12:59:40 +02:00
|
|
|
return pop(*_sentinel.next->key);
|
2015-04-15 20:39:58 +02:00
|
|
|
}
|
|
|
|
|
2015-10-01 13:51:01 +02:00
|
|
|
boost::optional<const Key &> peekKey() {
|
|
|
|
if(_sentinel.next == &_sentinel) {
|
|
|
|
return boost::none;
|
|
|
|
}
|
|
|
|
return *_sentinel.next->key;
|
|
|
|
}
|
|
|
|
|
2015-04-27 10:35:01 +02:00
|
|
|
boost::optional<const Value &> peek() {
|
|
|
|
if(_sentinel.next == &_sentinel) {
|
|
|
|
return boost::none;
|
|
|
|
}
|
2015-04-27 01:22:39 +02:00
|
|
|
return _sentinel.next->value();
|
2015-04-18 16:50:19 +02:00
|
|
|
}
|
|
|
|
|
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 {
|
2015-04-27 01:22:39 +02:00
|
|
|
public:
|
2015-11-26 19:29:16 +01:00
|
|
|
Entry(Entry *prev_, Entry *next_): prev(prev_), next(next_), key(nullptr), __value() {
|
2015-04-27 01:22:39 +02:00
|
|
|
}
|
|
|
|
void init(const Key *key_, Value value_) {
|
|
|
|
key = key_;
|
2015-11-26 19:29:16 +01:00
|
|
|
new(__value) Value(std::move(value_));
|
2015-04-27 01:22:39 +02:00
|
|
|
}
|
|
|
|
Value release() {
|
2015-11-26 19:29:16 +01:00
|
|
|
Value value = std::move(*_value());
|
|
|
|
_value()->~Value();
|
2015-04-27 01:22:39 +02:00
|
|
|
return value;
|
|
|
|
}
|
|
|
|
const Value &value() {
|
2015-11-26 19:29:16 +01:00
|
|
|
return *_value();
|
2015-04-17 12:59:40 +02:00
|
|
|
}
|
2015-04-15 20:39:58 +02:00
|
|
|
Entry *prev;
|
|
|
|
Entry *next;
|
2015-04-27 01:22:39 +02:00
|
|
|
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)];
|
2015-04-27 01:22:39 +02:00
|
|
|
DISALLOW_COPY_AND_ASSIGN(Entry);
|
2015-04-15 20:39:58 +02:00
|
|
|
};
|
|
|
|
|
2015-04-27 01:22:39 +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
|
|
|
}
|
|
|
|
|
2015-04-27 01:22:39 +02:00
|
|
|
std::unordered_map<Key, Entry> _entries;
|
2015-04-15 20:39:58 +02:00
|
|
|
Entry _sentinel;
|
2015-04-27 01:22:39 +02:00
|
|
|
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(QueueMap);
|
2015-04-15 20:39:58 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|