hash_map.hpp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. #pragma once
  2. #include <assert.h>
  3. #include <bit>
  4. #include <map>
  5. #include <string>
  6. #include <type_traits>
  7. #include <utility>
  8. #include <vector>
  9. #include <stdint.h>
  10. #include <types/allocator.hpp>
  11. #include <types/cplusplus.hpp>
  12. #include <types/types.h>
  13. namespace types {
  14. // taken from linux
  15. constexpr uint32_t GOLDEN_RATIO_32 = 0x61C88647;
  16. constexpr uint64_t GOLDEN_RATIO_64 = 0x61C8864680B583EBull;
  17. using hash_t = std::size_t;
  18. static inline constexpr hash_t _hash32(uint32_t val)
  19. {
  20. return val * GOLDEN_RATIO_32;
  21. }
  22. static inline constexpr hash_t hash32(uint32_t val, std::size_t bits)
  23. {
  24. // higher bits are more random
  25. return _hash32(val) >> (8 * sizeof(hash_t) - bits);
  26. }
  27. static inline constexpr hash_t _hash64(uint64_t val)
  28. {
  29. return val * GOLDEN_RATIO_64;
  30. }
  31. static inline constexpr hash_t hash64(uint64_t val, std::size_t bits)
  32. {
  33. // higher bits are more random
  34. return _hash64(val) >> (8 * sizeof(hash_t) - bits);
  35. }
  36. template <typename T>
  37. constexpr bool is_c_string_v = std::is_same_v<std::decay_t<T>, char*>
  38. || std::is_same_v<std::decay_t<T>, const char*>;
  39. template <typename T,
  40. std::enable_if_t<std::is_convertible_v<T, uint64_t>, bool> = true>
  41. inline hash_t hash(T val, std::size_t bits)
  42. {
  43. return hash64(static_cast<uint64_t>(val), bits);
  44. }
  45. template <typename T,
  46. std::enable_if_t<std::is_pointer_v<T> && !is_c_string_v<T>, bool> = true>
  47. inline hash_t hash(T val, std::size_t bits)
  48. {
  49. return hash(std::bit_cast<ptr_t>(val), bits);
  50. }
  51. inline hash_t hash(const char* str, std::size_t bits)
  52. {
  53. constexpr uint32_t seed = 131;
  54. uint32_t hash = 0;
  55. while (*str)
  56. hash = hash * seed + (*str++);
  57. return hash32(hash, bits);
  58. };
  59. template <template <typename, typename, typename> typename String,
  60. typename Char, typename Traits, typename Allocator,
  61. std::enable_if_t<
  62. std::is_same_v<
  63. std::decay_t<String<Char, Traits, Allocator>>,
  64. std::basic_string<Char, Traits, Allocator>
  65. >, bool
  66. > = true>
  67. inline hash_t hash(const String<Char, Traits, Allocator>& str, std::size_t bits)
  68. {
  69. return hash(str.c_str(), bits);
  70. }
  71. template <typename Key, typename Value,
  72. typename Allocator = std::allocator<std::pair<const Key, Value> >,
  73. std::enable_if_t<std::is_convertible_v<hash_t, decltype(
  74. hash(std::declval<Key>(), std::declval<std::size_t>())
  75. )>, bool> = true>
  76. class hash_map {
  77. public:
  78. template <bool Const>
  79. class iterator;
  80. using key_type = std::add_const_t<Key>;
  81. using value_type = Value;
  82. using size_type = size_t;
  83. using difference_type = ssize_t;
  84. using iterator_type = iterator<false>;
  85. using const_iterator_type = iterator<true>;
  86. using bucket_type = std::map<key_type,
  87. value_type,std::less<key_type>, Allocator>;
  88. using bucket_array_type = std::vector<bucket_type, typename
  89. std::allocator_traits<Allocator>:: template rebind_alloc<bucket_type>>;
  90. static constexpr size_type INITIAL_BUCKETS_ALLOCATED = 64;
  91. public:
  92. template <bool Const>
  93. class iterator {
  94. public:
  95. using bucket_iterator = std::conditional_t<Const,
  96. typename bucket_type::const_iterator,
  97. typename bucket_type::iterator>;
  98. using _Value = typename bucket_iterator::value_type;
  99. using Pointer = typename bucket_iterator::pointer;
  100. using Reference = typename bucket_iterator::reference;
  101. using hash_map_pointer = std::conditional_t<Const,
  102. const hash_map*, hash_map*>;
  103. friend class hash_map;
  104. public:
  105. constexpr iterator(const iterator& iter) noexcept
  106. : n(iter.n), iter(iter.iter), hmap(iter.hmap)
  107. {
  108. }
  109. constexpr iterator(iterator&& iter) noexcept
  110. : n(std::exchange(iter.n, 0))
  111. , iter(std::move(iter.iter))
  112. , hmap(std::exchange(iter.hmap, nullptr))
  113. {
  114. }
  115. constexpr iterator& operator=(const iterator& iter)
  116. {
  117. n = iter.n;
  118. this->iter = iter.iter;
  119. hmap = iter.hmap;
  120. return *this;
  121. }
  122. explicit constexpr iterator(std::size_t n, bucket_iterator iter,
  123. hash_map_pointer hmap) noexcept
  124. : n(n), iter(iter), hmap(hmap)
  125. {
  126. }
  127. constexpr bool operator==(const iterator& iter) const noexcept
  128. {
  129. return (!*this && !iter) || (hmap == iter.hmap && n == iter.n && this->iter == iter.iter);
  130. }
  131. constexpr bool operator!=(const iterator& iter) const noexcept
  132. {
  133. return !(*this == iter);
  134. }
  135. constexpr iterator operator++()
  136. {
  137. assert((bool)*this);
  138. ++iter;
  139. while (iter == hmap->buckets[n].end()) {
  140. ++n;
  141. if (n < hmap->buckets.size())
  142. iter = hmap->buckets[n].begin();
  143. else
  144. break;
  145. }
  146. return *this;
  147. }
  148. constexpr iterator operator++(int)
  149. {
  150. iterator ret { *this };
  151. (void)this->operator++();
  152. return ret;
  153. }
  154. constexpr operator bool(void) const
  155. {
  156. return hmap && n < hmap->buckets.size() && !!iter;
  157. }
  158. constexpr Reference operator*(void) const noexcept
  159. {
  160. return *iter;
  161. }
  162. constexpr Pointer operator->(void) const noexcept
  163. {
  164. return &*iter;
  165. }
  166. constexpr operator const_iterator_type() const noexcept
  167. {
  168. return const_iterator_type(n, iter, hmap);
  169. }
  170. protected:
  171. std::size_t n;
  172. bucket_iterator iter;
  173. hash_map_pointer hmap;
  174. };
  175. private:
  176. bucket_array_type buckets;
  177. protected:
  178. constexpr uint32_t hash_length(void) const
  179. {
  180. switch (buckets.capacity()) {
  181. case 32:
  182. return 5;
  183. case 64:
  184. return 6;
  185. case 128:
  186. return 7;
  187. case 256:
  188. return 8;
  189. // TODO
  190. default:
  191. return 9;
  192. }
  193. }
  194. public:
  195. explicit constexpr hash_map(void)
  196. : buckets(INITIAL_BUCKETS_ALLOCATED) {}
  197. constexpr hash_map(const hash_map& v)
  198. : buckets(v.buckets) {}
  199. constexpr hash_map(hash_map&& v)
  200. : buckets(std::move(v.buckets)) {}
  201. constexpr ~hash_map()
  202. {
  203. buckets.clear();
  204. }
  205. template <typename... Args>
  206. constexpr void emplace(Args&&... args)
  207. {
  208. std::pair<Key, Value> to_insert{std::forward<Args>(args)...};
  209. auto hash_value = hash(to_insert.first, hash_length());
  210. buckets.at(hash_value).emplace(to_insert);
  211. }
  212. constexpr void remove(const_iterator_type iter)
  213. {
  214. auto& bucket = buckets[iter.n];
  215. bucket.erase(iter.iter);
  216. }
  217. constexpr void remove(iterator_type iter)
  218. {
  219. return remove((const_iterator_type)iter);
  220. }
  221. constexpr void remove(const key_type& key)
  222. {
  223. const_iterator_type iter = find(key);
  224. if (!iter)
  225. return;
  226. remove(iter);
  227. }
  228. constexpr iterator_type find(const key_type& key)
  229. {
  230. auto hash_value = hash(key, hash_length());
  231. auto& bucket = buckets.at(hash_value);
  232. for (auto iter = bucket.begin(); iter != bucket.end(); ++iter) {
  233. if (key == iter->first)
  234. return iterator_type(hash_value, iter, this);
  235. }
  236. return this->end();
  237. }
  238. constexpr const_iterator_type find(const key_type& key) const
  239. {
  240. auto hash_value = hash(key, hash_length());
  241. const auto& bucket = buckets.at(hash_value);
  242. for (auto iter = bucket.cbegin(); iter != bucket.cend(); ++iter) {
  243. if (key == iter->first)
  244. return const_iterator_type(hash_value, iter, this);
  245. }
  246. return this->cend();
  247. }
  248. constexpr void clear(void)
  249. {
  250. for (auto& bucket : buckets)
  251. bucket.clear();
  252. }
  253. constexpr const_iterator_type cend() const noexcept
  254. {
  255. return const_iterator_type(buckets.size(), buckets[0].end(), this);
  256. }
  257. constexpr const_iterator_type end() const noexcept
  258. {
  259. return cend();
  260. }
  261. constexpr iterator_type end() noexcept
  262. {
  263. return iterator_type(buckets.size(), buckets[0].end(), this);
  264. }
  265. constexpr const_iterator_type cbegin() const noexcept
  266. {
  267. for (std::size_t i = 0; i < buckets.size(); ++i) {
  268. if (buckets[i].empty())
  269. continue;
  270. return const_iterator_type(i, buckets[i].begin(), this);
  271. }
  272. return cend();
  273. }
  274. constexpr const_iterator_type begin() const noexcept
  275. {
  276. return cbegin();
  277. }
  278. constexpr iterator_type begin() noexcept
  279. {
  280. for (std::size_t i = 0; i < buckets.size(); ++i) {
  281. if (buckets[i].empty())
  282. continue;
  283. return iterator_type(i, buckets[i].begin(), this);
  284. }
  285. return end();
  286. }
  287. };
  288. } // namespace types