Tools made in assistance of the Metacall Project
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

197 lignes
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. }