Tools made in assistance of the Metacall Project
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

197 line
4.3 KiB

  1. #pragma once
  2. #include <cstddef>
  3. #include <array>
  4. #include <utility>
  5. #include <atomic>
  6. #include <new>
  7. #include <memory>
  8. #include <cassert>
  9. #include <optional>
  10. /**
  11. Pensé en un mundo sin memoria, sin tiempo; consideré la posibilidad de un lenguaje
  12. que ignorara los sustantivos, un lenguaje de verbos impersonales y de indeclinables
  13. epítetos. Así fueron muriendo los días y con los días los años, pero algo parecido
  14. a la felicidad ocurrió una mañana. Llovió, con lentitud poderosa.
  15. **/
  16. namespace mct20 {
  17. template<typename T>
  18. class accessor {
  19. public:
  20. accessor(const T* ptr, std::atomic<unsigned int>& incremented_ref)
  21. : pointer(ptr)
  22. , reference_cnt(incremented_ref)
  23. {
  24. assert(reference_cnt.load() != 0);
  25. }
  26. accessor(const accessor& a)
  27. : pointer(a.pointer)
  28. , reference_cnt(a.reference_cnt)
  29. {
  30. reference_cnt.fetch_add(1);
  31. }
  32. accessor(const accessor&& a)
  33. : pointer(a.pointer)
  34. , reference_cnt(a.reference_cnt)
  35. {
  36. reference_cnt.fetch_add(1);
  37. }
  38. operator const T&() {
  39. return *pointer;
  40. }
  41. ~accessor() {
  42. reference_cnt.fetch_sub(1);
  43. }
  44. private:
  45. const T* pointer;
  46. std::atomic<unsigned int>& reference_cnt;
  47. };
  48. namespace _details_ {
  49. #ifdef __cpp_lib_hardware_interference_size
  50. constexpr size_t predictable_padding = std::hardware_constructive_interference_size;
  51. #else
  52. // Wild guess, may be suboptimal or plain wrong
  53. constexpr size_t predictable_padding = 128;
  54. #endif
  55. size_t rotl(size_t a, uint8_t b) {
  56. b%=sizeof(size_t)*8;
  57. return a << b | a >> (sizeof(size_t)*8 - b);
  58. }
  59. template<size_t against>
  60. constexpr size_t alignment =
  61. (against%predictable_padding!=0)*predictable_padding
  62. + (against/predictable_padding)*predictable_padding;
  63. template<typename K, typename V>
  64. class bucket {
  65. public:
  66. bucket()
  67. : start{nullptr}
  68. {}
  69. void push(size_t hash, const K& key, const V& value) {
  70. auto t = new node{
  71. .contents = node_contents{
  72. .key{key},
  73. .ptr{new V{value}},
  74. .hash{hash},
  75. .references{1}
  76. }
  77. };
  78. t->contents.next.store(t);
  79. node* expect;
  80. do {
  81. expect = start.load();
  82. t->contents.next.store(expect);
  83. } while(
  84. !std::atomic_compare_exchange_strong(
  85. &start,
  86. &expect,
  87. t
  88. )
  89. );
  90. }
  91. std::optional<accessor<V>> get(const size_t hash, const K& key) {
  92. auto v = start.load();
  93. while(v) {
  94. if(v->contents.references.fetch_add(1)!=0)
  95. {
  96. if(v->contents.hash == hash) {
  97. if(v->contents.key == key) {
  98. return accessor<V>(
  99. v->contents.ptr,
  100. v->contents.references
  101. );
  102. } else {
  103. auto n = reinterpret_cast<node*>(v->contents.next.load());
  104. v->contents.references.fetch_sub(1);
  105. v = n;
  106. }
  107. } else {
  108. auto n = reinterpret_cast<node*>(v->contents.next.load());
  109. v->contents.references.fetch_sub(1);
  110. v = n;
  111. }
  112. }
  113. else
  114. {
  115. auto n = reinterpret_cast<node*>(v->contents.next.load());
  116. v->contents.references.fetch_sub(1);
  117. v = n;
  118. }
  119. }
  120. return std::nullopt;
  121. }
  122. struct node_contents{
  123. std::atomic<void*> next;
  124. const K key;
  125. const V* ptr;
  126. size_t hash;
  127. std::atomic<unsigned int> references;
  128. };
  129. using node = union {
  130. alignas(alignment<sizeof(node_contents)>) node_contents contents;
  131. };
  132. using node_ptr = std::atomic<node*>;
  133. node_ptr start;
  134. };
  135. }
  136. template<typename K, typename V, size_t bucket_count, typename hash = std::hash<K>>
  137. class lfhmap {
  138. using bucket = _details_::bucket<K, V>;
  139. public:
  140. std::optional<accessor<V>> get(const K& key) {
  141. auto l = hash{}(key);
  142. auto ret = buckets[l%bucket_count].get(l, key);
  143. if(ret) return ret;
  144. l = _details_::rotl(l, sizeof(size_t)*4);
  145. return buckets[l%bucket_count].get(l, key);
  146. }
  147. void set(const K& key, const V& value) {
  148. const auto l = hash{}(key);
  149. auto& ref = buckets[l%bucket_count];
  150. if(ref.start.load() == nullptr)
  151. {
  152. ref.push(l, key, value);
  153. return;
  154. }
  155. const auto l2 = _details_::rotl(l, sizeof(size_t)*4);
  156. auto& ref2 = buckets[l2%bucket_count];
  157. if(ref2.start.load() == nullptr)
  158. {
  159. ref2.push(l2, key, value);
  160. return;
  161. }
  162. if((l^l2)&1) {
  163. ref.push(l, key, value);
  164. } else {
  165. ref2.push(l2, key, value);
  166. }
  167. return;
  168. }
  169. lfhmap() {
  170. for(auto& a : buckets) {
  171. a.start = nullptr;
  172. }
  173. }
  174. private:
  175. std::array<bucket, bucket_count> buckets;
  176. };
  177. }