|
|
@ -0,0 +1,198 @@ |
|
|
|
|
|
#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; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
} |