Small fixes not done yet, first the whole DB interface has to be finished

This commit is contained in:
Marko Budiselic
2016-08-14 13:45:47 +01:00
parent 6d8fccde8f
commit a9419b559d
6 changed files with 16 additions and 6 deletions

View File

@@ -0,0 +1,34 @@
#pragma once
#include <unordered_map>
#include "threading/sync/lockable.hpp"
#include "threading/sync/spinlock.hpp"
namespace lockfree
{
template <class K, class V>
class HashMap : Lockable<SpinLock>
{
public:
V at(const K& key)
{
auto guard = acquire_unique();
return hashmap[key];
}
void put(const K& key, const K& value)
{
auto guard = acquire_unique();
hashmap[key] = value;
}
private:
std::unordered_map<K, V> hashmap;
};
}

View File

@@ -0,0 +1,283 @@
#pragma once
#include "utils/crtp.hpp"
#include "utils/option_ptr.hpp"
#include <cstring>
#include <functional>
// RobinHood base.
// Entrys are POINTERS alligned to 8B.
// Entrys must know thers key.
// D must have method K& get_key()
// K must be comparable with ==.
template <class K, class D, size_t init_size_pow2 = 2>
class RhBase
{
protected:
class Combined
{
public:
Combined() : data(0) {}
Combined(D *data, size_t off)
{
// assert((((size_t)data) & 0x7) == 0 && off < 8);
this->data = ((size_t)data) | off;
}
bool valid() const { return data != 0; }
size_t off() const { return data & 0x7; }
void decrement_off_unsafe() { data--; }
bool decrement_off()
{
if (off() > 0) {
data--;
return true;
}
return false;
}
bool increment_off()
{
if (off() < 7) {
data++;
return true;
}
return false;
}
D *ptr() const { return (D *)(data & (~(0x7))); }
bool equal(const K &key, size_t off)
{
return this->off() == off && key == ptr()->get_key();
}
friend bool operator==(const Combined &a, const Combined &b)
{
return a.off() == b.off() &&
a.ptr()->get_key() == b.ptr()->get_key();
}
friend bool operator!=(const Combined &a, const Combined &b)
{
return !(a == b);
}
private:
size_t data;
};
template <class It>
class IteratorBase : public Crtp<It>
{
protected:
IteratorBase() : map(nullptr) { advanced = index = ~((size_t)0); }
IteratorBase(const RhBase *map) : map(map)
{
index = 0;
while (index < map->capacity && !map->array[index].valid()) {
index++;
}
if (index == map->capacity) {
map = nullptr;
advanced = index = ~((size_t)0);
} else {
advanced = index;
}
}
IteratorBase(const RhBase *map, size_t start)
: map(map), index(start), advanced(0)
{
}
const RhBase *map;
size_t advanced;
size_t index;
public:
IteratorBase(const IteratorBase &) = default;
IteratorBase(IteratorBase &&) = default;
D *operator*()
{
assert(index < map->capacity && map->array[index].valid());
return map->array[index].ptr();
}
D *operator->()
{
assert(index < map->capacity && map->array[index].valid());
return map->array[index].ptr();
}
It &operator++()
{
assert(index < map->capacity && map->array[index].valid());
auto mask = map->mask();
do {
advanced++;
if (advanced >= map->capacity) {
map = nullptr;
advanced = index = ~((size_t)0);
break;
}
index = (index + 1) & mask;
} while (!map->array[index].valid());
return this->derived();
}
It &operator++(int) { return operator++(); }
friend bool operator==(const It &a, const It &b)
{
return a.index == b.index && a.map == b.map;
}
friend bool operator!=(const It &a, const It &b) { return !(a == b); }
};
public:
class ConstIterator : public IteratorBase<ConstIterator>
{
friend class RhBase;
protected:
ConstIterator(const RhBase *map) : IteratorBase<ConstIterator>(map) {}
ConstIterator(const RhBase *map, size_t index)
: IteratorBase<ConstIterator>(map, index)
{
}
public:
ConstIterator() = default;
ConstIterator(const ConstIterator &) = default;
const D *operator->()
{
return IteratorBase<ConstIterator>::operator->();
}
const D *operator*()
{
return IteratorBase<ConstIterator>::operator*();
}
};
class Iterator : public IteratorBase<Iterator>
{
friend class RhBase;
protected:
Iterator(const RhBase *map) : IteratorBase<Iterator>(map) {}
Iterator(const RhBase *map, size_t index)
: IteratorBase<Iterator>(map, index)
{
}
public:
Iterator() = default;
Iterator(const Iterator &) = default;
};
RhBase() {}
RhBase(const RhBase &other)
{
capacity = other.capacity;
count = other.count;
if (capacity > 0) {
size_t bytes = sizeof(Combined) * capacity;
array = (Combined *)malloc(bytes);
memcpy(array, other.array, bytes);
} else {
array = nullptr;
}
}
~RhBase() { this->clear(); }
Iterator begin() { return Iterator(this); }
ConstIterator begin() const { return ConstIterator(this); }
ConstIterator cbegin() const { return ConstIterator(this); }
Iterator end() { return Iterator(); }
ConstIterator end() const { return ConstIterator(); }
ConstIterator cend() const { return ConstIterator(); }
protected:
void init_array(size_t size)
{
size_t bytes = sizeof(Combined) * size;
array = (Combined *)malloc(bytes);
std::memset(array, 0, bytes);
capacity = size;
}
// True if before array has some values.
// Before array has to be released also.
bool increase_size()
{
if (capacity == 0) {
// assert(array == nullptr && count == 0);
size_t new_size = 1 << init_size_pow2;
init_array(new_size);
return false;
}
size_t new_size = capacity * 2;
init_array(new_size);
count = 0;
return true;
}
Iterator create_it(size_t index) { return Iterator(this, index); }
public:
void clear()
{
free(array);
array = nullptr;
capacity = 0;
count = 0;
}
size_t size() const { return count; }
protected:
size_t before_index(size_t now, size_t mask)
{
return (now - 1) & mask; // THIS IS VALID
}
size_t index(const K &key, size_t mask) const
{
return hash(std::hash<K>()(key)) & mask;
}
// This is rather expensive but offers good distribution.
size_t hash(size_t x) const
{
x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb);
x = x ^ (x >> 31);
return x;
}
size_t mask() const { return capacity - 1; }
Combined *array = nullptr;
size_t capacity = 0;
size_t count = 0;
friend class IteratorBase<Iterator>;
friend class IteratorBase<ConstIterator>;
};

View File

@@ -0,0 +1,160 @@
#include <functional>
#include "utils/crtp.hpp"
#include "data_structures/map/rh_common.hpp"
#include "utils/option_ptr.hpp"
// HashMap with RobinHood collision resolution policy.
// Single threaded.
// Entrys are saved as pointers alligned to 8B.
// Entrys must know thers key.
// D must have method const K & get_key()
// K must be comparable with ==.
// HashMap behaves as if it isn't owner of entrys.
template <class K, class D, size_t init_size_pow2 = 2>
class RhHashMap : public RhBase<K, D, init_size_pow2>
{
typedef RhBase<K, D, init_size_pow2> base;
using base::array;
using base::index;
using base::capacity;
using base::count;
using typename base::Combined;
void increase_size()
{
size_t old_size = capacity;
auto a = array;
if (base::increase_size()) {
for (int i = 0; i < old_size; i++) {
if (a[i].valid()) {
insert(a[i].ptr());
}
}
}
free(a);
}
public:
using base::RhBase;
bool contains(const K &key) { return find(key).is_present(); }
OptionPtr<D> find(const K key)
{
size_t mask = this->mask();
size_t now = index(key, mask);
size_t off = 0;
size_t border = 8 <= capacity ? 8 : capacity;
while (off < border) {
Combined other = array[now];
if (other.valid()) {
auto other_off = other.off();
if (other_off == off && key == other.ptr()->get_key()) {
return OptionPtr<D>(other.ptr());
} else if (other_off < off) { // Other is rich
break;
} // Else other has equal or greater offset, so he is poor.
} else {
break;
}
off++;
now = (now + 1) & mask;
}
return OptionPtr<D>();
}
// Inserts element. Returns true if element wasn't in the map.
bool insert(D *data)
{
if (count < capacity) {
size_t mask = this->mask();
auto key = std::ref(data->get_key());
size_t now = index(key, mask);
size_t off = 0;
size_t border = 8 <= capacity ? 8 : capacity;
while (off < border) {
Combined other = array[now];
if (other.valid()) {
auto other_off = other.off();
if (other_off == off && key == other.ptr()->get_key()) {
return false;
} else if (other_off < off) { // Other is rich
array[now] = Combined(data, off);
while (other.increment_off()) {
now = (now + 1) & mask;
auto tmp = array[now];
array[now] = other;
other = tmp;
if (!other.valid()) {
count++;
return true;
}
}
data = other.ptr();
break; // Cant insert removed element
} // Else other has equal or greater offset, so he is poor.
} else {
array[now] = Combined(data, off);
count++;
return true;
}
off++;
now = (now + 1) & mask;
}
}
increase_size();
return insert(data);
}
// Removes element. Returns removed element if it existed.
OptionPtr<D> remove(const K &key)
{
size_t mask = this->mask();
size_t now = index(key, mask);
size_t off = 0;
size_t border = 8 <= capacity ? 8 : capacity;
while (off < border) {
Combined other = array[now];
if (other.valid()) {
auto other_off = other.off();
auto other_ptr = other.ptr();
if (other_off == off &&
key == other_ptr->get_key()) { // Found it
auto before = now;
do {
// This is alright even for off=0 on found element
// because it wont be seen.
other.decrement_off_unsafe();
array[before] = other;
before = now;
now = (now + 1) & mask;
other = array[now];
} while (other.valid() && other.off() > 0);
array[before] = Combined();
count--;
return OptionPtr<D>(other_ptr);
} else if (other_off < off) { // Other is rich
break;
} // Else other has equal or greater offset, so he is poor.
} else {
break;
}
off++;
now = (now + 1) & mask;
}
return OptionPtr<D>();
}
};

View File

@@ -0,0 +1,348 @@
#pragma mark
#include <cstring>
#include <functional>
#include "utils/crtp.hpp"
#include "utils/likely.hpp"
#include "utils/option_ptr.hpp"
#include "data_structures/map/rh_common.hpp"
// HashMultiMap with RobinHood collision resolution policy.
// Single threaded.
// Entrys are POINTERS alligned to 8B.
// Entrys must know thers key.
// D must have method K& get_key()
// K must be comparable with ==.
// HashMap behaves as if it isn't owner of entrys.
template <class K, class D, size_t init_size_pow2 = 2>
class RhHashMultiMap : public RhBase<K, D, init_size_pow2>
{
typedef RhBase<K, D, init_size_pow2> base;
using base::array;
using base::index;
using base::capacity;
using base::count;
using typename base::Combined;
using base::before_index;
using base::create_it;
void increase_size()
{
size_t old_size = capacity;
auto a = array;
if (base::increase_size()) {
for (int i = 0; i < old_size; i++) {
if (a[i].valid()) {
add(a[i].ptr());
}
}
}
free(a);
}
public:
using base::RhBase;
using base::end;
using typename base::ConstIterator;
using typename base::Iterator;
bool contains(const K &key) { return find(key) != end(); }
Iterator find(const K &key_in)
{
if (count > 0) {
auto key = std::ref(key_in);
size_t mask = this->mask();
size_t now = index(key, mask);
size_t off = 0;
size_t border = 8 <= capacity ? 8 : capacity;
Combined other = array[now];
while (other.valid() && off < border) {
auto other_off = other.off();
if (other_off == off && key == other.ptr()->get_key()) {
return create_it(now);
} else if (other_off < off) { // Other is rich
break;
} else { // Else other has equal or greater off, so he is poor.
if (UNLIKELY(skip(now, other, other_off, mask))) {
break;
}
off++;
}
}
}
return end();
}
// Inserts element.
void add(D *data) { add(data->get_key(), data); }
// Inserts element with the given key.
void add(const K &key_in, D *data)
{
assert(key_in == data->get_key());
if (count < capacity) {
auto key = std::ref(key_in);
size_t mask = this->mask();
size_t now = index(key, mask);
size_t start = now;
size_t off = 0;
size_t border = 8 <= capacity ? 8 : capacity;
Combined other = array[now];
while (off < border) {
if (other.valid()) {
const size_t other_off = other.off();
bool multi = false;
if (other_off == off && other.ptr()->get_key() == key) {
// Found the same
do {
now = (now + 1) & mask;
other = array[now];
if (!other.valid()) {
set(now, data, off);
return;
}
} while (other.equal(key, off));
multi = true;
} else if (other_off > off ||
other_poor(other, mask, start,
now)) { // Else other has equal or
// greater off, so he is poor.
skip(now, other, other_off, mask); // TRUE IS IMPOSSIBLE
off++;
continue;
}
array[now] = Combined(data, off);
auto start_insert = now;
while (is_off_adjusted(other, mask, start_insert, now,
multi) ||
other.increment_off()) {
now = (now + 1) & mask;
auto tmp = array[now];
array[now] = other;
other = tmp;
if (!other.valid()) {
count++;
return;
}
}
data = other.ptr();
break; // Cant insert removed element
} else {
set(now, data, off);
return;
}
}
}
increase_size();
add(data);
}
// Removes element equal by key and value. Returns true if it existed.
bool remove(D *data)
{
if (count > 0) {
auto key = std::ref(data->get_key());
size_t mask = this->mask();
size_t now = index(key, mask);
size_t off = 0;
size_t border = 8 <= capacity ? 8 : capacity;
Combined other = array[now];
while (other.valid() && off < border) {
const size_t other_off = other.off();
if (other_off == off && key == other.ptr()->get_key()) {
auto founded = capacity;
size_t started = now;
bool multi = false;
do {
if (other.ptr() == data) {
founded = now;
}
now = (now + 1) & mask;
other = array[now];
if (!other.valid() || UNLIKELY(started == now)) {
// Reason is possibility of map full of same values.
break;
}
} while (other.equal(key, off) && (multi = true));
// multi = true is correct
if (founded == capacity) {
return false;
}
auto bef = before_index(now, mask);
array[founded] = array[bef];
auto start_rem = bef;
while (other.valid() &&
(is_off_adjusted_rem(other, mask, start_rem, bef,
now, multi) ||
other.decrement_off())) {
array[bef] = other;
bef = now;
now = (now + 1) & mask;
other = array[now];
}
array[bef] = Combined();
count--;
return true;
} else if (other_off < off) { // Other is rich
break;
} else { // Else other has equal or greater off, so he is poor.
if (UNLIKELY(skip(now, other, other_off, mask))) {
break;
}
off++;
}
}
}
return false;
}
private:
// Skips same key valus as other. true if whole map is full of same key
// values.
bool skip(size_t &now, Combined &other, size_t other_off, size_t mask)
{
auto other_key = other.ptr()->get_key();
size_t start = now;
do {
now = (now + 1) & mask;
other = array[now];
if (UNLIKELY(start == now)) { // Reason is possibility of map
// full of same values.
return true;
}
} while (other.valid() && other.equal(other_key, other_off));
return false;
}
void set(size_t now, D *data, size_t off)
{
array[now] = Combined(data, off);
count++;
}
// True if no adjusment is needed, false otherwise.
bool is_off_adjusted(Combined &com, size_t mask, size_t start, size_t now,
bool multi)
{
if (com.off() == 0) { // Must be adjusted
return false;
}
size_t cin = index(com.ptr()->get_key(), mask);
if (outside(start, now, cin)) { // Outside [start,now] interval
return multi;
}
auto a = array[cin];
auto b = array[(cin + 1) & mask];
return a == b;
// Check if different key has eneterd in to
// range of other.
}
bool other_poor(Combined other, size_t mask, size_t start, size_t now)
{
// If other index is smaller then he is poorer.
return outside_left_weak(start, now,
index(other.ptr()->get_key(), mask));
}
// True if no adjusment is needed, false otherwise.
bool is_off_adjusted_rem(Combined &com, size_t mask, size_t start,
size_t bef, size_t now, bool multi)
{
if (com.off() == 0) { // Must be adjusted
return false;
}
size_t cin = index(com.ptr()->get_key(), mask);
if (cin == bef) {
return false;
}
if (outside(start, now, cin)) {
return multi;
}
auto a = array[cin];
auto b = array[before_index(cin, mask)];
return b.valid() && a == b;
// Check if different key has eneterd in to
// range of other.
}
// True if p is uutside [start,end] interval
bool outside(size_t start, size_t end, size_t p)
{
return (start <= end && (p < start || p > end)) ||
(end < start && p < start && p > end);
}
// True if p is outside <start,end] interval
bool outside_left_weak(size_t start, size_t end, size_t p)
{
return (start <= end && (p <= start || p > end)) ||
(end < start && p <= start && p > end);
}
};
// Unnecessary
// // Removes element. Returns removed element if it existed. It doesn't
// // specify which element from same key group will be removed.
// OptionPtr<D> remove(const K &key_in)
// {
//
// if (count > 0) {
// auto key = std::ref(key_in);
// size_t mask = this->mask();
// size_t now = index(key, mask);
// size_t off = 0;
// size_t checked = 0;
// size_t border = 8 <= capacity ? 8 : capacity;
// Combined other = array[now];
// while (other.valid() && off < border) {
// auto other_off = other.off();
// bool multi = false;
// if (other_off == off && key == other.ptr()->get_key()) {
// do {
// now = (now + 1) & mask;
// other = array[now];
// if (!other.valid()) {
// break;
// }
// other_off = other.off();
// } while (other_off == off &&
// other.ptr()->get_key() == key &&
// (multi = true)); // multi = true is correct
//
// auto bef = before_index(now, mask);
// auto ret = OptionPtr<D>(array[bef].ptr());
//
// move_before(now, bef, other, mask, multi);
// return ret;
//
// } else if (other_off < off) { // Other is rich
// break;
//
// } else { // Else other has equal or greater off, so he is
// poor.
// if (UNLIKELY(skip(now, other, other_off, mask))) {
// break;
// }
// off++;
// }
// }
// }
//
// return OptionPtr<D>();
// }

View File

@@ -0,0 +1,20 @@
#pragma once
template <class T>
class OptionPtr
{
public:
OptionPtr() {}
OptionPtr(T *ptr) : ptr(ptr) {}
bool is_present() { return ptr != nullptr; }
T *get()
{
assert(is_present());
return ptr;
}
private:
T *ptr = nullptr;
};