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