Tools made in assistance of the Metacall Project
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 

288 lignes
6.4 KiB

#pragma once
#include <cstddef>
#include <array>
#include <utility>
#include <atomic>
#include <new>
#include <memory>
#include <cassert>
#include <optional>
#include <chrono>
#include <thread>
using namespace std::chrono_literals;
/**
Pensé en un mundo sin memoria, sin tiempo; consideré la posibilidad de un lenguaje
que ignorara los sustantivos, un lenguaje de verbos impersonales y de indeclinables
epítetos. Así fueron muriendo los días y con los días los años, pero algo parecido
a la felicidad ocurrió una mañana. Llovió, con lentitud poderosa.
**/
namespace mct20 {
template<typename T>
class accessor {
public:
accessor(const T* ptr, std::atomic<unsigned int>& incremented_ref)
: pointer(ptr)
, reference_cnt(incremented_ref)
{
assert(reference_cnt.load() != 0);
}
accessor(const accessor& a)
: pointer(a.pointer)
, reference_cnt(a.reference_cnt)
{
reference_cnt.fetch_add(1);
}
accessor(const accessor&& a)
: pointer(a.pointer)
, reference_cnt(a.reference_cnt)
{
reference_cnt.fetch_add(1);
}
operator const T&() {
return *pointer;
}
~accessor() {
reference_cnt.fetch_sub(1);
}
private:
const T* pointer;
std::atomic<unsigned int>& reference_cnt;
};
namespace _details_ {
#ifdef __cpp_lib_hardware_interference_size
constexpr size_t predictable_padding = std::hardware_constructive_interference_size;
#else
// Wild guess, may be suboptimal or plain wrong
constexpr size_t predictable_padding = 128;
#endif
size_t rotl(size_t a, uint8_t b) {
b%=sizeof(size_t)*8;
return a << b | a >> (sizeof(size_t)*8 - b);
}
template<size_t against>
constexpr size_t alignment =
(against%predictable_padding!=0)*predictable_padding
+ (against/predictable_padding)*predictable_padding;
template<typename K, typename V>
class bucket {
constexpr static uint32_t delete_mode = 65536;
std::atomic<uint32_t> delete_lock;
void reader_lock() {
while(delete_lock.fetch_add(1) >= delete_mode) {
delete_lock.fetch_sub(1);
std::this_thread::yield();
}
return;
}
void writer_lock() {
while(delete_lock.fetch_add(delete_mode) >= delete_mode) {
delete_lock.fetch_sub(delete_mode);
std::this_thread::yield();
}
while(delete_lock.load() != delete_mode) {
}
return;
}
void reader_unlock() {
delete_lock.fetch_sub(1);
}
void writer_unlock() {
delete_lock.fetch_sub(delete_mode);
}
struct RGuard {
bucket& master;
RGuard(bucket& m)
: master(m)
{master.reader_lock();}
~RGuard()
{master.reader_unlock();}
};
struct WGuard {
bucket& master;
WGuard(bucket& m)
: master(m)
{master.writer_lock();}
~WGuard()
{master.writer_unlock();}
};
public:
bucket()
: start{nullptr}
{}
void remove(size_t hash, const K& key) {
WGuard _g(*this);
auto it = start.load();
auto prev = &start;
do{
if(it == nullptr) return;
while(it->contents.hash != hash)
{
prev = reinterpret_cast<node_ptr*>(&(it->contents.next));
it = (node*)it->contents.next.load();
if(it == nullptr) return;
}
if(it->contents.key == key) {
prev->store(reinterpret_cast<node*>(it->contents.next.load()));
it->contents.references.fetch_sub(1);
while(it->contents.references.load()!=0) {
std::this_thread::yield();
}
delete it->contents.ptr;
delete it;
return;
}
prev = reinterpret_cast<node_ptr*>(&(it->contents.next));
it = (node*)it->contents.next.load();
} while(true);
}
void push(size_t hash, const K& key, const V& value) {
RGuard _g(*this);
auto t = new node{
.contents = node_contents{
.key{key},
.ptr{new V{value}},
.hash{hash},
.references{1}
}
};
t->contents.next.store(t);
node* expect;
do {
expect = start.load();
t->contents.next.store(expect);
} while(
!std::atomic_compare_exchange_strong(
&start,
&expect,
t
)
);
}
std::optional<accessor<V>> get(const size_t hash, const K& key) {
RGuard _g(*this);
auto v = start.load();
while(v) {
if(v->contents.references.fetch_add(1)!=0)
{
if(v->contents.hash == hash) {
if(v->contents.key == key) {
return accessor<V>(
v->contents.ptr,
v->contents.references
);
} else {
auto n = reinterpret_cast<node*>(v->contents.next.load());
v->contents.references.fetch_sub(1);
v = n;
}
} else {
auto n = reinterpret_cast<node*>(v->contents.next.load());
v->contents.references.fetch_sub(1);
v = n;
}
}
else
{
auto n = reinterpret_cast<node*>(v->contents.next.load());
v->contents.references.fetch_sub(1);
v = n;
}
}
return std::nullopt;
}
struct node_contents{
std::atomic<void*> next;
const K key;
const V* ptr;
size_t hash;
std::atomic<unsigned int> references;
};
using node = union {
alignas(alignment<sizeof(node_contents)>) node_contents contents;
};
using node_ptr = std::atomic<node*>;
node_ptr start;
};
}
template<typename K, typename V, size_t bucket_count, typename hash = std::hash<K>>
class lfhmap {
using bucket = _details_::bucket<K, V>;
public:
std::optional<accessor<V>> get(const K& key) {
auto l = hash{}(key);
auto ret = buckets[l%bucket_count].get(l, key);
if(ret) return ret;
l = _details_::rotl(l, sizeof(size_t)*4);
return buckets[l%bucket_count].get(l, key);
}
void set(const K& key, const V& value) {
const auto l = hash{}(key);
auto& ref = buckets[l%bucket_count];
if(ref.start.load() == nullptr)
{
ref.push(l, key, value);
return;
}
const auto l2 = _details_::rotl(l, sizeof(size_t)*4);
auto& ref2 = buckets[l2%bucket_count];
if(ref2.start.load() == nullptr)
{
ref2.push(l2, key, value);
return;
}
if((l^l2)&1) {
ref.push(l, key, value);
} else {
ref2.push(l2, key, value);
}
return;
}
void remove(const K& key) {
const auto l = hash{}(key);
auto& ref = buckets[l%bucket_count];
ref.remove(l, key);
const auto l2 = _details_::rotl(l, sizeof(size_t)*4);
auto& ref2 = buckets[l2%bucket_count];
ref2.remove(l2, key);
return;
}
lfhmap() {
for(auto& a : buckets) {
a.start = nullptr;
}
}
private:
std::array<bucket, bucket_count> buckets;
};
}