#pragma once #include #include #include #include #include #include #include #include #include #include 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 class accessor { public: accessor(const T* ptr, std::atomic& 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& 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 constexpr size_t alignment = (against%predictable_padding!=0)*predictable_padding + (against/predictable_padding)*predictable_padding; template class bucket { constexpr static uint32_t delete_mode = 65536; std::atomic 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(&(it->contents.next)); it = (node*)it->contents.next.load(); if(it == nullptr) return; } if(it->contents.key == key) { prev->store(reinterpret_cast(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(&(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> 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->contents.ptr, v->contents.references ); } else { auto n = reinterpret_cast(v->contents.next.load()); v->contents.references.fetch_sub(1); v = n; } } else { auto n = reinterpret_cast(v->contents.next.load()); v->contents.references.fetch_sub(1); v = n; } } else { auto n = reinterpret_cast(v->contents.next.load()); v->contents.references.fetch_sub(1); v = n; } } return std::nullopt; } struct node_contents{ std::atomic next; const K key; const V* ptr; size_t hash; std::atomic references; }; using node = union { alignas(alignment) node_contents contents; }; using node_ptr = std::atomic; node_ptr start; }; } template> class lfhmap { using bucket = _details_::bucket; public: std::optional> 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 buckets; }; }