#pragma once #include #include #include #include #include #include #include #include /** 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 { 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> 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->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; } lfhmap() { for(auto& a : buckets) { a.start = nullptr; } } private: std::array buckets; }; }