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.
 
 
 

198 lignes
4.3 KiB

#pragma once
#include <cstddef>
#include <array>
#include <utility>
#include <atomic>
#include <new>
#include <memory>
#include <cassert>
#include <optional>
/**
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 {
public:
bucket()
: start{nullptr}
{}
void push(size_t hash, const K& key, const V& value) {
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) {
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;
}
lfhmap() {
for(auto& a : buckets) {
a.start = nullptr;
}
}
private:
std::array<bucket, bucket_count> buckets;
};
}