|
|
- #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;
- };
-
- }
|